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