diff options
| author | Greg Hudson <ghudson@mit.edu> | 2009-09-13 02:52:23 +0000 |
|---|---|---|
| committer | Greg Hudson <ghudson@mit.edu> | 2009-09-13 02:52:23 +0000 |
| commit | 0e39f8a3ad915eeb0131fb4a87b0fef304101cfd (patch) | |
| tree | 6c6d7fd4b23f4724156300b5505433b13cfe9fb6 /src/kdc | |
| parent | f89b62fe9fd7b0cb10d7e2ff542fb18c1b56d35d (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.c | 82 | ||||
| -rw-r--r-- | src/kdc/kdc_authdata.c | 9 | ||||
| -rw-r--r-- | src/kdc/kdc_preauth.c | 19 | ||||
| -rw-r--r-- | src/kdc/kdc_util.c | 480 | ||||
| -rw-r--r-- | src/kdc/kdc_util.h | 21 |
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, |
