From: Greg Hudson Date: Sat, 6 Jun 2015 19:45:39 +0000 (-0400) Subject: Only include one key in etype-info X-Git-Tag: krb5-1.14-alpha1~80 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=385cd2d07983a89892dad1606e1a41a78066c6ec;p=thirdparty%2Fkrb5.git Only include one key in etype-info As described in RFC 6113 section 2.1, the KDC can choose a single long-term key at the beginning of the preauth conversation based on the request enctype list. Implement this change for the PA-ETYPE-INFO and PA-ETYPE-INFO2 padata included in preauth hint lists, by selecting the client key before checking padata, making the client keyblock available in the preauth rock, and unifying the etype-info handlers to use a single helper function for edata and AS-REP padata. ticket: 8199 (new) --- diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c index c62f0b39d5..3e18e7ca44 100644 --- a/src/kdc/do_as_req.c +++ b/src/kdc/do_as_req.c @@ -90,6 +90,46 @@ get_key_exp(krb5_db_entry *entry) return min(entry->expiration, entry->pw_expiration); } +/* + * Find the key in client for the most preferred enctype in req_enctypes. Fill + * in *kb_out with the decrypted keyblock (which the caller must free) and set + * *kd_out to an alias to that key data entry. Set *kd_out to NULL and leave + * *kb_out zeroed if no key is found for any of the requested enctypes. + * kb_out->enctype may differ from the enctype of *kd_out for DES enctypes; in + * this case, kb_out->enctype is the requested enctype used to match the key + * data entry. + */ +static krb5_error_code +select_client_key(krb5_context context, krb5_db_entry *client, + krb5_enctype *req_enctypes, int n_req_enctypes, + krb5_keyblock *kb_out, krb5_key_data **kd_out) +{ + krb5_error_code ret; + krb5_key_data *kd; + krb5_enctype etype; + int i; + + memset(kb_out, 0, sizeof(*kb_out)); + *kd_out = NULL; + + for (i = 0; i < n_req_enctypes; i++) { + etype = req_enctypes[i]; + if (!krb5_c_valid_enctype(etype)) + continue; + if (krb5_dbe_find_enctype(context, client, etype, -1, 0, &kd) == 0) { + /* Decrypt the client key data and set its enctype to the request + * enctype (which may differ from the key data enctype for DES). */ + ret = krb5_dbe_decrypt_key_data(context, NULL, kd, kb_out, NULL); + if (ret) + return ret; + kb_out->enctype = etype; + *kd_out = kd; + return 0; + } + } + return 0; +} + struct as_req_state { loop_respond_fn respond; void *arg; @@ -104,6 +144,7 @@ struct as_req_state { krb5_db_entry *server; krb5_db_entry *local_tgt; krb5_db_entry *local_tgt_storage; + krb5_key_data *client_key; krb5_kdc_req *request; struct krb5_kdcpreauth_rock_st rock; const char *status; @@ -131,13 +172,10 @@ 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 = NULL; const char *emsg = 0; int did_log = 0; - register int i; - krb5_enctype useenctype; loop_respond_fn oldrespond; void *oldarg; kdc_realm_t *kdc_active_realm = state->active_realm; @@ -187,34 +225,6 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode) goto egress; } - /* - * Find the appropriate client key. We search in the order specified - * by request keytype list. - */ - client_key = NULL; - useenctype = 0; - 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 != NULL) { - /* Decrypt the client key data entry to get the real reply key. */ - errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL, client_key, - &state->client_keyblock, NULL); - if (errcode) { - state->status = "DECRYPT_CLIENT_KEY"; - goto egress; - } - state->client_keyblock.enctype = useenctype; - state->rock.client_key = client_key; - } - /* Start assembling the response */ state->reply.msg_type = KRB5_AS_REP; state->reply.client = state->enc_tkt_reply.client; /* post canonization */ @@ -327,8 +337,8 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode) &state->reply_encpart, 0, as_encrypting_key, &state->reply, &response); - if (client_key != NULL) - state->reply.enc_part.kvno = client_key->key_data_kvno; + if (state->client_key != NULL) + state->reply.enc_part.kvno = state->client_key->key_data_kvno; if (errcode) { state->status = "ENCODE_KDC_REP"; goto egress; @@ -772,6 +782,18 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, setflag(state->client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH); } + errcode = select_client_key(kdc_context, state->client, + state->request->ktype, state->request->nktypes, + &state->client_keyblock, &state->client_key); + if (errcode) { + state->status = "DECRYPT_CLIENT_KEY"; + goto errout; + } + if (state->client_key != NULL) { + state->rock.client_key = state->client_key; + state->rock.client_keyblock = &state->client_keyblock; + } + /* * Check the preauthentication if it is there. */ diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c index dd83844591..0a5d8f4d40 100644 --- a/src/kdc/kdc_preauth.c +++ b/src/kdc/kdc_preauth.c @@ -107,21 +107,6 @@ get_etype_info(krb5_context context, krb5_kdc_req *request, krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, krb5_kdcpreauth_edata_respond_fn respond, void *arg); -static void -get_etype_info2(krb5_context context, krb5_kdc_req *request, - krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, - krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, - krb5_kdcpreauth_edata_respond_fn respond, void *arg); - -static krb5_error_code -etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata, - krb5_db_entry *client, - krb5_kdc_req *request, krb5_kdc_rep *reply, - krb5_key_data *client_key, - krb5_keyblock *encrypting_key, - krb5_pa_data **send_pa, - int etype_info2); - static krb5_error_code return_etype_info(krb5_context, krb5_pa_data *padata, krb5_data *req_pkt, krb5_kdc_req *request, @@ -130,14 +115,6 @@ return_etype_info(krb5_context, krb5_pa_data *padata, krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, krb5_kdcpreauth_modreq modreq); -static krb5_error_code -return_etype_info2(krb5_context, krb5_pa_data *padata, - krb5_data *req_pkt, krb5_kdc_req *request, - krb5_kdc_rep *reply, krb5_keyblock *encrypting_key, - krb5_pa_data **send_pa, krb5_kdcpreauth_callbacks cb, - krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, - krb5_kdcpreauth_modreq modreq); - static krb5_error_code return_pw_salt(krb5_context, krb5_pa_data *padata, krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply, @@ -177,9 +154,9 @@ static preauth_system static_preauth_systems[] = { NULL, NULL, NULL, - get_etype_info2, + get_etype_info, 0, - return_etype_info2 + return_etype_info }, { "pw-salt", @@ -1288,17 +1265,6 @@ cleanup: return (retval); } -static krb5_boolean -request_contains_enctype(krb5_context context, const krb5_kdc_req *request, - krb5_enctype enctype) -{ - int i; - for (i =0; i < request->nktypes; i++) - if (request->ktype[i] == enctype) - return 1; - return 0; -} - static krb5_error_code _make_etype_info_entry(krb5_context context, krb5_principal client_princ, krb5_key_data *client_key, @@ -1353,80 +1319,27 @@ cleanup: return retval; } -/* Create etype information for a client for the preauth-required hint list, - * for either etype-info or etype-info2. */ -static void -etype_info_helper(krb5_context context, krb5_kdc_req *request, - krb5_db_entry *client, krb5_preauthtype pa_type, - krb5_kdcpreauth_edata_respond_fn respond, void *arg) +/* Create etype-info or etype-info2 padata for client_key with the given + * enctype, using client to compute the salt if necessary. */ +static krb5_error_code +make_etype_info(krb5_context context, krb5_preauthtype pa_type, + krb5_principal client, krb5_key_data *client_key, + krb5_enctype enctype, krb5_pa_data **pa_out) { krb5_error_code retval; krb5_pa_data *pa = NULL; krb5_etype_info_entry **entry = NULL; krb5_data *scratch = NULL; - krb5_key_data *client_key; - krb5_enctype db_etype; - int i = 0, start = 0, seen_des = 0; int etype_info2 = (pa_type == KRB5_PADATA_ETYPE_INFO2); - entry = k5calloc(client->n_key_data * 2 + 1, sizeof(*entry), &retval); + *pa_out = NULL; + + entry = k5calloc(2, sizeof(*entry), &retval); if (entry == NULL) goto cleanup; - entry[0] = NULL; - - while (1) { - retval = krb5_dbe_search_enctype(context, client, &start, -1, - -1, 0, &client_key); - if (retval == KRB5_KDB_NO_MATCHING_KEY) - break; - if (retval) - goto cleanup; - db_etype = client_key->key_data_type[0]; - if (db_etype == ENCTYPE_DES_CBC_MD4) - db_etype = ENCTYPE_DES_CBC_MD5; - - if (request_contains_enctype(context, request, db_etype)) { - assert(etype_info2 || - !enctype_requires_etype_info_2(db_etype)); - retval = _make_etype_info_entry(context, client->princ, client_key, - db_etype, &entry[i], etype_info2); - if (retval != 0) - goto cleanup; - i++; - } - - /* - * If there is a des key in the kdb, try the "similar" enctypes, - * avoid duplicate entries. - */ - if (!seen_des) { - switch (db_etype) { - case ENCTYPE_DES_CBC_MD5: - db_etype = ENCTYPE_DES_CBC_CRC; - break; - case ENCTYPE_DES_CBC_CRC: - db_etype = ENCTYPE_DES_CBC_MD5; - break; - default: - continue; - - } - if (krb5_is_permitted_enctype(context, db_etype) && - request_contains_enctype(context, request, db_etype)) { - retval = _make_etype_info_entry(context, client->princ, - client_key, db_etype, - &entry[i], etype_info2); - if (retval != 0) - goto cleanup; - entry[i+1] = 0; - i++; - } - seen_des++; - } - } - - /* If the list is empty, don't send it at all. */ - if (i == 0) + retval = _make_etype_info_entry(context, client, client_key, enctype, + &entry[0], etype_info2); + if (retval != 0) goto cleanup; if (etype_info2) @@ -1443,135 +1356,50 @@ etype_info_helper(krb5_context context, krb5_kdc_req *request, pa->contents = (unsigned char *)scratch->data; pa->length = scratch->length; scratch->data = NULL; + *pa_out = pa; cleanup: krb5_free_etype_info(context, entry); krb5_free_data(context, scratch); - (*respond)(arg, retval, pa); + return retval; } -static void -get_etype_info(krb5_context context, krb5_kdc_req *request, - krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, - krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, - krb5_kdcpreauth_edata_respond_fn respond, void *arg) +/* Return true if request's enctypes indicate support for etype-info2. */ +static krb5_boolean +requires_info2(const krb5_kdc_req *request) { int i; - for (i=0; i < request->nktypes; i++) { - if (enctype_requires_etype_info_2(request->ktype[i])) { - /* Requestor understands etype-info2, so don't send etype-info. */ - (*respond)(arg, KRB5KDC_ERR_PADATA_TYPE_NOSUPP, NULL); - return; - } + for (i = 0; i < request->nktypes; i++) { + if (enctype_requires_etype_info_2(request->ktype[i])) + return TRUE; } - - etype_info_helper(context, request, rock->client, pa_type, respond, arg); + return FALSE; } +/* Generate hint list padata for PA-ETYPE-INFO or PA-ETYPE-INFO2. */ static void -get_etype_info2(krb5_context context, krb5_kdc_req *request, - krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, - krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, - krb5_kdcpreauth_edata_respond_fn respond, void *arg) -{ - etype_info_helper(context, request, rock->client, pa_type, respond, arg); -} - -static krb5_error_code -etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata, - krb5_db_entry *client, - krb5_kdc_req *request, krb5_kdc_rep *reply, - krb5_key_data *client_key, - krb5_keyblock *encrypting_key, - krb5_pa_data **send_pa, - int etype_info2) +get_etype_info(krb5_context context, krb5_kdc_req *request, + krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, + krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, + krb5_kdcpreauth_edata_respond_fn respond, void *arg) { - int i; - krb5_error_code retval; - krb5_pa_data *tmp_padata; - krb5_etype_info_entry **entry = NULL; - krb5_data *scratch = NULL; - - if (client_key == NULL) - return 0; - - /* - * Skip PA-ETYPE-INFO completely if AS-REQ lists any "newer" - * enctypes. - */ - if (!etype_info2) { - for (i = 0; i < request->nktypes; i++) { - if (enctype_requires_etype_info_2(request->ktype[i])) { - *send_pa = NULL; - return 0; - } - } - } - - tmp_padata = malloc( sizeof(krb5_pa_data)); - if (tmp_padata == NULL) - return ENOMEM; - if (etype_info2) - tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO2; - else - tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO; - - entry = malloc(2 * sizeof(krb5_etype_info_entry *)); - if (entry == NULL) { - retval = ENOMEM; - goto cleanup; - } - entry[0] = NULL; - entry[1] = NULL; - retval = _make_etype_info_entry(context, client->princ, client_key, - encrypting_key->enctype, entry, - etype_info2); - if (retval) - goto cleanup; - - if (etype_info2) - retval = encode_krb5_etype_info2(entry, &scratch); - else - retval = encode_krb5_etype_info(entry, &scratch); - - if (retval) - goto cleanup; - tmp_padata->contents = (krb5_octet *)scratch->data; - tmp_padata->length = scratch->length; - *send_pa = tmp_padata; - - /* For cleanup - we no longer own the contents of the krb5_data - * only to pointer to the krb5_data - */ - scratch->data = 0; + krb5_error_code ret; + krb5_pa_data *pa = NULL; -cleanup: - if (entry) - krb5_free_etype_info(context, entry); - if (retval) { - if (tmp_padata) - free(tmp_padata); + if (rock->client_key == NULL) { + ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP; + } else if (pa_type == KRB5_PADATA_ETYPE_INFO && requires_info2(request)) { + ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP; + } else { + ret = make_etype_info(context, pa_type, rock->client->princ, + rock->client_key, rock->client_keyblock->enctype, + &pa); } - if (scratch) - krb5_free_data(context, scratch); - return retval; + (*respond)(arg, ret, pa); } -static krb5_error_code -return_etype_info2(krb5_context context, krb5_pa_data * padata, - krb5_data *req_pkt, krb5_kdc_req *request, - krb5_kdc_rep *reply, krb5_keyblock *encrypting_key, - krb5_pa_data **send_pa, krb5_kdcpreauth_callbacks cb, - krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, - krb5_kdcpreauth_modreq modreq) -{ - return etype_info_as_rep_helper(context, padata, rock->client, request, - reply, rock->client_key, encrypting_key, - send_pa, 1); -} - - +/* Generate AS-REP padata for PA-ETYPE-INFO or PA-ETYPE-INFO2. */ static krb5_error_code return_etype_info(krb5_context context, krb5_pa_data *padata, krb5_data *req_pkt, krb5_kdc_req *request, @@ -1580,9 +1408,13 @@ return_etype_info(krb5_context context, krb5_pa_data *padata, krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, krb5_kdcpreauth_modreq modreq) { - return etype_info_as_rep_helper(context, padata, rock->client, request, - reply, rock->client_key, encrypting_key, - send_pa, 0); + *send_pa = NULL; + if (rock->client_key == NULL) + return 0; + if (padata->pa_type == KRB5_PADATA_ETYPE_INFO && requires_info2(request)) + return 0; + return make_etype_info(context, padata->pa_type, rock->client->princ, + rock->client_key, encrypting_key->enctype, send_pa); } static krb5_error_code @@ -1597,15 +1429,10 @@ return_pw_salt(krb5_context context, krb5_pa_data *in_padata, krb5_data * salt = NULL; krb5_int16 salttype; krb5_key_data * client_key = rock->client_key; - int i; - if (client_key == NULL) + if (client_key == NULL || requires_info2(request)) return 0; - for (i = 0; i < request->nktypes; i++) { - if (enctype_requires_etype_info_2(request->ktype[i])) - return 0; - } retval = krb5_dbe_compute_salt(context, client_key, request->client, &salttype, &salt); if (retval) diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h index 0fa4fbb41f..97ae41f78a 100644 --- a/src/kdc/kdc_util.h +++ b/src/kdc/kdc_util.h @@ -393,6 +393,7 @@ struct krb5_kdcpreauth_rock_st { krb5_data *inner_body; krb5_db_entry *client; krb5_key_data *client_key; + krb5_keyblock *client_keyblock; struct kdc_request_state *rstate; verto_ctx *vctx; };