summaryrefslogtreecommitdiffstats
path: root/src/kdc
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2009-09-13 02:52:23 +0000
committerGreg Hudson <ghudson@mit.edu>2009-09-13 02:52:23 +0000
commit0e39f8a3ad915eeb0131fb4a87b0fef304101cfd (patch)
tree6c6d7fd4b23f4724156300b5505433b13cfe9fb6 /src/kdc
parentf89b62fe9fd7b0cb10d7e2ff542fb18c1b56d35d (diff)
Implement s4u extensions
Merge Luke's users/lhoward/s4u branch to trunk. Implements S4U2Self and S4U2Proxy extensions. ticket: 6563 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@22736 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/kdc')
-rw-r--r--src/kdc/do_tgs_req.c82
-rw-r--r--src/kdc/kdc_authdata.c9
-rw-r--r--src/kdc/kdc_preauth.c19
-rw-r--r--src/kdc/kdc_util.c480
-rw-r--r--src/kdc/kdc_util.h21
5 files changed, 464 insertions, 147 deletions
diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c
index a99dc35ba..37a69e10d 100644
--- a/src/kdc/do_tgs_req.c
+++ b/src/kdc/do_tgs_req.c
@@ -117,7 +117,7 @@ process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from,
krb5_enc_tkt_part *header_enc_tkt = NULL; /* ticket granting or evidence ticket */
krb5_db_entry client, krbtgt;
int c_nprincs = 0, k_nprincs = 0;
- krb5_pa_for_user *for_user = NULL; /* protocol transition request */
+ krb5_pa_s4u_x509_user *s4u_x509_user = NULL; /* protocol transition request */
krb5_authdata **kdc_issued_auth_data = NULL; /* auth data issued by KDC */
unsigned int c_flags = 0, s_flags = 0; /* client/server KDB flags */
char *s4u_name = NULL;
@@ -131,7 +131,7 @@ process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from,
krb5_data scratch;
session_key.contents = NULL;
-
+
retval = decode_krb5_tgs_req(pkt, &request);
if (retval)
return retval;
@@ -292,12 +292,20 @@ tgt_again:
!krb5_principal_compare(kdc_context, tgs_server, server.princ);
/* Check for protocol transition */
- errcode = kdc_process_s4u2self_req(kdc_context, request, header_enc_tkt->client,
- &server, header_enc_tkt->session, kdc_time,
- &for_user, &client, &c_nprincs, &status);
+ errcode = kdc_process_s4u2self_req(kdc_context,
+ request,
+ header_enc_tkt->client,
+ &server,
+ subkey,
+ header_enc_tkt->session,
+ kdc_time,
+ &s4u_x509_user,
+ &client,
+ &c_nprincs,
+ &status);
if (errcode)
goto cleanup;
- if (for_user != NULL)
+ if (s4u_x509_user != NULL)
setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION);
/*
@@ -438,19 +446,32 @@ tgt_again:
/* processing of any of these flags. For example, some */
/* realms may refuse to issue renewable tickets */
- if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE))
+ if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) {
setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
- if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
- if (!krb5_is_tgs_principal(server.princ) &&
- is_local_principal(server.princ)) {
- if (isflagset(server.attributes, KRB5_KDB_OK_TO_AUTH_AS_DELEGATE))
- setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
- else
+
+ if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
+ /*
+ * If S4U2Self principal is not forwardable, then mark ticket as
+ * unforwardable. This behaviour matches Windows, but it is
+ * different to the MIT AS-REQ path, which returns an error
+ * (KDC_ERR_POLICY) if forwardable tickets cannot be issued.
+ *
+ * Consider this block the S4U2Self equivalent to
+ * validate_forwardable().
+ */
+ if (c_nprincs &&
+ isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))
+ clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
+ /*
+ * OK_TO_AUTH_AS_DELEGATE must be set on the service requesting
+ * S4U2Self in order for forwardable tickets to be returned.
+ */
+ else if (!is_referral &&
+ !isflagset(server.attributes, KRB5_KDB_OK_TO_AUTH_AS_DELEGATE))
clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
}
- if (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))
- clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
}
+
if (isflagset(request->kdc_options, KDC_OPT_FORWARDED)) {
setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED);
@@ -560,7 +581,7 @@ tgt_again:
enc_tkt_reply.times.starttime = 0;
if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
- errcode = krb5_unparse_name(kdc_context, for_user->user, &s4u_name);
+ errcode = krb5_unparse_name(kdc_context, s4u_x509_user->user_id.user, &s4u_name);
} else if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
errcode = krb5_unparse_name(kdc_context, header_enc_tkt->client, &s4u_name);
} else {
@@ -670,8 +691,8 @@ tgt_again:
enc_tkt_reply.authorization_data = NULL;
if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
- is_local_principal(header_enc_tkt->client))
- enc_tkt_reply.client = for_user->user;
+ !isflagset(c_flags, KRB5_KDB_FLAG_CROSS_REALM))
+ enc_tkt_reply.client = s4u_x509_user->user_id.user;
else
enc_tkt_reply.client = header_enc_tkt->client;
@@ -685,7 +706,8 @@ tgt_again:
&encrypting_key, /* U2U or server key */
pkt,
request,
- for_user ? for_user->user : NULL,
+ s4u_x509_user ?
+ s4u_x509_user->user_id.user : NULL,
header_enc_tkt,
&enc_tkt_reply);
if (errcode) {
@@ -845,6 +867,20 @@ tgt_again:
/* Start assembling the response */
reply.msg_type = KRB5_TGS_REP;
reply.padata = 0;/* always */
+ if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
+ find_pa_data(request->padata, KRB5_PADATA_S4U_X509_USER) != NULL) {
+ errcode = kdc_make_s4u2self_rep(kdc_context,
+ subkey,
+ header_ticket->enc_part2->session,
+ s4u_x509_user,
+ &reply,
+ &reply_encpart);
+ if (errcode) {
+ status = "KDC_RETURN_S4U2SELF_PADATA";
+ goto cleanup;
+ }
+ }
+
reply.client = enc_tkt_reply.client;
reply.enc_part.kvno = 0;/* We are using the session key */
reply.ticket = &ticket_reply;
@@ -958,14 +994,18 @@ cleanup:
krb5_db_free_principal(kdc_context, &krbtgt, k_nprincs);
if (c_nprincs)
krb5_db_free_principal(kdc_context, &client, c_nprincs);
- if (for_user != NULL)
- krb5_free_pa_for_user(kdc_context, for_user);
+ if (s4u_x509_user != NULL)
+ krb5_free_pa_s4u_x509_user(kdc_context, s4u_x509_user);
if (kdc_issued_auth_data != NULL)
krb5_free_authdata(kdc_context, kdc_issued_auth_data);
if (s4u_name != NULL)
free(s4u_name);
if (subkey != NULL)
krb5_free_keyblock(kdc_context, subkey);
+ if (reply.padata)
+ krb5_free_pa_data(kdc_context, reply.padata);
+ if (reply_encpart.enc_padata)
+ krb5_free_pa_data(kdc_context, reply_encpart.enc_padata);
return retval;
}
diff --git a/src/kdc/kdc_authdata.c b/src/kdc/kdc_authdata.c
index 43ea0869a..504d3fbdd 100644
--- a/src/kdc/kdc_authdata.c
+++ b/src/kdc/kdc_authdata.c
@@ -544,9 +544,18 @@ handle_tgt_authdata (krb5_context context,
}
if (ad_nprincs != 0) {
+ /*
+ * This code was submitted by Novell; however there is no
+ * mention in [MS-SFU] of needing to examine the authorization
+ * data to clear the forwardable flag. My understanding is that
+ * the state of the forwardable flag is propagated through the
+ * cross-realm TGTs.
+ */
+#if 0
if (isflagset(flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
isflagset(ad_entry.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))
clear(enc_tkt_reply->flags, TKT_FLG_FORWARDABLE);
+#endif
krb5_db_free_principal(context, &ad_entry, ad_nprincs);
diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
index cc7ae34ed..2149fd1ac 100644
--- a/src/kdc/kdc_preauth.c
+++ b/src/kdc/kdc_preauth.c
@@ -1349,25 +1349,6 @@ cleanup:
}
static krb5_boolean
-enctype_requires_etype_info_2(krb5_enctype enctype)
-{
- switch(enctype) {
- case ENCTYPE_DES_CBC_CRC:
- case ENCTYPE_DES_CBC_MD4:
- case ENCTYPE_DES_CBC_MD5:
- case ENCTYPE_DES3_CBC_SHA1:
- case ENCTYPE_DES3_CBC_RAW:
- case ENCTYPE_ARCFOUR_HMAC:
- case ENCTYPE_ARCFOUR_HMAC_EXP :
- return 0;
- default:
- if (krb5_c_valid_enctype(enctype))
- return 1;
- else return 0;
- }
-}
-
-static krb5_boolean
request_contains_enctype (krb5_context context, const krb5_kdc_req *request,
krb5_enctype enctype)
{
diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c
index 88ef11062..6ac528953 100644
--- a/src/kdc/kdc_util.c
+++ b/src/kdc/kdc_util.c
@@ -223,7 +223,7 @@ comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket,
krb5_pa_data *
find_pa_data(krb5_pa_data **padata, krb5_preauthtype pa_type)
{
-return krb5int_find_pa_data(kdc_context, padata, pa_type);
+ return krb5int_find_pa_data(kdc_context, padata, pa_type);
}
krb5_error_code
@@ -371,7 +371,8 @@ kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
}
/* make sure the client is of proper lineage (see above) */
- if (foreign_server && !find_pa_data(request->padata, KRB5_PADATA_FOR_USER)) {
+ if (foreign_server &&
+ !find_pa_data(request->padata, KRB5_PADATA_FOR_USER)) {
if (is_local_principal((*ticket)->enc_part2->client)) {
/* someone in a foreign realm claiming to be local */
krb5_klog_syslog(LOG_INFO, "PROCESS_TGS: failed lineage check");
@@ -926,7 +927,8 @@ fail:
* as a com_err error number!
*/
#define AS_INVALID_OPTIONS (KDC_OPT_FORWARDED | KDC_OPT_PROXY |\
-KDC_OPT_VALIDATE | KDC_OPT_RENEW | KDC_OPT_ENC_TKT_IN_SKEY)
+ KDC_OPT_VALIDATE | KDC_OPT_RENEW | \
+ KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)
int
validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
krb5_db_entry server, krb5_timestamp kdc_time,
@@ -998,17 +1000,9 @@ validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
* preauthentication data is absent in the request.
*
* Hence, this check most be done after the check for preauth
- * data, and is now performed by validate_forwardable().
+ * data, and is now performed by validate_forwardable() (the
+ * contents of which were previously below).
*/
-#if 0
- /* Client and server must allow forwardable tickets */
- if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
- (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) ||
- isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) {
- *status = "FORWARDABLE NOT ALLOWED";
- return(KDC_ERR_POLICY);
- }
-#endif
/* Client and server must allow renewable tickets */
if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
@@ -1793,7 +1787,7 @@ sign_db_authdata (krb5_context context,
}
static krb5_error_code
-verify_s4u2self_checksum(krb5_context context,
+verify_for_user_checksum(krb5_context context,
krb5_keyblock *key,
krb5_pa_for_user *req)
{
@@ -1852,7 +1846,7 @@ verify_s4u2self_checksum(krb5_context context,
&valid);
if (code == 0 && valid == FALSE)
- code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ code = KRB5KRB_AP_ERR_MODIFIED;
free(data.data);
@@ -1860,55 +1854,246 @@ verify_s4u2self_checksum(krb5_context context,
}
/*
- * Protocol transition validation code based on AS-REQ
- * validation code
+ * Legacy protocol transition (Windows 2003 and above)
*/
-static int
-validate_s4u2self_request(krb5_kdc_req *request,
- const krb5_db_entry *client,
- krb5_timestamp kdc_time,
- const char **status)
+static krb5_error_code
+kdc_process_for_user(krb5_context context,
+ krb5_pa_data *pa_data,
+ krb5_keyblock *tgs_session,
+ krb5_pa_s4u_x509_user **s4u_x509_user,
+ const char **status)
{
- int errcode;
- krb5_db_entry server = { 0 };
-
- /* The client must not be expired */
- if (client->expiration && client->expiration < kdc_time) {
- *status = "CLIENT EXPIRED";
- return KDC_ERR_NAME_EXP;
+ krb5_error_code code;
+ krb5_pa_for_user *for_user;
+ krb5_data req_data;
+
+ req_data.length = pa_data->length;
+ req_data.data = (char *)pa_data->contents;
+
+ code = decode_krb5_pa_for_user(&req_data, &for_user);
+ if (code)
+ return code;
+
+ code = verify_for_user_checksum(context, tgs_session, for_user);
+ if (code) {
+ *status = "INVALID_S4U2SELF_CHECKSUM";
+ krb5_free_pa_for_user(kdc_context, for_user);
+ return code;
}
- /* The client's password must not be expired, unless the server is
- a KRB5_KDC_PWCHANGE_SERVICE. */
- if (client->pw_expiration && client->pw_expiration < kdc_time) {
- *status = "CLIENT KEY EXPIRED";
- return KDC_ERR_KEY_EXP;
+ *s4u_x509_user = calloc(1, sizeof(krb5_pa_s4u_x509_user));
+ if (*s4u_x509_user == NULL) {
+ krb5_free_pa_for_user(kdc_context, for_user);
+ return ENOMEM;
}
+ (*s4u_x509_user)->user_id.user = for_user->user;
+ for_user->user = NULL;
+ krb5_free_pa_for_user(context, for_user);
+
+ return 0;
+}
+
+static krb5_error_code
+verify_s4u_x509_user_checksum(krb5_context context,
+ krb5_keyblock *key,
+ krb5_data *req_data,
+ krb5_int32 kdc_req_nonce,
+ krb5_pa_s4u_x509_user *req)
+{
+ krb5_error_code code;
+ krb5_data scratch;
+ krb5_boolean valid = FALSE;
+
+ if (enctype_requires_etype_info_2(key->enctype) &&
+ !krb5_c_is_keyed_cksum(req->cksum.checksum_type))
+ return KRB5KRB_AP_ERR_INAPP_CKSUM;
+
+ if (req->user_id.nonce != kdc_req_nonce)
+ return KRB5KRB_AP_ERR_MODIFIED;
+
/*
- * If the client requires password changing, then return an
- * error; S4U2Self cannot be used to change a password.
+ * Verify checksum over the encoded userid. If that fails,
+ * re-encode, and verify that. This is similar to the
+ * behaviour in kdc_process_tgs_req().
*/
- if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PWCHANGE)) {
- *status = "REQUIRED PWCHANGE";
- return KDC_ERR_KEY_EXP;
+ if (fetch_asn1_field((unsigned char *)req_data->data, 1, 0, &scratch) < 0)
+ return ASN1_PARSE_ERROR;
+
+ code = krb5_c_verify_checksum(context,
+ key,
+ KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST,
+ &scratch,
+ &req->cksum,
+ &valid);
+ if (code != 0)
+ return code;
+
+ if (valid == FALSE) {
+ krb5_data *data;
+
+ code = encode_krb5_s4u_userid(&req->user_id, &data);
+ if (code != 0)
+ return code;
+
+ code = krb5_c_verify_checksum(context,
+ key,
+ KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST,
+ data,
+ &req->cksum,
+ &valid);
+
+ krb5_free_data(context, data);
+
+ if (code != 0)
+ return code;
}
- /* Check to see if client is locked out */
- if (isflagset(client->attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
- *status = "CLIENT LOCKED OUT";
- return KDC_ERR_C_PRINCIPAL_UNKNOWN;
+ return valid ? 0 : KRB5KRB_AP_ERR_MODIFIED;
+}
+
+/*
+ * New protocol transition request (Windows 2008 and above)
+ */
+static krb5_error_code
+kdc_process_s4u_x509_user(krb5_context context,
+ krb5_kdc_req *request,
+ krb5_pa_data *pa_data,
+ krb5_keyblock *tgs_subkey,
+ krb5_keyblock *tgs_session,
+ krb5_pa_s4u_x509_user **s4u_x509_user,
+ const char **status)
+{
+ krb5_error_code code;
+ krb5_data req_data;
+
+ req_data.length = pa_data->length;
+ req_data.data = (char *)pa_data->contents;
+
+ code = decode_krb5_pa_s4u_x509_user(&req_data, s4u_x509_user);
+ if (code)
+ return code;
+
+ code = verify_s4u_x509_user_checksum(context,
+ tgs_subkey ? tgs_subkey :
+ tgs_session,
+ &req_data,
+ request->nonce, *s4u_x509_user);
+
+ if (code) {
+ *status = "INVALID_S4U2SELF_CHECKSUM";
+ krb5_free_pa_s4u_x509_user(context, *s4u_x509_user);
+ *s4u_x509_user = NULL;
+ return code;
}
+ if (krb5_princ_size(context, (*s4u_x509_user)->user_id.user) == 0 ||
+ (*s4u_x509_user)->user_id.subject_cert.length != 0) {
+ *status = "INVALID_S4U2SELF_REQUEST";
+ krb5_free_pa_s4u_x509_user(context, *s4u_x509_user);
+ *s4u_x509_user = NULL;
+ return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+ }
+
+ return 0;
+}
+
+krb5_error_code
+kdc_make_s4u2self_rep(krb5_context context,
+ krb5_keyblock *tgs_subkey,
+ krb5_keyblock *tgs_session,
+ krb5_pa_s4u_x509_user *req_s4u_user,
+ krb5_kdc_rep *reply,
+ krb5_enc_kdc_rep_part *reply_encpart)
+{
+ krb5_error_code code;
+ krb5_data *data = NULL;
+ krb5_pa_s4u_x509_user rep_s4u_user;
+ krb5_pa_data padata;
+ krb5_enctype enctype;
+ krb5_keyusage usage;
+
+ memset(&rep_s4u_user, 0, sizeof(rep_s4u_user));
+
+ rep_s4u_user.user_id.nonce = req_s4u_user->user_id.nonce;
+ rep_s4u_user.user_id.user = req_s4u_user->user_id.user;
+ rep_s4u_user.user_id.options =
+ req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE;
+
+ code = encode_krb5_s4u_userid(&rep_s4u_user.user_id, &data);
+ if (code != 0)
+ goto cleanup;
+
+ if (req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE)
+ usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REPLY;
+ else
+ usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST;
+
+ code = krb5_c_make_checksum(context, req_s4u_user->cksum.checksum_type,
+ tgs_subkey != NULL ? tgs_subkey : tgs_session,
+ usage, data,
+ &rep_s4u_user.cksum);
+ if (code != 0)
+ goto cleanup;
+
+ krb5_free_data(context, data);
+ data = NULL;
+
+ code = encode_krb5_pa_s4u_x509_user(&rep_s4u_user, &data);
+ if (code != 0)
+ goto cleanup;
+
+ padata.magic = KV5M_PA_DATA;
+ padata.pa_type = KRB5_PADATA_S4U_X509_USER;
+ padata.length = data->length;
+ padata.contents = (krb5_octet *)data->data;
+
+ code = add_pa_data_element(context, &padata, &reply->padata, FALSE);
+ if (code != 0)
+ goto cleanup;
+
+ free(data);
+ data = NULL;
+
+ if (tgs_subkey != NULL)
+ enctype = tgs_subkey->enctype;
+ else
+ enctype = tgs_session->enctype;
+
/*
- * Check against local policy
+ * Owing to a bug in Windows, unkeyed checksums were used for older
+ * enctypes, including rc4-hmac. A forthcoming workaround for this
+ * includes the checksum bytes in the encrypted padata.
*/
- errcode = against_local_policy_as(request, *client, server,
- kdc_time, status);
- if (errcode)
- return errcode;
+ if ((req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE) &&
+ enctype_requires_etype_info_2(enctype) == FALSE) {
+ padata.length = req_s4u_user->cksum.length +
+ rep_s4u_user.cksum.length;
+ padata.contents = malloc(padata.length);
+ if (padata.contents == NULL) {
+ code = ENOMEM;
+ goto cleanup;
+ }
- return 0;
+ memcpy(padata.contents,
+ req_s4u_user->cksum.contents,
+ req_s4u_user->cksum.length);
+ memcpy(&padata.contents[req_s4u_user->cksum.length],
+ rep_s4u_user.cksum.contents,
+ rep_s4u_user.cksum.length);
+
+ code = add_pa_data_element(context,&padata,
+ &reply_encpart->enc_padata, FALSE);
+ if (code != 0)
+ goto cleanup;
+ }
+
+cleanup:
+ if (rep_s4u_user.cksum.contents != NULL)
+ krb5_free_checksum_contents(context, &rep_s4u_user.cksum);
+ krb5_free_data(context, data);
+
+ return code;
}
/*
@@ -1919,92 +2104,116 @@ kdc_process_s4u2self_req(krb5_context context,
krb5_kdc_req *request,
krb5_const_principal client_princ,
const krb5_db_entry *server,
- krb5_keyblock *subkey,
+ krb5_keyblock *tgs_subkey,
+ krb5_keyblock *tgs_session,
krb5_timestamp kdc_time,
- krb5_pa_for_user **for_user,
+ krb5_pa_s4u_x509_user **s4u_x509_user,
krb5_db_entry *princ,
int *nprincs,
const char **status)
{
krb5_error_code code;
- krb5_pa_data **pa_data;
- krb5_data req_data;
+ krb5_pa_data *pa_data;
krb5_boolean more;
+ int flags;
*nprincs = 0;
memset(princ, 0, sizeof(*princ));
- if (request->padata == NULL) {
- return 0;
- }
-
- for (pa_data = request->padata; *pa_data != NULL; pa_data++) {
- if ((*pa_data)->pa_type == KRB5_PADATA_FOR_USER)
- break;
- }
- if (*pa_data == NULL) {
- return 0;
+ pa_data = find_pa_data(request->padata, KRB5_PADATA_S4U_X509_USER);
+ if (pa_data != NULL) {
+ code = kdc_process_s4u_x509_user(context,
+ request,
+ pa_data,
+ tgs_subkey,
+ tgs_session,
+ s4u_x509_user,
+ status);
+ if (code != 0)
+ return code;
+ } else {
+ pa_data = find_pa_data(request->padata, KRB5_PADATA_FOR_USER);
+ if (pa_data != NULL) {
+ code = kdc_process_for_user(context,
+ pa_data,
+ tgs_session,
+ s4u_x509_user,
+ status);
+ if (code != 0)
+ return code;
+ } else
+ return 0;
}
-#if 0
/*
- * Ignore request if the server principal is a TGS, not so much
- * to avoid unconstrained tickets being issued (as that would
- * require knowing the TGS key anyway) but so that we do not
- * block the server referral path.
+ * We need to compare the client name in the TGT with the requested
+ * server name. Supporting server name aliases without assuming a
+ * global name service makes this difficult to do.
+ *
+ * The comparison below handles the following cases (note that the
+ * term "principal name" below excludes the realm).
+ *
+ * (1) The requested service is a host-based service with two name
+ * components, in which case we assume the principal name to
+ * contain sufficient qualifying information. The realm is
+ * ignored for the purpose of comparison.
+ *
+ * (2) The requested service name is an enterprise principal name:
+ * the service principal name is compared with the unparsed
+ * form of the client name (including its realm).
+ *
+ * (3) The requested service is some other name type: an exact
+ * match is required.
+ *
+ * An alternative would be to look up the server once again with
+ * FLAG_CANONICALIZE | FLAG_CLIENT_REFERRALS_ONLY set, do an exact
+ * match between the returned name and client_princ. However, this
+ * assumes that the client set FLAG_CANONICALIZE when requesting
+ * the TGT and that we have a global name service.
*/
- if (krb5_is_tgs_principal(server->princ)) {
- return 0;
- }
-#endif
-
- *status = "PROCESS_S4U2SELF_REQUEST";
-
- req_data.length = (*pa_data)->length;
- req_data.data = (char *)(*pa_data)->contents;
-
- code = decode_krb5_pa_for_user(&req_data, for_user);
- if (code) {
- return code;
- }
-
- if (krb5_princ_type(context, (*for_user)->user) !=
- KRB5_NT_ENTERPRISE_PRINCIPAL) {
- *status = "INVALID_S4U2SELF_REQUEST";
- return KRB5KDC_ERR_POLICY;
+ flags = 0;
+ switch (krb5_princ_type(kdc_context, request->server)) {
+ case KRB5_NT_SRV_HST: /* (1) */
+ if (krb5_princ_size(kdc_context, request->server) == 2)
+ flags |= KRB5_PRINCIPAL_COMPARE_IGNORE_REALM;
+ break;
+ case KRB5_NT_ENTERPRISE_PRINCIPAL: /* (2) */
+ flags |= KRB5_PRINCIPAL_COMPARE_ENTERPRISE;
+ break;
+ default: /* (3) */
+ break;
}
- code = verify_s4u2self_checksum(context, subkey, *for_user);
- if (code) {
- *status = "INVALID_S4U2SELF_CHECKSUM";
- krb5_free_pa_for_user(kdc_context, *for_user);
- *for_user = NULL;
- return code;
- }
- if (!krb5_principal_compare_flags(context, request->server, client_princ,
- KRB5_PRINCIPAL_COMPARE_ENTERPRISE)) {
+ if (!krb5_principal_compare_flags(context,
+ request->server,
+ client_princ,
+ flags)) {
*status = "INVALID_S4U2SELF_REQUEST";
- return KRB5KDC_ERR_POLICY;
+ return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; /* match Windows error code */
}
/*
* Protocol transition is mutually exclusive with renew/forward/etc
- * as well as user-to-user and constrained delegation.
+ * as well as user-to-user and constrained delegation. This check
+ * is also made in validate_as_request().
*
* We can assert from this check that the header ticket was a TGT, as
* that is validated previously in validate_tgs_request().
*/
- if (request->kdc_options & (NO_TGT_OPTION | KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)) {
+ if (request->kdc_options & AS_INVALID_OPTIONS) {
+ *status = "INVALID AS OPTIONS";
return KRB5KDC_ERR_BADOPTION;
}
/*
* Do not attempt to lookup principals in foreign realms.
*/
- if (is_local_principal((*for_user)->user)) {
+ if (is_local_principal((*s4u_x509_user)->user_id.user)) {
+ krb5_db_entry no_server;
+
*nprincs = 1;
code = krb5_db_get_principal_ext(kdc_context,
- (*for_user)->user,
+ (*s4u_x509_user)->user_id.user,
KRB5_KDB_FLAG_INCLUDE_PAC,
princ, nprincs, &more);
if (code) {
@@ -2021,14 +2230,15 @@ kdc_process_s4u2self_req(krb5_context context,
return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
}
- code = validate_s4u2self_request(request, princ, kdc_time, status);
+ memset(&no_server, 0, sizeof(no_server));
+
+ code = validate_as_request(request, *princ,
+ no_server, kdc_time, status);
if (code) {
return code;
}
}
- *status = NULL;
-
return 0;
}
@@ -2049,7 +2259,7 @@ check_allowed_to_delegate_to(krb5_context context,
/* Must be in same realm */
if (!krb5_realm_compare(context, server->princ, proxy)) {
- return KRB5KDC_ERR_BADOPTION;
+ return KRB5KDC_ERR_POLICY;
}
req.server = server;
@@ -2345,3 +2555,63 @@ log_tgs_alt_tgt(krb5_principal p)
/* OpenSolaris: audit_krb5kdc_tgs_req_alt_tgt(...) */
}
+krb5_boolean
+enctype_requires_etype_info_2(krb5_enctype enctype)
+{
+ switch(enctype) {
+ case ENCTYPE_DES_CBC_CRC:
+ case ENCTYPE_DES_CBC_MD4:
+ case ENCTYPE_DES_CBC_MD5:
+ case ENCTYPE_DES3_CBC_SHA1:
+ case ENCTYPE_DES3_CBC_RAW:
+ case ENCTYPE_ARCFOUR_HMAC:
+ case ENCTYPE_ARCFOUR_HMAC_EXP :
+ return 0;
+ default:
+ return krb5_c_valid_enctype(enctype);
+ }
+}
+
+/* XXX where are the generic helper routines for this? */
+krb5_error_code
+add_pa_data_element(krb5_context context,
+ krb5_pa_data *padata,
+ krb5_pa_data ***inout_padata,
+ krb5_boolean copy)
+{
+ int i;
+ krb5_pa_data **p;
+
+ if (*inout_padata != NULL) {
+ for (i = 0; (*inout_padata)[i] != NULL; i++)
+ ;
+ } else
+ i = 0;
+
+ p = realloc(*inout_padata, (i + 2) * sizeof(krb5_pa_data *));
+ if (p == NULL)
+ return ENOMEM;
+
+ *inout_padata = p;
+
+ p[i] = (krb5_pa_data *)malloc(sizeof(krb5_pa_data));
+ if (p[i] == NULL)
+ return ENOMEM;
+ *(p[i]) = *padata;
+
+ p[i + 1] = NULL;
+
+ if (copy) {
+ p[i]->contents = (krb5_octet *)malloc(padata->length);
+ if (p[i]->contents == NULL) {
+ free(p[i]);
+ p[i] = NULL;
+ return ENOMEM;
+ }
+
+ memcpy(p[i]->contents, padata->contents, padata->length);
+ }
+
+ return 0;
+}
+
diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h
index 060442604..cb8fb5f7a 100644
--- a/src/kdc/kdc_util.h
+++ b/src/kdc/kdc_util.h
@@ -150,6 +150,8 @@ int against_local_policy_tgs (krb5_kdc_req *, krb5_db_entry,
krb5_ticket *, const char **);
/* kdc_preauth.c */
+krb5_boolean enctype_requires_etype_info_2(krb5_enctype enctype);
+
const char * missing_required_preauth
(krb5_db_entry *client, krb5_db_entry *server,
krb5_enc_tkt_part *enc_tkt_reply);
@@ -177,6 +179,12 @@ krb5_error_code free_padata_context
krb5_pa_data *find_pa_data
(krb5_pa_data **padata, krb5_preauthtype pa_type);
+krb5_error_code add_pa_data_element
+ (krb5_context context,
+ krb5_pa_data *padata,
+ krb5_pa_data ***out_padata,
+ krb5_boolean copy);
+
/* kdc_authdata.c */
krb5_error_code load_authdata_plugins(krb5_context context);
krb5_error_code unload_authdata_plugins(krb5_context context);
@@ -239,13 +247,22 @@ krb5_error_code kdc_process_s4u2self_req
krb5_kdc_req *request,
krb5_const_principal client_princ,
const krb5_db_entry *server,
- krb5_keyblock *subkey,
+ krb5_keyblock *tgs_subkey,
+ krb5_keyblock *tgs_session,
krb5_timestamp kdc_time,
- krb5_pa_for_user **s4u2_req,
+ krb5_pa_s4u_x509_user **s4u2self_req,
krb5_db_entry *princ,
int *nprincs,
const char **status);
+krb5_error_code kdc_make_s4u2self_rep
+ (krb5_context context,
+ krb5_keyblock *tgs_subkey,
+ krb5_keyblock *tgs_session,
+ krb5_pa_s4u_x509_user *req_s4u_user,
+ krb5_kdc_rep *reply,
+ krb5_enc_kdc_rep_part *reply_encpart);
+
krb5_error_code kdc_process_s4u2proxy_req
(krb5_context context,
krb5_kdc_req *request,