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