From e10f8035338e23009c042ef2fd188f351794b43e Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Mon, 3 Oct 2011 19:14:01 +0000 Subject: Make check_padata() respond via a callback From npmccallum@redhat.com with changes. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25293 dc483132-0cff-0310-8789-dd5450dbe970 --- src/kdc/do_as_req.c | 893 +++++++++++++++++++++++++++----------------------- src/kdc/kdc_preauth.c | 35 +- src/kdc/kdc_util.h | 7 +- 3 files changed, 514 insertions(+), 421 deletions(-) diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c index 3351fdf342..ff7d421850 100644 --- a/src/kdc/do_as_req.c +++ b/src/kdc/do_as_req.c @@ -98,127 +98,463 @@ get_key_exp(krb5_db_entry *entry) return min(entry->expiration, entry->pw_expiration); } +struct as_req_state { + loop_respond_fn respond; + void *arg; + + krb5_enc_tkt_part enc_tkt_reply; + krb5_enc_kdc_rep_part reply_encpart; + krb5_ticket ticket_reply; + krb5_keyblock server_keyblock; + krb5_keyblock client_keyblock; + krb5_db_entry *client; + krb5_db_entry *server; + krb5_kdc_req *request; + const char *status; + krb5_data e_data; + krb5_kdc_rep reply; + krb5_timestamp kdc_time; + krb5_timestamp authtime; + krb5_keyblock session_key; + unsigned int c_flags; + krb5_data *req_pkt; + struct kdc_request_state *rstate; + char *sname, *cname; + void *pa_context; + const krb5_fulladdr *from; +}; + +static void +finish_process_as_req(struct as_req_state *state, krb5_error_code errcode) +{ + krb5_key_data *server_key; + krb5_key_data *client_key; + krb5_keyblock *as_encrypting_key = NULL; + krb5_data *response; + const char *emsg = 0; + int did_log = 0; + register int i; + krb5_enctype useenctype; + loop_respond_fn oldrespond; + void *oldarg; + + assert(state); + oldrespond = state->respond; + oldarg = state->arg; + + if (errcode) + goto egress; + + /* + * Final check before handing out ticket: If the client requires + * preauthentication, verify that the proper kind of + * preauthentication was carried out. + */ + state->status = missing_required_preauth(state->client, + state->server, + &state->enc_tkt_reply); + if (state->status) { + errcode = KRB5KDC_ERR_PREAUTH_REQUIRED; + get_preauth_hint_list(state->request, state->client, + state->server, &state->e_data); + goto egress; + } + + if ((errcode = validate_forwardable(state->request, *state->client, + *state->server, state->kdc_time, + &state->status))) { + errcode += ERROR_TABLE_BASE_krb5; + goto egress; + } + + state->ticket_reply.enc_part2 = &state->enc_tkt_reply; + + /* + * Find the server key + */ + if ((errcode = krb5_dbe_find_enctype(kdc_context, state->server, + -1, /* ignore keytype */ + -1, /* Ignore salttype */ + 0, /* Get highest kvno */ + &server_key))) { + state->status = "FINDING_SERVER_KEY"; + goto egress; + } + + /* + * Convert server->key into a real key + * (it may be encrypted in the database) + * + * server_keyblock is later used to generate auth data signatures + */ + if ((errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL, + server_key, + &state->server_keyblock, + NULL))) { + state->status = "DECRYPT_SERVER_KEY"; + goto egress; + } + + /* + * Find the appropriate client key. We search in the order specified + * by request keytype list. + */ + client_key = NULL; + for (i = 0; i < state->request->nktypes; i++) { + useenctype = state->request->ktype[i]; + if (!krb5_c_valid_enctype(useenctype)) + continue; + + if (!krb5_dbe_find_enctype(kdc_context, state->client, + useenctype, -1, 0, &client_key)) + break; + } + if (!(client_key)) { + /* Cannot find an appropriate key */ + state->status = "CANT_FIND_CLIENT_KEY"; + errcode = KRB5KDC_ERR_ETYPE_NOSUPP; + goto egress; + } + + /* convert client.key_data into a real key */ + if ((errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL, + client_key, + &state->client_keyblock, + NULL))) { + state->status = "DECRYPT_CLIENT_KEY"; + goto egress; + } + state->client_keyblock.enctype = useenctype; + + /* Start assembling the response */ + state->reply.msg_type = KRB5_AS_REP; + state->reply.client = state->enc_tkt_reply.client; /* post canonization */ + state->reply.ticket = &state->ticket_reply; + state->reply_encpart.session = &state->session_key; + if ((errcode = fetch_last_req_info(state->client, + &state->reply_encpart.last_req))) { + state->status = "FETCH_LAST_REQ"; + goto egress; + } + state->reply_encpart.nonce = state->request->nonce; + state->reply_encpart.key_exp = get_key_exp(state->client); + state->reply_encpart.flags = state->enc_tkt_reply.flags; + state->reply_encpart.server = state->ticket_reply.server; + + /* copy the time fields EXCEPT for authtime; it's location + * is used for ktime + */ + state->reply_encpart.times = state->enc_tkt_reply.times; + state->reply_encpart.times.authtime = state->authtime = state->kdc_time; + + state->reply_encpart.caddrs = state->enc_tkt_reply.caddrs; + state->reply_encpart.enc_padata = NULL; + + /* Fetch the padata info to be returned (do this before + * authdata to handle possible replacement of reply key + */ + errcode = return_padata(kdc_context, state->client, state->req_pkt, + state->request, &state->reply, client_key, + &state->client_keyblock, &state->pa_context); + if (errcode) { + state->status = "KDC_RETURN_PADATA"; + goto egress; + } + +#if APPLE_PKINIT + asReqDebug("process_as_req reply realm %s name %s\n", + reply.client->realm.data, reply.client->data->data); +#endif /* APPLE_PKINIT */ + + + + errcode = handle_authdata(kdc_context, + state->c_flags, + state->client, + state->server, + state->server, + &state->client_keyblock, + &state->server_keyblock, + &state->server_keyblock, + state->req_pkt, + state->request, + NULL, /* for_user_princ */ + NULL, /* enc_tkt_request */ + &state->enc_tkt_reply); + if (errcode) { + krb5_klog_syslog(LOG_INFO, _("AS_REQ : handle_authdata (%d)"), + errcode); + state->status = "HANDLE_AUTHDATA"; + goto egress; + } + + errcode = krb5_encrypt_tkt_part(kdc_context, &state->server_keyblock, + &state->ticket_reply); + if (errcode) { + state->status = "ENCRYPTING_TICKET"; + goto egress; + } + state->ticket_reply.enc_part.kvno = server_key->key_data_kvno; + errcode = kdc_fast_response_handle_padata(state->rstate, + state->request, + &state->reply, + state->client_keyblock.enctype); + if (errcode) { + state->status = "fast response handling"; + goto egress; + } + + /* now encode/encrypt the response */ + + state->reply.enc_part.enctype = state->client_keyblock.enctype; + + errcode = kdc_fast_handle_reply_key(state->rstate, &state->client_keyblock, + &as_encrypting_key); + if (errcode) { + state->status = "generating reply key"; + goto egress; + } + errcode = return_enc_padata(kdc_context, state->req_pkt, state->request, + as_encrypting_key, state->server, + &state->reply_encpart, FALSE); + if (errcode) { + state->status = "KDC_RETURN_ENC_PADATA"; + goto egress; + } + + errcode = krb5_encode_kdc_rep(kdc_context, KRB5_AS_REP, + &state->reply_encpart, 0, + as_encrypting_key, + &state->reply, &response); + state->reply.enc_part.kvno = client_key->key_data_kvno; + if (errcode) { + state->status = "ENCODE_KDC_REP"; + goto egress; + } + + /* 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(state->reply.enc_part.ciphertext.data, 0, + state->reply.enc_part.ciphertext.length); + free(state->reply.enc_part.ciphertext.data); + + log_as_req(state->from, state->request, &state->reply, + state->client, state->cname, state->server, + state->sname, state->authtime, 0, 0, 0); + did_log = 1; + +egress: + if (errcode != 0) + assert (state->status != 0); + free_padata_context(kdc_context, state->pa_context); + if (as_encrypting_key) + krb5_free_keyblock(kdc_context, as_encrypting_key); + if (errcode) + emsg = krb5_get_error_message(kdc_context, errcode); + + if (state->status) { + log_as_req(state->from, state->request, &state->reply, state->client, + state->cname, state->server, state->sname, state->authtime, + state->status, errcode, emsg); + did_log = 1; + } + if (errcode) { + if (state->status == 0) { + state->status = emsg; + } + if (errcode != KRB5KDC_ERR_DISCARD) { + errcode -= ERROR_TABLE_BASE_krb5; + if (errcode < 0 || errcode > 128) + errcode = KRB_ERR_GENERIC; + + errcode = prepare_error_as(state->rstate, state->request, + errcode, &state->e_data, + (state->client != NULL) ? + state->client->princ : + NULL, + &response, state->status); + state->status = 0; + } + } + + if (emsg) + krb5_free_error_message(kdc_context, emsg); + if (state->enc_tkt_reply.authorization_data != NULL) + krb5_free_authdata(kdc_context, + state->enc_tkt_reply.authorization_data); + if (state->server_keyblock.contents != NULL) + krb5_free_keyblock_contents(kdc_context, &state->server_keyblock); + if (state->client_keyblock.contents != NULL) + krb5_free_keyblock_contents(kdc_context, &state->client_keyblock); + if (state->reply.padata != NULL) + krb5_free_pa_data(kdc_context, state->reply.padata); + if (state->reply_encpart.enc_padata) + krb5_free_pa_data(kdc_context, state->reply_encpart.enc_padata); + + if (state->cname != NULL) + free(state->cname); + if (state->sname != NULL) + free(state->sname); + krb5_db_free_principal(kdc_context, state->client); + krb5_db_free_principal(kdc_context, state->server); + if (state->session_key.contents != NULL) + krb5_free_keyblock_contents(kdc_context, &state->session_key); + if (state->ticket_reply.enc_part.ciphertext.data != NULL) { + memset(state->ticket_reply.enc_part.ciphertext.data , 0, + state->ticket_reply.enc_part.ciphertext.length); + free(state->ticket_reply.enc_part.ciphertext.data); + } + + krb5_free_data_contents(kdc_context, &state->e_data); + kdc_free_rstate(state->rstate); + state->request->kdc_state = NULL; + krb5_free_kdc_req(kdc_context, state->request); + assert(did_log != 0); + + free(state); + (*oldrespond)(oldarg, errcode, response); +} + +static void +finish_preauth(void *arg, krb5_error_code errcode) +{ + struct as_req_state *state = arg; + + if (errcode) { + if (errcode == KRB5KDC_ERR_PREAUTH_FAILED) + get_preauth_hint_list(state->request, state->client, + state->server, &state->e_data); + + state->status = "PREAUTH_FAILED"; + if (vague_errors) + errcode = KRB5KRB_ERR_GENERIC; + } + + finish_process_as_req(state, errcode); +} + /*ARGSUSED*/ void process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, const krb5_fulladdr *from, loop_respond_fn respond, void *arg) { - krb5_db_entry *client = NULL, *server = NULL; - krb5_kdc_rep reply; - krb5_enc_kdc_rep_part reply_encpart; - krb5_ticket ticket_reply; - krb5_enc_tkt_part enc_tkt_reply; krb5_error_code errcode; - krb5_timestamp kdc_time, authtime = 0; - krb5_keyblock session_key; - const char *status; - krb5_key_data *server_key, *client_key; - krb5_keyblock server_keyblock, client_keyblock; - krb5_enctype useenctype; - krb5_data e_data = empty_data(); - register int i; krb5_timestamp rtime; - char *cname = 0, *sname = 0; - unsigned int c_flags = 0, s_flags = 0; + unsigned int s_flags = 0; krb5_principal_data client_princ; - void *pa_context = NULL; - int did_log = 0; - const char *emsg = 0; - struct kdc_request_state *state = NULL; krb5_data encoded_req_body; - krb5_keyblock *as_encrypting_key = NULL; - krb5_data *response; - + krb5_enctype useenctype; + struct as_req_state *state; + + state = malloc(sizeof(*state)); + if (!state) { + (*respond)(arg, ENOMEM, NULL); + return; + } + state->session_key.contents = 0; + state->enc_tkt_reply.authorization_data = NULL; + state->reply.padata = 0; + memset(&state->reply, 0, sizeof(state->reply)); + state->respond = respond; + state->arg = arg; + state->ticket_reply.enc_part.ciphertext.data = 0; + state->server_keyblock.contents = NULL; + state->client_keyblock.contents = NULL; + state->reply_encpart.enc_padata = 0; + state->client = NULL; + state->server = NULL; + state->request = request; + state->e_data = empty_data(); + state->authtime = 0; + state->req_pkt = req_pkt; + state->rstate = NULL; + state->sname = 0; + state->cname = 0; + state->pa_context = NULL; + state->from = from; #if APPLE_PKINIT asReqDebug("process_as_req top realm %s name %s\n", request->client->realm.data, request->client->data->data); #endif /* APPLE_PKINIT */ - ticket_reply.enc_part.ciphertext.data = 0; - server_keyblock.contents = NULL; - client_keyblock.contents = NULL; - reply.padata = 0; - reply_encpart.enc_padata = 0; - memset(&reply, 0, sizeof(reply)); - - session_key.contents = 0; - enc_tkt_reply.authorization_data = NULL; - - if (request->msg_type != KRB5_AS_REQ) { - status = "msg_type mismatch"; + if (state->request->msg_type != KRB5_AS_REQ) { + state->status = "msg_type mismatch"; errcode = KRB5_BADMSGTYPE; goto errout; } - errcode = kdc_make_rstate(&state); + errcode = kdc_make_rstate(&state->rstate); if (errcode != 0) { - status = "constructing state"; + state->status = "constructing state"; goto errout; } if (fetch_asn1_field((unsigned char *) req_pkt->data, 1, 4, &encoded_req_body) != 0) { errcode = ASN1_BAD_ID; - status = "Finding req_body"; + state->status = "Finding req_body"; goto errout; } - errcode = kdc_find_fast(&request, &encoded_req_body, - NULL /*TGS key*/, NULL, state); + errcode = kdc_find_fast(&state->request, &encoded_req_body, + NULL /*TGS key*/, NULL, state->rstate); if (errcode) { - status = "error decoding FAST"; + state->status = "error decoding FAST"; goto errout; } - request->kdc_state = state; - if (!request->client) { - status = "NULL_CLIENT"; + state->request->kdc_state = state->rstate; + if (!state->request->client) { + state->status = "NULL_CLIENT"; errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto errout; } - if ((errcode = krb5_unparse_name(kdc_context, request->client, &cname))) { - status = "UNPARSING_CLIENT"; + if ((errcode = krb5_unparse_name(kdc_context, + state->request->client, + &state->cname))) { + state->status = "UNPARSING_CLIENT"; goto errout; } - limit_string(cname); - if (!request->server) { - status = "NULL_SERVER"; + limit_string(state->cname); + if (!state->request->server) { + state->status = "NULL_SERVER"; errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto errout; } - if ((errcode = krb5_unparse_name(kdc_context, request->server, &sname))) { - status = "UNPARSING_SERVER"; + if ((errcode = krb5_unparse_name(kdc_context, + state->request->server, + &state->sname))) { + state->status = "UNPARSING_SERVER"; goto errout; } - limit_string(sname); + limit_string(state->sname); /* * We set KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY as a hint * to the backend to return naming information in lieu * of cross realm TGS entries. */ - setflag(c_flags, KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY); + setflag(state->c_flags, KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY); /* * Note that according to the referrals draft we should * always canonicalize enterprise principal names. */ - if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE) || - krb5_princ_type(kdc_context, - request->client) == KRB5_NT_ENTERPRISE_PRINCIPAL) { - setflag(c_flags, KRB5_KDB_FLAG_CANONICALIZE); - setflag(c_flags, KRB5_KDB_FLAG_ALIAS_OK); + if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE) || + state->request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL) { + setflag(state->c_flags, KRB5_KDB_FLAG_CANONICALIZE); + setflag(state->c_flags, KRB5_KDB_FLAG_ALIAS_OK); } - if (include_pac_p(kdc_context, request)) { - setflag(c_flags, KRB5_KDB_FLAG_INCLUDE_PAC); + if (include_pac_p(kdc_context, state->request)) { + setflag(state->c_flags, KRB5_KDB_FLAG_INCLUDE_PAC); } - errcode = krb5_db_get_principal(kdc_context, request->client, - c_flags, &client); + errcode = krb5_db_get_principal(kdc_context, state->request->client, + state->c_flags, &state->client); if (errcode == KRB5_KDB_NOENTRY) { - status = "CLIENT_NOT_FOUND"; + state->status = "CLIENT_NOT_FOUND"; if (vague_errors) errcode = KRB5KRB_ERR_GENERIC; else errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto errout; } else if (errcode) { - status = "LOOKING_UP_CLIENT"; + state->status = "LOOKING_UP_CLIENT"; goto errout; } @@ -226,9 +562,9 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, * If the backend returned a principal that is not in the local * realm, then we need to refer the client to that realm. */ - if (!is_local_principal(client->princ)) { + if (!is_local_principal(state->client->princ)) { /* Entry is a referral to another realm */ - status = "REFERRAL"; + state->status = "REFERRAL"; errcode = KRB5KDC_ERR_WRONG_REALM; goto errout; } @@ -247,30 +583,31 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, s_flags = 0; setflag(s_flags, KRB5_KDB_FLAG_ALIAS_OK); - if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE)) { + if (isflagset(state->request->kdc_options, KDC_OPT_CANONICALIZE)) { setflag(s_flags, KRB5_KDB_FLAG_CANONICALIZE); } - errcode = krb5_db_get_principal(kdc_context, request->server, - s_flags, &server); + errcode = krb5_db_get_principal(kdc_context, state->request->server, + s_flags, &state->server); if (errcode == KRB5_KDB_NOENTRY) { - status = "SERVER_NOT_FOUND"; + state->status = "SERVER_NOT_FOUND"; errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto errout; } else if (errcode) { - status = "LOOKING_UP_SERVER"; + state->status = "LOOKING_UP_SERVER"; goto errout; } - if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) { - status = "TIMEOFDAY"; + if ((errcode = krb5_timeofday(kdc_context, &state->kdc_time))) { + state->status = "TIMEOFDAY"; goto errout; } - authtime = kdc_time; /* for audit_as_request() */ + state->authtime = state->kdc_time; /* for audit_as_request() */ - if ((errcode = validate_as_request(request, *client, *server, - kdc_time, &status, &e_data))) { - if (!status) - status = "UNKNOWN_REASON"; + if ((errcode = validate_as_request(state->request, *state->client, + *state->server, state->kdc_time, + &state->status, &state->e_data))) { + if (!state->status) + state->status = "UNKNOWN_REASON"; errcode += ERROR_TABLE_BASE_krb5; goto errout; } @@ -278,18 +615,18 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, /* * Select the keytype for the ticket session key. */ - if ((useenctype = select_session_keytype(kdc_context, server, - request->nktypes, - request->ktype)) == 0) { + if ((useenctype = select_session_keytype(kdc_context, state->server, + state->request->nktypes, + state->request->ktype)) == 0) { /* unsupported ktype */ - status = "BAD_ENCRYPTION_TYPE"; + state->status = "BAD_ENCRYPTION_TYPE"; errcode = KRB5KDC_ERR_ETYPE_NOSUPP; goto errout; } if ((errcode = krb5_c_make_random_key(kdc_context, useenctype, - &session_key))) { - status = "RANDOM_KEY_FAILED"; + &state->session_key))) { + state->status = "RANDOM_KEY_FAILED"; goto errout; } @@ -299,18 +636,18 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, * aliases, nothing more). */ if (isflagset(s_flags, KRB5_KDB_FLAG_CANONICALIZE) && - krb5_is_tgs_principal(request->server) && - krb5_is_tgs_principal(server->princ)) { - ticket_reply.server = server->princ; + krb5_is_tgs_principal(state->request->server) && + krb5_is_tgs_principal(state->server->princ)) { + state->ticket_reply.server = state->server->princ; } else { - ticket_reply.server = request->server; + state->ticket_reply.server = state->request->server; } - enc_tkt_reply.flags = 0; - enc_tkt_reply.times.authtime = authtime; + state->enc_tkt_reply.flags = 0; + state->enc_tkt_reply.times.authtime = state->authtime; - setflag(enc_tkt_reply.flags, TKT_FLG_INITIAL); - setflag(enc_tkt_reply.flags, TKT_FLG_ENC_PA_REP); + setflag(state->enc_tkt_reply.flags, TKT_FLG_INITIAL); + setflag(state->enc_tkt_reply.flags, TKT_FLG_ENC_PA_REP); /* * It should be noted that local policy may affect the @@ -318,367 +655,109 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, * realms may refuse to issue renewable tickets */ - if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) - setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); + if (isflagset(state->request->kdc_options, KDC_OPT_FORWARDABLE)) + setflag(state->enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); - if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) - setflag(enc_tkt_reply.flags, TKT_FLG_PROXIABLE); + if (isflagset(state->request->kdc_options, KDC_OPT_PROXIABLE)) + setflag(state->enc_tkt_reply.flags, TKT_FLG_PROXIABLE); - if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE)) - setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE); + if (isflagset(state->request->kdc_options, KDC_OPT_ALLOW_POSTDATE)) + setflag(state->enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE); - enc_tkt_reply.session = &session_key; - if (isflagset(c_flags, KRB5_KDB_FLAG_CANONICALIZE)) { - client_princ = *(client->princ); + state->enc_tkt_reply.session = &state->session_key; + if (isflagset(state->c_flags, KRB5_KDB_FLAG_CANONICALIZE)) { + client_princ = *(state->client->princ); } else { - client_princ = *(request->client); + client_princ = *(state->request->client); /* The realm is always canonicalized */ - client_princ.realm = *(krb5_princ_realm(context, client->princ)); + client_princ.realm = state->client->princ->realm; } - enc_tkt_reply.client = &client_princ; - enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; - enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */ + state->enc_tkt_reply.client = &client_princ; + state->enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; + state->enc_tkt_reply.transited.tr_contents = empty_string; - if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) { - setflag(enc_tkt_reply.flags, TKT_FLG_POSTDATED); - setflag(enc_tkt_reply.flags, TKT_FLG_INVALID); - enc_tkt_reply.times.starttime = request->from; + if (isflagset(state->request->kdc_options, KDC_OPT_POSTDATED)) { + setflag(state->enc_tkt_reply.flags, TKT_FLG_POSTDATED); + setflag(state->enc_tkt_reply.flags, TKT_FLG_INVALID); + state->enc_tkt_reply.times.starttime = state->request->from; } else - enc_tkt_reply.times.starttime = kdc_time; + state->enc_tkt_reply.times.starttime = state->kdc_time; - kdc_get_ticket_endtime(kdc_context, - enc_tkt_reply.times.starttime, - kdc_infinity, - request->till, - client, - server, - &enc_tkt_reply.times.endtime); + kdc_get_ticket_endtime(kdc_context, state->enc_tkt_reply.times.starttime, + kdc_infinity, state->request->till, state->client, + state->server, &state->enc_tkt_reply.times.endtime); - if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) && - !isflagset(client->attributes, KRB5_KDB_DISALLOW_RENEWABLE) && - (enc_tkt_reply.times.endtime < request->till)) { + if (isflagset(state->request->kdc_options, KDC_OPT_RENEWABLE_OK) && + !isflagset(state->client->attributes, KRB5_KDB_DISALLOW_RENEWABLE) && + (state->enc_tkt_reply.times.endtime < state->request->till)) { /* we set the RENEWABLE option for later processing */ - setflag(request->kdc_options, KDC_OPT_RENEWABLE); - request->rtime = request->till; + setflag(state->request->kdc_options, KDC_OPT_RENEWABLE); + state->request->rtime = state->request->till; } - rtime = (request->rtime == 0) ? kdc_infinity : request->rtime; + rtime = (state->request->rtime == 0) ? kdc_infinity : + state->request->rtime; - if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) { + if (isflagset(state->request->kdc_options, KDC_OPT_RENEWABLE)) { /* * XXX Should we squelch the output renew_till to be no * earlier than the endtime of the ticket? */ - setflag(enc_tkt_reply.flags, TKT_FLG_RENEWABLE); - enc_tkt_reply.times.renew_till = - min(rtime, enc_tkt_reply.times.starttime + - min(client->max_renewable_life, - min(server->max_renewable_life, + setflag(state->enc_tkt_reply.flags, TKT_FLG_RENEWABLE); + state->enc_tkt_reply.times.renew_till = + min(rtime, state->enc_tkt_reply.times.starttime + + min(state->client->max_renewable_life, + min(state->server->max_renewable_life, max_renewable_life_for_realm))); } else - enc_tkt_reply.times.renew_till = 0; /* XXX */ + state->enc_tkt_reply.times.renew_till = 0; /* XXX */ /* * 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 (state->enc_tkt_reply.times.starttime == + state->enc_tkt_reply.times.authtime) + state->enc_tkt_reply.times.starttime = 0; - enc_tkt_reply.caddrs = request->addresses; - enc_tkt_reply.authorization_data = 0; + state->enc_tkt_reply.caddrs = state->request->addresses; + state->enc_tkt_reply.authorization_data = 0; /* If anonymous requests are being used, adjust the realm of the client * principal. */ - if (isflagset(request->kdc_options, KDC_OPT_REQUEST_ANONYMOUS)) { - if (!krb5_principal_compare_any_realm(kdc_context, request->client, + if (isflagset(state->request->kdc_options, KDC_OPT_REQUEST_ANONYMOUS)) { + if (!krb5_principal_compare_any_realm(kdc_context, + state->request->client, krb5_anonymous_principal())) { errcode = KRB5KDC_ERR_BADOPTION; - status = "Anonymous requested but anonymous principal not used."; + state->status = "Anonymous requested but anonymous " + "principal not used."; goto errout; } - setflag(enc_tkt_reply.flags, TKT_FLG_ANONYMOUS); - krb5_free_principal(kdc_context, request->client); + setflag(state->enc_tkt_reply.flags, TKT_FLG_ANONYMOUS); + krb5_free_principal(kdc_context, state->request->client); errcode = krb5_copy_principal(kdc_context, krb5_anonymous_principal(), - &request->client); + &state->request->client); if (errcode) { - status = "Copying anonymous principal"; + state->status = "Copying anonymous principal"; goto errout; } - enc_tkt_reply.client = request->client; - setflag(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH); + state->enc_tkt_reply.client = state->request->client; + setflag(state->client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH); } /* * Check the preauthentication if it is there. */ - if (request->padata) { - errcode = check_padata(kdc_context, client, req_pkt, request, - &enc_tkt_reply, &pa_context, &e_data); - if (errcode) { - if (errcode == KRB5KDC_ERR_PREAUTH_FAILED) - get_preauth_hint_list(request, client, server, &e_data); - - status = "PREAUTH_FAILED"; - if (vague_errors) - errcode = KRB5KRB_ERR_GENERIC; - goto errout; - } - } - - /* - * Final check before handing out ticket: If the client requires - * preauthentication, verify that the proper kind of - * preauthentication was carried out. - */ - status = missing_required_preauth(client, server, &enc_tkt_reply); - if (status) { - errcode = KRB5KDC_ERR_PREAUTH_REQUIRED; - get_preauth_hint_list(request, client, server, &e_data); - goto errout; - } - - if ((errcode = validate_forwardable(request, *client, *server, - kdc_time, &status))) { - errcode += ERROR_TABLE_BASE_krb5; - goto errout; - } - - ticket_reply.enc_part2 = &enc_tkt_reply; - - /* - * Find the server key - */ - if ((errcode = krb5_dbe_find_enctype(kdc_context, server, - -1, /* ignore keytype */ - -1, /* Ignore salttype */ - 0, /* Get highest kvno */ - &server_key))) { - status = "FINDING_SERVER_KEY"; - goto errout; - } - - /* - * Convert server->key into a real key - * (it may be encrypted in the database) - * - * server_keyblock is later used to generate auth data signatures - */ - if ((errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL, - server_key, &server_keyblock, - NULL))) { - status = "DECRYPT_SERVER_KEY"; - goto errout; - } - - /* - * Find the appropriate client key. We search in the order specified - * by request keytype list. - */ - client_key = (krb5_key_data *) NULL; - for (i = 0; i < request->nktypes; i++) { - useenctype = request->ktype[i]; - if (!krb5_c_valid_enctype(useenctype)) - continue; - - if (!krb5_dbe_find_enctype(kdc_context, client, useenctype, -1, - 0, &client_key)) - break; - } - if (!(client_key)) { - /* Cannot find an appropriate key */ - status = "CANT_FIND_CLIENT_KEY"; - errcode = KRB5KDC_ERR_ETYPE_NOSUPP; - goto errout; - } - - /* convert client.key_data into a real key */ - if ((errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL, - client_key, &client_keyblock, - NULL))) { - status = "DECRYPT_CLIENT_KEY"; - goto errout; - } - client_keyblock.enctype = useenctype; - - /* Start assembling the response */ - reply.msg_type = KRB5_AS_REP; - reply.client = enc_tkt_reply.client; /* post canonicalization */ - reply.ticket = &ticket_reply; - reply_encpart.session = &session_key; - if ((errcode = fetch_last_req_info(client, &reply_encpart.last_req))) { - status = "FETCH_LAST_REQ"; - goto errout; - } - reply_encpart.nonce = request->nonce; - reply_encpart.key_exp = get_key_exp(client); - reply_encpart.flags = enc_tkt_reply.flags; - reply_encpart.server = ticket_reply.server; - - /* copy the time fields EXCEPT for authtime; it's location - * is used for ktime - */ - reply_encpart.times = enc_tkt_reply.times; - reply_encpart.times.authtime = authtime = kdc_time; - - reply_encpart.caddrs = enc_tkt_reply.caddrs; - reply_encpart.enc_padata = NULL; - - /* Fetch the padata info to be returned (do this before - * authdata to handle possible replacement of reply key - */ - errcode = return_padata(kdc_context, client, req_pkt, request, - &reply, client_key, &client_keyblock, &pa_context); - if (errcode) { - status = "KDC_RETURN_PADATA"; - goto errout; - } - -#if APPLE_PKINIT - asReqDebug("process_as_req reply realm %s name %s\n", - reply.client->realm.data, reply.client->data->data); -#endif /* APPLE_PKINIT */ - - - - errcode = handle_authdata(kdc_context, - c_flags, - client, - server, - server, - &client_keyblock, - &server_keyblock, - &server_keyblock, - req_pkt, - request, - NULL, /* for_user_princ */ - NULL, /* enc_tkt_request */ - &enc_tkt_reply); - if (errcode) { - krb5_klog_syslog(LOG_INFO, _("AS_REQ : handle_authdata (%d)"), - errcode); - status = "HANDLE_AUTHDATA"; - goto errout; + if (state->request->padata) { + check_padata(kdc_context, state->client, state->req_pkt, + state->request, &state->enc_tkt_reply, &state->pa_context, + &state->e_data, finish_preauth, state); + return; } - errcode = krb5_encrypt_tkt_part(kdc_context, &server_keyblock, - &ticket_reply); - if (errcode) { - status = "ENCRYPTING_TICKET"; - goto errout; - } - ticket_reply.enc_part.kvno = server_key->key_data_kvno; - errcode = kdc_fast_response_handle_padata(state, request, &reply, - client_keyblock.enctype); - if (errcode) { - status = "fast response handling"; - goto errout; - } - - /* now encode/encrypt the response */ - - reply.enc_part.enctype = client_keyblock.enctype; - - errcode = kdc_fast_handle_reply_key(state, &client_keyblock, - &as_encrypting_key); - if (errcode) { - status = "generating reply key"; - goto errout; - } - errcode = return_enc_padata(kdc_context, req_pkt, request, - as_encrypting_key, server, &reply_encpart, - FALSE); - if (errcode) { - status = "KDC_RETURN_ENC_PADATA"; - goto errout; - } - - errcode = krb5_encode_kdc_rep(kdc_context, KRB5_AS_REP, &reply_encpart, - 0, as_encrypting_key, &reply, &response); - reply.enc_part.kvno = client_key->key_data_kvno; - if (errcode) { - status = "ENCODE_KDC_REP"; - goto errout; - } - - /* 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); - - log_as_req(from, request, &reply, client, cname, server, sname, - authtime, 0, 0, 0); - did_log = 1; - - goto egress; - errout: - assert (status != 0); - /* fall through */ - -egress: - free_padata_context(kdc_context, pa_context); - if (as_encrypting_key) - krb5_free_keyblock(kdc_context, as_encrypting_key); - if (errcode) - emsg = krb5_get_error_message(kdc_context, errcode); - - if (status) { - log_as_req(from, request, &reply, client, cname, server, sname, - authtime, status, errcode, emsg); - did_log = 1; - } - if (errcode) { - if (status == 0) { - status = emsg; - } - if (errcode == KRB5KDC_ERR_DISCARD) - goto discard; - errcode -= ERROR_TABLE_BASE_krb5; - if (errcode < 0 || errcode > 128) - errcode = KRB_ERR_GENERIC; - - errcode = prepare_error_as(state, request, errcode, &e_data, - (client != NULL) ? client->princ : NULL, - &response, status); - status = 0; - } - -discard: - if (emsg) - krb5_free_error_message(kdc_context, emsg); - if (enc_tkt_reply.authorization_data != NULL) - krb5_free_authdata(kdc_context, enc_tkt_reply.authorization_data); - if (server_keyblock.contents != NULL) - krb5_free_keyblock_contents(kdc_context, &server_keyblock); - if (client_keyblock.contents != NULL) - krb5_free_keyblock_contents(kdc_context, &client_keyblock); - if (reply.padata != NULL) - krb5_free_pa_data(kdc_context, reply.padata); - if (reply_encpart.enc_padata) - krb5_free_pa_data(kdc_context, reply_encpart.enc_padata); - - if (cname != NULL) - free(cname); - if (sname != NULL) - free(sname); - krb5_db_free_principal(kdc_context, client); - krb5_db_free_principal(kdc_context, server); - if (session_key.contents != NULL) - krb5_free_keyblock_contents(kdc_context, &session_key); - if (ticket_reply.enc_part.ciphertext.data != NULL) { - memset(ticket_reply.enc_part.ciphertext.data , 0, - ticket_reply.enc_part.ciphertext.length); - free(ticket_reply.enc_part.ciphertext.data); - } - - krb5_free_data_contents(kdc_context, &e_data); - kdc_free_rstate(state); - request->kdc_state = NULL; - krb5_free_kdc_req(kdc_context, request); - assert(did_log != 0); - - (*respond)(arg, errcode, response); + finish_process_as_req(state, errcode); } /* diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c index dabc9c159d..5e1312656e 100644 --- a/src/kdc/kdc_preauth.c +++ b/src/kdc/kdc_preauth.c @@ -941,10 +941,11 @@ add_authorization_data(krb5_enc_tkt_part *enc_tkt_part, krb5_authdata **ad) * an error code of some sort. */ -krb5_error_code +void check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply, - void **padata_context, krb5_data *e_data) + void **padata_context, krb5_data *e_data, + kdc_preauth_respond_fn respond, void *arg) { krb5_error_code retval = 0; krb5_pa_data **padata; @@ -957,11 +958,14 @@ check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt, const char *emsg; krb5_authdata **tmp_authz_data = NULL; - if (request->padata == 0) - return 0; + if (request->padata == 0) { + (*respond)(arg, 0); + return; + } if (make_padata_context(context, padata_context) != 0) { - return KRB5KRB_ERR_GENERIC; + (*respond)(arg, KRB5KRB_ERR_GENERIC); + return; } #ifdef DEBUG @@ -1056,7 +1060,8 @@ check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt, e_data->data = malloc(pa_e_data->length); if (e_data->data == NULL) { krb5_free_data(context, pa_e_data); - return KRB5KRB_ERR_GENERIC; + (*respond)(arg, KRB5KRB_ERR_GENERIC); + return; } memcpy(e_data->data, pa_e_data->data, pa_e_data->length); e_data->length = pa_e_data->length; @@ -1066,13 +1071,17 @@ check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt, retval = saved_retval; } - if (pa_ok) - return 0; + if (pa_ok) { + (*respond)(arg, 0); + return; + } /* pa system was not found; we may return PREAUTH_REQUIRED later, but we did not actually fail to verify the pre-auth. */ - if (!pa_found) - return 0; + if (!pa_found) { + (*respond)(arg, 0); + return; + } /* The following switch statement allows us @@ -1107,9 +1116,11 @@ check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt, case KRB5KDC_ERR_DISCARD: /* pkinit alg-agility */ case KRB5KDC_ERR_NO_ACCEPTABLE_KDF: - return retval; + (*respond)(arg, retval); + return; default: - return KRB5KDC_ERR_PREAUTH_FAILED; + (*respond)(arg, KRB5KDC_ERR_PREAUTH_FAILED); + return; } } diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h index 368f0806af..296d3e9fae 100644 --- a/src/kdc/kdc_util.h +++ b/src/kdc/kdc_util.h @@ -176,12 +176,15 @@ load_preauth_plugins(krb5_context context); void unload_preauth_plugins(krb5_context context); -krb5_error_code +typedef void (*kdc_preauth_respond_fn)(void *arg, krb5_error_code code); + +void check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply, - void **padata_context, krb5_data *e_data); + void **padata_context, krb5_data *e_data, + kdc_preauth_respond_fn respond, void *state); krb5_error_code return_padata (krb5_context context, krb5_db_entry *client, -- cgit