Add gss support from citi @ umich
[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 /* spkm3 seems to actually want it this big, yipes. */
46 #define MAX_CTX_LEN 4096
47
48 #ifdef HAVE_KRB5                /* MIT Kerberos */
49
50 #ifdef HAVE_LUCID_CONTEXT_SUPPORT
51
52 /* Don't use the private structure, use the exported lucid structure */
53 #include <gssapi/gssapi.h>
54 #include <gssapi/gssapi_krb5.h>
55
56 #elif (KRB5_VERSION > 131)
57 /* XXX argggg, there's gotta be a better way than just duplicating this
58  * whole struct.  Unfortunately, this is in a "private" header file,
59  * so this is our best choice at this point :-/
60  *
61  * XXX Does this match the Heimdal definition?  */
62
63 typedef struct _krb5_gss_ctx_id_rec {
64    unsigned int initiate : 1;   /* nonzero if initiating, zero if accepting */
65    unsigned int established : 1;
66    unsigned int big_endian : 1;
67    unsigned int have_acceptor_subkey : 1;
68    unsigned int seed_init : 1;  /* XXX tested but never actually set */
69 #ifdef CFX_EXERCISE
70    unsigned int testing_unknown_tokid : 1; /* for testing only */
71 #endif
72    OM_uint32 gss_flags;
73    unsigned char seed[16];
74    krb5_principal here;
75    krb5_principal there;
76    krb5_keyblock *subkey;
77    int signalg;
78    size_t cksum_size;
79    int sealalg;
80    krb5_keyblock *enc;
81    krb5_keyblock *seq;
82    krb5_timestamp endtime;
83    krb5_flags krb_flags;
84    /* XXX these used to be signed.  the old spec is inspecific, and
85       the new spec specifies unsigned.  I don't believe that the change
86       affects the wire encoding. */
87    uint64_t seq_send;           /* gssint_uint64 */
88    uint64_t seq_recv;           /* gssint_uint64 */
89    void *seqstate;
90    krb5_auth_context auth_context;
91    gss_buffer_desc *mech_used;  /* gss_OID_desc */
92     /* Protocol spec revision
93        0 => RFC 1964 with 3DES and RC4 enhancements
94        1 => draft-ietf-krb-wg-gssapi-cfx-01
95        No others defined so far.  */
96    int proto;
97    krb5_cksumtype cksumtype;    /* for "main" subkey */
98    krb5_keyblock *acceptor_subkey; /* CFX only */
99    krb5_cksumtype acceptor_subkey_cksumtype;
100 #ifdef CFX_EXERCISE
101     gss_buffer_desc init_token;
102 #endif
103 } krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t;
104
105 #else   /* KRB5_VERSION */
106
107 typedef struct _krb5_gss_ctx_id_rec {
108         int initiate;
109         u_int32_t gss_flags;
110         int seed_init;
111         unsigned char seed[16];
112         krb5_principal here;
113         krb5_principal there;
114         krb5_keyblock *subkey;
115         int signalg;
116         int cksum_size;
117         int sealalg;
118         krb5_keyblock *enc;
119         krb5_keyblock *seq;
120         krb5_timestamp endtime;
121         krb5_flags krb_flags;
122         krb5_ui_4 seq_send;
123         krb5_ui_4 seq_recv;
124         void *seqstate;
125         int established;
126         int big_endian;
127         krb5_auth_context auth_context;
128         gss_buffer_desc *mech_used;
129         int nctypes;
130         krb5_cksumtype *ctypes;
131 } krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t;
132
133 #endif /* KRB5_VERSION */
134 #endif /* HAVE_KRB5 */
135
136 /* XXX We have the same issue as above.  We can require SPKM-3 source
137  * at the time we compile gssd, or copy the context structure definitions
138  * here.
139  */
140
141 /* structure typedefs */
142
143 typedef struct spkm3_ctx_id_t {
144     int length;
145     unsigned char *data;
146 } spkm3_ctx_id,
147  *spkm3_ctx_id_t;
148
149 /* first pass at spkm3 context. will add a bunch of stuff .... */
150
151 typedef struct spkm3_gss_ctx_id_desc_t {
152     spkm3_ctx_id ctx_id;        /* per spkm token contextid */
153     int established;
154     int qop;                    /* negotiated qop */
155     gss_OID mech_used;
156     OM_uint32 ret_flags;
157     OM_uint32 req_flags;
158     /* DH should be abstracted to an EVP_ struct able to hold
159      * various kalg results */
160     /* XXX The following is defined as "DH *dh" in the original
161      * header we're gonna cheat and use "void *dh" here. */
162     void *dh;
163     gss_buffer_desc share_key;
164     /* derived keys are result from applying the owf_alg to the
165      * shared key - see spkm3_derive_supkey */
166     gss_buffer_desc derived_conf_key;
167     gss_buffer_desc derived_integ_key;
168     /* openssl NID's of the negotiated algorithms */
169     int keyestb_alg;            /* key establishment */
170     int owf_alg;                /* one way function */
171     int intg_alg;               /* integrity */
172     int conf_alg;               /* privacy */
173     /* der encoded REQ_TOKEN reqcontets and length */
174     unsigned char *der_reqcontents;
175     int der_req_len;
176 } spkm3_gss_ctx_id_desc;
177
178
179 /* adapted from mit kerberos 5 ../lib/gssapi/mechglue/mglueP.h
180  * this is what gets passed around when the mechglue code is enabled : */
181 typedef struct gss_union_ctx_id_t {
182         gss_OID         mech_type;
183         gss_ctx_id_t    internal_ctx_id;
184 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
185
186 #ifdef HAVE_KRB5                /* MIT Kerberos */
187 #ifdef HAVE_LUCID_CONTEXT_SUPPORT /* Lucid context support */
188 static int
189 write_lucid_keyblock(char **p, char *end, gss_krb5_lucid_key_t *key)
190 {
191         gss_buffer_desc tmp;
192
193         if (WRITE_BYTES(p, end, key->type)) return -1;
194         tmp.length = key->length;
195         tmp.value = key->data;
196         if (write_buffer(p, end, &tmp)) return -1;
197         return 0;
198 }
199
200 #else   /* lucid context support */
201
202 static int
203 write_keyblock(char **p, char *end, struct _krb5_keyblock *arg)
204 {
205         gss_buffer_desc tmp;
206
207         if (WRITE_BYTES(p, end, arg->enctype)) return -1;
208         tmp.length = arg->length;
209         tmp.value = arg->contents;
210         if (write_buffer(p, end, &tmp)) return -1;
211         return 0;
212 }
213 #endif  /* lucid context support */
214 #endif  /* HAVE_KRB5 */
215
216 #ifdef HAVE_KRB5
217 #ifdef HAVE_LUCID_CONTEXT_SUPPORT /* Lucid context support */
218 static int
219 prepare_krb5_rfc1964_buffer(gss_krb5_lucid_context_v1_t *lctx,
220         gss_buffer_desc *buf)
221 {
222         char *p, *end;
223         static int constant_zero = 0;
224         unsigned char fakeseed[16];
225         uint32_t word_send_seq;
226         gss_krb5_lucid_key_t enc_key;
227         int i;
228         char *skd, *dkd;
229         gss_buffer_desc fakeoid;
230
231         /*
232          * The new Kerberos interface to get the gss context
233          * does not include the seed or seed_init fields
234          * because we never really use them.  But for now,
235          * send down a fake buffer so we can use the same
236          * interface to the kernel.
237          */
238         memset(&enc_key, 0, sizeof(enc_key));
239         memset(&fakeoid, 0, sizeof(fakeoid));
240
241         if (!(buf->value = calloc(1, MAX_CTX_LEN)))
242                 goto out_err;
243         p = buf->value;
244         end = buf->value + MAX_CTX_LEN;
245
246         if (WRITE_BYTES(&p, end, lctx->initiate)) goto out_err;
247
248         /* seed_init and seed not used by kernel anyway */
249         if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
250         if (write_bytes(&p, end, &fakeseed, 16)) goto out_err;
251
252         if (WRITE_BYTES(&p, end, lctx->rfc1964_kd.sign_alg)) goto out_err;
253         if (WRITE_BYTES(&p, end, lctx->rfc1964_kd.seal_alg)) goto out_err;
254         if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err;
255         word_send_seq = lctx->send_seq; /* XXX send_seq is 64-bit */
256         if (WRITE_BYTES(&p, end, word_send_seq)) goto out_err;
257         if (write_buffer(&p, end, (gss_buffer_desc*)&krb5oid)) goto out_err;
258
259         /* derive the encryption key and copy it into buffer */
260         enc_key.type = lctx->rfc1964_kd.ctx_key.type;
261         enc_key.length = lctx->rfc1964_kd.ctx_key.length;
262         if ((enc_key.data = calloc(1, enc_key.length)) == NULL)
263                 goto out_err;
264         skd = (char *) lctx->rfc1964_kd.ctx_key.data;
265         dkd = (char *) enc_key.data;
266         for (i = 0; i < enc_key.length; i++)
267                 dkd[i] = skd[i] ^ 0xf0;
268         if (write_lucid_keyblock(&p, end, &enc_key)) {
269                 free(enc_key.data);
270                 goto out_err;
271         }
272         free(enc_key.data);
273
274         if (write_lucid_keyblock(&p, end, &lctx->rfc1964_kd.ctx_key))
275                 goto out_err;
276
277         buf->length = p - (char *)buf->value;
278         return 0;
279 out_err:
280         printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
281         if (buf->value) free(buf->value);
282         buf->length = 0;
283         if (enc_key.data) free(enc_key.data);
284         return -1;
285 }
286
287 static int
288 prepare_krb5_rfc_cfx_buffer(gss_krb5_lucid_context_v1_t *lctx,
289         gss_buffer_desc *buf)
290 {
291         printerr(0, "ERROR: prepare_krb5_rfc_cfx_buffer: not implemented\n");
292         return -1;
293 }
294
295 static int
296 serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
297 {
298         OM_uint32 maj_stat, min_stat;
299         void *return_ctx = 0;
300         OM_uint32 vers;
301         gss_krb5_lucid_context_v1_t *lctx = 0;
302         int retcode = 0;
303
304         printerr(2, "DEBUG: serialize_krb5_ctx: lucid version!\n");
305         maj_stat = gss_krb5_export_lucid_sec_context(&min_stat, &ctx,
306                                                      1, &return_ctx);
307         if (maj_stat != GSS_S_COMPLETE)
308                 goto out_err;
309
310         /* Check the version returned, we only support v1 right now */
311         vers = ((gss_krb5_lucid_context_version_t *)return_ctx)->version;
312         switch (vers) {
313         case 1:
314                 lctx = (gss_krb5_lucid_context_v1_t *) return_ctx;
315                 break;
316         default:
317                 printerr(0, "ERROR: unsupported lucid sec context version %d\n",
318                         vers);
319                 goto out_err;
320                 break;
321         }
322
323         /* Now lctx points to a lucid context that we can send down to kernel */
324         if (lctx->protocol == 0)
325                 retcode = prepare_krb5_rfc1964_buffer(lctx, buf);
326         else
327                 retcode = prepare_krb5_rfc_cfx_buffer(lctx, buf);
328
329         maj_stat = gss_krb5_free_lucid_sec_context(&min_stat,
330                                                    (void *)lctx);
331         if (maj_stat != GSS_S_COMPLETE)
332                 printerr(0, "WARN: failed to free lucid sec context\n");
333         if (retcode)
334                 goto out_err;
335
336         return 0;
337
338 out_err:
339         printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
340         return -1;
341 }
342
343
344 #else /* lucid context support */
345
346 static int
347 serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
348 {
349         krb5_gss_ctx_id_t       kctx = (krb5_gss_ctx_id_t)ctx;
350         char *p, *end;
351         static int constant_one = 1;
352         static int constant_zero = 0;
353         uint32_t word_seq_send;
354
355         if (!(buf->value = calloc(1, MAX_CTX_LEN)))
356                 goto out_err;
357         p = buf->value;
358         end = buf->value + MAX_CTX_LEN;
359
360         if (kctx->initiate) {
361                 if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
362         }
363         else {
364                 if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
365         }
366         if (kctx->seed_init) {
367                 if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
368         }
369         else {
370                 if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
371         }
372         if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed)))
373                 goto out_err;
374         if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err;
375         if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err;
376         if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err;
377         word_seq_send = kctx->seq_send;
378         if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err;
379         if (write_buffer(&p, end, kctx->mech_used)) goto out_err;
380         if (write_keyblock(&p, end, kctx->enc)) goto out_err;
381         if (write_keyblock(&p, end, kctx->seq)) goto out_err;
382
383         buf->length = p - (char *)buf->value;
384         return 0;
385 out_err:
386         printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
387         if (buf->value) free(buf->value);
388         buf->length = 0;
389         return -1;
390 }
391 #endif /* lucid context support */
392 #endif /* HAVE_KRB5 */
393
394
395 /* ANDROS: need to determine which fields of the spkm3_gss_ctx_id_desc_t
396  * are needed in the kernel for get_mic, validate, wrap, unwrap, and destroy
397  * and only export those fields to the kernel.
398  */
399 static int
400 serialize_spkm3_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
401 {
402         spkm3_gss_ctx_id_desc      *sctx = (spkm3_gss_ctx_id_desc *)ctx;
403         char *p, *end;
404
405         printerr(1, "serialize_spkm3_ctx called\n");
406
407         if (!(buf->value = calloc(1, MAX_CTX_LEN)))
408                 goto out_err;
409         p = buf->value;
410         end = buf->value + MAX_CTX_LEN;
411 /* buf->length
412 ctx_id 4 + 12
413 qop 4
414 mech_used 4 + 7
415 ret_fl  4
416 req_fl  4
417 share   4 + 16
418 conf_alg 4
419 d_conf_key 4 + 0
420 intg_alg 4
421 d_intg_key 4 + 0
422 kyestb 4
423 owl alg 4
424 */
425         if (write_buffer(&p, end, (gss_buffer_desc *)&sctx->ctx_id))
426                 goto out_err;
427         if (WRITE_BYTES(&p, end, sctx->qop)) goto out_err;
428         if (write_buffer(&p, end, (gss_buffer_desc *)sctx->mech_used)) goto out_err;
429         if (WRITE_BYTES(&p, end, sctx->ret_flags)) goto out_err;
430         if (WRITE_BYTES(&p, end, sctx->req_flags)) goto out_err;
431         if (write_buffer(&p, end, &sctx->share_key))
432                 goto out_err;
433
434         if (WRITE_BYTES(&p, end, sctx->conf_alg)) goto out_err;
435         if (write_buffer(&p, end, &sctx->derived_conf_key))
436                 goto out_err;
437
438         if (WRITE_BYTES(&p, end, sctx->intg_alg)) goto out_err;
439         if (write_buffer(&p, end, &sctx->derived_integ_key))
440                 goto out_err;
441
442         if (WRITE_BYTES(&p, end, sctx->keyestb_alg)) goto out_err;
443         if (WRITE_BYTES(&p, end, sctx->owf_alg)) goto out_err;
444
445         buf->length = p - (char *)buf->value;
446         return 0;
447 out_err:
448         if (buf->value) free(buf->value);
449         buf->length = 0;
450         return -1;
451 }
452
453 int
454 serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf)
455 {
456         gss_union_ctx_id_t      uctx = (gss_union_ctx_id_t)ctx;
457
458         if (g_OID_equal(&krb5oid, uctx->mech_type))
459                 return serialize_krb5_ctx(uctx->internal_ctx_id, buf);
460         else if (g_OID_equal(&spkm3oid, uctx->mech_type))
461                 return serialize_spkm3_ctx(uctx->internal_ctx_id, buf);
462         else {
463                 printerr(0, "ERROR: attempting to serialize context with "
464                                 "unknown mechanism oid\n");
465                 return -1;
466         }
467 }