]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/gssd/context.c
8f2f359e98e6f4638a25c794e1ea74a033fd4ecb
[nfs-utils.git] / utils / gssd / context.c
1 /*
2   Copyright (c) 2004 The Regents of the University of Michigan.
3   All rights reserved.
4
5   Redistribution and use in source and binary forms, with or without
6   modification, are permitted provided that the following conditions
7   are met:
8
9   1. Redistributions of source code must retain the above copyright
10      notice, this list of conditions and the following disclaimer.
11   2. Redistributions in binary form must reproduce the above copyright
12      notice, this list of conditions and the following disclaimer in the
13      documentation and/or other materials provided with the distribution.
14   3. Neither the name of the University nor the names of its
15      contributors may be used to endorse or promote products derived
16      from this software without specific prior written permission.
17
18   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
19   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include <stdio.h>
33 #include <syslog.h>
34 #include <string.h>
35 #ifdef HAVE_KRB5
36 #include <krb5.h>
37 #endif
38 #include <rpc/rpc.h>
39 #include <rpc/auth_gss.h>
40 #include "gss_util.h"
41 #include "gss_oids.h"
42 #include "err_util.h"
43 #include "context.h"
44
45 #ifdef HAVE_SPKM3_H
46 #include <spkm3.h>
47 #endif
48
49 /* spkm3 seems to actually want it this big, yipes. */
50 #define MAX_CTX_LEN 4096
51
52 #ifdef HAVE_KRB5                /* MIT Kerberos */
53
54 #ifdef HAVE_LUCID_CONTEXT_SUPPORT
55
56 /* Don't use the private structure, use the exported lucid structure */
57 #include <gssapi/gssapi.h>
58 #include <gssapi/gssapi_krb5.h>
59
60 #elif (KRB5_VERSION > 131)
61 /* XXX argggg, there's gotta be a better way than just duplicating this
62  * whole struct.  Unfortunately, this is in a "private" header file,
63  * so this is our best choice at this point :-/
64  *
65  * XXX Does this match the Heimdal definition?  */
66
67 typedef struct _krb5_gss_ctx_id_rec {
68    unsigned int initiate : 1;   /* nonzero if initiating, zero if accepting */
69    unsigned int established : 1;
70    unsigned int big_endian : 1;
71    unsigned int have_acceptor_subkey : 1;
72    unsigned int seed_init : 1;  /* XXX tested but never actually set */
73 #ifdef CFX_EXERCISE
74    unsigned int testing_unknown_tokid : 1; /* for testing only */
75 #endif
76    OM_uint32 gss_flags;
77    unsigned char seed[16];
78    krb5_principal here;
79    krb5_principal there;
80    krb5_keyblock *subkey;
81    int signalg;
82    size_t cksum_size;
83    int sealalg;
84    krb5_keyblock *enc;
85    krb5_keyblock *seq;
86    krb5_timestamp endtime;
87    krb5_flags krb_flags;
88    /* XXX these used to be signed.  the old spec is inspecific, and
89       the new spec specifies unsigned.  I don't believe that the change
90       affects the wire encoding. */
91    uint64_t seq_send;           /* gssint_uint64 */
92    uint64_t seq_recv;           /* gssint_uint64 */
93    void *seqstate;
94    krb5_auth_context auth_context;
95    gss_buffer_desc *mech_used;  /* gss_OID_desc */
96     /* Protocol spec revision
97        0 => RFC 1964 with 3DES and RC4 enhancements
98        1 => draft-ietf-krb-wg-gssapi-cfx-01
99        No others defined so far.  */
100    int proto;
101    krb5_cksumtype cksumtype;    /* for "main" subkey */
102    krb5_keyblock *acceptor_subkey; /* CFX only */
103    krb5_cksumtype acceptor_subkey_cksumtype;
104 #ifdef CFX_EXERCISE
105     gss_buffer_desc init_token;
106 #endif
107 } krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t;
108
109 #else   /* KRB5_VERSION */
110
111 typedef struct _krb5_gss_ctx_id_rec {
112         int initiate;
113         u_int32_t gss_flags;
114         int seed_init;
115         unsigned char seed[16];
116         krb5_principal here;
117         krb5_principal there;
118         krb5_keyblock *subkey;
119         int signalg;
120         int cksum_size;
121         int sealalg;
122         krb5_keyblock *enc;
123         krb5_keyblock *seq;
124         krb5_timestamp endtime;
125         krb5_flags krb_flags;
126         krb5_ui_4 seq_send;
127         krb5_ui_4 seq_recv;
128         void *seqstate;
129         int established;
130         int big_endian;
131         krb5_auth_context auth_context;
132         gss_buffer_desc *mech_used;
133         int nctypes;
134         krb5_cksumtype *ctypes;
135 } krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t;
136
137 #endif /* KRB5_VERSION */
138 #endif /* HAVE_KRB5 */
139
140 /* adapted from mit kerberos 5 ../lib/gssapi/mechglue/mglueP.h
141  * this is what gets passed around when the mechglue code is enabled : */
142 typedef struct gss_union_ctx_id_t {
143         gss_OID         mech_type;
144         gss_ctx_id_t    internal_ctx_id;
145 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
146
147 #ifdef HAVE_KRB5                /* MIT Kerberos */
148 #ifdef HAVE_LUCID_CONTEXT_SUPPORT /* Lucid context support */
149 static int
150 write_lucid_keyblock(char **p, char *end, gss_krb5_lucid_key_t *key)
151 {
152         gss_buffer_desc tmp;
153
154         if (WRITE_BYTES(p, end, key->type)) return -1;
155         tmp.length = key->length;
156         tmp.value = key->data;
157         if (write_buffer(p, end, &tmp)) return -1;
158         return 0;
159 }
160
161 #else   /* lucid context support */
162
163 static int
164 write_keyblock(char **p, char *end, struct _krb5_keyblock *arg)
165 {
166         gss_buffer_desc tmp;
167
168         if (WRITE_BYTES(p, end, arg->enctype)) return -1;
169         tmp.length = arg->length;
170         tmp.value = arg->contents;
171         if (write_buffer(p, end, &tmp)) return -1;
172         return 0;
173 }
174 #endif  /* lucid context support */
175 #endif  /* HAVE_KRB5 */
176
177 #ifdef HAVE_KRB5
178 #ifdef HAVE_LUCID_CONTEXT_SUPPORT /* Lucid context support */
179 static int
180 prepare_krb5_rfc1964_buffer(gss_krb5_lucid_context_v1_t *lctx,
181         gss_buffer_desc *buf)
182 {
183         char *p, *end;
184         static int constant_zero = 0;
185         unsigned char fakeseed[16];
186         uint32_t word_send_seq;
187         gss_krb5_lucid_key_t enc_key;
188         int i;
189         char *skd, *dkd;
190         gss_buffer_desc fakeoid;
191
192         /*
193          * The new Kerberos interface to get the gss context
194          * does not include the seed or seed_init fields
195          * because we never really use them.  But for now,
196          * send down a fake buffer so we can use the same
197          * interface to the kernel.
198          */
199         memset(&enc_key, 0, sizeof(enc_key));
200         memset(&fakeoid, 0, sizeof(fakeoid));
201
202         if (!(buf->value = calloc(1, MAX_CTX_LEN)))
203                 goto out_err;
204         p = buf->value;
205         end = buf->value + MAX_CTX_LEN;
206
207         if (WRITE_BYTES(&p, end, lctx->initiate)) goto out_err;
208
209         /* seed_init and seed not used by kernel anyway */
210         if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
211         if (write_bytes(&p, end, &fakeseed, 16)) goto out_err;
212
213         if (WRITE_BYTES(&p, end, lctx->rfc1964_kd.sign_alg)) goto out_err;
214         if (WRITE_BYTES(&p, end, lctx->rfc1964_kd.seal_alg)) goto out_err;
215         if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err;
216         word_send_seq = lctx->send_seq; /* XXX send_seq is 64-bit */
217         if (WRITE_BYTES(&p, end, word_send_seq)) goto out_err;
218         if (write_buffer(&p, end, (gss_buffer_desc*)&krb5oid)) goto out_err;
219
220         /* derive the encryption key and copy it into buffer */
221         enc_key.type = lctx->rfc1964_kd.ctx_key.type;
222         enc_key.length = lctx->rfc1964_kd.ctx_key.length;
223         if ((enc_key.data = calloc(1, enc_key.length)) == NULL)
224                 goto out_err;
225         skd = (char *) lctx->rfc1964_kd.ctx_key.data;
226         dkd = (char *) enc_key.data;
227         for (i = 0; i < enc_key.length; i++)
228                 dkd[i] = skd[i] ^ 0xf0;
229         if (write_lucid_keyblock(&p, end, &enc_key)) {
230                 free(enc_key.data);
231                 goto out_err;
232         }
233         free(enc_key.data);
234
235         if (write_lucid_keyblock(&p, end, &lctx->rfc1964_kd.ctx_key))
236                 goto out_err;
237
238         buf->length = p - (char *)buf->value;
239         return 0;
240 out_err:
241         printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
242         if (buf->value) free(buf->value);
243         buf->length = 0;
244         if (enc_key.data) free(enc_key.data);
245         return -1;
246 }
247
248 static int
249 prepare_krb5_rfc_cfx_buffer(gss_krb5_lucid_context_v1_t *lctx,
250         gss_buffer_desc *buf)
251 {
252         printerr(0, "ERROR: prepare_krb5_rfc_cfx_buffer: not implemented\n");
253         return -1;
254 }
255
256
257 static int
258 serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
259 {
260         OM_uint32 maj_stat, min_stat;
261         void *return_ctx = 0;
262         OM_uint32 vers;
263         gss_krb5_lucid_context_v1_t *lctx = 0;
264         int retcode = 0;
265
266         printerr(2, "DEBUG: serialize_krb5_ctx: lucid version!\n");
267         maj_stat = gss_krb5_export_lucid_sec_context(&min_stat, &ctx,
268                                                         1, &return_ctx);
269         if (maj_stat != GSS_S_COMPLETE)
270                 goto out_err;
271
272         /* Check the version returned, we only support v1 right now */
273         vers = ((gss_krb5_lucid_context_version_t *)return_ctx)->version;
274         switch (vers) {
275         case 1:
276                 lctx = (gss_krb5_lucid_context_v1_t *) return_ctx;
277                 break;
278         default:
279                 printerr(0, "ERROR: unsupported lucid sec context version %d\n",
280                         vers);
281                 goto out_err;
282                 break;
283         }
284
285         /* Now lctx points to a lucid context that we can send down to kernel */
286         if (lctx->protocol == 0)
287                 retcode = prepare_krb5_rfc1964_buffer(lctx, buf);
288         else
289                 retcode = prepare_krb5_rfc_cfx_buffer(lctx, buf);
290
291         maj_stat = gss_krb5_free_lucid_sec_context(&min_stat,
292                                                    (void *)lctx);
293         if (maj_stat != GSS_S_COMPLETE)
294                 printerr(0, "WARN: failed to free lucid sec context\n");
295         if (retcode)
296                 goto out_err;
297
298         return 0;
299
300 out_err:
301         printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
302         return -1;
303 }
304
305
306 #else /* lucid context support */
307
308 static int
309 serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
310 {
311         krb5_gss_ctx_id_t       kctx = (krb5_gss_ctx_id_t)ctx;
312         char *p, *end;
313         static int constant_one = 1;
314         static int constant_zero = 0;
315         uint32_t word_seq_send;
316
317         if (!(buf->value = calloc(1, MAX_CTX_LEN)))
318                 goto out_err;
319         p = buf->value;
320         end = buf->value + MAX_CTX_LEN;
321
322         if (kctx->initiate) {
323                 if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
324         }
325         else {
326                 if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
327         }
328         if (kctx->seed_init) {
329                 if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
330         }
331         else {
332                 if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
333         }
334         if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed)))
335                 goto out_err;
336         if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err;
337         if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err;
338         if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err;
339         word_seq_send = kctx->seq_send;
340         if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err;
341         if (write_buffer(&p, end, kctx->mech_used)) goto out_err;
342         if (write_keyblock(&p, end, kctx->enc)) goto out_err;
343         if (write_keyblock(&p, end, kctx->seq)) goto out_err;
344
345         buf->length = p - (char *)buf->value;
346         return 0;
347 out_err:
348         printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
349         if (buf->value) free(buf->value);
350         buf->length = 0;
351         return -1;
352 }
353 #endif /* lucid context support */
354 #endif /* HAVE_KRB5 */
355
356
357 #ifdef HAVE_SPKM3_H
358 /*
359  * Function: prepare_spkm3_ctx_buffer()
360  *
361  * Prepare spkm3 lucid context for the kernel
362  *
363  *      buf->length should be:
364  *
365  *      ctx_id 4 + 12
366  *      qop 4
367  *      mech_used 4 + 7
368  *      ret_fl  4
369  *      req_fl  4
370  *      share   4 + key_len
371  *      conf_alg 4 + oid_len
372  *      d_conf_key 4 + key_len
373  *      intg_alg 4 + oid_len
374  *      d_intg_key 4 + key_len
375  *      kyestb 4 + oid_len
376  *      owl alg 4 + oid_len
377 */
378 static int
379 prepare_spkm3_ctx_buffer(gss_spkm3_lucid_ctx_t *lctx, gss_buffer_desc *buf)
380 {
381         char *p, *end;
382         unsigned int buf_size = 0;
383
384         buf_size = lctx->ctx_id.length +
385                 sizeof(lctx->ctx_id.length) + sizeof(lctx->qop) +
386                 sizeof(lctx->mech_used.length) + lctx->mech_used.length +
387                 sizeof(lctx->ret_flags) + sizeof(lctx->req_flags) +
388                 sizeof(lctx->share_key.length) + lctx->share_key.length +
389                 sizeof(lctx->conf_alg.length) + lctx->conf_alg.length +
390                 sizeof(lctx->derived_conf_key.length) +
391                 lctx->derived_conf_key.length +
392                 sizeof(lctx->intg_alg.length) + lctx->intg_alg.length +
393                 sizeof(lctx->derived_integ_key.length) +
394                 lctx->derived_integ_key.length +
395                 sizeof(lctx->keyestb_alg.length) + lctx->keyestb_alg.length +
396                 sizeof(lctx->owf_alg.length) + lctx->owf_alg.length;
397
398         if (!(buf->value = calloc(1, buf_size)))
399                 goto out_err;
400         p = buf->value;
401         end = buf->value + buf_size;
402
403         if (write_buffer(&p, end, &lctx->ctx_id))
404                 goto out_err;
405
406         if (WRITE_BYTES(&p, end, lctx->qop))
407                 goto out_err;
408
409         if (write_buffer(&p, end, &lctx->mech_used))
410                 goto out_err;
411
412         if (WRITE_BYTES(&p, end, lctx->ret_flags))
413                 goto out_err;
414
415         if (WRITE_BYTES(&p, end, lctx->req_flags))
416                 goto out_err;
417
418         if (write_buffer(&p, end, &lctx->share_key))
419                 goto out_err;
420
421         if (write_buffer(&p, end, &lctx->conf_alg))
422                 goto out_err;
423
424         if (write_buffer(&p, end, &lctx->derived_conf_key))
425                 goto out_err;
426
427         if (write_buffer(&p, end, &lctx->intg_alg))
428                 goto out_err;
429
430         if (write_buffer(&p, end, &lctx->derived_integ_key))
431                 goto out_err;
432
433         if (write_buffer(&p, end, &lctx->keyestb_alg))
434                 goto out_err;
435
436         if (write_buffer(&p, end, &lctx->owf_alg))
437                 goto out_err;
438
439         buf->length = p - (char *)buf->value;
440         return 0;
441 out_err:
442         printerr(0, "ERROR: failed serializing spkm3 context for kernel\n");
443         if (buf->value) free(buf->value);
444         buf->length = 0;
445
446         return -1;
447 }
448
449 /* ANDROS: need to determine which fields of the spkm3_gss_ctx_id_desc_t
450  * are needed in the kernel for get_mic, validate, wrap, unwrap, and destroy
451  * and only export those fields to the kernel.
452  */
453 static int
454 serialize_spkm3_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
455 {
456         OM_uint32 vers, ret, maj_stat, min_stat;
457         void *ret_ctx = 0;
458         gss_spkm3_lucid_ctx_t     *lctx;
459
460         printerr(1, "serialize_spkm3_ctx called\n");
461
462         printerr(2, "DEBUG: serialize_spkm3_ctx: lucid version!\n");
463         maj_stat = gss_export_lucid_sec_context(&min_stat, ctx, 1, &ret_ctx);
464         if (maj_stat != GSS_S_COMPLETE)
465                 goto out_err;
466
467         lctx = (gss_spkm3_lucid_ctx_t *)ret_ctx;
468
469         vers = lctx->version;
470         if (vers != 1) {
471                 printerr(0, "ERROR: unsupported spkm3 context version %d\n",
472                         vers);
473                 goto out_err;
474         }
475         ret = prepare_spkm3_ctx_buffer(lctx, buf);
476
477         maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, ret_ctx);
478
479         if (maj_stat != GSS_S_COMPLETE)
480                 printerr(0, "WARN: failed to free lucid sec context\n");
481         if (ret)
482                 goto out_err;
483         printerr(2, "DEBUG: serialize_spkm3_ctx: success\n");
484         return 0;
485
486 out_err:
487         printerr(2, "DEBUG: serialize_spkm3_ctx: failed\n");
488         return -1;
489 }
490 #endif /* HAVE_SPKM3_H */
491
492 int
493 serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf)
494 {
495         gss_union_ctx_id_t      uctx = (gss_union_ctx_id_t)ctx;
496
497         if (g_OID_equal(&krb5oid, uctx->mech_type))
498                 return serialize_krb5_ctx(uctx->internal_ctx_id, buf);
499 #ifdef HAVE_SPKM3_H
500         else if (g_OID_equal(&spkm3oid, uctx->mech_type))
501                 return serialize_spkm3_ctx(uctx, buf);
502 #endif
503         else {
504                 printerr(0, "ERROR: attempting to serialize context with "
505                                 "unknown mechanism oid\n");
506                 return -1;
507         }
508 }