aboutsummaryrefslogtreecommitdiff
path: root/src/kdc/kdc_authdata.c
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2009-11-14 04:46:30 +0000
committerGreg Hudson <ghudson@mit.edu>2009-11-14 04:46:30 +0000
commit0524889196c42d81dcc4c74277522b46f987cabb (patch)
tree9f906eb1a4a32346ae94837c4fe199410e2dd10f /src/kdc/kdc_authdata.c
parent26044e2a3c3104b9c3f32a6ae58145e7e6394672 (diff)
downloadkrb5-0524889196c42d81dcc4c74277522b46f987cabb.zip
krb5-0524889196c42d81dcc4c74277522b46f987cabb.tar.gz
krb5-0524889196c42d81dcc4c74277522b46f987cabb.tar.bz2
Constrained delegation without PAC support
Merge Luke's users/lhoward/s4u2proxy branch to trunk. Implements a Heimdal-compatible mechanism for allowing constrained delegation without back-end support for PACs. Back-end support exists in LDAP only (via a new krbAllowedToDelegateTo attribute), not DB2. ticket: 6580 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23160 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/kdc/kdc_authdata.c')
-rw-r--r--src/kdc/kdc_authdata.c684
1 files changed, 625 insertions, 59 deletions
diff --git a/src/kdc/kdc_authdata.c b/src/kdc/kdc_authdata.c
index 3cef2d7..aa55ef4 100644
--- a/src/kdc/kdc_authdata.c
+++ b/src/kdc/kdc_authdata.c
@@ -3,7 +3,7 @@
* kdc/kdc_authdata.c
*
* Copyright (C) 2007 Apple Inc. All Rights Reserved.
- * Copyright (C) 2008 by the Massachusetts Institute of Technology.
+ * Copyright (C) 2008, 2009 by the Massachusetts Institute of Technology.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
@@ -36,7 +36,7 @@
#include <syslog.h>
#include <assert.h>
-#include "../include/krb5/authdata_plugin.h"
+#include <krb5/authdata_plugin.h>
#if TARGET_OS_MAC
static const char *objdirs[] = { KRB5_AUTHDATA_PLUGIN_BUNDLE_DIR,
@@ -70,7 +70,6 @@ typedef krb5_error_code (*init_proc)
typedef void (*fini_proc)
(krb5_context, void *);
-/* Internal authdata system for copying TGS-REQ authdata to ticket */
static krb5_error_code handle_request_authdata
(krb5_context context,
unsigned int flags,
@@ -86,7 +85,6 @@ static krb5_error_code handle_request_authdata
krb5_enc_tkt_part *enc_tkt_request,
krb5_enc_tkt_part *enc_tkt_reply);
-/* Internal authdata system for handling KDC-issued authdata */
static krb5_error_code handle_tgt_authdata
(krb5_context context,
unsigned int flags,
@@ -102,6 +100,27 @@ static krb5_error_code handle_tgt_authdata
krb5_enc_tkt_part *enc_tkt_request,
krb5_enc_tkt_part *enc_tkt_reply);
+static krb5_error_code
+handle_kdb_authdata(krb5_context context, unsigned int flags,
+ krb5_db_entry *client, krb5_db_entry *server,
+ krb5_db_entry *krbtgt, krb5_keyblock *client_key,
+ krb5_keyblock *server_key, krb5_keyblock *krbtgt_key,
+ krb5_data *req_pkt, krb5_kdc_req *request,
+ krb5_const_principal for_user_princ,
+ krb5_enc_tkt_part *enc_tkt_request,
+ krb5_enc_tkt_part *enc_tkt_reply);
+
+static krb5_error_code
+handle_signedpath_authdata(krb5_context context, unsigned int flags,
+ krb5_db_entry *client, krb5_db_entry *server,
+ krb5_db_entry *krbtgt, krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_keyblock *krbtgt_key,
+ krb5_data *req_pkt, krb5_kdc_req *request,
+ krb5_const_principal for_user_princ,
+ krb5_enc_tkt_part *enc_tkt_request,
+ krb5_enc_tkt_part *enc_tkt_reply);
+
typedef struct _krb5_authdata_systems {
const char *name;
#define AUTHDATA_SYSTEM_UNKNOWN -1
@@ -109,6 +128,7 @@ typedef struct _krb5_authdata_systems {
#define AUTHDATA_SYSTEM_V2 2
int type;
#define AUTHDATA_FLAG_CRITICAL 0x1
+#define AUTHDATA_FLAG_PRE_PLUGIN 0x2
int flags;
void *plugin_context;
init_proc init;
@@ -120,10 +140,46 @@ typedef struct _krb5_authdata_systems {
} krb5_authdata_systems;
static krb5_authdata_systems static_authdata_systems[] = {
- { "tgs_req", AUTHDATA_SYSTEM_V2, AUTHDATA_FLAG_CRITICAL,
- NULL, NULL, NULL, { handle_request_authdata } },
- { "tgt", AUTHDATA_SYSTEM_V2, AUTHDATA_FLAG_CRITICAL,
- NULL, NULL, NULL, { handle_tgt_authdata } },
+ {
+ /* Propagate client-submitted authdata */
+ "tgs_req",
+ AUTHDATA_SYSTEM_V2,
+ AUTHDATA_FLAG_CRITICAL | AUTHDATA_FLAG_PRE_PLUGIN,
+ NULL,
+ NULL,
+ NULL,
+ { handle_request_authdata }
+ },
+ {
+ /* Propagate TGT authdata */
+ "tgt",
+ AUTHDATA_SYSTEM_V2,
+ AUTHDATA_FLAG_CRITICAL,
+ NULL,
+ NULL,
+ NULL,
+ { handle_tgt_authdata }
+ },
+ {
+ /* Verify and issue KDB issued authdata */
+ "kdb",
+ AUTHDATA_SYSTEM_V2,
+ AUTHDATA_FLAG_CRITICAL,
+ NULL,
+ NULL,
+ NULL,
+ { handle_kdb_authdata }
+ },
+ {
+ /* Verify and issue signed delegation path */
+ "signedpath",
+ AUTHDATA_SYSTEM_V2,
+ AUTHDATA_FLAG_CRITICAL,
+ NULL,
+ NULL,
+ NULL,
+ { handle_signedpath_authdata }
+ }
};
static krb5_authdata_systems *authdata_systems;
@@ -204,6 +260,19 @@ load_authdata_plugins(krb5_context context)
k = 0;
+ /*
+ * Special case to ensure that handle_request_authdata is
+ * first in the list, to make unenc_authdata available to
+ * plugins.
+ */
+ for (i = 0; i < (sizeof(static_authdata_systems) /
+ sizeof(static_authdata_systems[0])); i++) {
+ if ((static_authdata_systems[i].flags & AUTHDATA_FLAG_PRE_PLUGIN) == 0)
+ continue;
+ assert(static_authdata_systems[i].init == NULL);
+ authdata_systems[k++] = static_authdata_systems[i];
+ }
+
/* Add dynamically loaded V2 plugins */
if (authdata_plugins_ftables_v2 != NULL) {
struct krb5plugin_authdata_server_ftable_v2 *ftable;
@@ -280,20 +349,13 @@ load_authdata_plugins(krb5_context context)
}
}
- /* Add the locally-supplied mechanisms to the dynamic list first. */
for (i = 0;
i < sizeof(static_authdata_systems) / sizeof(static_authdata_systems[0]);
i++) {
- authdata_systems[k] = static_authdata_systems[i];
- /* Try to initialize the authdata system. If it fails, we'll remove it
- * from the list of systems we'll be using. */
- server_init_proc = static_authdata_systems[i].init;
- if ((server_init_proc != NULL) &&
- ((*server_init_proc)(context, &authdata_systems[k].plugin_context) != 0)) {
- memset(&authdata_systems[k], 0, sizeof(authdata_systems[k]));
+ if (static_authdata_systems[i].flags & AUTHDATA_FLAG_PRE_PLUGIN)
continue;
- }
- k++;
+ assert(static_authdata_systems[i].init == NULL);
+ authdata_systems[k++] = static_authdata_systems[i];
}
n_authdata_systems = k;
@@ -331,12 +393,106 @@ unload_authdata_plugins(krb5_context context)
return 0;
}
-/* Merge authdata. If copy == 0, in_authdata is invalid on return */
+/*
+ * Returns TRUE if authdata should be filtered when copying from
+ * untrusted authdata.
+ */
+static krb5_boolean
+is_kdc_issued_authdatum (krb5_context context,
+ krb5_authdata *authdata,
+ krb5_authdatatype desired_type)
+{
+ krb5_boolean ret = FALSE;
+ krb5_authdatatype ad_type;
+ unsigned int i, count = 0;
+ krb5_authdatatype *ad_types = NULL;
+
+ if (authdata->ad_type == KRB5_AUTHDATA_IF_RELEVANT) {
+ if (krb5int_get_authdata_containee_types(context,
+ authdata,
+ &count,
+ &ad_types) != 0)
+ goto cleanup;
+ } else {
+ ad_type = authdata->ad_type;
+ count = 1;
+ ad_types = &ad_type;
+ }
+
+ for (i = 0; i < count; i++) {
+ switch (ad_types[i]) {
+ case KRB5_AUTHDATA_SIGNTICKET:
+ case KRB5_AUTHDATA_KDC_ISSUED:
+ case KRB5_AUTHDATA_WIN2K_PAC:
+ ret = desired_type ? (desired_type == ad_types[i]) : TRUE;
+ break;
+ default:
+ ret = FALSE;
+ break;
+ }
+ if (ret)
+ break;
+ }
+
+cleanup:
+ if (authdata->ad_type == KRB5_AUTHDATA_IF_RELEVANT &&
+ ad_types != NULL)
+ free(ad_types);
+
+ return ret;
+}
+
+static krb5_boolean
+has_kdc_issued_authdata (krb5_context context,
+ krb5_authdata **authdata,
+ krb5_authdatatype desired_type)
+{
+ int i;
+ krb5_boolean ret = FALSE;
+
+ if (authdata != NULL) {
+ for (i = 0; authdata[i] != NULL; i++) {
+ if (is_kdc_issued_authdatum(context, authdata[i], desired_type)) {
+ ret = TRUE;
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static krb5_boolean
+has_mandatory_for_kdc_authdata (krb5_context context,
+ krb5_authdata **authdata)
+{
+ int i;
+ krb5_boolean ret = FALSE;
+
+ if (authdata != NULL) {
+ for (i = 0; authdata[i] != NULL; i++) {
+ if (authdata[0]->ad_type == KRB5_AUTHDATA_MANDATORY_FOR_KDC) {
+ ret = TRUE;
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Merge authdata.
+ *
+ * If copy is FALSE, in_authdata is invalid on successful return.
+ * If ignore_kdc_issued is TRUE, KDC-issued authdata is not copied.
+ */
static krb5_error_code
merge_authdata (krb5_context context,
krb5_authdata **in_authdata,
krb5_authdata ***out_authdata,
- krb5_boolean copy)
+ krb5_boolean copy,
+ krb5_boolean ignore_kdc_issued)
{
size_t i, nadata = 0;
krb5_authdata **authdata = *out_authdata;
@@ -356,7 +512,7 @@ merge_authdata (krb5_context context,
authdata = (krb5_authdata **)calloc(i + 1, sizeof(krb5_authdata *));
} else {
authdata = (krb5_authdata **)realloc(authdata,
- ((nadata + i + 1) * sizeof(krb5_authdata *)));
+ ((nadata + i + 1) * sizeof(krb5_authdata *)));
}
if (authdata == NULL)
return ENOMEM;
@@ -372,13 +528,24 @@ merge_authdata (krb5_context context,
in_authdata = tmp;
}
- for (i = 0; in_authdata[i] != NULL; i++)
- authdata[nadata + i] = in_authdata[i];
+ for (i = 0; in_authdata[i] != NULL; i++) {
+ if (ignore_kdc_issued &&
+ is_kdc_issued_authdatum(context, in_authdata[i], 0)) {
+ free(in_authdata[i]->contents);
+ free(in_authdata[i]);
+ } else
+ authdata[nadata + i] = in_authdata[i];
+ }
authdata[nadata + i] = NULL;
free(in_authdata);
+ if (authdata[0] == NULL) {
+ free(authdata);
+ authdata = NULL;
+ }
+
*out_authdata = authdata;
return 0;
@@ -453,13 +620,19 @@ handle_request_authdata (krb5_context context,
free(scratch.data);
- code = merge_authdata(context, request->unenc_authdata,
- &enc_tkt_reply->authorization_data, TRUE /* copy */);
+ if (has_mandatory_for_kdc_authdata(context, request->unenc_authdata))
+ return KRB5KDC_ERR_POLICY;
+
+ code = merge_authdata(context,
+ request->unenc_authdata,
+ &enc_tkt_reply->authorization_data,
+ TRUE, /* copy */
+ TRUE); /* ignore_kdc_issued */
return code;
}
-/* Handle backend-managed authorization data */
+/* Handle copying TGT authorization data into reply */
static krb5_error_code
handle_tgt_authdata (krb5_context context,
unsigned int flags,
@@ -475,6 +648,36 @@ handle_tgt_authdata (krb5_context context,
krb5_enc_tkt_part *enc_tkt_request,
krb5_enc_tkt_part *enc_tkt_reply)
{
+ if (request->msg_type != KRB5_TGS_REQ)
+ return 0;
+
+ if (has_mandatory_for_kdc_authdata(context,
+ enc_tkt_request->authorization_data))
+ return KRB5KDC_ERR_POLICY;
+
+ return merge_authdata(context,
+ enc_tkt_request->authorization_data,
+ &enc_tkt_reply->authorization_data,
+ TRUE, /* copy */
+ TRUE); /* ignore_kdc_issued */
+}
+
+/* Handle backend-managed authorization data */
+static krb5_error_code
+handle_kdb_authdata (krb5_context context,
+ unsigned int flags,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_keyblock *krbtgt_key,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_const_principal for_user_princ,
+ krb5_enc_tkt_part *enc_tkt_request,
+ krb5_enc_tkt_part *enc_tkt_reply)
+{
krb5_error_code code;
krb5_authdata **db_authdata = NULL;
krb5_boolean tgs_req = (request->msg_type == KRB5_TGS_REQ);
@@ -489,12 +692,6 @@ handle_tgt_authdata (krb5_context context,
* or protocol transition/constrained delegation was used, or, in
* the AS-REQ case, if the pre-auth data indicated the PAC should
* be present.
- *
- * We permit sign_authorization_data() to return a krb5_db_entry
- * representing the principal associated with the authorization
- * data, in case that principal is not local to our realm and we
- * need to perform additional checks (such as disabling delegation
- * for cross-realm protocol transition below).
*/
if (tgs_req) {
assert(enc_tkt_request != NULL);
@@ -522,16 +719,6 @@ handle_tgt_authdata (krb5_context context,
else
actual_client = enc_tkt_reply->client;
- /*
- * If the backend does not implement the sign authdata method, then
- * just copy the TGT authorization data into the reply, except for
- * the constrained delegation case (which requires special handling
- * because it will promote untrusted auth data to KDC issued auth
- * data; this requires backend-specific code)
- *
- * Presently this interface does not support using request auth data
- * to influence (eg. possibly restrict) the reply auth data.
- */
code = sign_db_authdata(context,
flags,
actual_client,
@@ -545,26 +732,16 @@ handle_tgt_authdata (krb5_context context,
tgs_req ? enc_tkt_request->authorization_data : NULL,
enc_tkt_reply->session,
&db_authdata);
- if (code == KRB5_KDB_DBTYPE_NOSUP) {
- assert(db_authdata == NULL);
-
- if (isflagset(flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION))
- return KRB5KDC_ERR_POLICY;
-
- if (tgs_req)
- return merge_authdata(context, enc_tkt_request->authorization_data,
- &enc_tkt_reply->authorization_data, TRUE);
- else
- return 0;
- }
-
- if (db_authdata != NULL) {
- code = merge_authdata(context, db_authdata,
+ if (code == 0) {
+ code = merge_authdata(context,
+ db_authdata,
&enc_tkt_reply->authorization_data,
- FALSE);
+ FALSE, /* !copy */
+ FALSE); /* !ignore_kdc_issued */
if (code != 0)
krb5_free_authdata(context, db_authdata);
- }
+ } else if (code == KRB5_KDB_DBTYPE_NOSUP)
+ code = 0;
return code;
}
@@ -627,3 +804,392 @@ handle_authdata (krb5_context context,
return code;
}
+
+static krb5_error_code
+make_ad_signedpath_data(krb5_context context,
+ krb5_const_principal client,
+ krb5_timestamp authtime,
+ krb5_principal *deleg_path,
+ krb5_pa_data **method_data,
+ krb5_authdata **authdata,
+ krb5_data **data)
+{
+ krb5_ad_signedpath_data sp_data;
+ krb5_authdata **sign_authdata = NULL;
+ int i, j;
+ krb5_error_code code;
+
+ memset(&sp_data, 0, sizeof(sp_data));
+
+ if (authdata != NULL) {
+ for (i = 0; authdata[i] != NULL; i++)
+ ;
+ } else
+ i = 0;
+
+ if (i != 0) {
+ sign_authdata = k5alloc((i + 1) * sizeof(krb5_authdata *), &code);
+ if (code != 0)
+ return code;
+
+ for (i = 0, j = 0; authdata[i] != NULL; i++) {
+ if (is_kdc_issued_authdatum(context, authdata[i],
+ KRB5_AUTHDATA_SIGNTICKET))
+ continue;
+
+ sign_authdata[j++] = authdata[i];
+ }
+
+ sign_authdata[j] = NULL;
+ }
+
+ sp_data.client = (krb5_principal)client;
+ sp_data.authtime = authtime;
+ sp_data.delegated = deleg_path;
+ sp_data.method_data = method_data;
+ sp_data.authorization_data = sign_authdata;
+
+ code = encode_krb5_ad_signedpath_data(&sp_data, data);
+
+ if (sign_authdata != NULL)
+ free(sign_authdata);
+
+ return code;
+}
+
+static krb5_error_code
+verify_ad_signedpath_checksum(krb5_context context,
+ const krb5_db_entry *krbtgt,
+ krb5_keyblock *krbtgt_key,
+ krb5_enc_tkt_part *enc_tkt_part,
+ krb5_principal *deleg_path,
+ krb5_pa_data **method_data,
+ krb5_checksum *cksum,
+ krb5_boolean *valid)
+{
+ krb5_error_code code;
+ krb5_data *data;
+
+ *valid = FALSE;
+
+ if (!krb5_c_is_keyed_cksum(cksum->checksum_type))
+ return KRB5KRB_AP_ERR_INAPP_CKSUM;
+
+ code = make_ad_signedpath_data(context,
+ enc_tkt_part->client,
+ enc_tkt_part->times.authtime,
+ deleg_path,
+ method_data,
+ enc_tkt_part->authorization_data,
+ &data);
+ if (code != 0)
+ return code;
+
+ code = krb5_c_verify_checksum(context,
+ krbtgt_key,
+ KRB5_KEYUSAGE_AD_SIGNEDPATH,
+ data,
+ cksum,
+ valid);
+
+ krb5_free_data(context, data);
+
+ if (code == 0 && *valid == FALSE)
+ code = KRB5KRB_AP_ERR_MODIFIED;
+
+ return code;
+}
+
+
+static krb5_error_code
+verify_ad_signedpath(krb5_context context,
+ krb5_db_entry *krbtgt,
+ krb5_keyblock *krbtgt_key,
+ krb5_enc_tkt_part *enc_tkt_part,
+ krb5_principal **pdelegated,
+ krb5_boolean *path_is_signed)
+{
+ krb5_error_code code;
+ krb5_ad_signedpath *sp = NULL;
+ krb5_authdata **sp_authdata = NULL;
+ krb5_data enc_sp;
+
+ *pdelegated = NULL;
+ *path_is_signed = FALSE;
+
+ code = krb5int_find_authdata(context,
+ enc_tkt_part->authorization_data,
+ NULL,
+ KRB5_AUTHDATA_SIGNTICKET,
+ &sp_authdata);
+ if (code != 0)
+ goto cleanup;
+
+ if (sp_authdata == NULL ||
+ sp_authdata[0]->ad_type != KRB5_AUTHDATA_SIGNTICKET ||
+ sp_authdata[1] != NULL)
+ goto cleanup;
+
+ enc_sp.data = (char *)sp_authdata[0]->contents;
+ enc_sp.length = sp_authdata[0]->length;
+
+ code = decode_krb5_ad_signedpath(&enc_sp, &sp);
+ if (code != 0)
+ goto cleanup;
+
+ code = verify_ad_signedpath_checksum(context,
+ krbtgt,
+ krbtgt_key,
+ enc_tkt_part,
+ sp->delegated,
+ sp->method_data,
+ &sp->checksum,
+ path_is_signed);
+ if (code != 0)
+ goto cleanup;
+
+ *pdelegated = sp->delegated;
+ sp->delegated = NULL;
+
+cleanup:
+ krb5_free_ad_signedpath(context, sp);
+ krb5_free_authdata(context, sp_authdata);
+
+ return code;
+}
+
+static krb5_error_code
+make_ad_signedpath_checksum(krb5_context context,
+ krb5_const_principal for_user_princ,
+ const krb5_db_entry *krbtgt,
+ krb5_keyblock *krbtgt_key,
+ krb5_enc_tkt_part *enc_tkt_part,
+ krb5_principal *deleg_path,
+ krb5_pa_data **method_data,
+ krb5_checksum *cksum)
+{
+ krb5_error_code code;
+ krb5_data *data;
+ krb5_cksumtype cksumtype;
+ krb5_const_principal client;
+
+ if (for_user_princ != NULL)
+ client = for_user_princ;
+ else
+ client = enc_tkt_part->client;
+
+ code = make_ad_signedpath_data(context,
+ client,
+ enc_tkt_part->times.authtime,
+ deleg_path,
+ method_data,
+ enc_tkt_part->authorization_data,
+ &data);
+ if (code != 0)
+ return code;
+
+ code = krb5int_c_mandatory_cksumtype(context,
+ krbtgt_key->enctype,
+ &cksumtype);
+ if (code != 0) {
+ krb5_free_data(context, data);
+ return code;
+ }
+
+ if (!krb5_c_is_keyed_cksum(cksumtype)) {
+ krb5_free_data(context, data);
+ return KRB5KRB_AP_ERR_INAPP_CKSUM;
+ }
+
+ code = krb5_c_make_checksum(context, cksumtype, krbtgt_key,
+ KRB5_KEYUSAGE_AD_SIGNEDPATH, data,
+ cksum);
+
+ krb5_free_data(context, data);
+
+ return code;
+}
+
+static krb5_error_code
+make_ad_signedpath(krb5_context context,
+ krb5_const_principal for_user_princ,
+ krb5_principal server,
+ const krb5_db_entry *krbtgt,
+ krb5_keyblock *krbtgt_key,
+ krb5_principal *deleg_path,
+ krb5_enc_tkt_part *enc_tkt_reply)
+{
+ krb5_error_code code;
+ krb5_ad_signedpath sp;
+ int i;
+ krb5_data *data = NULL;
+ krb5_authdata ad_datum, *ad_data[2];
+ krb5_authdata **if_relevant = NULL;
+
+ memset(&sp, 0, sizeof(sp));
+
+ sp.enctype = krbtgt_key->enctype;
+
+ if (deleg_path != NULL) {
+ for (i = 0; deleg_path[i] != NULL; i++)
+ ;
+ } else
+ i = 0;
+
+ sp.delegated = k5alloc((i + (server ? 1 : 0) + 1) *
+ sizeof(krb5_principal), &code);
+ if (code != 0)
+ goto cleanup;
+
+ /* Combine existing and new transited services, if any */
+ if (deleg_path != NULL)
+ memcpy(sp.delegated, deleg_path, i * sizeof(krb5_principal));
+ if (server != NULL)
+ sp.delegated[i++] = server;
+ sp.delegated[i] = NULL;
+ sp.method_data = NULL;
+
+ code = make_ad_signedpath_checksum(context,
+ for_user_princ,
+ krbtgt,
+ krbtgt_key,
+ enc_tkt_reply,
+ sp.delegated,
+ sp.method_data,
+ &sp.checksum);
+ if (code != 0) {
+ if (code == KRB5KRB_AP_ERR_INAPP_CKSUM) {
+ /*
+ * In the hopefully unlikely case the TGS key enctype
+ * has an unkeyed mandatory checksum type, do not fail
+ * so we do not prevent the KDC from servicing requests.
+ */
+ code = 0;
+ }
+ goto cleanup;
+ }
+
+ code = encode_krb5_ad_signedpath(&sp, &data);
+ if (code != 0)
+ goto cleanup;
+
+ ad_datum.ad_type = KRB5_AUTHDATA_SIGNTICKET;
+ ad_datum.contents = (krb5_octet *)data->data;
+ ad_datum.length = data->length;
+
+ ad_data[0] = &ad_datum;
+ ad_data[1] = NULL;
+
+ code = krb5_encode_authdata_container(context,
+ KRB5_AUTHDATA_IF_RELEVANT,
+ ad_data,
+ &if_relevant);
+ if (code != 0)
+ goto cleanup;
+
+ code = merge_authdata(context,
+ if_relevant,
+ &enc_tkt_reply->authorization_data,
+ FALSE, /* !copy */
+ FALSE); /* !ignore_kdc_issued */
+ if (code != 0)
+ goto cleanup;
+
+ if_relevant = NULL; /* merge_authdata() freed */
+
+cleanup:
+ if (sp.delegated != NULL)
+ free(sp.delegated);
+ krb5_free_authdata(context, if_relevant);
+ krb5_free_data(context, data);
+ krb5_free_checksum_contents(context, &sp.checksum);
+ krb5_free_pa_data(context, sp.method_data);
+
+ return code;
+}
+
+static void
+free_deleg_path(krb5_context context, krb5_principal *deleg_path)
+{
+ if (deleg_path != NULL) {
+ int i;
+
+ for (i = 0; deleg_path[i] != NULL; i++)
+ krb5_free_principal(context, deleg_path[i]);
+ free(deleg_path);
+ }
+}
+
+/*
+ * Returns TRUE if the Windows 2000 PAC is the only element in the
+ * supplied authorization data.
+ */
+static krb5_boolean
+only_pac_p(krb5_context context, krb5_authdata **authdata)
+{
+ return has_kdc_issued_authdata(context,
+ authdata, KRB5_AUTHDATA_WIN2K_PAC) &&
+ (authdata[1] == NULL);
+}
+
+static krb5_error_code
+handle_signedpath_authdata (krb5_context context,
+ unsigned int flags,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_keyblock *krbtgt_key,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_const_principal for_user_princ,
+ krb5_enc_tkt_part *enc_tkt_request,
+ krb5_enc_tkt_part *enc_tkt_reply)
+{
+ krb5_error_code code = 0;
+ krb5_principal *deleg_path = NULL;
+ krb5_boolean signed_path = FALSE;
+ krb5_boolean s4u2proxy;
+
+ s4u2proxy = isflagset(flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);
+
+ /*
+ * The Windows PAC fulfils the same role as the signed path
+ * if it is the only authorization data element.
+ */
+ if (request->msg_type == KRB5_TGS_REQ &&
+ !only_pac_p(context, enc_tkt_request->authorization_data)) {
+ code = verify_ad_signedpath(context,
+ krbtgt,
+ krbtgt_key,
+ enc_tkt_request,
+ &deleg_path,
+ &signed_path);
+ if (code != 0)
+ goto cleanup;
+
+ if (s4u2proxy && signed_path == FALSE) {
+ code = KRB5KDC_ERR_BADOPTION;
+ goto cleanup;
+ }
+ }
+
+ if (!isflagset(flags, KRB5_KDB_FLAG_CROSS_REALM) &&
+ !only_pac_p(context, enc_tkt_reply->authorization_data)) {
+ code = make_ad_signedpath(context,
+ for_user_princ,
+ s4u2proxy ? client->princ : NULL,
+ krbtgt,
+ krbtgt_key,
+ deleg_path,
+ enc_tkt_reply);
+ if (code != 0)
+ goto cleanup;
+ }
+
+cleanup:
+ free_deleg_path(context, deleg_path);
+
+ return code;
+}