1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kdc/kdc_authdata.c - Authorization data routines for the KDC */
3 /*
4 * Copyright (C) 2007 Apple Inc. All Rights Reserved.
5 * Copyright (C) 2008, 2009 by the Massachusetts Institute of Technology.
6 *
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
25 */
26
27 #include "k5-int.h"
28 #include "kdc_util.h"
29 #include "extern.h"
30 #include <stdio.h>
31 #include "adm_proto.h"
32
33 #include <syslog.h>
34
35 #include <assert.h>
36 #include <krb5/kdcauthdata_plugin.h>
37
38 typedef struct kdcauthdata_handle_st {
39 struct krb5_kdcauthdata_vtable_st vt;
40 krb5_kdcauthdata_moddata data;
41 } kdcauthdata_handle;
42
43 static kdcauthdata_handle *authdata_modules;
44 static size_t n_authdata_modules;
45
46 /* Load authdata plugin modules. */
47 krb5_error_code
load_authdata_plugins(krb5_context context)48 load_authdata_plugins(krb5_context context)
49 {
50 krb5_error_code ret;
51 krb5_plugin_initvt_fn *modules = NULL, *mod;
52 kdcauthdata_handle *list, *h;
53 size_t count;
54
55 ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_KDCAUTHDATA, &modules);
56 if (ret)
57 return ret;
58
59 /* Allocate a large enough list of handles. */
60 for (count = 0; modules[count] != NULL; count++);
61 list = calloc(count + 1, sizeof(*list));
62 if (list == NULL) {
63 k5_plugin_free_modules(context, modules);
64 return ENOMEM;
65 }
66
67 /* Initialize each module's vtable and module data. */
68 count = 0;
69 for (mod = modules; *mod != NULL; mod++) {
70 h = &list[count];
71 memset(h, 0, sizeof(*h));
72 ret = (*mod)(context, 1, 1, (krb5_plugin_vtable)&h->vt);
73 if (ret) /* Version mismatch, keep going. */
74 continue;
75 if (h->vt.init != NULL) {
76 ret = h->vt.init(context, &h->data);
77 if (ret) {
78 kdc_err(context, ret, _("while loading authdata module %s"),
79 h->vt.name);
80 continue;
81 }
82 }
83 count++;
84 }
85
86 authdata_modules = list;
87 n_authdata_modules = count;
88 k5_plugin_free_modules(context, modules);
89 return 0;
90 }
91
92 krb5_error_code
unload_authdata_plugins(krb5_context context)93 unload_authdata_plugins(krb5_context context)
94 {
95 kdcauthdata_handle *h;
96 size_t i;
97
98 for (i = 0; i < n_authdata_modules; i++) {
99 h = &authdata_modules[i];
100 if (h->vt.fini != NULL)
101 h->vt.fini(context, h->data);
102 }
103 free(authdata_modules);
104 authdata_modules = NULL;
105 return 0;
106 }
107
108 /* Return true if authdata should be filtered when copying from untrusted
109 * authdata. If desired_type is non-zero, look only for that type. */
110 static krb5_boolean
is_kdc_issued_authdatum(krb5_authdata * authdata,krb5_authdatatype desired_type)111 is_kdc_issued_authdatum(krb5_authdata *authdata,
112 krb5_authdatatype desired_type)
113 {
114 krb5_boolean result = FALSE;
115 krb5_authdatatype ad_type;
116 unsigned int i, count = 0;
117 krb5_authdatatype *ad_types, *containee_types = NULL;
118
119 if (authdata->ad_type == KRB5_AUTHDATA_IF_RELEVANT) {
120 if (krb5int_get_authdata_containee_types(NULL, authdata, &count,
121 &containee_types) != 0)
122 goto cleanup;
123 ad_types = containee_types;
124 } else {
125 ad_type = authdata->ad_type;
126 count = 1;
127 ad_types = &ad_type;
128 }
129
130 for (i = 0; i < count; i++) {
131 switch (ad_types[i]) {
132 case KRB5_AUTHDATA_SIGNTICKET:
133 case KRB5_AUTHDATA_KDC_ISSUED:
134 case KRB5_AUTHDATA_WIN2K_PAC:
135 case KRB5_AUTHDATA_CAMMAC:
136 case KRB5_AUTHDATA_AUTH_INDICATOR:
137 result = desired_type ? (desired_type == ad_types[i]) : TRUE;
138 break;
139 default:
140 result = FALSE;
141 break;
142 }
143 if (result)
144 break;
145 }
146
147 cleanup:
148 free(containee_types);
149 return result;
150 }
151
152 /* Return true if authdata contains any mandatory-for-KDC elements. */
153 static krb5_boolean
has_mandatory_for_kdc_authdata(krb5_context context,krb5_authdata ** authdata)154 has_mandatory_for_kdc_authdata(krb5_context context, krb5_authdata **authdata)
155 {
156 int i;
157
158 if (authdata == NULL)
159 return FALSE;
160 for (i = 0; authdata[i] != NULL; i++) {
161 if (authdata[i]->ad_type == KRB5_AUTHDATA_MANDATORY_FOR_KDC)
162 return TRUE;
163 }
164 return FALSE;
165 }
166
167 /* Add elements from *new_elements to *existing_list, reallocating as
168 * necessary. On success, release *new_elements and set it to NULL. */
169 static krb5_error_code
merge_authdata(krb5_authdata *** existing_list,krb5_authdata *** new_elements)170 merge_authdata(krb5_authdata ***existing_list, krb5_authdata ***new_elements)
171 {
172 size_t count = 0, ncount = 0;
173 krb5_authdata **list = *existing_list, **nlist = *new_elements;
174
175 if (nlist == NULL)
176 return 0;
177
178 for (count = 0; list != NULL && list[count] != NULL; count++);
179 for (ncount = 0; nlist[ncount] != NULL; ncount++);
180
181 list = realloc(list, (count + ncount + 1) * sizeof(*list));
182 if (list == NULL)
183 return ENOMEM;
184
185 memcpy(list + count, nlist, ncount * sizeof(*nlist));
186 list[count + ncount] = NULL;
187 free(nlist);
188
189 if (list[0] == NULL) {
190 free(list);
191 list = NULL;
192 }
193
194 *new_elements = NULL;
195 *existing_list = list;
196 return 0;
197 }
198
199 /* Add a copy of new_elements to *existing_list, omitting KDC-issued
200 * authdata. */
201 static krb5_error_code
add_filtered_authdata(krb5_authdata *** existing_list,krb5_authdata ** new_elements)202 add_filtered_authdata(krb5_authdata ***existing_list,
203 krb5_authdata **new_elements)
204 {
205 krb5_error_code ret;
206 krb5_authdata **copy;
207 size_t i, j;
208
209 if (new_elements == NULL)
210 return 0;
211
212 ret = krb5_copy_authdata(NULL, new_elements, ©);
213 if (ret)
214 return ret;
215
216 /* Remove KDC-issued elements from copy. */
217 j = 0;
218 for (i = 0; copy[i] != NULL; i++) {
219 if (is_kdc_issued_authdatum(copy[i], 0)) {
220 free(copy[i]->contents);
221 free(copy[i]);
222 } else {
223 copy[j++] = copy[i];
224 }
225 }
226 copy[j] = NULL;
227
228 /* Destructively merge the filtered copy into existing_list. */
229 ret = merge_authdata(existing_list, ©);
230 krb5_free_authdata(NULL, copy);
231 return ret;
232 }
233
234 /* Copy TGS-REQ authorization data into the ticket authdata. */
235 static krb5_error_code
copy_request_authdata(krb5_context context,krb5_keyblock * client_key,krb5_kdc_req * req,krb5_enc_tkt_part * enc_tkt_req,krb5_authdata *** tkt_authdata)236 copy_request_authdata(krb5_context context, krb5_keyblock *client_key,
237 krb5_kdc_req *req, krb5_enc_tkt_part *enc_tkt_req,
238 krb5_authdata ***tkt_authdata)
239 {
240 krb5_error_code ret;
241 krb5_data plaintext;
242
243 assert(enc_tkt_req != NULL);
244
245 ret = alloc_data(&plaintext, req->authorization_data.ciphertext.length);
246 if (ret)
247 return ret;
248
249 /*
250 * RFC 4120 requires authdata in the TGS body to be encrypted in the subkey
251 * with usage 5 if a subkey is present, and in the TGS session key with key
252 * usage 4 if it is not. Prior to krb5 1.7, we got this wrong, always
253 * decrypting the authorization data with the TGS session key and usage 4.
254 * For the sake of conservatism, try the decryption the old way (wrong if
255 * client_key is a subkey) first, and then try again the right way (in the
256 * case where client_key is a subkey) if the first way fails.
257 */
258 ret = krb5_c_decrypt(context, enc_tkt_req->session,
259 KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY, 0,
260 &req->authorization_data, &plaintext);
261 if (ret) {
262 ret = krb5_c_decrypt(context, client_key,
263 KRB5_KEYUSAGE_TGS_REQ_AD_SUBKEY, 0,
264 &req->authorization_data, &plaintext);
265 }
266 if (ret)
267 goto cleanup;
268
269 /* Decode the decrypted authdata and make it available to modules in the
270 * request. */
271 ret = decode_krb5_authdata(&plaintext, &req->unenc_authdata);
272 if (ret)
273 goto cleanup;
274
275 if (has_mandatory_for_kdc_authdata(context, req->unenc_authdata)) {
276 ret = KRB5KDC_ERR_POLICY;
277 goto cleanup;
278 }
279
280 ret = add_filtered_authdata(tkt_authdata, req->unenc_authdata);
281
282 cleanup:
283 free(plaintext.data);
284 return ret;
285 }
286
287 /* Copy TGT authorization data into the ticket authdata. */
288 static krb5_error_code
copy_tgt_authdata(krb5_context context,krb5_kdc_req * request,krb5_authdata ** tgt_authdata,krb5_authdata *** tkt_authdata)289 copy_tgt_authdata(krb5_context context, krb5_kdc_req *request,
290 krb5_authdata **tgt_authdata, krb5_authdata ***tkt_authdata)
291 {
292 if (has_mandatory_for_kdc_authdata(context, tgt_authdata))
293 return KRB5KDC_ERR_POLICY;
294
295 return add_filtered_authdata(tkt_authdata, tgt_authdata);
296 }
297
298 /* Add authentication indicator authdata to enc_tkt_reply, wrapped in a CAMMAC
299 * and an IF-RELEVANT container. */
300 static krb5_error_code
add_auth_indicators(krb5_context context,krb5_data * const * auth_indicators,krb5_keyblock * server_key,krb5_db_entry * krbtgt,krb5_keyblock * krbtgt_key,krb5_enc_tkt_part * enc_tkt_reply)301 add_auth_indicators(krb5_context context, krb5_data *const *auth_indicators,
302 krb5_keyblock *server_key, krb5_db_entry *krbtgt,
303 krb5_keyblock *krbtgt_key,
304 krb5_enc_tkt_part *enc_tkt_reply)
305 {
306 krb5_error_code ret;
307 krb5_data *der_indicators = NULL;
308 krb5_authdata ad, *list[2], **cammac = NULL;
309
310 if (auth_indicators == NULL || *auth_indicators == NULL)
311 return 0;
312
313 /* Format the authentication indicators into an authdata list. */
314 ret = encode_utf8_strings(auth_indicators, &der_indicators);
315 if (ret)
316 goto cleanup;
317 ad.ad_type = KRB5_AUTHDATA_AUTH_INDICATOR;
318 ad.length = der_indicators->length;
319 ad.contents = (uint8_t *)der_indicators->data;
320 list[0] = &ad;
321 list[1] = NULL;
322
323 /* Wrap the list in CAMMAC and IF-RELEVANT containers. */
324 ret = cammac_create(context, enc_tkt_reply, server_key, krbtgt, krbtgt_key,
325 list, &cammac);
326 if (ret)
327 goto cleanup;
328
329 /* Add the wrapped authdata to the ticket, without copying or filtering. */
330 ret = merge_authdata(&enc_tkt_reply->authorization_data, &cammac);
331
332 cleanup:
333 krb5_free_data(context, der_indicators);
334 krb5_free_authdata(context, cammac);
335 return ret;
336 }
337
338 /* Extract any properly verified authentication indicators from the authdata in
339 * enc_tkt. */
340 krb5_error_code
get_auth_indicators(krb5_context context,krb5_enc_tkt_part * enc_tkt,krb5_db_entry * local_tgt,krb5_keyblock * local_tgt_key,krb5_data *** indicators_out)341 get_auth_indicators(krb5_context context, krb5_enc_tkt_part *enc_tkt,
342 krb5_db_entry *local_tgt, krb5_keyblock *local_tgt_key,
343 krb5_data ***indicators_out)
344 {
345 krb5_error_code ret;
346 krb5_authdata **cammacs = NULL, **adp;
347 krb5_cammac *cammac = NULL;
348 krb5_data **indicators = NULL, der_cammac;
349
350 *indicators_out = NULL;
351
352 ret = krb5_find_authdata(context, enc_tkt->authorization_data, NULL,
353 KRB5_AUTHDATA_CAMMAC, &cammacs);
354 if (ret)
355 goto cleanup;
356
357 for (adp = cammacs; adp != NULL && *adp != NULL; adp++) {
358 der_cammac = make_data((*adp)->contents, (*adp)->length);
359 ret = decode_krb5_cammac(&der_cammac, &cammac);
360 if (ret)
361 goto cleanup;
362 if (cammac_check_kdcver(context, cammac, enc_tkt, local_tgt,
363 local_tgt_key)) {
364 ret = authind_extract(context, cammac->elements, &indicators);
365 if (ret)
366 goto cleanup;
367 }
368 k5_free_cammac(context, cammac);
369 cammac = NULL;
370 }
371
372 *indicators_out = indicators;
373 indicators = NULL;
374
375 cleanup:
376 krb5_free_authdata(context, cammacs);
377 k5_free_cammac(context, cammac);
378 k5_free_data_ptr_list(indicators);
379 return ret;
380 }
381
382 static krb5_error_code
update_delegation_info(krb5_context context,krb5_kdc_req * req,krb5_pac old_pac,krb5_pac new_pac)383 update_delegation_info(krb5_context context, krb5_kdc_req *req,
384 krb5_pac old_pac, krb5_pac new_pac)
385 {
386 krb5_error_code ret;
387 krb5_data ndr_di_in = empty_data(), ndr_di_out = empty_data();
388 struct pac_s4u_delegation_info *di = NULL;
389 char *namestr = NULL;
390
391 ret = krb5_pac_get_buffer(context, old_pac, KRB5_PAC_DELEGATION_INFO,
392 &ndr_di_in);
393 if (ret && ret != ENOENT)
394 goto cleanup;
395 if (ret) {
396 /* Create new delegation info. */
397 di = k5alloc(sizeof(*di), &ret);
398 if (di == NULL)
399 goto cleanup;
400 di->transited_services = k5calloc(1, sizeof(char *), &ret);
401 if (di->transited_services == NULL)
402 goto cleanup;
403 } else {
404 /* Decode and modify old delegation info. */
405 ret = ndr_dec_delegation_info(&ndr_di_in, &di);
406 if (ret)
407 goto cleanup;
408 }
409
410 /* Set proxy_target to the requested server, without realm. */
411 ret = krb5_unparse_name_flags(context, req->server,
412 KRB5_PRINCIPAL_UNPARSE_DISPLAY |
413 KRB5_PRINCIPAL_UNPARSE_NO_REALM,
414 &namestr);
415 if (ret)
416 goto cleanup;
417 free(di->proxy_target);
418 di->proxy_target = namestr;
419
420 /* Add a transited entry for the requesting service, with realm. */
421 assert(req->second_ticket != NULL && req->second_ticket[0] != NULL);
422 ret = krb5_unparse_name(context, req->second_ticket[0]->server, &namestr);
423 if (ret)
424 goto cleanup;
425 di->transited_services[di->transited_services_length++] = namestr;
426
427 ret = ndr_enc_delegation_info(di, &ndr_di_out);
428 if (ret)
429 goto cleanup;
430
431 ret = krb5_pac_add_buffer(context, new_pac, KRB5_PAC_DELEGATION_INFO,
432 &ndr_di_out);
433
434 cleanup:
435 krb5_free_data_contents(context, &ndr_di_in);
436 krb5_free_data_contents(context, &ndr_di_out);
437 ndr_free_delegation_info(di);
438 return ret;
439 }
440
441 static krb5_error_code
copy_pac_buffer(krb5_context context,uint32_t buffer_type,krb5_pac old_pac,krb5_pac new_pac)442 copy_pac_buffer(krb5_context context, uint32_t buffer_type, krb5_pac old_pac,
443 krb5_pac new_pac)
444 {
445 krb5_error_code ret;
446 krb5_data data;
447
448 ret = krb5_pac_get_buffer(context, old_pac, buffer_type, &data);
449 if (ret)
450 return ret;
451 ret = krb5_pac_add_buffer(context, new_pac, buffer_type, &data);
452 krb5_free_data_contents(context, &data);
453 return ret;
454 }
455
456 /*
457 * Possibly add a signed PAC to enc_tkt_reply. Also possibly add auth
458 * indicators; these are handled here so that the KDB module's issue_pac()
459 * method can alter the auth indicator list.
460 */
461 static krb5_error_code
handle_pac(kdc_realm_t * realm,unsigned int flags,krb5_db_entry * client,krb5_db_entry * server,krb5_db_entry * subject_server,krb5_db_entry * local_tgt,krb5_keyblock * local_tgt_key,krb5_keyblock * server_key,krb5_keyblock * subject_key,krb5_keyblock * replaced_reply_key,krb5_enc_tkt_part * subject_tkt,krb5_pac subject_pac,krb5_kdc_req * req,krb5_const_principal altcprinc,krb5_timestamp authtime,krb5_enc_tkt_part * enc_tkt_reply,krb5_data *** auth_indicators)462 handle_pac(kdc_realm_t *realm, unsigned int flags, krb5_db_entry *client,
463 krb5_db_entry *server, krb5_db_entry *subject_server,
464 krb5_db_entry *local_tgt, krb5_keyblock *local_tgt_key,
465 krb5_keyblock *server_key, krb5_keyblock *subject_key,
466 krb5_keyblock *replaced_reply_key, krb5_enc_tkt_part *subject_tkt,
467 krb5_pac subject_pac, krb5_kdc_req *req,
468 krb5_const_principal altcprinc, krb5_timestamp authtime,
469 krb5_enc_tkt_part *enc_tkt_reply, krb5_data ***auth_indicators)
470 {
471 krb5_context context = realm->realm_context;
472 krb5_error_code ret;
473 krb5_pac new_pac = NULL;
474 krb5_const_principal pac_client = NULL;
475 krb5_boolean with_realm, is_as_req = (req->msg_type == KRB5_AS_REQ);
476 krb5_db_entry *signing_tgt;
477 krb5_keyblock *privsvr_key = NULL;
478
479 /* Don't add a PAC or auth indicators if the server disables authdata. */
480 if (server->attributes & KRB5_KDB_NO_AUTH_DATA_REQUIRED)
481 return 0;
482
483 /*
484 * Don't add a PAC if the realm disables them, or to an anonymous ticket,
485 * or for an AS-REQ if the client requested not to get one, or for a
486 * TGS-REQ if the subject ticket didn't contain one.
487 */
488 if (realm->realm_disable_pac ||
489 (enc_tkt_reply->flags & TKT_FLG_ANONYMOUS) ||
490 (is_as_req && !include_pac_p(context, req)) ||
491 (!is_as_req && subject_pac == NULL)) {
492 return add_auth_indicators(context, *auth_indicators, server_key,
493 local_tgt, local_tgt_key, enc_tkt_reply);
494 }
495
496 ret = krb5_pac_init(context, &new_pac);
497 if (ret)
498 goto cleanup;
499
500 if (subject_pac == NULL)
501 signing_tgt = NULL;
502 else if (krb5_is_tgs_principal(subject_server->princ))
503 signing_tgt = subject_server;
504 else
505 signing_tgt = local_tgt;
506
507 ret = krb5_db_issue_pac(context, flags, client, replaced_reply_key, server,
508 signing_tgt, authtime, subject_pac, new_pac,
509 auth_indicators);
510 if (ret) {
511 if (ret == KRB5_PLUGIN_OP_NOTSUPP)
512 ret = 0;
513 if (ret)
514 goto cleanup;
515 }
516
517 ret = add_auth_indicators(context, *auth_indicators, server_key,
518 local_tgt, local_tgt_key, enc_tkt_reply);
519
520 if ((flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) &&
521 !(flags & KRB5_KDB_FLAG_CROSS_REALM)) {
522 /* Add delegation info for the first S4U2Proxy request. */
523 ret = update_delegation_info(context, req, subject_pac, new_pac);
524 if (ret)
525 goto cleanup;
526 } else if (subject_pac != NULL) {
527 /* Copy delegation info if it was present in the subject PAC. */
528 ret = copy_pac_buffer(context, KRB5_PAC_DELEGATION_INFO, subject_pac,
529 new_pac);
530 if (ret && ret != ENOENT)
531 goto cleanup;
532 }
533
534 if ((flags & KRB5_KDB_FLAGS_S4U) &&
535 (flags & KRB5_KDB_FLAG_ISSUING_REFERRAL)) {
536 /* When issuing a referral for either kind of S4U request, add client
537 * info for the subject with realm. */
538 pac_client = altcprinc;
539 with_realm = TRUE;
540 } else if (subject_pac == NULL || (flags & KRB5_KDB_FLAGS_S4U)) {
541 /* For a new PAC or when issuing a final ticket for either kind of S4U
542 * request, add client info for the ticket client without the realm. */
543 pac_client = enc_tkt_reply->client;
544 with_realm = FALSE;
545 } else {
546 /*
547 * For regular TGS and transitive RBCD requests, copy the client info
548 * from the incoming PAC, and don't add client info during signing. We
549 * validated the incoming client info in validate_tgs_request().
550 */
551 ret = copy_pac_buffer(context, KRB5_PAC_CLIENT_INFO, subject_pac,
552 new_pac);
553 if (ret)
554 goto cleanup;
555 pac_client = NULL;
556 with_realm = FALSE;
557 }
558
559 ret = pac_privsvr_key(context, server, local_tgt_key, &privsvr_key);
560 if (ret)
561 goto cleanup;
562 ret = krb5_kdc_sign_ticket(context, enc_tkt_reply, new_pac, server->princ,
563 pac_client, server_key, privsvr_key,
564 with_realm);
565 if (ret)
566 goto cleanup;
567
568 ret = 0;
569
570 cleanup:
571 krb5_pac_free(context, new_pac);
572 krb5_free_keyblock(context, privsvr_key);
573 return ret;
574 }
575
576 krb5_error_code
handle_authdata(kdc_realm_t * realm,unsigned int flags,krb5_db_entry * client,krb5_db_entry * server,krb5_db_entry * subject_server,krb5_db_entry * local_tgt,krb5_keyblock * local_tgt_key,krb5_keyblock * client_key,krb5_keyblock * server_key,krb5_keyblock * subject_key,krb5_keyblock * replaced_reply_key,krb5_data * req_pkt,krb5_kdc_req * req,krb5_const_principal altcprinc,krb5_pac subject_pac,krb5_enc_tkt_part * enc_tkt_req,krb5_data *** auth_indicators,krb5_enc_tkt_part * enc_tkt_reply)577 handle_authdata(kdc_realm_t *realm, unsigned int flags, krb5_db_entry *client,
578 krb5_db_entry *server, krb5_db_entry *subject_server,
579 krb5_db_entry *local_tgt, krb5_keyblock *local_tgt_key,
580 krb5_keyblock *client_key, krb5_keyblock *server_key,
581 krb5_keyblock *subject_key, krb5_keyblock *replaced_reply_key,
582 krb5_data *req_pkt, krb5_kdc_req *req,
583 krb5_const_principal altcprinc, krb5_pac subject_pac,
584 krb5_enc_tkt_part *enc_tkt_req, krb5_data ***auth_indicators,
585 krb5_enc_tkt_part *enc_tkt_reply)
586 {
587 krb5_context context = realm->realm_context;
588 kdcauthdata_handle *h;
589 krb5_error_code ret = 0;
590 size_t i;
591
592 if (req->msg_type == KRB5_TGS_REQ &&
593 req->authorization_data.ciphertext.data != NULL) {
594 /* Copy TGS request authdata. This must be done first so that modules
595 * have access to the unencrypted request authdata. */
596 ret = copy_request_authdata(context, client_key, req, enc_tkt_req,
597 &enc_tkt_reply->authorization_data);
598 if (ret)
599 return ret;
600 }
601
602 /* Invoke loaded module handlers. */
603 if (!isflagset(enc_tkt_reply->flags, TKT_FLG_ANONYMOUS)) {
604 for (i = 0; i < n_authdata_modules; i++) {
605 h = &authdata_modules[i];
606 ret = h->vt.handle(context, h->data, flags, client, server,
607 subject_server, client_key, server_key,
608 subject_key, req_pkt, req, altcprinc,
609 enc_tkt_req, enc_tkt_reply);
610 if (ret)
611 kdc_err(context, ret, "from authdata module %s", h->vt.name);
612 }
613 }
614
615 if (req->msg_type == KRB5_TGS_REQ) {
616 /* Copy authdata from the TGT to the issued ticket. */
617 ret = copy_tgt_authdata(context, req, enc_tkt_req->authorization_data,
618 &enc_tkt_reply->authorization_data);
619 if (ret)
620 return ret;
621 }
622
623 return handle_pac(realm, flags, client, server, subject_server, local_tgt,
624 local_tgt_key, server_key, subject_key,
625 replaced_reply_key, enc_tkt_req, subject_pac, req,
626 altcprinc, enc_tkt_reply->times.authtime, enc_tkt_reply,
627 auth_indicators);
628 }
629