diff options
author | Greg Hudson <ghudson@mit.edu> | 2022-08-09 12:22:43 -0400 |
---|---|---|
committer | Greg Hudson <ghudson@mit.edu> | 2022-10-03 19:33:18 -0400 |
commit | a9705a1e0b2cf0cde3e6f8dee14c25ffc074c00a (patch) | |
tree | 214890b43482c76732af282064484264612cfc86 | |
parent | 29600cf1db888d91c42cbd6cf72652afe8c1ee66 (diff) | |
download | krb5-a9705a1e0b2cf0cde3e6f8dee14c25ffc074c00a.tar.gz |
Refactor KDC TGS processing code
Split the TGS processing code into information gathering, constraint
and policy checking, and ticket-issuing steps, using a structure to
hold the gathered information. Split validate_tgs_request() into
validate_tgs_constraints() and check_tgs_policy() for better auditing.
Fold kdc_process_s4u2proxy_req() into check_tgs_policy(), except for
the get_pac_princ_with_realm() step which is now performed in
gather_tgs_req_info(). Modify some other utility functions to fit the
new design.
-rw-r--r-- | src/kdc/do_as_req.c | 3 | ||||
-rw-r--r-- | src/kdc/do_tgs_req.c | 1402 | ||||
-rw-r--r-- | src/kdc/fast_util.c | 2 | ||||
-rw-r--r-- | src/kdc/kdc_audit.c | 2 | ||||
-rw-r--r-- | src/kdc/kdc_log.c | 3 | ||||
-rw-r--r-- | src/kdc/kdc_util.c | 126 | ||||
-rw-r--r-- | src/kdc/kdc_util.h | 42 | ||||
-rw-r--r-- | src/kdc/tgs_policy.c | 116 |
8 files changed, 872 insertions, 824 deletions
diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c index 02b6eac04..6fb214b77 100644 --- a/src/kdc/do_as_req.c +++ b/src/kdc/do_as_req.c @@ -699,7 +699,8 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, state->server, &state->enc_tkt_reply.times.endtime); kdc_get_ticket_renewtime(realm, state->request, NULL, state->client, - state->server, &state->enc_tkt_reply); + state->server, &state->enc_tkt_reply.flags, + &state->enc_tkt_reply.times); /* * starttime is optional, and treated as authtime if not present. diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c index 63e79c082..bdf6a13ae 100644 --- a/src/kdc/do_tgs_req.c +++ b/src/kdc/do_tgs_req.c @@ -69,705 +69,114 @@ #include "adm_proto.h" #include <ctype.h> -static krb5_error_code -prepare_error_tgs(struct kdc_request_state *, krb5_kdc_req *, krb5_ticket *, - krb5_error_code, krb5_principal, krb5_data **, const char *, - krb5_pa_data **); - -static krb5_error_code -decrypt_2ndtkt(krb5_context, krb5_kdc_req *, krb5_flags, krb5_db_entry *, - krb5_keyblock *, const krb5_ticket **, krb5_pac *, - krb5_db_entry **, krb5_keyblock **, const char **); - -static krb5_error_code -gen_session_key(krb5_context, krb5_kdc_req *, krb5_db_entry *, - krb5_keyblock *, const char **); - -static krb5_int32 -find_referral_tgs(kdc_realm_t *, krb5_kdc_req *, krb5_principal *); - -static krb5_error_code -db_get_svc_princ(krb5_context, krb5_principal, krb5_flags, - krb5_db_entry **, const char **); - -static krb5_error_code -search_sprinc(kdc_realm_t *, krb5_kdc_req *, krb5_flags, - krb5_db_entry **, const char **); - -/*ARGSUSED*/ -krb5_error_code -process_tgs_req(krb5_kdc_req *request, krb5_data *pkt, - const krb5_fulladdr *from, kdc_realm_t *realm, - krb5_data **response) -{ - krb5_context context = realm->realm_context; - krb5_keyblock * subkey = 0; - krb5_keyblock *header_key = NULL; - krb5_keyblock *stkt_server_key = NULL; - krb5_keyblock *subject_key; - krb5_db_entry *server = NULL; - krb5_db_entry *stkt_server = NULL; - krb5_db_entry *subject_server; - krb5_kdc_rep reply; - krb5_enc_kdc_rep_part reply_encpart; - krb5_ticket ticket_reply, *header_ticket = 0; - const krb5_ticket *stkt = NULL; - krb5_enc_tkt_part enc_tkt_reply; - int newtransited = 0; - krb5_error_code retval = 0, errcode; - krb5_keyblock server_keyblock, *encrypting_key; - krb5_timestamp kdc_time, authtime = 0; - krb5_keyblock session_key, local_tgt_key; - krb5_keyblock *reply_key = NULL; - krb5_principal cprinc = NULL, sprinc = NULL, altcprinc = NULL; - krb5_principal stkt_authdata_client = NULL; - krb5_last_req_entry *nolrarray[2], nolrentry; - const char *status = 0; - krb5_enc_tkt_part *header_enc_tkt = NULL; /* TGT */ - krb5_enc_tkt_part *subject_tkt = NULL; /* TGT or evidence ticket */ - krb5_db_entry *client = NULL, *header_server = NULL; - krb5_db_entry *local_tgt, *local_tgt_storage = NULL; - 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 */ - krb5_boolean is_referral, is_crossrealm; - const char *emsg = NULL; - krb5_kvno ticket_kvno = 0; - struct kdc_request_state *state = NULL; - krb5_pa_data *pa_tgs_req; /*points into request*/ - krb5_data scratch; - krb5_pa_data **e_data = NULL; - krb5_audit_state *au_state = NULL; - krb5_data **auth_indicators = NULL; - krb5_pac header_pac = NULL, stkt_pac = NULL, subject_pac; - - memset(&reply, 0, sizeof(reply)); - memset(&reply_encpart, 0, sizeof(reply_encpart)); - memset(&ticket_reply, 0, sizeof(ticket_reply)); - memset(&enc_tkt_reply, 0, sizeof(enc_tkt_reply)); - memset(&server_keyblock, 0, sizeof(server_keyblock)); - memset(&local_tgt_key, 0, sizeof(local_tgt_key)); - session_key.contents = NULL; - - /* Save pointer to client-requested service principal, in case of - * errors before a successful call to search_sprinc(). */ - sprinc = request->server; - - if (request->msg_type != KRB5_TGS_REQ) { - krb5_free_kdc_req(context, request); - return KRB5_BADMSGTYPE; - } - - errcode = kdc_make_rstate(realm, &state); - if (errcode != 0) - goto cleanup; - - /* Initialize audit state. */ - errcode = kau_init_kdc_req(context, request, from, &au_state); - if (errcode) - goto cleanup; - - /* Seed the audit trail with the request ID and basic information. */ - kau_tgs_req(context, TRUE, au_state); - - errcode = kdc_process_tgs_req(realm, request, from, pkt, &header_ticket, - &header_server, &header_key, &subkey, - &pa_tgs_req); - if (header_ticket && header_ticket->enc_part2) - cprinc = header_ticket->enc_part2->client; - - if (errcode) { - status = "PROCESS_TGS"; - goto cleanup; - } - - if (!header_ticket) { - errcode = KRB5_NO_TKT_SUPPLIED; /* XXX? */ - goto cleanup; - } - errcode = kau_make_tkt_id(context, header_ticket, &au_state->tkt_in_id); - if (errcode) - goto cleanup; - - scratch.length = pa_tgs_req->length; - scratch.data = (char *) pa_tgs_req->contents; - errcode = kdc_find_fast(&request, &scratch, subkey, - header_ticket->enc_part2->session, state, NULL); - /* Reset sprinc because kdc_find_fast() can replace request. */ - sprinc = request->server; - if (errcode !=0) { - status = "FIND_FAST"; - goto cleanup; - } - if (sprinc == NULL) { - status = "NULL_SERVER"; - errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; - goto cleanup; - } - - errcode = get_local_tgt(context, &sprinc->realm, header_server, - &local_tgt, &local_tgt_storage, &local_tgt_key); - if (errcode) { - status = "GET_LOCAL_TGT"; - goto cleanup; - } - - errcode = get_verified_pac(context, header_ticket->enc_part2, - header_server->princ, header_key, local_tgt, - &local_tgt_key, &header_pac); - if (errcode) { - status = "HEADER_PAC"; - goto cleanup; - } - - /* Ignore (for now) the request modification due to FAST processing. */ - au_state->request = request; +struct tgs_req_info { + /* The decoded request. Ownership is transferred to this structure. This + * will be replaced with the inner FAST body if present. */ + krb5_kdc_req *req; /* - * Pointer to the encrypted part of the header ticket, which may be - * replaced to point to the encrypted part of the evidence ticket - * if constrained delegation is used. This simplifies the number of - * special cases for constrained delegation. + * The decrypted authentication header ticket from the request's + * PA-TGS-REQ, the KDB entry for its server, its encryption key, the + * PA-TGS-REQ subkey if present, and the decoded and verified header ticket + * PAC if present. */ - header_enc_tkt = header_ticket->enc_part2; + krb5_ticket *header_tkt; + krb5_db_entry *header_server; + krb5_keyblock *header_key; + krb5_keyblock *subkey; + krb5_pac header_pac; /* - * We've already dealt with the AP_REQ authentication, so we can - * use header_ticket freely. The encrypted part (if any) has been - * decrypted with the session key. + * If a second ticket is present and this is a U2U or S4U2Proxy request, + * the decoded and verified PAC if present, the KDB entry for the second + * ticket server server, and the key used to decrypt the second ticket. */ - - au_state->stage = SRVC_PRINC; - - /* XXX make sure server here has the proper realm...taken from AP_REQ - header? */ - - if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE)) - setflag(s_flags, KRB5_KDB_FLAG_REFERRAL_OK); - - errcode = search_sprinc(realm, request, s_flags, &server, &status); - if (errcode != 0) - goto cleanup; - sprinc = server->princ; - - /* If we got a cross-realm TGS which is not the requested server, we are - * issuing a referral (or alternate TGT, which we treat similarly). */ - is_referral = is_cross_tgs_principal(server->princ) && - !krb5_principal_compare(context, request->server, server->princ); - - au_state->stage = VALIDATE_POL; - - errcode = krb5_timeofday(context, &kdc_time); - if (errcode) - goto cleanup; - - is_crossrealm = !data_eq(header_server->princ->realm, sprinc->realm); - if (is_crossrealm) - setflag(c_flags, KRB5_KDB_FLAG_CROSS_REALM); - if (is_referral) - setflag(c_flags, KRB5_KDB_FLAG_ISSUING_REFERRAL); - - /* Check for protocol transition */ - errcode = kdc_process_s4u2self_req(context, request, server, subkey, - header_enc_tkt->session, &s4u_x509_user, - &client, &status); - if (s4u_x509_user != NULL || errcode != 0) { - if (s4u_x509_user != NULL) - au_state->s4u2self_user = s4u_x509_user->user_id.user; - au_state->status = status; - kau_s4u2self(context, errcode ? FALSE : TRUE, au_state); - au_state->s4u2self_user = NULL; - if (errcode) - goto cleanup; - } - if (s4u_x509_user != NULL) - setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION); - - /* For user-to-user and S4U2Proxy requests, decrypt the second ticket. */ - errcode = decrypt_2ndtkt(context, request, c_flags, local_tgt, - &local_tgt_key, &stkt, &stkt_pac, &stkt_server, - &stkt_server_key, &status); - if (errcode) - goto cleanup; - - errcode = validate_tgs_request(realm, request, server, header_ticket, - header_pac, stkt, stkt_pac, stkt_server, - kdc_time, s4u_x509_user, client, - is_crossrealm, is_referral, &status, - &e_data); - if (errcode) { - if (errcode == KRB5KDC_ERR_POLICY || errcode == KRB5KDC_ERR_BADOPTION) - au_state->violation = PROT_CONSTRAINT; - goto cleanup; - } - - if (isflagset(request->kdc_options, KDC_OPT_CNAME_IN_ADDL_TKT)) { - /* Do constrained delegation protocol and authorization checks. */ - setflag(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION); - - errcode = kdc_process_s4u2proxy_req(context, c_flags, request, - header_pac, stkt->enc_part2, - stkt_pac, stkt_server, - stkt_server_key, - header_ticket->enc_part2->client, - server, &stkt_authdata_client, - &status); - if (errcode == KRB5KDC_ERR_POLICY || errcode == KRB5KDC_ERR_BADOPTION) - au_state->violation = PROT_CONSTRAINT; - else if (errcode) - au_state->violation = LOCAL_POLICY; - au_state->status = status; - retval = kau_make_tkt_id(context, stkt, &au_state->evid_tkt_id); - if (retval) { - errcode = retval; - goto cleanup; - } - kau_s4u2proxy(context, errcode ? FALSE : TRUE, au_state); - if (errcode) - goto cleanup; - - assert(krb5_is_tgs_principal(header_ticket->server)); - } - - au_state->stage = ISSUE_TKT; - - errcode = gen_session_key(context, request, server, &session_key, &status); - if (errcode) - goto cleanup; + krb5_pac stkt_pac; + krb5_db_entry *stkt_server; + krb5_keyblock *stkt_server_key; + /* For cross-realm S4U2Proxy requests, the client principal retrieved from + * stkt_pac. */ + krb5_principal stkt_pac_client; + + /* Storage for the local TGT KDB entry for the service realm if that isn't + * the header server. */ + krb5_db_entry *local_tgt_storage; + /* The decrypted first key of the local TGT entry. */ + krb5_keyblock local_tgt_key; + + /* The server KDB entry. Normally the requested server, but for referral + * and alternate TGS replies this will be a cross-realm TGT entry. */ + krb5_db_entry *server; /* - * subject_tkt will refer to the evidence ticket (for constrained - * delegation) or the TGT. The distinction from header_enc_tkt is - * necessary because the TGS signature only protects some fields: - * the others could be forged by a malicious server. + * The subject client KDB entry for an S4U2Self request, or the header + * ticket client KDB entry for other requests. NULL if + * NO_AUTH_DATA_REQUIRED is set on the server KDB entry and this isn't an + * S4U2Self request, or if the client is in another realm and the KDB + * cannot map its principal name. */ + krb5_db_entry *client; - if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) { - subject_tkt = stkt->enc_part2; - subject_pac = stkt_pac; - subject_server = stkt_server; - subject_key = stkt_server_key; - } else { - subject_tkt = header_enc_tkt; - subject_pac = header_pac; - subject_server = header_server; - subject_key = header_key; - } - authtime = subject_tkt->times.authtime; - - /* Extract and check auth indicators from the subject ticket, except for - * S4U2Self requests (where the client didn't authenticate). */ - if (s4u_x509_user == NULL) { - errcode = get_auth_indicators(context, subject_tkt, local_tgt, - &local_tgt_key, &auth_indicators); - if (errcode) { - status = "GET_AUTH_INDICATORS"; - goto cleanup; - } + /* The decoded S4U2Self padata from the request, if present. */ + krb5_pa_s4u_x509_user *s4u2self; - errcode = check_indicators(context, server, auth_indicators); - if (errcode) { - status = "HIGHER_AUTHENTICATION_REQUIRED"; - goto cleanup; - } - } + /* Authentication indicators retrieved from the header ticket, for + * non-S4U2Self requests. */ + krb5_data **auth_indicators; - if (is_referral) - ticket_reply.server = server->princ; - else - ticket_reply.server = request->server; /* XXX careful for realm... */ + /* Storage for a transited list with the header TGT realm added, if that + * realm is different from the client and server realm. */ + krb5_data new_transited; - enc_tkt_reply.flags = get_ticket_flags(request->kdc_options, client, - server, header_enc_tkt); - enc_tkt_reply.times.starttime = 0; + /* The KDB flags applicable to this request (a subset of {CROSS_REALM, + * ISSUING_REFERRAL, PROTOCOL_TRANSITION, CONSTRAINED_DELEGATION}). */ + unsigned int flags; - if (s4u_x509_user != NULL && !is_referral) { - /* Check if we need to suppress the forwardable ticket flag. */ - errcode = s4u2self_forwardable(context, server, &enc_tkt_reply); - if (errcode) - goto cleanup; - } + /* Booleans for two of the above flags, for convenience. */ + krb5_boolean is_referral; + krb5_boolean is_crossrealm; - /* don't use new addresses unless forwarded, see below */ + /* The authtime of subject_tkt. On early failures this may be 0. */ + krb5_timestamp authtime; - enc_tkt_reply.caddrs = header_enc_tkt->caddrs; - /* noaddrarray[0] = 0; */ - reply_encpart.caddrs = 0;/* optional...don't put it in */ - reply_encpart.enc_padata = NULL; + /* The following fields are (or contain) alias pointers and should not be + * freed. */ - /* - * It should be noted that local policy may affect the - * processing of any of these flags. For example, some - * realms may refuse to issue renewable tickets - */ + /* The transited list implied by the request, aliasing new_transited or the + * header ticket transited field. */ + krb5_transited transited; - if (isflagset(request->kdc_options, KDC_OPT_FORWARDED) || - isflagset(request->kdc_options, KDC_OPT_PROXY)) { + /* Alias to the decrypted second ticket within req, if one applies to this + * request. */ + const krb5_ticket *stkt; - /* include new addresses in ticket & reply */ + /* Alias to stkt for S4U2Proxy requests, header_tkt otherwise. */ + krb5_enc_tkt_part *subject_tkt; - enc_tkt_reply.caddrs = request->addresses; - reply_encpart.caddrs = request->addresses; - } + /* Alias to local_tgt_storage or header_server. */ + krb5_db_entry *local_tgt; - if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) - enc_tkt_reply.times.starttime = request->from; - else - enc_tkt_reply.times.starttime = kdc_time; - - if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) { - assert(isflagset(c_flags, KRB5_KDB_FLAGS_S4U) == 0); - /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs - to the caller */ - ticket_reply = *(header_ticket); - enc_tkt_reply = *(header_ticket->enc_part2); - enc_tkt_reply.authorization_data = NULL; - clear(enc_tkt_reply.flags, TKT_FLG_INVALID); - } + /* For either kind of S4U request, an alias to the requested client + * principal name. */ + krb5_principal s4u_cprinc; - if (isflagset(request->kdc_options, KDC_OPT_RENEW)) { - krb5_timestamp old_starttime; - krb5_deltat old_life; + /* An alias to the client principal name we should issue the ticket for + * (either header_tkt->enc_part2->client or s4u_cprinc). */ + krb5_principal tkt_client; - assert(isflagset(c_flags, KRB5_KDB_FLAGS_S4U) == 0); - /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs - to the caller */ - ticket_reply = *(header_ticket); - enc_tkt_reply = *(header_ticket->enc_part2); - enc_tkt_reply.authorization_data = NULL; + /* The client principal of the PA-TGS-REQ header ticket. On early failures + * this may be NULL. */ + krb5_principal cprinc; - old_starttime = enc_tkt_reply.times.starttime ? - enc_tkt_reply.times.starttime : enc_tkt_reply.times.authtime; - old_life = ts_delta(enc_tkt_reply.times.endtime, old_starttime); + /* The canonicalized request server principal or referral/alternate TGT. + * On early failures this may be the requested server instead. */ + krb5_principal sprinc; - enc_tkt_reply.times.starttime = kdc_time; - enc_tkt_reply.times.endtime = - ts_min(header_ticket->enc_part2->times.renew_till, - ts_incr(kdc_time, old_life)); - } else { - /* not a renew request */ - enc_tkt_reply.times.starttime = kdc_time; +}; - kdc_get_ticket_endtime(realm, enc_tkt_reply.times.starttime, - header_enc_tkt->times.endtime, request->till, - client, server, &enc_tkt_reply.times.endtime); - } - - kdc_get_ticket_renewtime(realm, request, header_enc_tkt, client, server, - &enc_tkt_reply); - - errcode = check_kdcpolicy_tgs(context, request, server, header_ticket, - auth_indicators, kdc_time, - &enc_tkt_reply.times, &status); - if (errcode) - goto cleanup; - - /* - * Set authtime to be the same as header or evidence ticket's - */ - enc_tkt_reply.times.authtime = authtime; - - /* starttime is optional, and treated as authtime if not present. - so we can nuke it if it matches */ - if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime) - enc_tkt_reply.times.starttime = 0; - - if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) { - altcprinc = s4u_x509_user->user_id.user; - } else if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) { - /* kdc_process_s4u2proxy_req() only allows cross-realm requests if - * stkt_authdata_client is set. */ - altcprinc = is_crossrealm ? stkt_authdata_client : subject_tkt->client; - } else { - altcprinc = NULL; - } - if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) { - encrypting_key = stkt->enc_part2->session; - } else { - errcode = get_first_current_key(context, server, &server_keyblock); - if (errcode) { - status = "FINDING_SERVER_KEY"; - goto cleanup; - } - encrypting_key = &server_keyblock; - } - - if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) { - /* - * For consistency with Active Directory, don't allow authorization - * data to be disabled if S4U2Self is requested. The server likely - * needs a PAC to inspect or for an S4U2Proxy operation, even if it - * doesn't need authorization data in tickets received from clients. - */ - clear(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED); - } - if (isflagset(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED) == 0) { - /* If we are not doing protocol transition, try to look up the subject - * principal so that KDB modules can add additional authdata. */ - if (!isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) { - setflag(c_flags, KRB5_KDB_FLAG_CLIENT); - /* Map principals from foreign (possibly non-AD) realms */ - setflag(c_flags, KRB5_KDB_FLAG_MAP_PRINCIPALS); - - assert(client == NULL); /* should not have been set already */ - - errcode = krb5_db_get_principal(context, subject_tkt->client, - c_flags, &client); - } - } - - if (isflagset(c_flags, KRB5_KDB_FLAGS_S4U) && !is_referral) - enc_tkt_reply.client = altcprinc; - else - enc_tkt_reply.client = header_enc_tkt->client; - - enc_tkt_reply.session = &session_key; - enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; - enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */ - - /* - * Only add the realm of the presented tgt to the transited list if - * it is different than the server realm (cross-realm) and it is different - * than the realm of the client (since the realm of the client is already - * implicitly part of the transited list and should not be explicitly - * listed). - */ - if (!is_crossrealm || - krb5_realm_compare(context, header_ticket->server, - enc_tkt_reply.client)) { - /* tgt issued by local realm or issued by realm of client */ - enc_tkt_reply.transited = header_enc_tkt->transited; - } else { - /* tgt issued by some other realm and not the realm of the client */ - /* assemble new transited field into allocated storage */ - if (header_enc_tkt->transited.tr_type != - KRB5_DOMAIN_X500_COMPRESS) { - status = "VALIDATE_TRANSIT_TYPE"; - errcode = KRB5KDC_ERR_TRTYPE_NOSUPP; - goto cleanup; - } - memset(&enc_tkt_reply.transited, 0, sizeof(enc_tkt_reply.transited)); - enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; - if ((errcode = - add_to_transited(&header_enc_tkt->transited.tr_contents, - &enc_tkt_reply.transited.tr_contents, - header_ticket->server, - enc_tkt_reply.client, - request->server))) { - status = "ADD_TO_TRANSITED_LIST"; - goto cleanup; - } - newtransited = 1; - } - if (!isflagset (request->kdc_options, KDC_OPT_DISABLE_TRANSITED_CHECK)) { - errcode = kdc_check_transited_list(context, - &enc_tkt_reply.transited.tr_contents, - &header_enc_tkt->client->realm, - &request->server->realm); - if (errcode == 0) { - setflag (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED); - } else { - log_tgs_badtrans(context, cprinc, sprinc, - &enc_tkt_reply.transited.tr_contents, errcode); - } - } else - krb5_klog_syslog(LOG_INFO, _("not checking transit path")); - if (realm->realm_reject_bad_transit && - !isflagset(enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED)) { - errcode = KRB5KDC_ERR_POLICY; - status = "BAD_TRANSIT"; - au_state->violation = LOCAL_POLICY; - goto cleanup; - } - - errcode = handle_authdata(realm, c_flags, client, server, subject_server, - local_tgt, &local_tgt_key, - subkey != NULL ? subkey : - header_ticket->enc_part2->session, - encrypting_key, subject_key, NULL, pkt, request, - altcprinc, subject_pac, subject_tkt, - &auth_indicators, &enc_tkt_reply); - if (errcode) { - krb5_klog_syslog(LOG_INFO, _("TGS_REQ : handle_authdata (%d)"), - errcode); - status = "HANDLE_AUTHDATA"; - goto cleanup; - } - - ticket_reply.enc_part2 = &enc_tkt_reply; - - /* If we are doing user-to-user authentication, encrypt the ticket using - * the session key of the second ticket. */ - if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) { - ticket_kvno = 0; - ticket_reply.enc_part.enctype = stkt->enc_part2->session->enctype; - kau_u2u(context, TRUE, au_state); - } else { - ticket_kvno = current_kvno(server); - } - - errcode = krb5_encrypt_tkt_part(context, encrypting_key, &ticket_reply); - if (errcode) - goto cleanup; - ticket_reply.enc_part.kvno = ticket_kvno; - /* Start assembling the response */ - au_state->stage = ENCR_REP; - reply.msg_type = KRB5_TGS_REP; - if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) && - krb5int_find_pa_data(context, request->padata, - KRB5_PADATA_S4U_X509_USER) != NULL) { - errcode = kdc_make_s4u2self_rep(context, subkey, - header_ticket->enc_part2->session, - s4u_x509_user, &reply, &reply_encpart); - if (errcode) - au_state->status = status; - kau_s4u2self(context, errcode ? FALSE : TRUE, au_state); - if (errcode) - goto cleanup; - } - - reply.client = enc_tkt_reply.client; - reply.enc_part.kvno = 0;/* We are using the session key */ - reply.ticket = &ticket_reply; - - reply_encpart.session = &session_key; - reply_encpart.nonce = request->nonce; - - /* copy the time fields */ - reply_encpart.times = enc_tkt_reply.times; - - nolrentry.lr_type = KRB5_LRQ_NONE; - nolrentry.value = 0; - nolrentry.magic = 0; - nolrarray[0] = &nolrentry; - nolrarray[1] = 0; - reply_encpart.last_req = nolrarray; /* not available for TGS reqs */ - reply_encpart.key_exp = 0;/* ditto */ - reply_encpart.flags = enc_tkt_reply.flags; - reply_encpart.server = ticket_reply.server; - - /* use the session key in the ticket, unless there's a subsession key - in the AP_REQ */ - reply.enc_part.enctype = subkey ? subkey->enctype : - header_ticket->enc_part2->session->enctype; - errcode = kdc_fast_response_handle_padata(state, request, &reply, - subkey ? subkey->enctype : header_ticket->enc_part2->session->enctype); - if (errcode) - goto cleanup; - errcode =kdc_fast_handle_reply_key(state, - subkey?subkey:header_ticket->enc_part2->session, &reply_key); - if (errcode) - goto cleanup; - errcode = return_enc_padata(context, pkt, request, reply_key, server, - &reply_encpart, - is_referral && - isflagset(s_flags, KRB5_KDB_FLAG_REFERRAL_OK)); - if (errcode) { - status = "KDC_RETURN_ENC_PADATA"; - goto cleanup; - } - - errcode = kau_make_tkt_id(context, &ticket_reply, &au_state->tkt_out_id); - if (errcode) - goto cleanup; - - if (kdc_fast_hide_client(state)) - reply.client = (krb5_principal)krb5_anonymous_principal(); - errcode = krb5_encode_kdc_rep(context, KRB5_TGS_REP, &reply_encpart, - subkey ? 1 : 0, reply_key, &reply, response); - if (!errcode) - status = "ISSUE"; - - memset(ticket_reply.enc_part.ciphertext.data, 0, - ticket_reply.enc_part.ciphertext.length); - free(ticket_reply.enc_part.ciphertext.data); - /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we - can use them in raw form if needed. But, we don't... */ - memset(reply.enc_part.ciphertext.data, 0, - reply.enc_part.ciphertext.length); - free(reply.enc_part.ciphertext.data); - -cleanup: - if (status == NULL) - status = "UNKNOWN_REASON"; - krb5_free_keyblock_contents(context, &server_keyblock); - if (reply_key) - krb5_free_keyblock(context, reply_key); - if (stkt_server_key) - krb5_free_keyblock(context, stkt_server_key); - if (errcode) - emsg = krb5_get_error_message(context, errcode); - - if (au_state != NULL) { - au_state->status = status; - if (!errcode) - au_state->reply = &reply; - kau_tgs_req(context, errcode ? FALSE : TRUE, au_state); - kau_free_kdc_req(au_state); - } - - log_tgs_req(context, from, request, &reply, cprinc, sprinc, altcprinc, - authtime, c_flags, status, errcode, emsg); - if (errcode) { - krb5_free_error_message(context, emsg); - emsg = NULL; - } - - if (errcode && state != NULL) { - int got_err = 0; - if (status == 0) { - status = krb5_get_error_message(context, errcode); - got_err = 1; - } - - retval = prepare_error_tgs(state, request, header_ticket, errcode, - (server != NULL) ? server->princ : NULL, - response, status, e_data); - if (got_err) { - krb5_free_error_message(context, status); - status = 0; - } - } - - if (header_ticket != NULL) - krb5_free_ticket(context, header_ticket); - if (request != NULL) - krb5_free_kdc_req(context, request); - if (state) - kdc_free_rstate(state); - krb5_db_free_principal(context, server); - krb5_db_free_principal(context, stkt_server); - krb5_db_free_principal(context, header_server); - krb5_db_free_principal(context, client); - krb5_db_free_principal(context, local_tgt_storage); - if (local_tgt_key.contents != NULL) - krb5_free_keyblock_contents(context, &local_tgt_key); - if (session_key.contents != NULL) - krb5_free_keyblock_contents(context, &session_key); - if (newtransited) - free(enc_tkt_reply.transited.tr_contents.data); - if (s4u_x509_user != NULL) - krb5_free_pa_s4u_x509_user(context, s4u_x509_user); - if (kdc_issued_auth_data != NULL) - krb5_free_authdata(context, kdc_issued_auth_data); - if (subkey != NULL) - krb5_free_keyblock(context, subkey); - if (header_key != NULL) - krb5_free_keyblock(context, header_key); - if (reply.padata) - krb5_free_pa_data(context, reply.padata); - if (reply_encpart.enc_padata) - krb5_free_pa_data(context, reply_encpart.enc_padata); - if (enc_tkt_reply.authorization_data != NULL) - krb5_free_authdata(context, enc_tkt_reply.authorization_data); - krb5_free_pa_data(context, e_data); - k5_free_data_ptr_list(auth_indicators); - krb5_pac_free(context, header_pac); - krb5_pac_free(context, stkt_pac); - krb5_free_principal(context, stkt_authdata_client); - - return retval; -} +static krb5_error_code +db_get_svc_princ(krb5_context, krb5_principal, krb5_flags, + krb5_db_entry **, const char **); static krb5_error_code prepare_error_tgs(struct kdc_request_state *state, krb5_kdc_req *request, @@ -1170,3 +579,650 @@ cleanup: krb5_free_principal(context, reftgs); return ret; } + +/* + * Transfer ownership of *reqptr to *t and fill *t with information about the + * request. Decode the PA-TGS-REQ header ticket and the second ticket if + * applicable, and decode and verify their PACs if present. Decode and verify + * the S4U2Self request pa-data if present. Extract authentication indicators + * from the subject ticket. Construct the transited list implied by the + * request. + */ +static krb5_error_code +gather_tgs_req_info(kdc_realm_t *realm, krb5_kdc_req **reqptr, krb5_data *pkt, + const krb5_fulladdr *from, + struct kdc_request_state *fast_state, + krb5_audit_state *au_state, struct tgs_req_info *t, + const char **status) +{ + krb5_context context = realm->realm_context; + krb5_error_code ret; + krb5_pa_data *pa_tgs_req; + unsigned int s_flags; + krb5_enc_tkt_part *header_enc; + krb5_data d; + + /* Transfer ownership of *reqptr to *t. */ + t->req = *reqptr; + *reqptr = NULL; + + if (t->req->msg_type != KRB5_TGS_REQ) + return KRB5_BADMSGTYPE; + + /* Initially set t->sprinc to the outer request server, for logging of + * early failures. */ + t->sprinc = t->req->server; + + /* Read the PA-TGS-REQ authenticator and decrypt the header ticket. */ + ret = kdc_process_tgs_req(realm, t->req, from, pkt, &t->header_tkt, + &t->header_server, &t->header_key, &t->subkey, + &pa_tgs_req); + if (t->header_tkt != NULL && t->header_tkt->enc_part2 != NULL) + t->cprinc = t->header_tkt->enc_part2->client; + if (ret) { + *status = "PROCESS_TGS"; + return ret; + } + ret = kau_make_tkt_id(context, t->header_tkt, &au_state->tkt_in_id); + if (ret) + return ret; + header_enc = t->header_tkt->enc_part2; + + /* If PA-FX-FAST-REQUEST padata is present, replace t->req with the inner + * request body. */ + d = make_data(pa_tgs_req->contents, pa_tgs_req->length); + ret = kdc_find_fast(&t->req, &d, t->subkey, header_enc->session, + fast_state, NULL); + if (ret) { + *status = "FIND_FAST"; + return ret; + } + /* Reset t->sprinc for the inner body and check it. */ + t->sprinc = t->req->server; + if (t->sprinc == NULL) { + *status = "NULL_SERVER"; + return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; + } + + /* The header ticket server is usually a TGT, but if it is not, fetch the + * local TGT for the realm. Get the decrypted first local TGT key. */ + ret = get_local_tgt(context, &t->sprinc->realm, t->header_server, + &t->local_tgt, &t->local_tgt_storage, + &t->local_tgt_key); + if (ret) { + *status = "GET_LOCAL_TGT"; + return ret; + } + + /* Decode and verify the header ticket PAC. */ + ret = get_verified_pac(context, header_enc, t->header_server->princ, + t->header_key, t->local_tgt, &t->local_tgt_key, + &t->header_pac); + if (ret) { + *status = "HEADER_PAC"; + return ret; + } + + au_state->request = t->req; + au_state->stage = SRVC_PRINC; + + /* Look up the server principal entry, or a referral/alternate TGT. Reset + * t->sprinc to the canonical server name (its final value). */ + s_flags = (t->req->kdc_options & KDC_OPT_CANONICALIZE) ? + KRB5_KDB_FLAG_REFERRAL_OK : 0; + ret = search_sprinc(realm, t->req, s_flags, &t->server, status); + if (ret) + return ret; + t->sprinc = t->server->princ; + + /* If we got a cross-realm TGS which is not the requested server, we are + * issuing a referral (or alternate TGT, which we treat similarly). */ + if (is_cross_tgs_principal(t->server->princ) && + !krb5_principal_compare(context, t->req->server, t->server->princ)) + t->flags |= KRB5_KDB_FLAG_ISSUING_REFERRAL; + + /* Mark the request as cross-realm if the header ticket server is not from + * this realm. */ + if (!data_eq(t->header_server->princ->realm, t->sprinc->realm)) + t->flags |= KRB5_KDB_FLAG_CROSS_REALM; + + t->is_referral = (t->flags & KRB5_KDB_FLAG_ISSUING_REFERRAL); + t->is_crossrealm = (t->flags & KRB5_KDB_FLAG_CROSS_REALM); + + /* If S4U2Self padata is present, read it to get the requested principal + * name. Look up the requested client if it is in this realm. */ + ret = kdc_process_s4u2self_req(context, t->req, t->server, t->subkey, + header_enc->session, &t->s4u2self, + &t->client, status); + if (t->s4u2self != NULL || ret) { + if (t->s4u2self != NULL) + au_state->s4u2self_user = t->s4u2self->user_id.user; + au_state->status = *status; + kau_s4u2self(context, !ret, au_state); + au_state->s4u2self_user = NULL; + } + if (ret) + return ret; + if (t->s4u2self != NULL) { + t->flags |= KRB5_KDB_FLAG_PROTOCOL_TRANSITION; + t->s4u_cprinc = t->s4u2self->user_id.user; + + /* + * For consistency with Active Directory, don't allow authorization + * data to be disabled if S4U2Self is requested. The requesting + * service likely needs a PAC for an S4U2Proxy operation, even if it + * doesn't need authorization data in tickets received from clients. + */ + t->server->attributes &= ~KRB5_KDB_NO_AUTH_DATA_REQUIRED; + } + + /* For U2U or S4U2Proxy requests, decrypt the second ticket and read its + * PAC. */ + ret = decrypt_2ndtkt(context, t->req, t->flags, t->local_tgt, + &t->local_tgt_key, &t->stkt, &t->stkt_pac, + &t->stkt_server, &t->stkt_server_key, status); + if (ret) + return ret; + + /* Determine the subject ticket and set the authtime for logging. For + * S4U2Proxy requests determine the requested client principal. */ + if (t->req->kdc_options & KDC_OPT_CNAME_IN_ADDL_TKT) { + t->flags |= KRB5_KDB_FLAG_CONSTRAINED_DELEGATION; + ret = kau_make_tkt_id(context, t->stkt, &au_state->evid_tkt_id); + if (ret) + return ret; + if (t->is_crossrealm) { + /* For cross-realm S4U2PROXY requests, the second ticket is a + * cross TGT with the requested client principal in its PAC. */ + if (t->stkt_pac == NULL || + get_pac_princ_with_realm(context, t->stkt_pac, + &t->stkt_pac_client, NULL) != 0) { + au_state->status = *status = "RBCD_PAC_PRINC"; + au_state->violation = PROT_CONSTRAINT; + kau_s4u2proxy(context, FALSE, au_state); + return KRB5KDC_ERR_BADOPTION; + } + t->s4u_cprinc = t->stkt_pac_client; + } else { + /* Otherwise the requested client is the evidence ticket client. */ + t->s4u_cprinc = t->stkt->enc_part2->client; + } + t->subject_tkt = t->stkt->enc_part2; + } else { + t->subject_tkt = header_enc; + } + t->authtime = t->subject_tkt->times.authtime; + + /* For final S4U requests (either type) the issued ticket will be for the + * requested name; otherwise it will be for the header ticket client. */ + t->tkt_client = ((t->flags & KRB5_KDB_FLAGS_S4U) && !t->is_referral) ? + t->s4u_cprinc : header_enc->client; + + if (t->s4u2self == NULL) { + /* Extract auth indicators from the subject ticket. Skip this for + * S4U2Self requests as the subject didn't authenticate. */ + ret = get_auth_indicators(context, t->subject_tkt, t->local_tgt, + &t->local_tgt_key, &t->auth_indicators); + if (ret) { + *status = "GET_AUTH_INDICATORS"; + return ret; + } + + if (!(t->server->attributes & KRB5_KDB_NO_AUTH_DATA_REQUIRED)) { + /* Try to look up the subject principal so that KDB modules can add + * additional authdata. Ask the KDB to map foreign principals. */ + assert(t->client == NULL); + (void)krb5_db_get_principal(context, t->subject_tkt->client, + t->flags | KRB5_KDB_FLAG_CLIENT | + KRB5_KDB_FLAG_MAP_PRINCIPALS, + &t->client); + } + } + + /* + * Compute the transited list implied by the request. Use the existing + * transited list if the realm of the header ticket server is the same as + * the subject or server realm. + */ + if (!t->is_crossrealm || + data_eq(t->header_tkt->server->realm, t->tkt_client->realm)) { + t->transited = header_enc->transited; + } else { + if (header_enc->transited.tr_type != KRB5_DOMAIN_X500_COMPRESS) { + *status = "VALIDATE_TRANSIT_TYPE"; + return KRB5KDC_ERR_TRTYPE_NOSUPP; + } + ret = add_to_transited(&header_enc->transited.tr_contents, + &t->new_transited, t->header_tkt->server, + t->tkt_client, t->req->server); + if (ret) { + *status = "ADD_TO_TRANSITED_LIST"; + return ret; + } + t->transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; + t->transited.tr_contents = t->new_transited; + } + + return 0; +} + +/* Fill in *times_out with the times of the ticket to be issued. Set the + * TKT_FLG_RENEWABLE bit in *tktflags if the ticket will be renewable. */ +static void +compute_ticket_times(kdc_realm_t *realm, struct tgs_req_info *t, + krb5_timestamp kdc_time, krb5_flags *tktflags, + krb5_ticket_times *times) +{ + krb5_timestamp hstarttime; + krb5_deltat hlife; + krb5_ticket_times *htimes = &t->header_tkt->enc_part2->times; + + if (t->req->kdc_options & KDC_OPT_VALIDATE) { + /* Validation requests preserve the header ticket times. */ + *times = *htimes; + return; + } + + /* Preserve the authtime from the subject ticket. */ + times->authtime = t->authtime; + + times->starttime = (t->req->kdc_options & KDC_OPT_POSTDATED) ? + t->req->from : kdc_time; + + if (t->req->kdc_options & KDC_OPT_RENEW) { + /* Give the new ticket the same lifetime as the header ticket, but no + * later than the renewable end time. */ + hstarttime = htimes->starttime ? htimes->starttime : htimes->authtime; + hlife = ts_delta(htimes->endtime, hstarttime); + times->endtime = ts_min(htimes->renew_till, + ts_incr(times->starttime, hlife)); + } else { + kdc_get_ticket_endtime(realm, times->starttime, htimes->endtime, + t->req->till, t->client, t->server, + ×->endtime); + } + + kdc_get_ticket_renewtime(realm, t->req, t->header_tkt->enc_part2, + t->client, t->server, tktflags, times); + + /* starttime is optional, and treated as authtime if not present. + * so we can omit it if it matches. */ + if (times->starttime == times->authtime) + times->starttime = 0; +} + +/* Check the request in *t against semantic protocol constraints and local + * policy. Determine flags and times for the ticket to be issued. */ +static krb5_error_code +check_tgs_req(kdc_realm_t *realm, struct tgs_req_info *t, + krb5_audit_state *au_state, krb5_flags *tktflags, + krb5_ticket_times *times, const char **status, + krb5_pa_data ***e_data) +{ + krb5_context context = realm->realm_context; + krb5_error_code ret; + krb5_timestamp kdc_time; + + au_state->stage = VALIDATE_POL; + + ret = krb5_timeofday(context, &kdc_time); + if (ret) + return ret; + + ret = check_tgs_constraints(realm, t->req, t->server, t->header_tkt, + t->header_pac, t->stkt, t->stkt_pac, + t->stkt_server, kdc_time, t->s4u2self, + t->client, t->is_crossrealm, t->is_referral, + status, e_data); + if (ret) { + au_state->violation = PROT_CONSTRAINT; + return ret; + } + + ret = check_tgs_policy(realm, t->req, t->server, t->header_tkt, + t->header_pac, t->stkt, t->stkt_pac, + t->stkt_pac_client, t->stkt_server, kdc_time, + t->is_crossrealm, t->is_referral, status, e_data); + if (ret) { + au_state->violation = LOCAL_POLICY; + if (t->flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) { + au_state->status = *status; + kau_s4u2proxy(context, FALSE, au_state); + } + return ret; + } + + /* Check auth indicators from the subject ticket, except for S4U2Self + * requests (where the client didn't authenticate). */ + if (t->s4u2self == NULL) { + ret = check_indicators(context, t->server, t->auth_indicators); + if (ret) { + *status = "HIGHER_AUTHENTICATION_REQUIRED"; + return ret; + } + } + + *tktflags = get_ticket_flags(t->req->kdc_options, t->client, t->server, + t->header_tkt->enc_part2); + compute_ticket_times(realm, t, kdc_time, tktflags, times); + + /* For S4U2Self requests, check if we need to suppress the forwardable + * ticket flag. */ + if (t->s4u2self != NULL && !t->is_referral) { + ret = s4u2self_forwardable(context, t->server, tktflags); + if (ret) + return ret; + } + + /* Consult kdcpolicy modules, giving them a chance to modify the times of + * the issued ticket. */ + ret = check_kdcpolicy_tgs(context, t->req, t->server, t->header_tkt, + t->auth_indicators, kdc_time, times, status); + if (ret) + return ret; + + if (!(t->req->kdc_options & KDC_OPT_DISABLE_TRANSITED_CHECK)) { + /* Check the transited path for the issued ticket and set the + * transited-policy-checked flag if successful. */ + ret = kdc_check_transited_list(context, &t->transited.tr_contents, + &t->subject_tkt->client->realm, + &t->req->server->realm); + if (ret) { + /* Log the transited-check failure and continue. */ + log_tgs_badtrans(context, t->cprinc, t->sprinc, + &t->transited.tr_contents, ret); + } else { + *tktflags |= TKT_FLG_TRANSIT_POLICY_CHECKED; + } + } else { + krb5_klog_syslog(LOG_INFO, _("not checking transit path")); + } + + /* By default, reject the request if the transited path was not checked + * successfully. */ + if (realm->realm_reject_bad_transit && + !(*tktflags & TKT_FLG_TRANSIT_POLICY_CHECKED)) { + *status = "BAD_TRANSIT"; + au_state->violation = LOCAL_POLICY; + return KRB5KDC_ERR_POLICY; + } + + return 0; +} + +/* Construct a response issuing a ticket for the request in *t, using tktflags + * and *times for the ticket flags and times. */ +static krb5_error_code +tgs_issue_ticket(kdc_realm_t *realm, struct tgs_req_info *t, + krb5_flags tktflags, krb5_ticket_times *times, krb5_data *pkt, + const krb5_fulladdr *from, + struct kdc_request_state *fast_state, + krb5_audit_state *au_state, const char **status, + krb5_data **response) +{ + krb5_context context = realm->realm_context; + krb5_error_code ret; + krb5_keyblock session_key = { 0 }, server_key = { 0 }; + krb5_keyblock *ticket_encrypting_key, *subject_key; + krb5_keyblock *initial_reply_key, *fast_reply_key = NULL; + krb5_enc_tkt_part enc_tkt_reply = { 0 }; + krb5_ticket ticket_reply = { 0 }; + krb5_enc_kdc_rep_part reply_encpart = { 0 }; + krb5_kdc_rep reply = { 0 }; + krb5_pac subject_pac; + krb5_db_entry *subject_server; + krb5_enc_tkt_part *header_enc_tkt = t->header_tkt->enc_part2; + krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 }; + krb5_last_req_entry *nolrarray[2] = { &nolrentry, NULL }; + + au_state->stage = ISSUE_TKT; + + ret = gen_session_key(context, t->req, t->server, &session_key, status); + if (ret) + goto cleanup; + + if (t->flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) { + subject_pac = t->stkt_pac; + subject_server = t->stkt_server; + subject_key = t->stkt_server_key; + } else { + subject_pac = t->header_pac; + subject_server = t->header_server; + subject_key = t->header_key; + } + + initial_reply_key = (t->subkey != NULL) ? t->subkey : + t->header_tkt->enc_part2->session; + + if (t->req->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) { + /* For user-to-user, encrypt the ticket with the second ticket's + * session key. */ + ticket_encrypting_key = t->stkt->enc_part2->session; + } else { + /* Otherwise encrypt the ticket with the server entry's first long-term + * key. */ + ret = get_first_current_key(context, t->server, &server_key); + if (ret) { + *status = "FINDING_SERVER_KEY"; + goto cleanup; + } + ticket_encrypting_key = &server_key; + } + + if (t->req->kdc_options & (KDC_OPT_VALIDATE | KDC_OPT_RENEW)) { + /* Copy the whole header ticket except for authorization data. */ + ticket_reply = *t->header_tkt; + enc_tkt_reply = *t->header_tkt->enc_part2; + enc_tkt_reply.authorization_data = NULL; + } else { + if (t->req->kdc_options & (KDC_OPT_FORWARDED | KDC_OPT_PROXY)) { + /* Include the requested addresses in the ticket and reply. */ + enc_tkt_reply.caddrs = t->req->addresses; + reply_encpart.caddrs = t->req->addresses; + } else { + /* Use the header ticket addresses and omit them from the reply. */ + enc_tkt_reply.caddrs = header_enc_tkt->caddrs; + reply_encpart.caddrs = NULL; + } + + ticket_reply.server = t->is_referral ? t->sprinc : t->req->server; + } + + enc_tkt_reply.flags = tktflags; + enc_tkt_reply.times = *times; + enc_tkt_reply.client = t->tkt_client; + enc_tkt_reply.session = &session_key; + enc_tkt_reply.transited = t->transited; + + ret = handle_authdata(realm, t->flags, t->client, t->server, + subject_server, t->local_tgt, &t->local_tgt_key, + initial_reply_key, ticket_encrypting_key, + subject_key, NULL, pkt, t->req, t->s4u_cprinc, + subject_pac, t->subject_tkt, &t->auth_indicators, + &enc_tkt_reply); + if (ret) { + krb5_klog_syslog(LOG_INFO, _("TGS_REQ : handle_authdata (%d)"), ret); + *status = "HANDLE_AUTHDATA"; + goto cleanup; + } + + ticket_reply.enc_part2 = &enc_tkt_reply; + + ret = krb5_encrypt_tkt_part(context, ticket_encrypting_key, &ticket_reply); + if (ret) + goto cleanup; + + if (t->req->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) { + ticket_reply.enc_part.kvno = 0; + kau_u2u(context, TRUE, au_state); + } else { + ticket_reply.enc_part.kvno = current_kvno(t->server); + } + + au_state->stage = ENCR_REP; + + if (t->s4u2self != NULL && + krb5int_find_pa_data(context, t->req->padata, + KRB5_PADATA_S4U_X509_USER) != NULL) { + /* Add an S4U2Self response to the encrypted padata (skipped if the + * request only included PA-FOR-USER padata). */ + ret = kdc_make_s4u2self_rep(context, t->subkey, + t->header_tkt->enc_part2->session, + t->s4u2self, &reply, &reply_encpart); + if (ret) + goto cleanup; + } + + reply_encpart.session = &session_key; + reply_encpart.nonce = t->req->nonce; + reply_encpart.times = enc_tkt_reply.times; + reply_encpart.last_req = nolrarray; + reply_encpart.key_exp = 0; + reply_encpart.flags = enc_tkt_reply.flags; + reply_encpart.server = ticket_reply.server; + + reply.msg_type = KRB5_TGS_REP; + reply.client = enc_tkt_reply.client; + reply.ticket = &ticket_reply; + reply.enc_part.kvno = 0; + reply.enc_part.enctype = initial_reply_key->enctype; + ret = kdc_fast_response_handle_padata(fast_state, t->req, &reply, + initial_reply_key->enctype); + if (ret) + goto cleanup; + ret = kdc_fast_handle_reply_key(fast_state, initial_reply_key, + &fast_reply_key); + if (ret) + goto cleanup; + ret = return_enc_padata(context, pkt, t->req, fast_reply_key, t->server, + &reply_encpart, + t->is_referral && + (t->req->kdc_options & KDC_OPT_CANONICALIZE)); + if (ret) { + *status = "KDC_RETURN_ENC_PADATA"; + goto cleanup; + } + + ret = kau_make_tkt_id(context, &ticket_reply, &au_state->tkt_out_id); + if (ret) + goto cleanup; + + if (kdc_fast_hide_client(fast_state)) + reply.client = (krb5_principal)krb5_anonymous_principal(); + ret = krb5_encode_kdc_rep(context, KRB5_TGS_REP, &reply_encpart, + t->subkey != NULL, fast_reply_key, &reply, + response); + if (ret) + goto cleanup; + + log_tgs_req(context, from, t->req, &reply, t->cprinc, t->sprinc, + t->s4u_cprinc, t->authtime, t->flags, "ISSUE", 0, NULL); + au_state->status = "ISSUE"; + au_state->reply = &reply; + if (t->flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) + kau_s4u2proxy(context, TRUE, au_state); + kau_tgs_req(context, TRUE, au_state); + au_state->reply = NULL; + +cleanup: + zapfree(ticket_reply.enc_part.ciphertext.data, + ticket_reply.enc_part.ciphertext.length); + zapfree(reply.enc_part.ciphertext.data, reply.enc_part.ciphertext.length); + krb5_free_pa_data(context, reply.padata); + krb5_free_pa_data(context, reply_encpart.enc_padata); + krb5_free_authdata(context, enc_tkt_reply.authorization_data); + krb5_free_keyblock_contents(context, &session_key); + krb5_free_keyblock_contents(context, &server_key); + krb5_free_keyblock(context, fast_reply_key); + return ret; +} + +static void +free_req_info(krb5_context context, struct tgs_req_info *t) +{ + krb5_free_kdc_req(context, t->req); + krb5_free_ticket(context, t->header_tkt); + krb5_db_free_principal(context, t->header_server); + krb5_free_keyblock(context, t->header_key); + krb5_free_keyblock(context, t->subkey); + krb5_pac_free(context, t->header_pac); + krb5_pac_free(context, t->stkt_pac); + krb5_db_free_principal(context, t->stkt_server); + krb5_free_keyblock(context, t->stkt_server_key); + krb5_db_free_principal(context, t->local_tgt_storage); + krb5_free_keyblock_contents(context, &t->local_tgt_key); + krb5_db_free_principal(context, t->server); + krb5_db_free_principal(context, t->client); + krb5_free_pa_s4u_x509_user(context, t->s4u2self); + krb5_free_principal(context, t->stkt_pac_client); + k5_free_data_ptr_list(t->auth_indicators); + krb5_free_data_contents(context, &t->new_transited); +} + +krb5_error_code +process_tgs_req(krb5_kdc_req *request, krb5_data *pkt, + const krb5_fulladdr *from, kdc_realm_t *realm, + krb5_data **response) +{ + krb5_context context = realm->realm_context; + krb5_error_code ret; + struct tgs_req_info t = { 0 }; + struct kdc_request_state *fast_state = NULL; + krb5_audit_state *au_state = NULL; + krb5_pa_data **e_data = NULL; + krb5_flags tktflags; + krb5_ticket_times times = { 0 }; + const char *emsg = NULL, *status = NULL; + + ret = kdc_make_rstate(realm, &fast_state); + if (ret) + goto cleanup; + ret = kau_init_kdc_req(context, request, from, &au_state); + if (ret) + goto cleanup; + kau_tgs_req(context, TRUE, au_state); + + ret = gather_tgs_req_info(realm, &request, pkt, from, fast_state, au_state, + &t, &status); + if (ret) + goto cleanup; + + ret = check_tgs_req(realm, &t, au_state, &tktflags, ×, &status, + &e_data); + if (ret) + goto cleanup; + + ret = tgs_issue_ticket(realm, &t, tktflags, ×, pkt, from, fast_state, + au_state, &status, response); + if (ret) + goto cleanup; + +cleanup: + if (status == NULL) + status = "UNKNOWN_REASON"; + + if (ret) { + emsg = krb5_get_error_message(context, ret); + log_tgs_req(context, from, t.req, NULL, t.cprinc, t.sprinc, + t.s4u_cprinc, t.authtime, t.flags, status, ret, emsg); + krb5_free_error_message(context, emsg); + + if (au_state != NULL) { + au_state->status = status; + kau_tgs_req(context, FALSE, au_state); + } + } + + if (ret && fast_state != NULL) { + ret = prepare_error_tgs(fast_state, t.req, t.header_tkt, ret, + (t.server != NULL) ? t.server->princ : NULL, + response, status, e_data); + } + + krb5_free_kdc_req(context, request); + kdc_free_rstate(fast_state); + kau_free_kdc_req(au_state); + free_req_info(context, &t); + krb5_free_pa_data(context, e_data); + return ret; +} diff --git a/src/kdc/fast_util.c b/src/kdc/fast_util.c index 9044a1844..7a6579f1d 100644 --- a/src/kdc/fast_util.c +++ b/src/kdc/fast_util.c @@ -262,6 +262,8 @@ kdc_make_rstate(kdc_realm_t *active_realm, struct kdc_request_state **out) void kdc_free_rstate (struct kdc_request_state *s) { + if (s == NULL) + return; if (s->armor_key) krb5_free_keyblock(s->realm_data->realm_context, s->armor_key); if (s->strengthen_key) diff --git a/src/kdc/kdc_audit.c b/src/kdc/kdc_audit.c index f40913dc8..2333171d7 100644 --- a/src/kdc/kdc_audit.c +++ b/src/kdc/kdc_audit.c @@ -206,6 +206,8 @@ kau_init_kdc_req(krb5_context context, void kau_free_kdc_req(krb5_audit_state *state) { + if (state == NULL) + return; free(state->tkt_in_id); free(state->tkt_out_id); free(state->evid_tkt_id); diff --git a/src/kdc/kdc_log.c b/src/kdc/kdc_log.c index f86e0780d..9a8894c9a 100644 --- a/src/kdc/kdc_log.c +++ b/src/kdc/kdc_log.c @@ -149,7 +149,8 @@ log_tgs_req(krb5_context ctx, const krb5_fulladdr *from, important). */ if (errcode != KRB5KDC_ERR_SERVER_NOMATCH) { ktypestr = ktypes2str(request->ktype, request->nktypes); - rep_etypestr = rep_etypes2str(reply); + if (reply != NULL) + rep_etypestr = rep_etypes2str(reply); krb5_klog_syslog(LOG_INFO, _("TGS_REQ (%s) %s: %s: authtime %u, %s%s " "%s for %s%s%s"), ktypestr ? ktypestr : "", fromstring, status, diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c index d6c61f4aa..f5cb2abf8 100644 --- a/src/kdc/kdc_util.c +++ b/src/kdc/kdc_util.c @@ -625,15 +625,6 @@ fetch_last_req_info(krb5_db_entry *dbentry, krb5_last_req_entry ***lrentry) } -/* XXX! This is a temporary place-holder */ - -krb5_error_code -check_hot_list(krb5_ticket *ticket) -{ - return 0; -} - - /* Convert an API error code to a protocol error code. */ int errcode_to_protocol(krb5_error_code code) @@ -762,6 +753,10 @@ get_ticket_flags(krb5_flags reqflags, krb5_db_entry *client, { krb5_flags flags; + /* Validation and renewal TGS requests preserve the header ticket flags. */ + if ((reqflags & (KDC_OPT_VALIDATE | KDC_OPT_RENEW)) && header_enc != NULL) + return header_enc->flags & ~TKT_FLG_INVALID; + /* Indicate support for encrypted padata (RFC 6806), and set flags based on * request options and the header ticket. */ flags = OPTS2FLAGS(reqflags) | TKT_FLG_ENC_PA_REP; @@ -1553,7 +1548,7 @@ kdc_process_s4u2self_req(krb5_context context, krb5_kdc_req *request, * S4U2Self tickets according to [MS-SFU] 3.2.5.1.2. */ krb5_error_code s4u2self_forwardable(krb5_context context, krb5_db_entry *server, - krb5_enc_tkt_part *tkt) + krb5_flags *tktflags) { krb5_error_code ret; @@ -1565,98 +1560,13 @@ s4u2self_forwardable(krb5_context context, krb5_db_entry *server, * targets for traditional S4U2Proxy. */ ret = krb5_db_check_allowed_to_delegate(context, NULL, server, NULL); if (!ret) - tkt->flags &= ~TKT_FLG_FORWARDABLE; + *tktflags &= ~TKT_FLG_FORWARDABLE; if (ret == KRB5KDC_ERR_BADOPTION || ret == KRB5_PLUGIN_OP_NOTSUPP) return 0; return ret; } -/* - * Determine if an S4U2Proxy request is authorized. Set **stkt_ad_info to the - * KDB authdata handle for the second ticket if the KDB module supplied one. - * Set *stkt_authdata_client to the subject client name if the KDB module - * supplied one; it must do so for a cross-realm request to be authorized. - */ -krb5_error_code -kdc_process_s4u2proxy_req(krb5_context context, unsigned int flags, - krb5_kdc_req *request, krb5_pac header_pac, - const krb5_enc_tkt_part *t2enc, krb5_pac t2_pac, - const krb5_db_entry *server, - krb5_keyblock *server_key, - krb5_const_principal server_princ, - const krb5_db_entry *proxy, - krb5_principal *stkt_pac_client, - const char **status) -{ - krb5_error_code errcode; - krb5_boolean support_rbcd; - krb5_principal client_princ = t2enc->client, t2_pac_princ = NULL; - - *stkt_pac_client = NULL; - - /* Check if the client supports resource-based constrained delegation. */ - errcode = kdc_get_pa_pac_rbcd(context, request->padata, &support_rbcd); - if (errcode) - return errcode; - - /* For an RBCD final request, recover the reply ticket client name from - * the evidence ticket PAC. */ - if (flags & KRB5_KDB_FLAG_CROSS_REALM) { - if (get_pac_princ_with_realm(context, t2_pac, &t2_pac_princ, - NULL) != 0) { - *status = "RBCD_PAC_PRINC"; - errcode = KRB5KDC_ERR_BADOPTION; - goto done; - } - client_princ = t2_pac_princ; - } - - /* If both are in the same realm, try allowed_to_delegate first. */ - if (krb5_realm_compare(context, server->princ, request->server)) { - - errcode = krb5_db_check_allowed_to_delegate(context, client_princ, - server, request->server); - if (errcode != KRB5KDC_ERR_BADOPTION && - errcode != KRB5_PLUGIN_OP_NOTSUPP) - goto done; - - /* Fall back to resource-based constrained-delegation. */ - } - - if (!support_rbcd) { - *status = "UNSUPPORTED_S4U2PROXY_REQUEST"; - errcode = KRB5KDC_ERR_BADOPTION; - goto done; - } - - /* If we are issuing a referral, the KDC in the resource realm will check - * if delegation is allowed. */ - if (isflagset(flags, KRB5_KDB_FLAG_ISSUING_REFERRAL)) { - errcode = 0; - goto done; - } - - errcode = krb5_db_allowed_to_delegate_from(context, client_princ, - server_princ, header_pac, - proxy); - if (errcode == KRB5_PLUGIN_OP_NOTSUPP) { - *status = "UNSUPPORTED_S4U2PROXY_REQUEST"; - errcode = KRB5KDC_ERR_BADOPTION; - goto done; - } else if (errcode) { - *status = "NOT_ALLOWED_TO_DELEGATE"; - goto done; - } - - *stkt_pac_client = t2_pac_princ; - t2_pac_princ = NULL; - -done: - krb5_free_principal(context, t2_pac_princ); - return errcode; -} - krb5_error_code kdc_check_transited_list(krb5_context context, const krb5_data *trans, const krb5_data *realm1, const krb5_data *realm2) @@ -1718,19 +1628,21 @@ kdc_get_ticket_endtime(kdc_realm_t *realm, krb5_timestamp starttime, } /* - * Set tkt->renew_till to the requested renewable lifetime as modified by - * policy. Set the TKT_FLG_RENEWABLE flag if we set a nonzero renew_till. - * client and tgt may be NULL. + * Set times->renew_till to the requested renewable lifetime as modified by + * policy. Set the TKT_FLG_RENEWABLE bit in *tktflags if we set a nonzero + * renew_till. *times must be filled in except for renew_till. client and tgt + * may be NULL. */ void kdc_get_ticket_renewtime(kdc_realm_t *realm, krb5_kdc_req *request, krb5_enc_tkt_part *tgt, krb5_db_entry *client, - krb5_db_entry *server, krb5_enc_tkt_part *tkt) + krb5_db_entry *server, krb5_flags *tktflags, + krb5_ticket_times *times) { krb5_timestamp rtime, max_rlife; - clear(tkt->flags, TKT_FLG_RENEWABLE); - tkt->times.renew_till = 0; + *tktflags &= ~TKT_FLG_RENEWABLE; + times->renew_till = 0; /* Don't issue renewable tickets if the client or server don't allow it, * or if this is a TGS request and the TGT isn't renewable. */ @@ -1745,7 +1657,7 @@ kdc_get_ticket_renewtime(kdc_realm_t *realm, krb5_kdc_req *request, if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) rtime = request->rtime ? request->rtime : kdc_infinity; else if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) && - ts_after(request->till, tkt->times.endtime)) + ts_after(request->till, times->endtime)) rtime = request->till; else return; @@ -1756,16 +1668,16 @@ kdc_get_ticket_renewtime(kdc_realm_t *realm, krb5_kdc_req *request, max_rlife = min(server->max_renewable_life, realm->realm_maxrlife); if (client != NULL) max_rlife = min(max_rlife, client->max_renewable_life); - rtime = ts_min(rtime, ts_incr(tkt->times.starttime, max_rlife)); + rtime = ts_min(rtime, ts_incr(times->starttime, max_rlife)); /* If the client only specified renewable-ok, don't issue a renewable * ticket unless the truncated renew time exceeds the ticket end time. */ if (!isflagset(request->kdc_options, KDC_OPT_RENEWABLE) && - !ts_after(rtime, tkt->times.endtime)) + !ts_after(rtime, times->endtime)) return; - setflag(tkt->flags, TKT_FLG_RENEWABLE); - tkt->times.renew_till = rtime; + *tktflags |= TKT_FLG_RENEWABLE; + times->renew_till = rtime; } /** diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h index 852d9c025..684201308 100644 --- a/src/kdc/kdc_util.h +++ b/src/kdc/kdc_util.h @@ -36,7 +36,6 @@ #include "realm_data.h" #include "reqstate.h" -krb5_error_code check_hot_list (krb5_ticket *); krb5_boolean krb5_is_tgs_principal (krb5_const_principal); krb5_boolean is_cross_tgs_principal(krb5_const_principal); krb5_boolean is_local_tgs_principal(krb5_const_principal); @@ -84,14 +83,23 @@ validate_as_request (kdc_realm_t *, krb5_kdc_req *, krb5_db_entry *, const char **, krb5_pa_data ***); krb5_error_code -validate_tgs_request(kdc_realm_t *realm, krb5_kdc_req *request, - krb5_db_entry *server, krb5_ticket *ticket, krb5_pac pac, - const krb5_ticket *stkt, krb5_pac stkt_pac, - krb5_db_entry *stkt_server, krb5_timestamp kdc_time, - krb5_pa_s4u_x509_user *s4u_x509_user, - krb5_db_entry *s4u2self_client, - krb5_boolean is_crossrealm, krb5_boolean is_referral, - const char **status, krb5_pa_data ***e_data); +check_tgs_constraints(kdc_realm_t *realm, krb5_kdc_req *request, + krb5_db_entry *server, krb5_ticket *ticket, krb5_pac pac, + const krb5_ticket *stkt, krb5_pac stkt_pac, + krb5_db_entry *stkt_server, krb5_timestamp kdc_time, + krb5_pa_s4u_x509_user *s4u_x509_user, + krb5_db_entry *s4u2self_client, + krb5_boolean is_crossrealm, krb5_boolean is_referral, + const char **status, krb5_pa_data ***e_data); + +krb5_error_code +check_tgs_policy(kdc_realm_t *realm, krb5_kdc_req *request, + krb5_db_entry *server, krb5_ticket *ticket, + krb5_pac pac, const krb5_ticket *stkt, krb5_pac stkt_pac, + krb5_principal stkt_pac_client, krb5_db_entry *stkt_server, + krb5_timestamp kdc_time, krb5_boolean is_crossrealm, + krb5_boolean is_referral, const char **status, + krb5_pa_data ***e_data); krb5_flags get_ticket_flags(krb5_flags reqflags, krb5_db_entry *client, @@ -275,7 +283,7 @@ kdc_process_s4u2self_req(krb5_context context, krb5_kdc_req *request, krb5_error_code s4u2self_forwardable(krb5_context context, krb5_db_entry *server, - krb5_enc_tkt_part *enc_tkt); + krb5_flags *tktflags); krb5_error_code kdc_make_s4u2self_rep (krb5_context context, @@ -286,17 +294,6 @@ kdc_make_s4u2self_rep (krb5_context context, krb5_enc_kdc_rep_part *reply_encpart); krb5_error_code -kdc_process_s4u2proxy_req(krb5_context context, unsigned int flags, - krb5_kdc_req *request, krb5_pac header_pac, - const krb5_enc_tkt_part *t2enc, krb5_pac t2_pac, - const krb5_db_entry *server, - krb5_keyblock *server_key, - krb5_const_principal server_princ, - const krb5_db_entry *proxy, - krb5_principal *stkt_ad_client, - const char **status); - -krb5_error_code kdc_check_transited_list(krb5_context context, const krb5_data *trans, const krb5_data *realm1, const krb5_data *realm2); @@ -309,7 +306,8 @@ kdc_get_ticket_endtime(kdc_realm_t *realm, krb5_timestamp now, void kdc_get_ticket_renewtime(kdc_realm_t *realm, krb5_kdc_req *request, krb5_enc_tkt_part *tgt, krb5_db_entry *client, - krb5_db_entry *server, krb5_enc_tkt_part *tkt); + krb5_db_entry *server, krb5_flags *tktflags, + krb5_ticket_times *times); void log_as_req(krb5_context context, diff --git a/src/kdc/tgs_policy.c b/src/kdc/tgs_policy.c index 1a1648eb0..33a8242cf 100644 --- a/src/kdc/tgs_policy.c +++ b/src/kdc/tgs_policy.c @@ -517,6 +517,60 @@ check_tgs_s4u2proxy(krb5_context context, krb5_kdc_req *req, return 0; } +/* Check the KDB policy for a final RBCD request. */ +static krb5_error_code +check_s4u2proxy_policy(krb5_context context, krb5_kdc_req *req, + krb5_principal desired_client, + krb5_principal impersonator_name, + krb5_db_entry *impersonator, krb5_pac impersonator_pac, + krb5_principal resource_name, krb5_db_entry *resource, + krb5_boolean is_crossrealm, krb5_boolean is_referral, + const char **status) +{ + krb5_error_code ret; + krb5_boolean support_rbcd, policy_denial = FALSE; + + /* Check if the client supports resource-based constrained delegation. */ + ret = kdc_get_pa_pac_rbcd(context, req->padata, &support_rbcd); + if (ret) + return ret; + + if (is_referral) { + if (!support_rbcd) { + /* The client must support RBCD for a referral to be useful. */ + *status = "UNSUPPORTED_S4U2PROXY_REQUEST"; + return KRB5KDC_ERR_BADOPTION; + } + /* Policy will be checked in the resource realm. */ + return 0; + } + + /* Try resource-based authorization if the client supports RBCD. */ + if (support_rbcd) { + ret = krb5_db_allowed_to_delegate_from(context, desired_client, + impersonator_name, + impersonator_pac, resource); + if (ret == KRB5KDC_ERR_BADOPTION) + policy_denial = TRUE; + else if (ret != KRB5_PLUGIN_OP_NOTSUPP) + return ret; + } + + /* Try traditional authorization if the requestor is in this realm. */ + if (!is_crossrealm) { + ret = krb5_db_check_allowed_to_delegate(context, desired_client, + impersonator, resource_name); + if (ret == KRB5KDC_ERR_BADOPTION) + policy_denial = TRUE; + else if (ret != KRB5_PLUGIN_OP_NOTSUPP) + return ret; + } + + *status = policy_denial ? "NOT_ALLOWED_TO_DELEGATE" : + "UNSUPPORTED_S4U2PROXY_REQUEST"; + return KRB5KDC_ERR_BADOPTION; +} + static krb5_error_code check_tgs_u2u(krb5_context context, krb5_kdc_req *req, const krb5_ticket *stkt, krb5_db_entry *server, const char **status) @@ -612,18 +666,17 @@ check_tgs_tgt(krb5_kdc_req *req, krb5_ticket *tkt, const char **status) } krb5_error_code -validate_tgs_request(kdc_realm_t *realm, krb5_kdc_req *request, - krb5_db_entry *server, krb5_ticket *ticket, krb5_pac pac, - const krb5_ticket *stkt, krb5_pac stkt_pac, - krb5_db_entry *stkt_server, krb5_timestamp kdc_time, - krb5_pa_s4u_x509_user *s4u_x509_user, - krb5_db_entry *s4u2self_client, - krb5_boolean is_crossrealm, krb5_boolean is_referral, - const char **status, krb5_pa_data ***e_data) +check_tgs_constraints(kdc_realm_t *realm, krb5_kdc_req *request, + krb5_db_entry *server, krb5_ticket *ticket, krb5_pac pac, + const krb5_ticket *stkt, krb5_pac stkt_pac, + krb5_db_entry *stkt_server, krb5_timestamp kdc_time, + krb5_pa_s4u_x509_user *s4u_x509_user, + krb5_db_entry *s4u2self_client, + krb5_boolean is_crossrealm, krb5_boolean is_referral, + const char **status, krb5_pa_data ***e_data) { krb5_context context = realm->realm_context; int errcode; - krb5_error_code ret; /* Depends only on request and ticket. */ errcode = check_tgs_opts(request, ticket, status); @@ -636,10 +689,6 @@ validate_tgs_request(kdc_realm_t *realm, krb5_kdc_req *request, if (errcode != 0) return errcode; - errcode = check_tgs_svc_policy(request, server, ticket, kdc_time, status); - if (errcode != 0) - return errcode; - if (request->kdc_options & NON_TGT_OPTION) errcode = check_tgs_nontgt(context, request, ticket, status); else @@ -647,12 +696,6 @@ validate_tgs_request(kdc_realm_t *realm, krb5_kdc_req *request, if (errcode != 0) return errcode; - /* Check the hot list */ - if (check_hot_list(ticket)) { - *status = "HOT_LIST"; - return(KRB5KRB_AP_ERR_REPEAT); - } - if (s4u_x509_user != NULL) { errcode = check_tgs_s4u2self(realm, request, server, ticket, pac, kdc_time, s4u_x509_user, s4u2self_client, @@ -683,9 +726,42 @@ validate_tgs_request(kdc_realm_t *realm, krb5_kdc_req *request, return errcode; } + return 0; +} + +krb5_error_code +check_tgs_policy(kdc_realm_t *realm, krb5_kdc_req *request, + krb5_db_entry *server, krb5_ticket *ticket, + krb5_pac pac, const krb5_ticket *stkt, krb5_pac stkt_pac, + krb5_principal stkt_pac_client, krb5_db_entry *stkt_server, + krb5_timestamp kdc_time, krb5_boolean is_crossrealm, + krb5_boolean is_referral, const char **status, + krb5_pa_data ***e_data) +{ + krb5_context context = realm->realm_context; + int errcode; + krb5_error_code ret; + krb5_principal desired_client; + + errcode = check_tgs_svc_policy(request, server, ticket, kdc_time, status); + if (errcode != 0) + return errcode; + + if (request->kdc_options & KDC_OPT_CNAME_IN_ADDL_TKT) { + desired_client = (stkt_pac_client != NULL) ? stkt_pac_client : + stkt->enc_part2->client; + errcode = check_s4u2proxy_policy(context, request, desired_client, + ticket->enc_part2->client, + stkt_server, pac, request->server, + server, is_crossrealm, is_referral, + status); + if (errcode != 0) + return errcode; + } + if (check_anon(realm, ticket->enc_part2->client, request->server) != 0) { *status = "ANONYMOUS NOT ALLOWED"; - return(KRB5KDC_ERR_POLICY); + return KRB5KDC_ERR_POLICY; } /* Perform KDB module policy checks. */ |