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