1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kdc/kdc_authdata.c - Authorization data routines for the KDC */
4 * Copyright (C) 2007 Apple Inc. All Rights Reserved.
5 * Copyright (C) 2008, 2009 by the Massachusetts Institute of Technology.
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.
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.
31 #include "adm_proto.h"
36 #include <krb5/authdata_plugin.h>
39 static const char *objdirs[] = { KRB5_AUTHDATA_PLUGIN_BUNDLE_DIR,
40 LIBDIR "/krb5/plugins/authdata",
41 NULL }; /* should be a list */
43 static const char *objdirs[] = { LIBDIR "/krb5/plugins/authdata", NULL };
46 /* MIT Kerberos 1.6 (V0) authdata plugin callback */
47 typedef krb5_error_code (*authdata_proc_0)(
49 krb5_db_entry *client,
51 krb5_kdc_req *request,
52 krb5_enc_tkt_part * enc_tkt_reply);
53 /* MIT Kerberos 1.8 (V2) authdata plugin callback */
54 typedef krb5_error_code (*authdata_proc_2)(
55 krb5_context, unsigned int flags,
56 krb5_db_entry *client, krb5_db_entry *server,
57 krb5_db_entry *krbtgt,
58 krb5_keyblock *client_key,
59 krb5_keyblock *server_key,
60 krb5_keyblock *krbtgt_key,
62 krb5_kdc_req *request,
63 krb5_const_principal for_user_princ,
64 krb5_enc_tkt_part *enc_tkt_request,
65 krb5_enc_tkt_part *enc_tkt_reply);
66 typedef krb5_error_code (*init_proc)(krb5_context, void **);
67 typedef void (*fini_proc)(krb5_context, void *);
69 static krb5_error_code handle_request_authdata(
72 krb5_db_entry *client,
73 krb5_db_entry *server,
74 krb5_db_entry *krbtgt,
75 krb5_keyblock *client_key,
76 krb5_keyblock *server_key,
77 krb5_keyblock *krbtgt_key,
79 krb5_kdc_req *request,
80 krb5_const_principal for_user_princ,
81 krb5_enc_tkt_part *enc_tkt_request,
82 krb5_enc_tkt_part *enc_tkt_reply);
84 static krb5_error_code handle_tgt_authdata(
87 krb5_db_entry *client,
88 krb5_db_entry *server,
89 krb5_db_entry *krbtgt,
90 krb5_keyblock *client_key,
91 krb5_keyblock *server_key,
92 krb5_keyblock *krbtgt_key,
94 krb5_kdc_req *request,
95 krb5_const_principal for_user_princ,
96 krb5_enc_tkt_part *enc_tkt_request,
97 krb5_enc_tkt_part *enc_tkt_reply);
99 static krb5_error_code
100 handle_kdb_authdata(krb5_context context, unsigned int flags,
101 krb5_db_entry *client, krb5_db_entry *server,
102 krb5_db_entry *krbtgt, krb5_keyblock *client_key,
103 krb5_keyblock *server_key, krb5_keyblock *krbtgt_key,
104 krb5_data *req_pkt, krb5_kdc_req *request,
105 krb5_const_principal for_user_princ,
106 krb5_enc_tkt_part *enc_tkt_request,
107 krb5_enc_tkt_part *enc_tkt_reply);
109 static krb5_error_code
110 handle_signedpath_authdata(krb5_context context, unsigned int flags,
111 krb5_db_entry *client, krb5_db_entry *server,
112 krb5_db_entry *krbtgt, krb5_keyblock *client_key,
113 krb5_keyblock *server_key,
114 krb5_keyblock *krbtgt_key,
115 krb5_data *req_pkt, krb5_kdc_req *request,
116 krb5_const_principal for_user_princ,
117 krb5_enc_tkt_part *enc_tkt_request,
118 krb5_enc_tkt_part *enc_tkt_reply);
120 typedef struct _krb5_authdata_systems {
122 #define AUTHDATA_SYSTEM_UNKNOWN -1
123 #define AUTHDATA_SYSTEM_V0 0
124 #define AUTHDATA_SYSTEM_V2 2
126 #define AUTHDATA_FLAG_CRITICAL 0x1
127 #define AUTHDATA_FLAG_PRE_PLUGIN 0x2
128 #define AUTHDATA_FLAG_ANONYMOUS 0x4 /* Use plugin even for anonymous tickets */
130 void *plugin_context;
137 } krb5_authdata_systems;
139 static krb5_authdata_systems static_authdata_systems[] = {
141 /* Propagate client-submitted authdata */
144 AUTHDATA_FLAG_CRITICAL | AUTHDATA_FLAG_PRE_PLUGIN |
145 AUTHDATA_FLAG_ANONYMOUS,
149 { handle_request_authdata }
152 /* Propagate TGT authdata */
155 AUTHDATA_FLAG_CRITICAL | AUTHDATA_FLAG_ANONYMOUS,
159 { handle_tgt_authdata }
162 /* Verify and issue KDB issued authdata */
165 AUTHDATA_FLAG_CRITICAL,
169 { handle_kdb_authdata }
172 /* Verify and issue signed delegation path */
175 AUTHDATA_FLAG_CRITICAL,
179 { handle_signedpath_authdata }
183 static krb5_authdata_systems *authdata_systems;
184 static int n_authdata_systems;
185 static struct plugin_dir_handle authdata_plugins;
187 /* Load both v0 and v2 authdata plugins */
189 load_authdata_plugins(krb5_context context)
191 void **authdata_plugins_ftables_v0 = NULL;
192 void **authdata_plugins_ftables_v2 = NULL;
195 init_proc server_init_proc = NULL;
196 krb5_error_code code;
198 /* Attempt to load all of the authdata plugins we can find. */
199 PLUGIN_DIR_INIT(&authdata_plugins);
200 if (PLUGIN_DIR_OPEN(&authdata_plugins) == 0) {
201 if (krb5int_open_plugin_dirs(objdirs, NULL,
202 &authdata_plugins, &context->err) != 0) {
203 return KRB5_PLUGIN_NO_HANDLE;
207 /* Get the method tables provided by the loaded plugins. */
208 authdata_plugins_ftables_v0 = NULL;
209 authdata_plugins_ftables_v2 = NULL;
210 n_authdata_systems = 0;
212 if (krb5int_get_plugin_dir_data(&authdata_plugins,
214 &authdata_plugins_ftables_v2,
215 &context->err) != 0 ||
216 krb5int_get_plugin_dir_data(&authdata_plugins,
218 &authdata_plugins_ftables_v0,
219 &context->err) != 0) {
220 code = KRB5_PLUGIN_NO_HANDLE;
224 /* Count the valid modules. */
227 if (authdata_plugins_ftables_v2 != NULL) {
228 struct krb5plugin_authdata_server_ftable_v2 *ftable;
230 for (i = 0; authdata_plugins_ftables_v2[i] != NULL; i++) {
231 ftable = authdata_plugins_ftables_v2[i];
232 if (ftable->authdata_proc != NULL)
237 if (authdata_plugins_ftables_v0 != NULL) {
238 struct krb5plugin_authdata_server_ftable_v0 *ftable;
240 for (i = 0; authdata_plugins_ftables_v0[i] != NULL; i++) {
241 ftable = authdata_plugins_ftables_v0[i];
242 if (ftable->authdata_proc != NULL)
247 module_count += sizeof(static_authdata_systems)
248 / sizeof(static_authdata_systems[0]);
250 /* Build the complete list of supported authdata options, and
251 * leave room for a terminator entry.
253 authdata_systems = calloc(module_count + 1, sizeof(krb5_authdata_systems));
254 if (authdata_systems == NULL) {
262 * Special case to ensure that handle_request_authdata is
263 * first in the list, to make unenc_authdata available to
266 for (i = 0; i < (sizeof(static_authdata_systems) /
267 sizeof(static_authdata_systems[0])); i++) {
268 if ((static_authdata_systems[i].flags & AUTHDATA_FLAG_PRE_PLUGIN) == 0)
270 assert(static_authdata_systems[i].init == NULL);
271 authdata_systems[k++] = static_authdata_systems[i];
274 /* Add dynamically loaded V2 plugins */
275 if (authdata_plugins_ftables_v2 != NULL) {
276 struct krb5plugin_authdata_server_ftable_v2 *ftable;
278 for (i = 0; authdata_plugins_ftables_v2[i] != NULL; i++) {
279 krb5_error_code initerr;
282 ftable = authdata_plugins_ftables_v2[i];
283 if ((ftable->authdata_proc == NULL)) {
286 server_init_proc = ftable->init_proc;
287 if ((server_init_proc != NULL) &&
288 ((initerr = (*server_init_proc)(context, &pctx)) != 0)) {
290 emsg = krb5_get_error_message(context, initerr);
291 krb5_klog_syslog(LOG_ERR,
292 _("authdata %s failed to initialize: %s"),
294 krb5_free_error_message(context, emsg);
295 memset(&authdata_systems[k], 0, sizeof(authdata_systems[k]));
300 authdata_systems[k].name = ftable->name;
301 authdata_systems[k].type = AUTHDATA_SYSTEM_V2;
302 authdata_systems[k].init = server_init_proc;
303 authdata_systems[k].fini = ftable->fini_proc;
304 authdata_systems[k].handle_authdata.v2 = ftable->authdata_proc;
305 authdata_systems[k].plugin_context = pctx;
310 /* Add dynamically loaded V0 plugins */
311 if (authdata_plugins_ftables_v0 != NULL) {
312 struct krb5plugin_authdata_server_ftable_v0 *ftable;
314 for (i = 0; authdata_plugins_ftables_v0[i] != NULL; i++) {
315 krb5_error_code initerr;
318 ftable = authdata_plugins_ftables_v0[i];
319 if ((ftable->authdata_proc == NULL)) {
322 server_init_proc = ftable->init_proc;
323 if ((server_init_proc != NULL) &&
324 ((initerr = (*server_init_proc)(context, &pctx)) != 0)) {
326 emsg = krb5_get_error_message(context, initerr);
327 krb5_klog_syslog(LOG_ERR,
328 _("authdata %s failed to initialize: %s"),
330 krb5_free_error_message(context, emsg);
331 memset(&authdata_systems[k], 0, sizeof(authdata_systems[k]));
336 authdata_systems[k].name = ftable->name;
337 authdata_systems[k].type = AUTHDATA_SYSTEM_V0;
338 authdata_systems[k].init = server_init_proc;
339 authdata_systems[k].fini = ftable->fini_proc;
340 authdata_systems[k].handle_authdata.v0 = ftable->authdata_proc;
341 authdata_systems[k].plugin_context = pctx;
347 i < sizeof(static_authdata_systems) / sizeof(static_authdata_systems[0]);
349 if (static_authdata_systems[i].flags & AUTHDATA_FLAG_PRE_PLUGIN)
351 assert(static_authdata_systems[i].init == NULL);
352 authdata_systems[k++] = static_authdata_systems[i];
355 n_authdata_systems = k;
356 /* Add the end-of-list marker. */
357 authdata_systems[k].name = "[end]";
358 authdata_systems[k].type = AUTHDATA_SYSTEM_UNKNOWN;
362 if (authdata_plugins_ftables_v2 != NULL)
363 krb5int_free_plugin_dir_data(authdata_plugins_ftables_v2);
364 if (authdata_plugins_ftables_v0 != NULL)
365 krb5int_free_plugin_dir_data(authdata_plugins_ftables_v0);
371 unload_authdata_plugins(krb5_context context)
374 if (authdata_systems != NULL) {
375 for (i = 0; i < n_authdata_systems; i++) {
376 if (authdata_systems[i].fini != NULL) {
377 (*authdata_systems[i].fini)(context,
378 authdata_systems[i].plugin_context);
380 memset(&authdata_systems[i], 0, sizeof(authdata_systems[i]));
382 free(authdata_systems);
383 authdata_systems = NULL;
384 n_authdata_systems = 0;
385 krb5int_close_plugin_dirs(&authdata_plugins);
391 * Returns TRUE if authdata should be filtered when copying from
392 * untrusted authdata.
395 is_kdc_issued_authdatum (krb5_context context,
396 krb5_authdata *authdata,
397 krb5_authdatatype desired_type)
399 krb5_boolean ret = FALSE;
400 krb5_authdatatype ad_type;
401 unsigned int i, count = 0;
402 krb5_authdatatype *ad_types = NULL;
404 if (authdata->ad_type == KRB5_AUTHDATA_IF_RELEVANT) {
405 if (krb5int_get_authdata_containee_types(context,
411 ad_type = authdata->ad_type;
416 for (i = 0; i < count; i++) {
417 switch (ad_types[i]) {
418 case KRB5_AUTHDATA_SIGNTICKET:
419 case KRB5_AUTHDATA_KDC_ISSUED:
420 case KRB5_AUTHDATA_WIN2K_PAC:
421 ret = desired_type ? (desired_type == ad_types[i]) : TRUE;
432 if (authdata->ad_type == KRB5_AUTHDATA_IF_RELEVANT &&
440 has_kdc_issued_authdata (krb5_context context,
441 krb5_authdata **authdata,
442 krb5_authdatatype desired_type)
445 krb5_boolean ret = FALSE;
447 if (authdata != NULL) {
448 for (i = 0; authdata[i] != NULL; i++) {
449 if (is_kdc_issued_authdatum(context, authdata[i], desired_type)) {
460 has_mandatory_for_kdc_authdata (krb5_context context,
461 krb5_authdata **authdata)
464 krb5_boolean ret = FALSE;
466 if (authdata != NULL) {
467 for (i = 0; authdata[i] != NULL; i++) {
468 if (authdata[i]->ad_type == KRB5_AUTHDATA_MANDATORY_FOR_KDC) {
481 * If copy is FALSE, in_authdata is invalid on successful return.
482 * If ignore_kdc_issued is TRUE, KDC-issued authdata is not copied.
484 static krb5_error_code
485 merge_authdata (krb5_context context,
486 krb5_authdata **in_authdata,
487 krb5_authdata ***out_authdata,
489 krb5_boolean ignore_kdc_issued)
491 size_t i, j, nadata = 0;
492 krb5_authdata **authdata = *out_authdata;
494 if (in_authdata == NULL || in_authdata[0] == NULL)
497 if (authdata != NULL) {
498 for (nadata = 0; authdata[nadata] != NULL; nadata++)
502 for (i = 0; in_authdata[i] != NULL; i++)
505 if (authdata == NULL) {
506 authdata = (krb5_authdata **)calloc(i + 1, sizeof(krb5_authdata *));
508 authdata = (krb5_authdata **)realloc(authdata,
509 ((nadata + i + 1) * sizeof(krb5_authdata *)));
511 if (authdata == NULL)
515 krb5_error_code code;
518 code = krb5_copy_authdata(context, in_authdata, &tmp);
525 for (i = 0, j = 0; in_authdata[i] != NULL; i++) {
526 if (ignore_kdc_issued &&
527 is_kdc_issued_authdatum(context, in_authdata[i], 0)) {
528 free(in_authdata[i]->contents);
529 free(in_authdata[i]);
531 authdata[nadata + j++] = in_authdata[i];
534 authdata[nadata + j] = NULL;
538 if (authdata[0] == NULL) {
543 *out_authdata = authdata;
548 /* Handle copying TGS-REQ authorization data into reply */
549 static krb5_error_code
550 handle_request_authdata (krb5_context context,
552 krb5_db_entry *client,
553 krb5_db_entry *server,
554 krb5_db_entry *krbtgt,
555 krb5_keyblock *client_key,
556 krb5_keyblock *server_key,
557 krb5_keyblock *krbtgt_key,
559 krb5_kdc_req *request,
560 krb5_const_principal for_user_princ,
561 krb5_enc_tkt_part *enc_tkt_request,
562 krb5_enc_tkt_part *enc_tkt_reply)
564 krb5_error_code code;
567 if (request->msg_type != KRB5_TGS_REQ ||
568 request->authorization_data.ciphertext.data == NULL)
571 assert(enc_tkt_request != NULL);
573 scratch.length = request->authorization_data.ciphertext.length;
574 scratch.data = malloc(scratch.length);
575 if (scratch.data == NULL)
579 * RFC 4120 requires authdata in the TGS body to be encrypted in
580 * the subkey with usage 5 if a subkey is present, and in the TGS
581 * session key with key usage 4 if it is not. Prior to krb5 1.7,
582 * we got this wrong, always decrypting the authorization data
583 * with the TGS session key and usage 4. For the sake of
584 * conservatism, try the decryption the old way (wrong if
585 * client_key is a subkey) first, and then try again the right way
586 * (in the case where client_key is a subkey) if the first way
589 code = krb5_c_decrypt(context,
590 enc_tkt_request->session,
591 KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY,
592 0, &request->authorization_data,
595 code = krb5_c_decrypt(context,
597 KRB5_KEYUSAGE_TGS_REQ_AD_SUBKEY,
598 0, &request->authorization_data,
606 /* scratch now has the authorization data, so we decode it, and make
607 * it available to subsequent authdata plugins
609 code = decode_krb5_authdata(&scratch, &request->unenc_authdata);
617 if (has_mandatory_for_kdc_authdata(context, request->unenc_authdata))
618 return KRB5KDC_ERR_POLICY;
620 code = merge_authdata(context,
621 request->unenc_authdata,
622 &enc_tkt_reply->authorization_data,
624 TRUE); /* ignore_kdc_issued */
629 /* Handle copying TGT authorization data into reply */
630 static krb5_error_code
631 handle_tgt_authdata (krb5_context context,
633 krb5_db_entry *client,
634 krb5_db_entry *server,
635 krb5_db_entry *krbtgt,
636 krb5_keyblock *client_key,
637 krb5_keyblock *server_key,
638 krb5_keyblock *krbtgt_key,
640 krb5_kdc_req *request,
641 krb5_const_principal for_user_princ,
642 krb5_enc_tkt_part *enc_tkt_request,
643 krb5_enc_tkt_part *enc_tkt_reply)
645 if (request->msg_type != KRB5_TGS_REQ)
648 if (has_mandatory_for_kdc_authdata(context,
649 enc_tkt_request->authorization_data))
650 return KRB5KDC_ERR_POLICY;
652 return merge_authdata(context,
653 enc_tkt_request->authorization_data,
654 &enc_tkt_reply->authorization_data,
656 TRUE); /* ignore_kdc_issued */
659 /* Handle backend-managed authorization data */
660 static krb5_error_code
661 handle_kdb_authdata (krb5_context context,
663 krb5_db_entry *client,
664 krb5_db_entry *server,
665 krb5_db_entry *krbtgt,
666 krb5_keyblock *client_key,
667 krb5_keyblock *server_key,
668 krb5_keyblock *krbtgt_key,
670 krb5_kdc_req *request,
671 krb5_const_principal for_user_princ,
672 krb5_enc_tkt_part *enc_tkt_request,
673 krb5_enc_tkt_part *enc_tkt_reply)
675 krb5_error_code code;
676 krb5_authdata **tgt_authdata, **db_authdata = NULL;
677 krb5_boolean tgs_req = (request->msg_type == KRB5_TGS_REQ);
678 krb5_const_principal actual_client;
681 * Check whether KDC issued authorization data should be included.
682 * A server can explicitly disable the inclusion of authorization
683 * data by setting the KRB5_KDB_NO_AUTH_DATA_REQUIRED flag on its
684 * principal entry. Otherwise authorization data will be included
685 * if it was present in the TGT, the client is from another realm
686 * or protocol transition/constrained delegation was used, or, in
687 * the AS-REQ case, if the pre-auth data indicated the PAC should
691 assert(enc_tkt_request != NULL);
693 if (isflagset(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED))
696 if (enc_tkt_request->authorization_data == NULL &&
697 !isflagset(flags, KRB5_KDB_FLAG_CROSS_REALM | KRB5_KDB_FLAGS_S4U))
700 assert(enc_tkt_reply->times.authtime == enc_tkt_request->times.authtime);
702 if (!isflagset(flags, KRB5_KDB_FLAG_INCLUDE_PAC))
707 * We have this special case for protocol transition, because for
708 * cross-realm protocol transition the ticket reply client will
709 * not be changed until the final hop.
711 if (isflagset(flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION))
712 actual_client = for_user_princ;
714 actual_client = enc_tkt_reply->client;
716 tgt_authdata = tgs_req ? enc_tkt_request->authorization_data : NULL;
717 code = krb5_db_sign_authdata(context, flags, actual_client, client,
718 server, krbtgt, client_key, server_key,
719 krbtgt_key, enc_tkt_reply->session,
720 enc_tkt_reply->times.authtime, tgt_authdata,
723 code = merge_authdata(context,
725 &enc_tkt_reply->authorization_data,
727 FALSE); /* !ignore_kdc_issued */
729 krb5_free_authdata(context, db_authdata);
730 } else if (code == KRB5_PLUGIN_OP_NOTSUPP)
737 handle_authdata (krb5_context context,
739 krb5_db_entry *client,
740 krb5_db_entry *server,
741 krb5_db_entry *krbtgt,
742 krb5_keyblock *client_key,
743 krb5_keyblock *server_key,
744 krb5_keyblock *krbtgt_key,
746 krb5_kdc_req *request,
747 krb5_const_principal for_user_princ,
748 krb5_enc_tkt_part *enc_tkt_request,
749 krb5_enc_tkt_part *enc_tkt_reply)
751 krb5_error_code code = 0;
754 for (i = 0; i < n_authdata_systems; i++) {
755 const krb5_authdata_systems *asys = &authdata_systems[i];
756 if (isflagset(enc_tkt_reply->flags, TKT_FLG_ANONYMOUS) &&
757 !isflagset(asys->flags, AUTHDATA_FLAG_ANONYMOUS))
760 switch (asys->type) {
761 case AUTHDATA_SYSTEM_V0:
762 /* V0 was only in AS-REQ code path */
763 if (request->msg_type != KRB5_AS_REQ)
766 code = (*asys->handle_authdata.v0)(context, client, req_pkt,
767 request, enc_tkt_reply);
769 case AUTHDATA_SYSTEM_V2:
770 code = (*asys->handle_authdata.v2)(context, flags,
771 client, server, krbtgt,
772 client_key, server_key, krbtgt_key,
773 req_pkt, request, for_user_princ,
784 emsg = krb5_get_error_message (context, code);
785 krb5_klog_syslog(LOG_INFO, _("authdata (%s) handling failure: %s"),
787 krb5_free_error_message (context, emsg);
789 if (asys->flags & AUTHDATA_FLAG_CRITICAL)
797 static krb5_error_code
798 make_ad_signedpath_data(krb5_context context,
799 krb5_const_principal client,
800 krb5_timestamp authtime,
801 krb5_principal *deleg_path,
802 krb5_pa_data **method_data,
803 krb5_authdata **authdata,
806 krb5_ad_signedpath_data sp_data;
807 krb5_authdata **sign_authdata = NULL;
809 krb5_error_code code;
811 memset(&sp_data, 0, sizeof(sp_data));
813 if (authdata != NULL) {
814 for (i = 0; authdata[i] != NULL; i++)
820 sign_authdata = k5alloc((i + 1) * sizeof(krb5_authdata *), &code);
821 if (sign_authdata == NULL)
824 for (i = 0, j = 0; authdata[i] != NULL; i++) {
825 if (is_kdc_issued_authdatum(context, authdata[i],
826 KRB5_AUTHDATA_SIGNTICKET))
829 sign_authdata[j++] = authdata[i];
832 sign_authdata[j] = NULL;
835 sp_data.client = (krb5_principal)client;
836 sp_data.authtime = authtime;
837 sp_data.delegated = deleg_path;
838 sp_data.method_data = method_data;
839 sp_data.authorization_data = sign_authdata;
841 code = encode_krb5_ad_signedpath_data(&sp_data, data);
843 if (sign_authdata != NULL)
849 static krb5_error_code
850 verify_ad_signedpath_checksum(krb5_context context,
851 const krb5_db_entry *krbtgt,
852 krb5_keyblock *krbtgt_key,
853 krb5_enc_tkt_part *enc_tkt_part,
854 krb5_principal *deleg_path,
855 krb5_pa_data **method_data,
856 krb5_checksum *cksum,
859 krb5_error_code code;
864 if (!krb5_c_is_keyed_cksum(cksum->checksum_type))
865 return KRB5KRB_AP_ERR_INAPP_CKSUM;
867 code = make_ad_signedpath_data(context,
868 enc_tkt_part->client,
869 enc_tkt_part->times.authtime,
872 enc_tkt_part->authorization_data,
877 code = krb5_c_verify_checksum(context,
879 KRB5_KEYUSAGE_AD_SIGNEDPATH,
884 krb5_free_data(context, data);
889 static krb5_error_code
890 verify_ad_signedpath(krb5_context context,
891 krb5_db_entry *krbtgt,
892 krb5_keyblock *krbtgt_key,
893 krb5_enc_tkt_part *enc_tkt_part,
894 krb5_principal **pdelegated,
895 krb5_boolean *path_is_signed)
897 krb5_error_code code;
898 krb5_ad_signedpath *sp = NULL;
899 krb5_authdata **sp_authdata = NULL;
903 *path_is_signed = FALSE;
905 code = krb5_find_authdata(context, enc_tkt_part->authorization_data, NULL,
906 KRB5_AUTHDATA_SIGNTICKET, &sp_authdata);
910 if (sp_authdata == NULL ||
911 sp_authdata[0]->ad_type != KRB5_AUTHDATA_SIGNTICKET ||
912 sp_authdata[1] != NULL)
915 enc_sp.data = (char *)sp_authdata[0]->contents;
916 enc_sp.length = sp_authdata[0]->length;
918 code = decode_krb5_ad_signedpath(&enc_sp, &sp);
920 /* Treat an invalid signedpath authdata element as a missing one, since
921 * we believe MS is using the same number for something else. */
926 code = verify_ad_signedpath_checksum(context,
937 if (*path_is_signed) {
938 *pdelegated = sp->delegated;
939 sp->delegated = NULL;
943 krb5_free_ad_signedpath(context, sp);
944 krb5_free_authdata(context, sp_authdata);
949 static krb5_error_code
950 make_ad_signedpath_checksum(krb5_context context,
951 krb5_const_principal for_user_princ,
952 const krb5_db_entry *krbtgt,
953 krb5_keyblock *krbtgt_key,
954 krb5_enc_tkt_part *enc_tkt_part,
955 krb5_principal *deleg_path,
956 krb5_pa_data **method_data,
957 krb5_checksum *cksum)
959 krb5_error_code code;
961 krb5_cksumtype cksumtype;
962 krb5_const_principal client;
964 if (for_user_princ != NULL)
965 client = for_user_princ;
967 client = enc_tkt_part->client;
969 code = make_ad_signedpath_data(context,
971 enc_tkt_part->times.authtime,
974 enc_tkt_part->authorization_data,
979 code = krb5int_c_mandatory_cksumtype(context,
983 krb5_free_data(context, data);
987 if (!krb5_c_is_keyed_cksum(cksumtype)) {
988 krb5_free_data(context, data);
989 return KRB5KRB_AP_ERR_INAPP_CKSUM;
992 code = krb5_c_make_checksum(context, cksumtype, krbtgt_key,
993 KRB5_KEYUSAGE_AD_SIGNEDPATH, data,
996 krb5_free_data(context, data);
1001 static krb5_error_code
1002 make_ad_signedpath(krb5_context context,
1003 krb5_const_principal for_user_princ,
1004 krb5_principal server,
1005 const krb5_db_entry *krbtgt,
1006 krb5_keyblock *krbtgt_key,
1007 krb5_principal *deleg_path,
1008 krb5_enc_tkt_part *enc_tkt_reply)
1010 krb5_error_code code;
1011 krb5_ad_signedpath sp;
1013 krb5_data *data = NULL;
1014 krb5_authdata ad_datum, *ad_data[2];
1015 krb5_authdata **if_relevant = NULL;
1017 memset(&sp, 0, sizeof(sp));
1019 sp.enctype = krbtgt_key->enctype;
1021 if (deleg_path != NULL) {
1022 for (i = 0; deleg_path[i] != NULL; i++)
1027 sp.delegated = k5alloc((i + (server ? 1 : 0) + 1) *
1028 sizeof(krb5_principal), &code);
1032 /* Combine existing and new transited services, if any */
1033 if (deleg_path != NULL)
1034 memcpy(sp.delegated, deleg_path, i * sizeof(krb5_principal));
1036 sp.delegated[i++] = server;
1037 sp.delegated[i] = NULL;
1038 sp.method_data = NULL;
1040 code = make_ad_signedpath_checksum(context,
1049 if (code == KRB5KRB_AP_ERR_INAPP_CKSUM) {
1051 * In the hopefully unlikely case the TGS key enctype
1052 * has an unkeyed mandatory checksum type, do not fail
1053 * so we do not prevent the KDC from servicing requests.
1060 code = encode_krb5_ad_signedpath(&sp, &data);
1064 ad_datum.ad_type = KRB5_AUTHDATA_SIGNTICKET;
1065 ad_datum.contents = (krb5_octet *)data->data;
1066 ad_datum.length = data->length;
1068 ad_data[0] = &ad_datum;
1071 code = krb5_encode_authdata_container(context,
1072 KRB5_AUTHDATA_IF_RELEVANT,
1078 code = merge_authdata(context,
1080 &enc_tkt_reply->authorization_data,
1082 FALSE); /* !ignore_kdc_issued */
1086 if_relevant = NULL; /* merge_authdata() freed */
1089 if (sp.delegated != NULL)
1091 krb5_free_authdata(context, if_relevant);
1092 krb5_free_data(context, data);
1093 krb5_free_checksum_contents(context, &sp.checksum);
1094 krb5_free_pa_data(context, sp.method_data);
1100 free_deleg_path(krb5_context context, krb5_principal *deleg_path)
1102 if (deleg_path != NULL) {
1105 for (i = 0; deleg_path[i] != NULL; i++)
1106 krb5_free_principal(context, deleg_path[i]);
1112 * Returns TRUE if the Windows 2000 PAC is the only element in the
1113 * supplied authorization data.
1116 only_pac_p(krb5_context context, krb5_authdata **authdata)
1118 return has_kdc_issued_authdata(context,
1119 authdata, KRB5_AUTHDATA_WIN2K_PAC) &&
1120 (authdata[1] == NULL);
1123 static krb5_error_code
1124 handle_signedpath_authdata (krb5_context context,
1126 krb5_db_entry *client,
1127 krb5_db_entry *server,
1128 krb5_db_entry *krbtgt,
1129 krb5_keyblock *client_key,
1130 krb5_keyblock *server_key,
1131 krb5_keyblock *krbtgt_key,
1133 krb5_kdc_req *request,
1134 krb5_const_principal for_user_princ,
1135 krb5_enc_tkt_part *enc_tkt_request,
1136 krb5_enc_tkt_part *enc_tkt_reply)
1138 krb5_error_code code = 0;
1139 krb5_principal *deleg_path = NULL;
1140 krb5_boolean signed_path = FALSE;
1141 krb5_boolean s4u2proxy;
1143 s4u2proxy = isflagset(flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);
1146 * The Windows PAC fulfils the same role as the signed path
1147 * if it is the only authorization data element.
1149 if (request->msg_type == KRB5_TGS_REQ &&
1150 !only_pac_p(context, enc_tkt_request->authorization_data)) {
1151 code = verify_ad_signedpath(context,
1160 if (s4u2proxy && signed_path == FALSE) {
1161 code = KRB5KDC_ERR_BADOPTION;
1166 /* No point in including signedpath authdata for a cross-realm TGT, since
1167 * it will be presented to a different KDC. */
1168 if (!is_cross_tgs_principal(server->princ) &&
1169 !only_pac_p(context, enc_tkt_reply->authorization_data)) {
1170 code = make_ad_signedpath(context,
1172 s4u2proxy ? client->princ : NULL,
1182 free_deleg_path(context, deleg_path);