krb5 commit: Only include one key in etype-info
Greg Hudson
ghudson at mit.edu
Wed Jul 8 18:24:53 EDT 2015
https://github.com/krb5/krb5/commit/385cd2d07983a89892dad1606e1a41a78066c6ec
commit 385cd2d07983a89892dad1606e1a41a78066c6ec
Author: Greg Hudson <ghudson at mit.edu>
Date: Sat Jun 6 15:45:39 2015 -0400
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)
src/kdc/do_as_req.c | 88 ++++++++++------
src/kdc/kdc_preauth.c | 269 +++++++++----------------------------------------
src/kdc/kdc_util.h | 1 +
3 files changed, 104 insertions(+), 254 deletions(-)
diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c
index c62f0b3..3e18e7c 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 dd83844..0a5d8f4 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,
@@ -131,14 +116,6 @@ return_etype_info(krb5_context, krb5_pa_data *padata,
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,
krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
@@ -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 0fa4fbb..97ae41f 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;
};
More information about the cvs-krb5
mailing list