From 65f078dfc68f5680e87e686a59970291b64ebd95 Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Sun, 11 Feb 2018 15:23:35 -0500 Subject: [PATCH] Simplify kdc_preauth.c systems table Get rid of static_preauth_systems, and replace it with explicit calls to helper functions in get_preauth_hint_list() and return_padata(). Stop preallocating pa-data lists, instead reallocating on each addition using add_pa_data_element(). Also simplify maybe_add_etype_info2() using add_pa_data_element(). The KRB5_PADATA_PAC_REQUEST table entry did nothing, and was probably originally added back when the KDC would error out on unrecognized padata types. The KRB5_PADATA_SERVER_REFERRAL entry has been disabled since it was first added. (cherry picked from commit fea1a488924faa3938ef723feaa1ff12d22a91ff) --- src/kdc/kdc_preauth.c | 526 +++++++++++++++--------------------------- 1 file changed, 184 insertions(+), 342 deletions(-) diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c index edc30bd83..6f34dc289 100644 --- a/src/kdc/kdc_preauth.c +++ b/src/kdc/kdc_preauth.c @@ -101,108 +101,14 @@ typedef struct preauth_system_st { krb5_kdcpreauth_loop_fn loop; } preauth_system; +static preauth_system *preauth_systems; +static size_t n_preauth_systems; + 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); -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); - -static krb5_error_code -return_etype_info(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, - krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, - krb5_kdcpreauth_moddata moddata, krb5_kdcpreauth_modreq modreq); - - - -static preauth_system static_preauth_systems[] = { - { - "FAST", - KRB5_PADATA_FX_FAST, - PA_HARDWARE, - NULL, - NULL, - NULL, - NULL, - NULL, - 0 - }, - { - "etype-info", - KRB5_PADATA_ETYPE_INFO, - PA_HARDWARE, - NULL, - NULL, - NULL, - get_etype_info, - 0, - return_etype_info - }, - { - "etype-info2", - KRB5_PADATA_ETYPE_INFO2, - PA_HARDWARE, - NULL, - NULL, - NULL, - get_etype_info, - 0, - return_etype_info - }, - { - "pw-salt", - KRB5_PADATA_PW_SALT, - PA_PSEUDO, /* Don't include this in the error list */ - NULL, - NULL, - NULL, - 0, - 0, - return_pw_salt - }, - { - "pac-request", - KRB5_PADATA_PAC_REQUEST, - PA_PSEUDO, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL - }, -#if 0 - { - "server-referral", - KRB5_PADATA_SERVER_REFERRAL, - PA_PSEUDO, - 0, - 0, - return_server_referral - }, -#endif -}; - -#define NUM_STATIC_PREAUTH_SYSTEMS (sizeof(static_preauth_systems) / \ - sizeof(*static_preauth_systems)) - -static preauth_system *preauth_systems; -static size_t n_preauth_systems; - /* Get all available kdcpreauth vtables and a count of preauth types they * support. Return an empty list on failure. */ static void @@ -284,7 +190,6 @@ load_preauth_plugins(struct server_handle *handle, krb5_context context, get_plugin_vtables(context, &vtables, &n_tables, &n_systems); /* Allocate the list of static and plugin preauth systems. */ - n_systems += NUM_STATIC_PREAUTH_SYSTEMS; preauth_systems = calloc(n_systems + 1, sizeof(preauth_system)); if (preauth_systems == NULL) goto cleanup; @@ -292,13 +197,8 @@ load_preauth_plugins(struct server_handle *handle, krb5_context context, if (get_realm_names(handle, &realm_names)) goto cleanup; - /* Add the static system to the list first. No static systems require - * initialization, so just make a direct copy. */ - memcpy(preauth_systems, static_preauth_systems, - sizeof(static_preauth_systems)); - /* Add the dynamically-loaded mechanisms to the list. */ - n_systems = NUM_STATIC_PREAUTH_SYSTEMS; + n_systems = 0; for (i = 0; i < n_tables; i++) { /* Try to initialize this module. */ vt = &vtables[i]; @@ -622,7 +522,9 @@ find_pa_system(int type, preauth_system **preauth) { preauth_system *ap; - ap = preauth_systems ? preauth_systems : static_preauth_systems; + if (preauth_systems == NULL) + return KRB5_PREAUTH_BAD_TYPE; + ap = preauth_systems; while ((ap->type != -1) && (ap->type != type)) ap++; if (ap->type == -1) @@ -776,6 +678,98 @@ const char *missing_required_preauth(krb5_db_entry *client, return 0; } +/* 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])) + return TRUE; + } + return FALSE; +} + +/* Add PA-ETYPE-INFO2 and possibly PA-ETYPE-INFO entries to pa_list as + * appropriate for the request and client principal. */ +static krb5_error_code +add_etype_info(krb5_context context, krb5_kdcpreauth_rock rock, + krb5_pa_data ***pa_list) +{ + krb5_error_code ret; + krb5_pa_data *pa; + + if (rock->client_key == NULL) + return 0; + + if (!requires_info2(rock->request)) { + /* Include PA-ETYPE-INFO only for old clients. */ + ret = make_etype_info(context, KRB5_PADATA_ETYPE_INFO, + rock->client->princ, rock->client_key, + rock->client_keyblock->enctype, &pa); + if (ret) + return ret; + /* add_pa_data_element() claims pa on success or failure. */ + ret = add_pa_data_element(pa_list, pa); + if (ret) + return ret; + } + + /* Always include PA-ETYPE-INFO2. */ + ret = make_etype_info(context, KRB5_PADATA_ETYPE_INFO2, + rock->client->princ, rock->client_key, + rock->client_keyblock->enctype, &pa); + if (ret) + return ret; + /* add_pa_data_element() claims pa on success or failure. */ + return add_pa_data_element(pa_list, pa); +} + +/* Add PW-SALT or AFS3-SALT entries to pa_list as appropriate for the request + * and client principal. */ +static krb5_error_code +add_pw_salt(krb5_context context, krb5_kdcpreauth_rock rock, + krb5_pa_data ***pa_list) +{ + krb5_error_code ret; + krb5_pa_data *pa; + krb5_data *salt = NULL; + krb5_int16 salttype; + + /* Only include this pa-data for old clients. */ + if (rock->client_key == NULL || requires_info2(rock->request)) + return 0; + + ret = krb5_dbe_compute_salt(context, rock->client_key, + rock->request->client, &salttype, &salt); + if (ret) + return 0; + + if (salttype == KRB5_KDB_SALTTYPE_AFS3) { + ret = alloc_pa_data(KRB5_PADATA_AFS3_SALT, salt->length + 1, &pa); + if (ret) + goto cleanup; + memcpy(pa->contents, salt->data, salt->length); + pa->contents[salt->length] = '\0'; + } else { + /* Steal memory from salt to make the pa-data entry. */ + ret = alloc_pa_data(KRB5_PADATA_PW_SALT, 0, &pa); + if (ret) + goto cleanup; + pa->length = salt->length; + pa->contents = (uint8_t *)salt->data; + salt->data = NULL; + } + + /* add_pa_data_element() claims pa on success or failure. */ + ret = add_pa_data_element(pa_list, pa); + +cleanup: + krb5_free_data(context, salt); + return ret; +} + struct hint_state { kdc_hint_respond_fn respond; void *arg; @@ -787,7 +781,7 @@ struct hint_state { int hw_only; preauth_system *ap; - krb5_pa_data **pa_data, **pa_cur; + krb5_pa_data **pa_data; krb5_preauthtype pa_type; }; @@ -799,7 +793,7 @@ hint_list_finish(struct hint_state *state, krb5_error_code code) kdc_realm_t *kdc_active_realm = state->realm; if (!code) { - if (state->pa_data[0] == 0) { + if (state->pa_data == NULL) { krb5_klog_syslog(LOG_INFO, _("%spreauth required but hint list is empty"), state->hw_only ? "hw" : ""); @@ -820,20 +814,27 @@ hint_list_next(struct hint_state *arg); static void finish_get_edata(void *arg, krb5_error_code code, krb5_pa_data *pa) { + krb5_error_code ret; struct hint_state *state = arg; if (code == 0) { if (pa == NULL) { - /* Include an empty value of the current type. */ - pa = calloc(1, sizeof(*pa)); - pa->magic = KV5M_PA_DATA; - pa->pa_type = state->pa_type; + ret = alloc_pa_data(state->pa_type, 0, &pa); + if (ret) + goto error; } - *state->pa_cur++ = pa; + /* add_pa_data_element() claims pa on success or failure. */ + ret = add_pa_data_element(&state->pa_data, pa); + if (ret) + goto error; } state->ap++; hint_list_next(state); + return; + +error: + hint_list_finish(state, ret); } static void @@ -870,16 +871,16 @@ get_preauth_hint_list(krb5_kdc_req *request, krb5_kdcpreauth_rock rock, krb5_pa_data ***e_data_out, kdc_hint_respond_fn respond, void *arg) { + kdc_realm_t *kdc_active_realm = rock->rstate->realm_data; struct hint_state *state; + krb5_pa_data *pa; *e_data_out = NULL; /* Allocate our state. */ state = calloc(1, sizeof(*state)); - if (state == NULL) { - (*respond)(arg); - return; - } + if (state == NULL) + goto error; state->hw_only = isflagset(rock->client->attributes, KRB5_KDB_REQUIRES_HW_AUTH); state->respond = respond; @@ -888,17 +889,27 @@ get_preauth_hint_list(krb5_kdc_req *request, krb5_kdcpreauth_rock rock, state->rock = rock; state->realm = rock->rstate->realm_data; state->e_data_out = e_data_out; - - state->pa_data = calloc(n_preauth_systems + 1, sizeof(krb5_pa_data *)); - if (!state->pa_data) { - free(state); - (*respond)(arg); - return; - } - - state->pa_cur = state->pa_data; + state->pa_data = NULL; state->ap = preauth_systems; + + /* Add an empty PA-FX-FAST element to advertise FAST support. */ + if (alloc_pa_data(KRB5_PADATA_FX_FAST, 0, &pa) != 0) + goto error; + /* add_pa_data_element() claims pa on success or failure. */ + if (add_pa_data_element(&state->pa_data, pa) != 0) + goto error; + + if (add_etype_info(kdc_context, rock, &state->pa_data) != 0) + goto error; + hint_list_next(state); + return; + +error: + if (state != NULL) + krb5_free_pa_data(kdc_context, state->pa_data); + free(state); + (*respond)(arg); } /* @@ -1029,10 +1040,10 @@ filter_preauth_error(krb5_error_code code) static krb5_error_code maybe_add_etype_info2(struct padata_state *state, krb5_error_code code) { + krb5_error_code ret; krb5_context context = state->context; krb5_kdcpreauth_rock rock = state->rock; - krb5_pa_data **list = state->pa_e_data; - size_t count; + krb5_pa_data *pa; /* Only add key information when requesting another preauth round trip. */ if (code != KRB5KDC_ERR_MORE_PREAUTH_DATA_REQUIRED) @@ -1048,18 +1059,14 @@ maybe_add_etype_info2(struct padata_state *state, krb5_error_code code) KRB5_PADATA_FX_COOKIE) != NULL) return 0; - /* Reallocate state->pa_e_data to make room for the etype-info2 element. */ - for (count = 0; list != NULL && list[count] != NULL; count++); - list = realloc(list, (count + 2) * sizeof(*list)); - if (list == NULL) - return ENOMEM; - list[count] = list[count + 1] = NULL; - state->pa_e_data = list; + ret = make_etype_info(context, KRB5_PADATA_ETYPE_INFO2, + rock->client->princ, rock->client_key, + rock->client_keyblock->enctype, &pa); + if (ret) + return ret; - /* Generate an etype-info2 element in the new slot. */ - return make_etype_info(context, KRB5_PADATA_ETYPE_INFO2, - rock->client->princ, rock->client_key, - rock->client_keyblock->enctype, &list[count]); + /* add_pa_data_element() claims pa on success or failure. */ + return add_pa_data_element(&state->pa_e_data, pa); } /* Release state and respond to the AS-REQ processing code with the result of @@ -1279,17 +1286,20 @@ return_padata(krb5_context context, krb5_kdcpreauth_rock rock, { krb5_error_code retval; krb5_pa_data ** padata; - krb5_pa_data ** send_pa_list; - krb5_pa_data ** send_pa; + krb5_pa_data ** send_pa_list = NULL; + krb5_pa_data * send_pa; krb5_pa_data * pa = 0; krb5_pa_data null_item; preauth_system * ap; - int * pa_order; + int * pa_order = NULL; int * pa_type; int size = 0; krb5_kdcpreauth_modreq *modreq_ptr; krb5_boolean key_modified; krb5_keyblock original_key; + + memset(&original_key, 0, sizeof(original_key)); + if ((!*padata_context) && (make_padata_context(context, padata_context) != 0)) { return KRB5KRB_ERR_GENERIC; @@ -1300,26 +1310,18 @@ return_padata(krb5_context context, krb5_kdcpreauth_rock rock, size++; } - if ((send_pa_list = malloc((size+1) * sizeof(krb5_pa_data *))) == NULL) - return ENOMEM; - if ((pa_order = malloc((size+1) * sizeof(int))) == NULL) { - free(send_pa_list); - return ENOMEM; - } + pa_order = k5calloc(size + 1, sizeof(int), &retval); + if (pa_order == NULL) + goto cleanup; sort_pa_order(context, request, pa_order); retval = krb5_copy_keyblock_contents(context, encrypting_key, &original_key); - if (retval) { - free(send_pa_list); - free(pa_order); - return retval; - } + if (retval) + goto cleanup; key_modified = FALSE; null_item.contents = NULL; null_item.length = 0; - send_pa = send_pa_list; - *send_pa = 0; for (pa_type = pa_order; *pa_type != -1; pa_type++) { ap = &preauth_systems[*pa_type]; @@ -1349,20 +1351,30 @@ return_padata(krb5_context context, krb5_kdcpreauth_rock rock, } } } + send_pa = NULL; retval = ap->return_padata(context, pa, req_pkt, request, reply, - encrypting_key, send_pa, &callbacks, rock, + encrypting_key, &send_pa, &callbacks, rock, ap->moddata, *modreq_ptr); if (retval) goto cleanup; - if (*send_pa) - send_pa++; - *send_pa = 0; + if (send_pa != NULL) { + /* add_pa_data_element() claims send_pa on success or failure. */ + retval = add_pa_data_element(&send_pa_list, send_pa); + if (retval) + goto cleanup; + } } - retval = 0; + /* Add etype-info and pw-salt pa-data as needed. */ + retval = add_etype_info(context, rock, &send_pa_list); + if (retval) + goto cleanup; + retval = add_pw_salt(context, rock, &send_pa_list); + if (retval) + goto cleanup; - if (send_pa_list[0]) { + if (send_pa_list != NULL) { reply->padata = send_pa_list; send_pa_list = 0; } @@ -1370,8 +1382,7 @@ return_padata(krb5_context context, krb5_kdcpreauth_rock rock, cleanup: krb5_free_keyblock_contents(context, &original_key); free(pa_order); - if (send_pa_list) - krb5_free_pa_data(context, send_pa_list); + krb5_free_pa_data(context, send_pa_list); return (retval); } @@ -1438,9 +1449,8 @@ make_etype_info(krb5_context context, krb5_preauthtype pa_type, 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_data *der_etype_info = NULL; int etype_info2 = (pa_type == KRB5_PADATA_ETYPE_INFO2); *pa_out = NULL; @@ -1454,125 +1464,23 @@ make_etype_info(krb5_context context, krb5_preauthtype pa_type, goto cleanup; if (etype_info2) - retval = encode_krb5_etype_info2(entry, &scratch); + retval = encode_krb5_etype_info2(entry, &der_etype_info); else - retval = encode_krb5_etype_info(entry, &scratch); + retval = encode_krb5_etype_info(entry, &der_etype_info); if (retval) goto cleanup; - pa = k5alloc(sizeof(*pa), &retval); - if (pa == NULL) + + /* Steal the data from der_etype_info to create a pa-data element. */ + retval = alloc_pa_data(pa_type, 0, pa_out); + if (retval) goto cleanup; - pa->magic = KV5M_PA_DATA; - pa->pa_type = pa_type; - pa->contents = (unsigned char *)scratch->data; - pa->length = scratch->length; - scratch->data = NULL; - *pa_out = pa; + (*pa_out)->contents = (uint8_t *)der_etype_info->data; + (*pa_out)->length = der_etype_info->length; + der_etype_info->data = NULL; cleanup: krb5_free_etype_info(context, entry); - krb5_free_data(context, scratch); - return retval; -} - -/* 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])) - return TRUE; - } - return FALSE; -} - -/* Generate hint list padata for PA-ETYPE-INFO or PA-ETYPE-INFO2. */ -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) -{ - krb5_error_code ret; - krb5_pa_data *pa = NULL; - - 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); - } - (*respond)(arg, ret, pa); -} - -/* 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, - 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) -{ - *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 -return_pw_salt(krb5_context context, krb5_pa_data *in_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) -{ - krb5_error_code retval; - krb5_pa_data * padata; - krb5_data * salt = NULL; - krb5_int16 salttype; - krb5_key_data * client_key = rock->client_key; - - if (client_key == NULL || requires_info2(request)) - return 0; - - retval = krb5_dbe_compute_salt(context, client_key, request->client, - &salttype, &salt); - if (retval) - return 0; - - padata = k5alloc(sizeof(*padata), &retval); - if (padata == NULL) - goto cleanup; - padata->magic = KV5M_PA_DATA; - - if (salttype == KRB5_KDB_SALTTYPE_AFS3) { - padata->contents = k5memdup0(salt->data, salt->length, &retval); - if (padata->contents == NULL) - goto cleanup; - padata->pa_type = KRB5_PADATA_AFS3_SALT; - padata->length = salt->length + 1; - } else { - padata->pa_type = KRB5_PADATA_PW_SALT; - padata->length = salt->length; - padata->contents = (krb5_octet *)salt->data; - salt->data = NULL; - } - - *send_pa = padata; - padata = NULL; - -cleanup: - free(padata); - krb5_free_data(context, salt); + krb5_free_data(context, der_etype_info); return retval; } @@ -1656,69 +1564,3 @@ return_enc_padata(krb5_context context, krb5_data *req_pkt, cleanup: return code; } - - -#if 0 -static krb5_error_code return_server_referral(krb5_context context, - krb5_pa_data * padata, - krb5_db_entry *client, - krb5_db_entry *server, - krb5_kdc_req *request, - krb5_kdc_rep *reply, - krb5_key_data *client_key, - krb5_keyblock *encrypting_key, - krb5_pa_data **send_pa) -{ - krb5_error_code code; - krb5_tl_data tl_data; - krb5_pa_data *pa_data; - krb5_enc_data enc_data; - krb5_data plain; - krb5_data *enc_pa_data; - - *send_pa = NULL; - - tl_data.tl_data_type = KRB5_TL_SERVER_REFERRAL; - - code = krb5_dbe_lookup_tl_data(context, server, &tl_data); - if (code || tl_data.tl_data_length == 0) - return 0; /* no server referrals to return */ - - plain.length = tl_data.tl_data_length; - plain.data = tl_data.tl_data_contents; - - /* Encrypt ServerReferralData */ - code = krb5_encrypt_helper(context, encrypting_key, - KRB5_KEYUSAGE_PA_SERVER_REFERRAL_DATA, - &plain, &enc_data); - if (code) - return code; - - /* Encode ServerReferralData into PA-SERVER-REFERRAL-DATA */ - code = encode_krb5_enc_data(&enc_data, &enc_pa_data); - if (code) { - krb5_free_data_contents(context, &enc_data.ciphertext); - return code; - } - - krb5_free_data_contents(context, &enc_data.ciphertext); - - /* Return PA-SERVER-REFERRAL-DATA */ - pa_data = (krb5_pa_data *)malloc(sizeof(*pa_data)); - if (pa_data == NULL) { - krb5_free_data(context, enc_pa_data); - return ENOMEM; - } - - pa_data->magic = KV5M_PA_DATA; - pa_data->pa_type = KRB5_PADATA_SVR_REFERRAL_INFO; - pa_data->length = enc_pa_data->length; - pa_data->contents = enc_pa_data->data; - - free(enc_pa_data); /* don't free contents */ - - *send_pa = pa_data; - - return 0; -} -#endif