1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Copyright 2009 by the Massachusetts Institute of Technology.  All
4  * Rights Reserved.
5  *
6  * Export of this software from the United States of America may
7  *   require a specific license from the United States Government.
8  *   It is the responsibility of any person or organization contemplating
9  *   export to obtain such a license before exporting.
10  *
11  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12  * distribute this software and its documentation for any purpose and
13  * without fee is hereby granted, provided that the above copyright
14  * notice appear in all copies and that both that copyright notice and
15  * this permission notice appear in supporting documentation, and that
16  * the name of M.I.T. not be used in advertising or publicity pertaining
17  * to distribution of the software without specific, written prior
18  * permission.  Furthermore if you modify this software you must label
19  * your software as modified software and not distribute it in such a
20  * fashion that it might be confused with the original M.I.T. software.
21  * M.I.T. makes no representations about the suitability of
22  * this software for any purpose.  It is provided "as is" without express
23  * or implied warranty.
24  *
25  */
26 
27 #include "k5-int.h"
28 #include "authdata.h"
29 #include "auth_con.h"
30 #include "int-proto.h"
31 
32 /* Loosely based on preauth2.c */
33 
34 #define IS_PRIMARY_INSTANCE(_module) ((_module)->client_req_init != NULL)
35 
36 static const char *objdirs[] = {
37 #if TARGET_OS_MAC
38     KRB5_AUTHDATA_PLUGIN_BUNDLE_DIR,
39 #endif
40     LIBDIR "/krb5/plugins/authdata",
41     NULL
42 }; /* should be a list */
43 
44 /* Internal authdata systems */
45 static krb5plugin_authdata_client_ftable_v0 *authdata_systems[] = {
46     &k5_mspac_ad_client_ftable,
47     &k5_s4u2proxy_ad_client_ftable,
48     &k5_authind_ad_client_ftable,
49     NULL
50 };
51 
52 static inline int
k5_ad_module_count(krb5plugin_authdata_client_ftable_v0 * table)53 k5_ad_module_count(krb5plugin_authdata_client_ftable_v0 *table)
54 {
55     int i;
56 
57     if (table->ad_type_list == NULL)
58         return 0;
59 
60     for (i = 0; table->ad_type_list[i]; i++)
61         ;
62 
63     return i;
64 }
65 
66 static krb5_error_code
k5_ad_init_modules(krb5_context kcontext,krb5_authdata_context context,krb5plugin_authdata_client_ftable_v0 * table,int * module_count)67 k5_ad_init_modules(krb5_context kcontext,
68                    krb5_authdata_context context,
69                    krb5plugin_authdata_client_ftable_v0 *table,
70                    int *module_count)
71 {
72     int j, k = *module_count;
73     krb5_error_code code;
74     void *plugin_context = NULL;
75     void **rcpp = NULL;
76 
77     if (table->ad_type_list == NULL) {
78 #ifdef DEBUG
79         fprintf(stderr, "warning: module \"%s\" does not advertise "
80                 "any AD types\n", table->name);
81 #endif
82         return ENOENT;
83     }
84 
85     if (table->init == NULL)
86         return ENOSYS;
87 
88     code = (*table->init)(kcontext, &plugin_context);
89     if (code != 0) {
90 #ifdef DEBUG
91         fprintf(stderr, "warning: skipping module \"%s\" which "
92                 "failed to initialize\n", table->name);
93 #endif
94         return code;
95     }
96 
97     for (j = 0; table->ad_type_list[j] != 0; j++) {
98         context->modules[k].ad_type = table->ad_type_list[j];
99         context->modules[k].plugin_context = plugin_context;
100         if (j == 0)
101             context->modules[k].client_fini = table->fini;
102         else
103             context->modules[k].client_fini = NULL;
104         context->modules[k].ftable = table;
105         context->modules[k].name = table->name;
106         if (table->flags != NULL) {
107             (*table->flags)(kcontext, plugin_context,
108                             context->modules[k].ad_type,
109                             &context->modules[k].flags);
110         } else {
111             context->modules[k].flags = 0;
112         }
113         context->modules[k].request_context = NULL;
114         if (j == 0) {
115             context->modules[k].client_req_init = table->request_init;
116             context->modules[k].client_req_fini = table->request_fini;
117             rcpp = &context->modules[k].request_context;
118 
119             /* For now, single request per context. That may change */
120             code = (*table->request_init)(kcontext,
121                                           context,
122                                           plugin_context,
123                                           rcpp);
124             if ((code != 0 && code != ENOMEM) &&
125                 (context->modules[k].flags & AD_INFORMATIONAL))
126                 code = 0;
127             if (code != 0)
128                 break;
129         } else {
130             context->modules[k].client_req_init = NULL;
131             context->modules[k].client_req_fini = NULL;
132         }
133         context->modules[k].request_context_pp = rcpp;
134 
135 #ifdef DEBUG
136         fprintf(stderr, "init module \"%s\", ad_type %d, flags %08x\n",
137                 context->modules[k].name,
138                 context->modules[k].ad_type,
139                 context->modules[k].flags);
140 #endif
141         k++;
142     }
143     *module_count = k;
144 
145     return code;
146 }
147 
148 /*
149  * Determine size of to-be-externalized authdata context, for
150  * modules that match given flags mask. Note that this size
151  * does not include the magic identifier/trailer.
152  */
153 static krb5_error_code
k5_ad_size(krb5_context kcontext,krb5_authdata_context context,krb5_flags flags,size_t * sizep)154 k5_ad_size(krb5_context kcontext,
155            krb5_authdata_context context,
156            krb5_flags flags,
157            size_t *sizep)
158 {
159     int i;
160     krb5_error_code code = 0;
161 
162     *sizep += sizeof(krb5_int32); /* count */
163 
164     for (i = 0; i < context->n_modules; i++) {
165         struct _krb5_authdata_context_module *module = &context->modules[i];
166         size_t size;
167 
168         if ((module->flags & flags) == 0)
169             continue;
170 
171         /* externalize request context for the first instance only */
172         if (!IS_PRIMARY_INSTANCE(module))
173             continue;
174 
175         if (module->ftable->size == NULL)
176             continue;
177 
178         assert(module->ftable->externalize != NULL);
179 
180         size = sizeof(krb5_int32) /* namelen */ + strlen(module->name);
181 
182         code = (*module->ftable->size)(kcontext,
183                                        context,
184                                        module->plugin_context,
185                                        *(module->request_context_pp),
186                                        &size);
187         if (code != 0)
188             break;
189 
190         *sizep += size;
191     }
192 
193     return code;
194 }
195 
196 /*
197  * Externalize authdata context, for modules that match given flags
198  * mask. Note that the magic identifier/trailer is not included.
199  */
200 static krb5_error_code
k5_ad_externalize(krb5_context kcontext,krb5_authdata_context context,krb5_flags flags,krb5_octet ** buffer,size_t * lenremain)201 k5_ad_externalize(krb5_context kcontext,
202                   krb5_authdata_context context,
203                   krb5_flags flags,
204                   krb5_octet **buffer,
205                   size_t *lenremain)
206 {
207     int i;
208     krb5_error_code code;
209     krb5_int32 ad_count = 0;
210     krb5_octet *bp;
211     size_t remain;
212 
213     bp = *buffer;
214     remain = *lenremain;
215 
216     /* placeholder for count */
217     code = krb5_ser_pack_int32(0, &bp, &remain);
218     if (code != 0)
219         return code;
220 
221     for (i = 0; i < context->n_modules; i++) {
222         struct _krb5_authdata_context_module *module = &context->modules[i];
223         size_t namelen;
224 
225         if ((module->flags & flags) == 0)
226             continue;
227 
228         /* externalize request context for the first instance only */
229         if (!IS_PRIMARY_INSTANCE(module))
230             continue;
231 
232         if (module->ftable->externalize == NULL)
233             continue;
234 
235         /*
236          * We use the module name rather than the authdata type, because
237          * there may be multiple modules for a particular authdata type.
238          */
239         namelen = strlen(module->name);
240 
241         code = krb5_ser_pack_int32((krb5_int32)namelen, &bp, &remain);
242         if (code != 0)
243             break;
244 
245         code = krb5_ser_pack_bytes((krb5_octet *)module->name,
246                                    namelen, &bp, &remain);
247         if (code != 0)
248             break;
249 
250         code = (*module->ftable->externalize)(kcontext,
251                                               context,
252                                               module->plugin_context,
253                                               *(module->request_context_pp),
254                                               &bp,
255                                               &remain);
256         if (code != 0)
257             break;
258 
259         ad_count++;
260     }
261 
262     if (code == 0) {
263         /* store actual count */
264         krb5_ser_pack_int32(ad_count, buffer, lenremain);
265 
266         *buffer = bp;
267         *lenremain = remain;
268     }
269 
270     return code;
271 }
272 
273 /*
274  * Find authdata module for authdata type that matches flag mask
275  */
276 static struct _krb5_authdata_context_module *
k5_ad_find_module(krb5_context kcontext,krb5_authdata_context context,krb5_flags flags,const krb5_data * name)277 k5_ad_find_module(krb5_context kcontext,
278                   krb5_authdata_context context,
279                   krb5_flags flags,
280                   const krb5_data *name)
281 {
282     int i;
283     struct _krb5_authdata_context_module *ret = NULL;
284 
285     for (i = 0; i < context->n_modules; i++) {
286         struct _krb5_authdata_context_module *module = &context->modules[i];
287 
288         if ((module->flags & flags) == 0)
289             continue;
290 
291         /* internalize request context for the first instance only */
292         if (!IS_PRIMARY_INSTANCE(module))
293             continue;
294 
295         /* check for name match */
296         if (!data_eq_string(*name, module->name))
297             continue;
298 
299         ret = module;
300         break;
301     }
302 
303     return ret;
304 }
305 
306 /*
307  * In-place internalize authdata context, for modules that match given
308  * flags mask. The magic identifier/trailer is not expected by this.
309  */
310 static krb5_error_code
k5_ad_internalize(krb5_context kcontext,krb5_authdata_context context,krb5_flags flags,krb5_octet ** buffer,size_t * lenremain)311 k5_ad_internalize(krb5_context kcontext,
312                   krb5_authdata_context context,
313                   krb5_flags flags,
314                   krb5_octet **buffer,
315                   size_t *lenremain)
316 {
317     krb5_error_code code = 0;
318     krb5_int32 i, count;
319     krb5_octet *bp;
320     size_t remain;
321 
322     bp = *buffer;
323     remain = *lenremain;
324 
325     code = krb5_ser_unpack_int32(&count, &bp, &remain);
326     if (code != 0)
327         return code;
328 
329     for (i = 0; i < count; i++) {
330         struct _krb5_authdata_context_module *module;
331         krb5_int32 namelen;
332         krb5_data name;
333 
334         code = krb5_ser_unpack_int32(&namelen, &bp, &remain);
335         if (code != 0)
336             break;
337 
338         if (remain < (size_t)namelen) {
339             code = ENOMEM;
340             break;
341         }
342 
343         name.length = namelen;
344         name.data = (char *)bp;
345 
346         module = k5_ad_find_module(kcontext, context, flags, &name);
347         if (module == NULL || module->ftable->internalize == NULL) {
348             code = EINVAL;
349             break;
350         }
351 
352         bp += namelen;
353         remain -= namelen;
354 
355         code = (*module->ftable->internalize)(kcontext,
356                                               context,
357                                               module->plugin_context,
358                                               *(module->request_context_pp),
359                                               &bp,
360                                               &remain);
361         if (code != 0)
362             break;
363     }
364 
365     if (code == 0) {
366         *buffer = bp;
367         *lenremain = remain;
368     }
369 
370     return code;
371 }
372 
373 krb5_error_code KRB5_CALLCONV
krb5_authdata_context_init(krb5_context kcontext,krb5_authdata_context * pcontext)374 krb5_authdata_context_init(krb5_context kcontext,
375                            krb5_authdata_context *pcontext)
376 {
377     int n_modules, n_tables, i, k;
378     void **tables = NULL;
379     krb5plugin_authdata_client_ftable_v0 *table;
380     krb5_authdata_context context = NULL;
381     int internal_count = 0;
382     struct plugin_dir_handle plugins;
383     krb5_error_code code;
384 
385     *pcontext = NULL;
386     memset(&plugins, 0, sizeof(plugins));
387 
388     n_modules = 0;
389     for (n_tables = 0; authdata_systems[n_tables] != NULL; n_tables++) {
390         n_modules += k5_ad_module_count(authdata_systems[n_tables]);
391     }
392     internal_count = n_tables;
393 
394     if (PLUGIN_DIR_OPEN(&plugins) == 0 &&
395         krb5int_open_plugin_dirs(objdirs, NULL,
396                                  &plugins,
397                                  &kcontext->err) == 0 &&
398         krb5int_get_plugin_dir_data(&plugins,
399                                     "authdata_client_0",
400                                     &tables,
401                                     &kcontext->err) == 0 &&
402         tables != NULL)
403     {
404         for (; tables[n_tables - internal_count] != NULL; n_tables++) {
405             table = tables[n_tables - internal_count];
406             n_modules += k5_ad_module_count(table);
407         }
408     }
409 
410     context = calloc(1, sizeof(*context));
411     if (context == NULL) {
412         code = ENOMEM;
413         goto cleanup;
414     }
415     context->magic = KV5M_AUTHDATA_CONTEXT;
416     context->modules = calloc(n_modules, sizeof(context->modules[0]));
417     if (context->modules == NULL) {
418         code = ENOMEM;
419         goto cleanup;
420     }
421     context->n_modules = n_modules;
422 
423     /* fill in the structure */
424     for (i = 0, k = 0, code = 0; i < n_tables - internal_count; i++) {
425         code = k5_ad_init_modules(kcontext, context, tables[i], &k);
426         if (code != 0)
427             goto cleanup;
428     }
429 
430     for (i = 0; i < internal_count; i++) {
431         code = k5_ad_init_modules(kcontext, context, authdata_systems[i], &k);
432         if (code != 0)
433             goto cleanup;
434     }
435 
436     context->plugins = plugins;
437 
438 cleanup:
439     if (tables != NULL)
440         krb5int_free_plugin_dir_data(tables);
441 
442     if (code != 0) {
443         krb5int_close_plugin_dirs(&plugins);
444         krb5_authdata_context_free(kcontext, context);
445     } else {
446         /* plugins is owned by context now */
447         *pcontext = context;
448     }
449 
450     return code;
451 }
452 
453 void KRB5_CALLCONV
krb5_authdata_context_free(krb5_context kcontext,krb5_authdata_context context)454 krb5_authdata_context_free(krb5_context kcontext,
455                            krb5_authdata_context context)
456 {
457     int i;
458 
459     if (context == NULL)
460         return;
461 
462     for (i = 0; i < context->n_modules; i++) {
463         struct _krb5_authdata_context_module *module = &context->modules[i];
464 
465         if (module->client_req_fini != NULL &&
466             module->request_context != NULL)
467             (*module->client_req_fini)(kcontext,
468                                        context,
469                                        module->plugin_context,
470                                        module->request_context);
471 
472         if (module->client_fini != NULL)
473             (*module->client_fini)(kcontext, module->plugin_context);
474 
475         memset(module, 0, sizeof(*module));
476     }
477 
478     if (context->modules != NULL) {
479         free(context->modules);
480         context->modules = NULL;
481     }
482     krb5int_close_plugin_dirs(&context->plugins);
483     zapfree(context, sizeof(*context));
484 }
485 
486 krb5_error_code KRB5_CALLCONV
krb5_authdata_import_attributes(krb5_context kcontext,krb5_authdata_context context,krb5_flags usage,const krb5_data * attrs)487 krb5_authdata_import_attributes(krb5_context kcontext,
488                                 krb5_authdata_context context,
489                                 krb5_flags usage,
490                                 const krb5_data *attrs)
491 {
492     krb5_octet *bp;
493     size_t remain;
494 
495     bp = (krb5_octet *)attrs->data;
496     remain = attrs->length;
497 
498     return k5_ad_internalize(kcontext, context, usage, &bp, &remain);
499 }
500 
501 /* Return 0 with *kdc_issued_authdata == NULL on verification failure. */
502 static krb5_error_code
k5_get_kdc_issued_authdata(krb5_context kcontext,const krb5_ap_req * ap_req,krb5_principal * kdc_issuer,krb5_authdata *** kdc_issued_authdata)503 k5_get_kdc_issued_authdata(krb5_context kcontext,
504                            const krb5_ap_req *ap_req,
505                            krb5_principal *kdc_issuer,
506                            krb5_authdata ***kdc_issued_authdata)
507 {
508     krb5_error_code code;
509     krb5_authdata **authdata;
510     krb5_authdata **ticket_authdata;
511 
512     *kdc_issuer = NULL;
513     *kdc_issued_authdata = NULL;
514 
515     ticket_authdata = ap_req->ticket->enc_part2->authorization_data;
516 
517     code = krb5_find_authdata(kcontext, ticket_authdata, NULL,
518                               KRB5_AUTHDATA_KDC_ISSUED, &authdata);
519     if (code != 0 || authdata == NULL)
520         return code;
521 
522     /*
523      * Note: a module must still implement a verify_authdata
524      * method, even it is a NOOP that simply records the value
525      * of the kdc_issued_flag.
526      */
527     code = krb5_verify_authdata_kdc_issued(kcontext,
528                                            ap_req->ticket->enc_part2->session,
529                                            authdata[0],
530                                            kdc_issuer,
531                                            kdc_issued_authdata);
532 
533     if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY ||
534         code == KRB5KRB_AP_ERR_INAPP_CKSUM ||
535         code == KRB5_BAD_ENCTYPE || code == KRB5_BAD_MSIZE)
536         code = 0;
537 
538     krb5_free_authdata(kcontext, authdata);
539 
540     return code;
541 }
542 
543 /* Decode and verify each CAMMAC and collect the resulting authdata,
544  * ignoring those that failed verification. */
545 static krb5_error_code
extract_cammacs(krb5_context kcontext,krb5_authdata ** cammacs,const krb5_keyblock * key,krb5_authdata *** ad_out)546 extract_cammacs(krb5_context kcontext, krb5_authdata **cammacs,
547                 const krb5_keyblock *key, krb5_authdata ***ad_out)
548 {
549     krb5_error_code ret = 0;
550     krb5_authdata **list = NULL, **elements = NULL, **new_list;
551     size_t i, n_elements, count = 0;
552 
553     *ad_out = NULL;
554 
555     for (i = 0; cammacs != NULL && cammacs[i] != NULL; i++) {
556         ret = k5_unwrap_cammac_svc(kcontext, cammacs[i], key, &elements);
557         if (ret && ret != KRB5KRB_AP_ERR_BAD_INTEGRITY)
558             goto cleanup;
559         ret = 0;
560         if (elements == NULL)
561             continue;
562 
563         /* Add the verified elements to list and free the container array. */
564         for (n_elements = 0; elements[n_elements] != NULL; n_elements++);
565         new_list = realloc(list, (count + n_elements + 1) * sizeof(*list));
566         if (new_list == NULL) {
567             ret = ENOMEM;
568             goto cleanup;
569         }
570         list = new_list;
571         memcpy(list + count, elements, n_elements * sizeof(*list));
572         count += n_elements;
573         list[count] = NULL;
574         free(elements);
575         elements = NULL;
576     }
577 
578     *ad_out = list;
579     list = NULL;
580 
581 cleanup:
582     krb5_free_authdata(kcontext, list);
583     krb5_free_authdata(kcontext, elements);
584     return ret;
585 }
586 
587 /* Retrieve verified CAMMAC contained elements. */
588 static krb5_error_code
get_cammac_authdata(krb5_context kcontext,const krb5_ap_req * ap_req,const krb5_keyblock * key,krb5_authdata *** elems_out)589 get_cammac_authdata(krb5_context kcontext, const krb5_ap_req *ap_req,
590                     const krb5_keyblock *key, krb5_authdata ***elems_out)
591 {
592     krb5_error_code ret = 0;
593     krb5_authdata **ticket_authdata, **cammacs, **elements;
594 
595     *elems_out = NULL;
596 
597     ticket_authdata = ap_req->ticket->enc_part2->authorization_data;
598     ret = krb5_find_authdata(kcontext, ticket_authdata, NULL,
599                              KRB5_AUTHDATA_CAMMAC, &cammacs);
600     if (ret || cammacs == NULL)
601         return ret;
602 
603     ret = extract_cammacs(kcontext, cammacs, key, &elements);
604     if (!ret)
605         *elems_out = elements;
606 
607     krb5_free_authdata(kcontext, cammacs);
608     return ret;
609 }
610 
611 krb5_error_code
krb5int_authdata_verify(krb5_context kcontext,krb5_authdata_context context,krb5_flags usage,const krb5_auth_context * auth_context,const krb5_keyblock * key,const krb5_ap_req * ap_req)612 krb5int_authdata_verify(krb5_context kcontext,
613                         krb5_authdata_context context,
614                         krb5_flags usage,
615                         const krb5_auth_context *auth_context,
616                         const krb5_keyblock *key,
617                         const krb5_ap_req *ap_req)
618 {
619     int i;
620     krb5_error_code code = 0;
621     krb5_authdata **authen_authdata;
622     krb5_authdata **ticket_authdata;
623     krb5_principal kdc_issuer = NULL;
624     krb5_authdata **kdc_issued_authdata = NULL;
625     krb5_authdata **cammac_authdata = NULL;
626 
627     authen_authdata = (*auth_context)->authentp->authorization_data;
628     ticket_authdata = ap_req->ticket->enc_part2->authorization_data;
629 
630     code = k5_get_kdc_issued_authdata(kcontext, ap_req, &kdc_issuer,
631                                       &kdc_issued_authdata);
632     if (code)
633         goto cleanup;
634 
635     code = get_cammac_authdata(kcontext, ap_req, key, &cammac_authdata);
636     if (code)
637         goto cleanup;
638 
639     for (i = 0; i < context->n_modules; i++) {
640         struct _krb5_authdata_context_module *module = &context->modules[i];
641         krb5_authdata **authdata = NULL;
642         krb5_boolean kdc_issued_flag = FALSE;
643 
644         if ((module->flags & usage) == 0)
645             continue;
646 
647         if (module->ftable->import_authdata == NULL)
648             continue;
649 
650         if (kdc_issued_authdata != NULL &&
651             (module->flags & AD_USAGE_KDC_ISSUED)) {
652             code = krb5_find_authdata(kcontext, kdc_issued_authdata, NULL,
653                                       module->ad_type, &authdata);
654             if (code != 0)
655                 break;
656 
657             kdc_issued_flag = TRUE;
658         }
659 
660         if (cammac_authdata != NULL && (module->flags & AD_CAMMAC_PROTECTED)) {
661             code = krb5_find_authdata(kcontext, cammac_authdata, NULL,
662                                       module->ad_type, &authdata);
663             if (code)
664                 break;
665 
666             kdc_issued_flag = TRUE;
667         }
668 
669         if (authdata == NULL) {
670             krb5_boolean ticket_usage = FALSE;
671             krb5_boolean authen_usage = FALSE;
672 
673             /*
674              * Determine which authdata sources to interrogate based on the
675              * module's usage. This is important if the authdata is signed
676              * by the KDC with the TGT key (as the user can forge that in
677              * the AP-REQ).
678              */
679             if (module->flags & (AD_USAGE_AS_REQ | AD_USAGE_TGS_REQ))
680                 ticket_usage = TRUE;
681             if (module->flags & AD_USAGE_AP_REQ)
682                 authen_usage = TRUE;
683 
684             code = krb5_find_authdata(kcontext,
685                                       ticket_usage ? ticket_authdata : NULL,
686                                       authen_usage ? authen_authdata : NULL,
687                                       module->ad_type, &authdata);
688             if (code != 0)
689                 break;
690         }
691 
692         if (authdata == NULL)
693             continue;
694 
695         assert(authdata[0] != NULL);
696 
697         code = (*module->ftable->import_authdata)(kcontext,
698                                                   context,
699                                                   module->plugin_context,
700                                                   *(module->request_context_pp),
701                                                   authdata,
702                                                   kdc_issued_flag,
703                                                   kdc_issuer);
704         if (code == 0 && module->ftable->verify != NULL) {
705             code = (*module->ftable->verify)(kcontext,
706                                              context,
707                                              module->plugin_context,
708                                              *(module->request_context_pp),
709                                              auth_context,
710                                              key,
711                                              ap_req);
712         }
713         if (code != 0 && (module->flags & AD_INFORMATIONAL))
714             code = 0;
715         krb5_free_authdata(kcontext, authdata);
716         if (code != 0)
717             break;
718     }
719 
720 cleanup:
721     krb5_free_principal(kcontext, kdc_issuer);
722     krb5_free_authdata(kcontext, kdc_issued_authdata);
723     krb5_free_authdata(kcontext, cammac_authdata);
724 
725     return code;
726 }
727 
728 static krb5_error_code
k5_merge_data_list(krb5_data ** dst,krb5_data * src,unsigned int * len)729 k5_merge_data_list(krb5_data **dst, krb5_data *src, unsigned int *len)
730 {
731     unsigned int i;
732     krb5_data *d;
733 
734     if (src == NULL)
735         return 0;
736 
737     for (i = 0; src[i].data != NULL; i++)
738         ;
739 
740     d = realloc(*dst, (*len + i + 1) * sizeof(krb5_data));
741     if (d == NULL)
742         return ENOMEM;
743 
744     memcpy(&d[*len], src, i * sizeof(krb5_data));
745 
746     *len += i;
747 
748     d[*len].data = NULL;
749     d[*len].length = 0;
750 
751     *dst = d;
752 
753     return 0;
754 }
755 
756 krb5_error_code KRB5_CALLCONV
krb5_authdata_get_attribute_types(krb5_context kcontext,krb5_authdata_context context,krb5_data ** out_attrs)757 krb5_authdata_get_attribute_types(krb5_context kcontext,
758                                   krb5_authdata_context context,
759                                   krb5_data **out_attrs)
760 {
761     int i;
762     krb5_error_code code = 0;
763     krb5_data *attrs = NULL;
764     unsigned int attrs_len = 0;
765 
766     for (i = 0; i < context->n_modules; i++) {
767         struct _krb5_authdata_context_module *module = &context->modules[i];
768         krb5_data *attrs2 = NULL;
769 
770         if (module->ftable->get_attribute_types == NULL)
771             continue;
772 
773         if ((*module->ftable->get_attribute_types)(kcontext,
774                                                    context,
775                                                    module->plugin_context,
776                                                    *(module->request_context_pp),
777                                                    &attrs2))
778             continue;
779 
780         code = k5_merge_data_list(&attrs, attrs2, &attrs_len);
781         if (code != 0) {
782             krb5int_free_data_list(kcontext, attrs2);
783             break;
784         }
785         if (attrs2 != NULL)
786             free(attrs2);
787     }
788 
789     if (code != 0) {
790         krb5int_free_data_list(kcontext, attrs);
791         attrs = NULL;
792     }
793 
794     *out_attrs = attrs;
795 
796     return code;
797 }
798 
799 krb5_error_code KRB5_CALLCONV
krb5_authdata_get_attribute(krb5_context kcontext,krb5_authdata_context context,const krb5_data * attribute,krb5_boolean * authenticated,krb5_boolean * complete,krb5_data * value,krb5_data * display_value,int * more)800 krb5_authdata_get_attribute(krb5_context kcontext,
801                             krb5_authdata_context context,
802                             const krb5_data *attribute,
803                             krb5_boolean *authenticated,
804                             krb5_boolean *complete,
805                             krb5_data *value,
806                             krb5_data *display_value,
807                             int *more)
808 {
809     int i;
810     krb5_error_code code = ENOENT;
811 
812     *authenticated = FALSE;
813     *complete = FALSE;
814 
815     value->data = NULL;
816     value->length = 0;
817 
818     display_value->data = NULL;
819     display_value->length = 0;
820 
821     /*
822      * NB at present a module is presumed to be authoritative for
823      * an attribute; not sure how to federate "more" across module
824      * yet
825      */
826     for (i = 0; i < context->n_modules; i++) {
827         struct _krb5_authdata_context_module *module = &context->modules[i];
828 
829         if (module->ftable->get_attribute == NULL)
830             continue;
831 
832         code = (*module->ftable->get_attribute)(kcontext,
833                                                 context,
834                                                 module->plugin_context,
835                                                 *(module->request_context_pp),
836                                                 attribute,
837                                                 authenticated,
838                                                 complete,
839                                                 value,
840                                                 display_value,
841                                                 more);
842         if (code == 0)
843             break;
844     }
845 
846     if (code != 0)
847         *more = 0;
848 
849     return code;
850 }
851 
852 krb5_error_code KRB5_CALLCONV
krb5_authdata_set_attribute(krb5_context kcontext,krb5_authdata_context context,krb5_boolean complete,const krb5_data * attribute,const krb5_data * value)853 krb5_authdata_set_attribute(krb5_context kcontext,
854                             krb5_authdata_context context,
855                             krb5_boolean complete,
856                             const krb5_data *attribute,
857                             const krb5_data *value)
858 {
859     int i;
860     krb5_error_code code = 0;
861     int found = 0;
862 
863     for (i = 0; i < context->n_modules; i++) {
864         struct _krb5_authdata_context_module *module = &context->modules[i];
865 
866         if (module->ftable->set_attribute == NULL)
867             continue;
868 
869         code = (*module->ftable->set_attribute)(kcontext,
870                                                 context,
871                                                 module->plugin_context,
872                                                 *(module->request_context_pp),
873                                                 complete,
874                                                 attribute,
875                                                 value);
876         if (code == ENOENT)
877             code = 0;
878         else if (code == 0)
879             found++;
880         else
881             break;
882     }
883 
884     if (code == 0 && found == 0)
885         code = ENOENT;
886 
887     return code;
888 }
889 
890 krb5_error_code KRB5_CALLCONV
krb5_authdata_delete_attribute(krb5_context kcontext,krb5_authdata_context context,const krb5_data * attribute)891 krb5_authdata_delete_attribute(krb5_context kcontext,
892                                krb5_authdata_context context,
893                                const krb5_data *attribute)
894 {
895     int i;
896     krb5_error_code code = ENOENT;
897     int found = 0;
898 
899     for (i = 0; i < context->n_modules; i++) {
900         struct _krb5_authdata_context_module *module = &context->modules[i];
901 
902         if (module->ftable->delete_attribute == NULL)
903             continue;
904 
905         code = (*module->ftable->delete_attribute)(kcontext,
906                                                    context,
907                                                    module->plugin_context,
908                                                    *(module->request_context_pp),
909                                                    attribute);
910         if (code == ENOENT)
911             code = 0;
912         else if (code == 0)
913             found++;
914         else
915             break;
916     }
917 
918     if (code == 0 && found == 0)
919         code = ENOENT;
920 
921     return code;
922 }
923 
924 krb5_error_code KRB5_CALLCONV
krb5_authdata_export_attributes(krb5_context kcontext,krb5_authdata_context context,krb5_flags flags,krb5_data ** attrsp)925 krb5_authdata_export_attributes(krb5_context kcontext,
926                                 krb5_authdata_context context,
927                                 krb5_flags flags,
928                                 krb5_data **attrsp)
929 {
930     krb5_error_code code;
931     size_t required = 0;
932     krb5_octet *bp;
933     size_t remain;
934     krb5_data *attrs;
935 
936     code = k5_ad_size(kcontext, context, AD_USAGE_MASK, &required);
937     if (code != 0)
938         return code;
939 
940     attrs = malloc(sizeof(*attrs));
941     if (attrs == NULL)
942         return ENOMEM;
943 
944     attrs->magic = KV5M_DATA;
945     attrs->length = 0;
946     attrs->data = malloc(required);
947     if (attrs->data == NULL) {
948         free(attrs);
949         return ENOMEM;
950     }
951 
952     bp = (krb5_octet *)attrs->data;
953     remain = required;
954 
955     code = k5_ad_externalize(kcontext, context, AD_USAGE_MASK, &bp, &remain);
956     if (code != 0) {
957         krb5_free_data(kcontext, attrs);
958         return code;
959     }
960 
961     attrs->length = (bp - (krb5_octet *)attrs->data);
962 
963     *attrsp = attrs;
964 
965     return 0;
966 }
967 
968 krb5_error_code KRB5_CALLCONV
krb5_authdata_export_internal(krb5_context kcontext,krb5_authdata_context context,krb5_boolean restrict_authenticated,const char * module_name,void ** ptr)969 krb5_authdata_export_internal(krb5_context kcontext,
970                               krb5_authdata_context context,
971                               krb5_boolean restrict_authenticated,
972                               const char *module_name,
973                               void **ptr)
974 {
975     krb5_error_code code;
976     krb5_data name;
977     struct _krb5_authdata_context_module *module;
978 
979     *ptr = NULL;
980 
981     name = make_data((char *)module_name, strlen(module_name));
982     module = k5_ad_find_module(kcontext, context, AD_USAGE_MASK, &name);
983     if (module == NULL)
984         return ENOENT;
985 
986     if (module->ftable->export_internal == NULL)
987         return ENOENT;
988 
989     code = (*module->ftable->export_internal)(kcontext,
990                                               context,
991                                               module->plugin_context,
992                                               *(module->request_context_pp),
993                                               restrict_authenticated,
994                                               ptr);
995 
996     return code;
997 }
998 
999 krb5_error_code KRB5_CALLCONV
krb5_authdata_free_internal(krb5_context kcontext,krb5_authdata_context context,const char * module_name,void * ptr)1000 krb5_authdata_free_internal(krb5_context kcontext,
1001                             krb5_authdata_context context,
1002                             const char *module_name,
1003                             void *ptr)
1004 {
1005     krb5_data name;
1006     struct _krb5_authdata_context_module *module;
1007 
1008     name = make_data((char *)module_name, strlen(module_name));
1009     module = k5_ad_find_module(kcontext, context, AD_USAGE_MASK, &name);
1010     if (module == NULL)
1011         return ENOENT;
1012 
1013     if (module->ftable->free_internal == NULL)
1014         return ENOENT;
1015 
1016     (*module->ftable->free_internal)(kcontext,
1017                                      context,
1018                                      module->plugin_context,
1019                                      *(module->request_context_pp),
1020                                      ptr);
1021 
1022     return 0;
1023 }
1024 
1025 static krb5_error_code
k5_copy_ad_module_data(krb5_context kcontext,krb5_authdata_context context,struct _krb5_authdata_context_module * src_module,krb5_authdata_context dst)1026 k5_copy_ad_module_data(krb5_context kcontext,
1027                        krb5_authdata_context context,
1028                        struct _krb5_authdata_context_module *src_module,
1029                        krb5_authdata_context dst)
1030 {
1031     int i;
1032     krb5_error_code code;
1033     struct _krb5_authdata_context_module *dst_module = NULL;
1034 
1035     for (i = 0; i < dst->n_modules; i++) {
1036         struct _krb5_authdata_context_module *module = &dst->modules[i];
1037 
1038         if (module->ftable == src_module->ftable) {
1039             /* XXX is this safe to assume these pointers are interned? */
1040             dst_module = module;
1041             break;
1042         }
1043     }
1044 
1045     if (dst_module == NULL)
1046         return ENOENT;
1047 
1048     /* copy request context for the first instance only */
1049     if (!IS_PRIMARY_INSTANCE(dst_module))
1050         return 0;
1051 
1052     assert(strcmp(dst_module->name, src_module->name) == 0);
1053 
1054     /* If copy is unimplemented, externalize/internalize */
1055     if (src_module->ftable->copy == NULL) {
1056         size_t size = 0, remain;
1057         krb5_octet *contents, *bp;
1058 
1059         assert(src_module->ftable->size != NULL);
1060         assert(src_module->ftable->externalize != NULL);
1061         assert(dst_module->ftable->internalize != NULL);
1062 
1063         code = (*src_module->ftable->size)(kcontext,
1064                                            context,
1065                                            src_module->plugin_context,
1066                                            src_module->request_context,
1067                                            &size);
1068         if (code != 0)
1069             return code;
1070 
1071         contents = malloc(size);
1072         if (contents == NULL)
1073             return ENOMEM;
1074 
1075         bp = contents;
1076         remain = size;
1077 
1078         code = (*src_module->ftable->externalize)(kcontext,
1079                                                   context,
1080                                                   src_module->plugin_context,
1081                                                   *(src_module->request_context_pp),
1082                                                   &bp,
1083                                                   &remain);
1084         if (code != 0) {
1085             free(contents);
1086             return code;
1087         }
1088 
1089         remain = (bp - contents);
1090         bp = contents;
1091 
1092         code = (*dst_module->ftable->internalize)(kcontext,
1093                                                   context,
1094                                                   dst_module->plugin_context,
1095                                                   *(dst_module->request_context_pp),
1096                                                   &bp,
1097                                                   &remain);
1098         if (code != 0) {
1099             free(contents);
1100             return code;
1101         }
1102 
1103         free(contents);
1104     } else {
1105         assert(src_module->request_context_pp == &src_module->request_context);
1106         assert(dst_module->request_context_pp == &dst_module->request_context);
1107 
1108         code = (*src_module->ftable->copy)(kcontext,
1109                                            context,
1110                                            src_module->plugin_context,
1111                                            src_module->request_context,
1112                                            dst_module->plugin_context,
1113                                            dst_module->request_context);
1114     }
1115 
1116     return code;
1117 }
1118 
1119 krb5_error_code KRB5_CALLCONV
krb5_authdata_context_copy(krb5_context kcontext,krb5_authdata_context src,krb5_authdata_context * pdst)1120 krb5_authdata_context_copy(krb5_context kcontext,
1121                            krb5_authdata_context src,
1122                            krb5_authdata_context *pdst)
1123 {
1124     int i;
1125     krb5_error_code code;
1126     krb5_authdata_context dst;
1127 
1128     /* XXX we need to init a new context because we can't copy plugins */
1129     code = krb5_authdata_context_init(kcontext, &dst);
1130     if (code != 0)
1131         return code;
1132 
1133     for (i = 0; i < src->n_modules; i++) {
1134         struct _krb5_authdata_context_module *module = &src->modules[i];
1135 
1136         code = k5_copy_ad_module_data(kcontext, src, module, dst);
1137         if (code != 0)
1138             break;
1139     }
1140 
1141     if (code != 0) {
1142         krb5_authdata_context_free(kcontext, dst);
1143         return code;
1144     }
1145 
1146     *pdst = dst;
1147 
1148     return 0;
1149 }
1150 
1151 /*
1152  * Calculate size of to-be-externalized authdata context.
1153  */
1154 krb5_error_code
k5_size_authdata_context(krb5_context kcontext,krb5_authdata_context context,size_t * sizep)1155 k5_size_authdata_context(krb5_context kcontext, krb5_authdata_context context,
1156                          size_t *sizep)
1157 {
1158     krb5_error_code code;
1159 
1160     code = k5_ad_size(kcontext, context, AD_USAGE_MASK, sizep);
1161     if (code != 0)
1162         return code;
1163 
1164     *sizep += 2 * sizeof(krb5_int32); /* identifier/trailer */
1165 
1166     return 0;
1167 }
1168 
1169 /*
1170  * Externalize an authdata context.
1171  */
1172 krb5_error_code
k5_externalize_authdata_context(krb5_context kcontext,krb5_authdata_context context,krb5_octet ** buffer,size_t * lenremain)1173 k5_externalize_authdata_context(krb5_context kcontext,
1174                                 krb5_authdata_context context,
1175                                 krb5_octet **buffer, size_t *lenremain)
1176 {
1177     krb5_error_code code;
1178     krb5_octet *bp;
1179     size_t remain;
1180 
1181     bp = *buffer;
1182     remain = *lenremain;
1183 
1184     /* Our identifier */
1185     code = krb5_ser_pack_int32(KV5M_AUTHDATA_CONTEXT, &bp, &remain);
1186     if (code != 0)
1187         return code;
1188 
1189     /* The actual context data */
1190     code = k5_ad_externalize(kcontext, context, AD_USAGE_MASK,
1191                              &bp, &remain);
1192     if (code != 0)
1193         return code;
1194 
1195     /* Our trailer */
1196     code = krb5_ser_pack_int32(KV5M_AUTHDATA_CONTEXT, &bp, &remain);
1197     if (code != 0)
1198         return code;
1199 
1200     *buffer = bp;
1201     *lenremain = remain;
1202 
1203     return 0;
1204 }
1205 
1206 /*
1207  * Internalize an authdata context.
1208  */
1209 krb5_error_code
k5_internalize_authdata_context(krb5_context kcontext,krb5_authdata_context * ptr,krb5_octet ** buffer,size_t * lenremain)1210 k5_internalize_authdata_context(krb5_context kcontext,
1211                                 krb5_authdata_context *ptr,
1212                                 krb5_octet **buffer, size_t *lenremain)
1213 {
1214     krb5_error_code code;
1215     krb5_authdata_context context;
1216     krb5_int32 ibuf;
1217     krb5_octet *bp;
1218     size_t remain;
1219 
1220     bp = *buffer;
1221     remain = *lenremain;
1222 
1223     code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
1224     if (code != 0)
1225         return code;
1226 
1227     if (ibuf != KV5M_AUTHDATA_CONTEXT)
1228         return EINVAL;
1229 
1230     code = krb5_authdata_context_init(kcontext, &context);
1231     if (code != 0)
1232         return code;
1233 
1234     code = k5_ad_internalize(kcontext, context, AD_USAGE_MASK,
1235                              &bp, &remain);
1236     if (code != 0) {
1237         krb5_authdata_context_free(kcontext, context);
1238         return code;
1239     }
1240 
1241     code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
1242     if (code != 0)
1243         return code;
1244 
1245     if (ibuf != KV5M_AUTHDATA_CONTEXT) {
1246         krb5_authdata_context_free(kcontext, context);
1247         return EINVAL;
1248     }
1249 
1250     *buffer = bp;
1251     *lenremain = remain;
1252     *ptr = context;
1253 
1254     return 0;
1255 }
1256 
1257 krb5_error_code
krb5int_copy_authdatum(krb5_context context,const krb5_authdata * inad,krb5_authdata ** outad)1258 krb5int_copy_authdatum(krb5_context context,
1259                        const krb5_authdata *inad, krb5_authdata **outad)
1260 {
1261     krb5_authdata *tmpad;
1262 
1263     if (!(tmpad = (krb5_authdata *)malloc(sizeof(*tmpad))))
1264         return ENOMEM;
1265     *tmpad = *inad;
1266     if (!(tmpad->contents = (krb5_octet *)malloc(inad->length))) {
1267         free(tmpad);
1268         return ENOMEM;
1269     }
1270     memcpy(tmpad->contents, inad->contents, inad->length);
1271     *outad = tmpad;
1272     return 0;
1273 }
1274 
1275 void KRB5_CALLCONV
krb5_free_authdata(krb5_context context,krb5_authdata ** val)1276 krb5_free_authdata(krb5_context context, krb5_authdata **val)
1277 {
1278     krb5_authdata **temp;
1279 
1280     if (val == NULL)
1281         return;
1282     for (temp = val; *temp; temp++) {
1283         free((*temp)->contents);
1284         free(*temp);
1285     }
1286     free(val);
1287 }
1288