From 1b885bfc9a05e3e030a0f49c16493f114bb661a1 Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Sat, 15 Oct 2011 15:06:37 +0000 Subject: [PATCH] Make kdcpreauth edata method respond via callback From npmccallum@redhat.com with changes. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25348 dc483132-0cff-0310-8789-dd5450dbe970 --- src/include/krb5/preauth_plugin.h | 30 ++- src/kdc/kdc_preauth.c | 249 +++++++++++------- src/kdc/kdc_preauth_ec.c | 8 +- src/kdc/kdc_preauth_encts.c | 7 +- .../preauth/cksum_body/cksum_body_main.c | 22 +- src/plugins/preauth/pkinit/pkinit_srv.c | 14 +- src/plugins/preauth/wpse/wpse_main.c | 11 +- 7 files changed, 218 insertions(+), 123 deletions(-) diff --git a/src/include/krb5/preauth_plugin.h b/src/include/krb5/preauth_plugin.h index e618edd97..7d5dc99b1 100644 --- a/src/include/krb5/preauth_plugin.h +++ b/src/include/krb5/preauth_plugin.h @@ -418,21 +418,31 @@ typedef int (*krb5_kdcpreauth_flags_fn)(krb5_context context, krb5_preauthtype pa_type); /* - * Optional: fill in pa_out->length and pa_out->contents with data to send to - * the client as part of the "you need to use preauthentication" error. If - * this function returns non-zero, the padata type will not be included in the - * list; if this function is not provided or returns zero without changing - * pa_out, the padata type will be included in the list with an empty value. - * This function not allowed to create a context because we have no guarantee - * that the client will ever call again (or that it will hit this server if it - * does), in which case a context might otherwise hang around forever. + * Responder for krb5_kdcpreauth_edata_fn. If invoked with a non-zero code, pa + * will be ignored and the padata type will not be included in the hint list. + * If invoked with a zero code and a null pa value, the padata type will be + * included in the list with an empty value. If invoked with a zero code and a + * non-null pa value, pa will be included in the hint list and will later be + * freed by the KDC. */ -typedef krb5_error_code +typedef void +(*krb5_kdcpreauth_edata_respond_fn)(void *arg, krb5_error_code code, + krb5_pa_data *pa); + +/* + * Optional: provide pa_data to send to the client as part of the "you need to + * use preauthentication" error. This function is not allowed to create a + * modreq object because we have no guarantee that the client will ever make a + * follow-up request, or that it will hit this KDC if it does. + */ +typedef void (*krb5_kdcpreauth_edata_fn)(krb5_context context, krb5_kdc_req *request, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, - krb5_pa_data *pa_out); + krb5_preauthtype pa_type, + krb5_kdcpreauth_edata_respond_fn respond, + void *arg); /* * Responder for krb5_kdcpreauth_verify_fn. Invoke with the arg parameter diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c index 13b51e329..93fddd775 100644 --- a/src/kdc/kdc_preauth.c +++ b/src/kdc/kdc_preauth.c @@ -104,15 +104,17 @@ typedef struct preauth_system_st { krb5_kdcpreauth_free_modreq_fn free_modreq; } preauth_system; -static krb5_error_code +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_pa_data *data); + krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, + krb5_kdcpreauth_edata_respond_fn respond, void *arg); -static krb5_error_code +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_pa_data *pa_data); + 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, @@ -744,67 +746,132 @@ const char *missing_required_preauth(krb5_db_entry *client, return 0; } +struct hint_state { + kdc_hint_respond_fn respond; + void *arg; + kdc_realm_t *realm; + + krb5_kdcpreauth_rock rock; + krb5_kdc_req *request; + krb5_pa_data ***e_data_out; + + int hw_only; + preauth_system *ap; + krb5_pa_data **pa_data, **pa_cur; + krb5_preauthtype pa_type; +}; + +static void +hint_list_finish(struct hint_state *state, krb5_error_code code) +{ + kdc_hint_respond_fn oldrespond = state->respond; + void *oldarg = state->arg; + + if (!code) { + if (state->pa_data[0] == 0) { + krb5_klog_syslog(LOG_INFO, + _("%spreauth required but hint list is empty"), + state->hw_only ? "hw" : ""); + } + /* If we fail to get the cookie it is probably still reasonable to + * continue with the response. */ + kdc_preauth_get_cookie(state->rock->rstate, state->pa_cur); + + *state->e_data_out = state->pa_data; + state->pa_data = NULL; + } + + krb5_free_pa_data(kdc_context, state->pa_data); + free(state); + (*oldrespond)(oldarg); +} + +static void +hint_list_next(struct hint_state *arg); + +static void +finish_get_edata(void *arg, krb5_error_code code, krb5_pa_data *pa) +{ + struct hint_state *state = arg; + + kdc_active_realm = state->realm; + 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; + } + *state->pa_cur++ = pa; + } + + state->ap++; + hint_list_next(state); +} + +static void +hint_list_next(struct hint_state *state) +{ + preauth_system *ap = state->ap; + + if (ap->type == -1) { + hint_list_finish(state, 0); + return; + } + + if (state->hw_only && !(ap->flags & PA_HARDWARE)) + goto next; + if (ap->flags & PA_PSEUDO) + goto next; + + state->pa_type = ap->type; + if (ap->get_edata) { + ap->get_edata(kdc_context, state->request, &callbacks, state->rock, + ap->moddata, ap->type, finish_get_edata, state); + } else + finish_get_edata(state, ap->type, NULL); + return; + +next: + state->ap++; + hint_list_next(state); +} + void 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) { - int hw_only; - preauth_system *ap; - krb5_pa_data **pa_data, **pa; - krb5_error_code retval; + struct hint_state *state; *e_data_out = NULL; - hw_only = isflagset(rock->client->attributes, KRB5_KDB_REQUIRES_HW_AUTH); - /* Allocate two extra entries for the cookie and the terminator. */ - pa_data = calloc(n_preauth_systems + 2, sizeof(krb5_pa_data *)); - if (pa_data == 0) { + /* Allocate our state. */ + state = malloc(sizeof(*state)); + if (!state) { (*respond)(arg); return; } - pa = pa_data; + state->hw_only = isflagset(rock->client->attributes, + KRB5_KDB_REQUIRES_HW_AUTH); + state->respond = respond; + state->arg = arg; + state->request = request; + state->rock = rock; + state->realm = kdc_active_realm; + state->e_data_out = e_data_out; - for (ap = preauth_systems; ap->type != -1; ap++) { - if (hw_only && !(ap->flags & PA_HARDWARE)) - continue; - if (ap->flags & PA_PSEUDO) - continue; - *pa = malloc(sizeof(krb5_pa_data)); - if (*pa == 0) - goto errout; - memset(*pa, 0, sizeof(krb5_pa_data)); - (*pa)->magic = KV5M_PA_DATA; - (*pa)->pa_type = ap->type; - if (ap->get_edata) { - retval = ap->get_edata(kdc_context, request, &callbacks, rock, - ap->moddata, *pa); - if (retval) { - /* just failed on this type, continue */ - free(*pa); - *pa = 0; - continue; - } - } - pa++; - } - if (pa_data[0] == 0) { - krb5_klog_syslog(LOG_INFO, - _("%spreauth required but hint list is empty"), - hw_only ? "hw" : ""); + /* Allocate two extra entries for the cookie and the terminator. */ + state->pa_data = calloc(n_preauth_systems + 2, sizeof(krb5_pa_data *)); + if (!state->pa_data) { + free(state); + (*respond)(arg); + return; } - /* - * If we fail to get the cookie it is probably - * still reasonable to continue with the response - */ - kdc_preauth_get_cookie(rock->rstate, pa); - *e_data_out = pa_data; - pa_data = NULL; - -errout: - krb5_free_pa_data(kdc_context, pa_data); - (*respond)(arg); + state->pa_cur = state->pa_data; + state->ap = preauth_systems; + hint_list_next(state); } /* @@ -1306,29 +1373,26 @@ fail: free(salt.data); return retval; } -/* - * This function returns the etype information for a particular - * client, to be passed back in the preauth list in the KRB_ERROR - * message. It supports generating both etype_info and etype_info2 - * as most of the work is the same. - */ -static krb5_error_code + +/* 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_pa_data *pa_data, - int etype_info2) + krb5_db_entry *client, krb5_preauthtype pa_type, + krb5_kdcpreauth_edata_respond_fn respond, void *arg) { - krb5_etype_info_entry ** entry = 0; - krb5_key_data *client_key; - krb5_error_code retval; - krb5_data * scratch; - krb5_enctype db_etype; - int i = 0; - int start = 0; - int seen_des = 0; + 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 = malloc((client->n_key_data * 2 + 1) * sizeof(krb5_etype_info_entry *)); + entry = k5alloc((client->n_key_data * 2 + 1) * sizeof(*entry), &retval); if (entry == NULL) - return ENOMEM; + goto cleanup; entry[0] = NULL; while (1) { @@ -1349,7 +1413,6 @@ etype_info_helper(krb5_context context, krb5_kdc_req *request, db_etype, &entry[i], etype_info2); if (retval != 0) goto cleanup; - entry[i+1] = 0; i++; } @@ -1387,39 +1450,47 @@ etype_info_helper(krb5_context context, krb5_kdc_req *request, retval = encode_krb5_etype_info(entry, &scratch); if (retval) goto cleanup; - pa_data->contents = (unsigned char *)scratch->data; - pa_data->length = scratch->length; - free(scratch); - - retval = 0; + pa = k5alloc(sizeof(*pa), &retval); + if (pa == NULL) + 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; cleanup: - if (entry) - krb5_free_etype_info(context, entry); - return retval; + krb5_free_etype_info(context, entry); + krb5_free_data(context, scratch); + (*respond)(arg, retval, pa); } -static krb5_error_code +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_pa_data *pa_data) + krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, + krb5_kdcpreauth_edata_respond_fn respond, void *arg) { int i; + for (i=0; i < request->nktypes; i++) { - if (enctype_requires_etype_info_2(request->ktype[i])) - return KRB5KDC_ERR_PADATA_TYPE_NOSUPP ;;;; /*Caller will - * skip this - * type*/ + 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; + } } - return etype_info_helper(context, request, rock->client, pa_data, 0); + + etype_info_helper(context, request, rock->client, pa_type, respond, arg); } -static krb5_error_code +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_pa_data *pa_data) + krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, + krb5_kdcpreauth_edata_respond_fn respond, void *arg) { - return etype_info_helper(context, request, rock->client, pa_data, 1); + etype_info_helper(context, request, rock->client, pa_type, respond, arg); } static krb5_error_code diff --git a/src/kdc/kdc_preauth_ec.c b/src/kdc/kdc_preauth_ec.c index e5f5d6e58..9d7236c10 100644 --- a/src/kdc/kdc_preauth_ec.c +++ b/src/kdc/kdc_preauth_ec.c @@ -33,14 +33,14 @@ #include #include "kdc_util.h" -static krb5_error_code +static void ec_edata(krb5_context context, krb5_kdc_req *request, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, - krb5_kdcpreauth_moddata moddata, krb5_pa_data *data) + krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, + krb5_kdcpreauth_edata_respond_fn respond, void *arg) { krb5_keyblock *armor_key = cb->fast_armor(context, rock); - - return (armor_key == NULL) ? ENOENT : 0; + (*respond)(arg, (armor_key == NULL) ? ENOENT : 0, NULL); } static void diff --git a/src/kdc/kdc_preauth_encts.c b/src/kdc/kdc_preauth_encts.c index 48626d972..a63298a9c 100644 --- a/src/kdc/kdc_preauth_encts.c +++ b/src/kdc/kdc_preauth_encts.c @@ -28,14 +28,15 @@ #include #include "kdc_util.h" -static krb5_error_code +static void enc_ts_get(krb5_context context, krb5_kdc_req *request, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, - krb5_kdcpreauth_moddata moddata, krb5_pa_data *data) + krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, + krb5_kdcpreauth_edata_respond_fn respond, void *arg) { krb5_keyblock *armor_key = cb->fast_armor(context, rock); - return (armor_key != NULL) ? ENOENT : 0; + (*respond)(arg, (armor_key != NULL) ? ENOENT : 0, NULL); } static void diff --git a/src/plugins/preauth/cksum_body/cksum_body_main.c b/src/plugins/preauth/cksum_body/cksum_body_main.c index da2643fc1..6643e8e04 100644 --- a/src/plugins/preauth/cksum_body/cksum_body_main.c +++ b/src/plugins/preauth/cksum_body/cksum_body_main.c @@ -271,13 +271,15 @@ server_fini(krb5_context kcontext, krb5_kdcpreauth_moddata moddata) /* Obtain and return any preauthentication data (which is destined for the * client) which matches type data->pa_type. */ -static krb5_error_code +static void server_get_edata(krb5_context kcontext, krb5_kdc_req *request, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, - krb5_kdcpreauth_moddata moddata, krb5_pa_data *data) + krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type, + krb5_kdcpreauth_edata_respond_fn respond, void *arg) { krb5_keyblock *keys; krb5_int32 *enctypes, enctype; + krb5_pa_data *data; int i; /* Retrieve the client's keys. */ @@ -285,7 +287,8 @@ server_get_edata(krb5_context kcontext, krb5_kdc_req *request, #ifdef DEBUG fprintf(stderr, "Error retrieving client keys.\n"); #endif - return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; + (*respond)(arg, KRB5KDC_ERR_PADATA_TYPE_NOSUPP, NULL); + return; } /* Count which types of keys we've got. */ @@ -295,7 +298,8 @@ server_get_edata(krb5_context kcontext, krb5_kdc_req *request, enctypes = malloc((unsigned)i * 4); if (enctypes == NULL) { cb->free_keys(kcontext, rock, keys); - return ENOMEM; + (*respond)(arg, ENOMEM, NULL); + return; } #ifdef DEBUG fprintf(stderr, "Supported enctypes = {"); @@ -310,11 +314,17 @@ server_get_edata(krb5_context kcontext, krb5_kdc_req *request, #ifdef DEBUG fprintf(stderr, "}.\n"); #endif + cb->free_keys(kcontext, rock, keys); + data = malloc(sizeof(*data)); + if (data == NULL) { + free(enctypes); + (*respond)(arg, ENOMEM, NULL); + } + data->magic = KV5M_PA_DATA; data->pa_type = KRB5_PADATA_CKSUM_BODY_REQ; data->length = (i * 4); data->contents = (unsigned char *) enctypes; - cb->free_keys(kcontext, rock, keys); - return 0; + (*respond)(arg, 0, data); } /* Verify a request from a client. */ diff --git a/src/plugins/preauth/pkinit/pkinit_srv.c b/src/plugins/preauth/pkinit/pkinit_srv.c index d209f9e76..e43fb7e81 100644 --- a/src/plugins/preauth/pkinit/pkinit_srv.c +++ b/src/plugins/preauth/pkinit/pkinit_srv.c @@ -95,13 +95,15 @@ cleanup: return retval; } -static krb5_error_code +static void pkinit_server_get_edata(krb5_context context, krb5_kdc_req *request, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, - krb5_pa_data *data) + krb5_preauthtype pa_type, + krb5_kdcpreauth_edata_respond_fn respond, + void *arg) { krb5_error_code retval = 0; pkinit_kdc_context plgctx = NULL; @@ -111,8 +113,10 @@ pkinit_server_get_edata(krb5_context context, /* Remove (along with armor_key) when FAST PKINIT is settled. */ /* Don't advertise PKINIT if the client used FAST. */ - if (armor_key != NULL) - return EINVAL; + if (armor_key != NULL) { + (*respond)(arg, EINVAL, NULL); + return; + } /* * If we don't have a realm context for the given realm, @@ -122,7 +126,7 @@ pkinit_server_get_edata(krb5_context context, if (plgctx == NULL) retval = EINVAL; - return retval; + (*respond)(arg, retval, NULL); } static krb5_error_code diff --git a/src/plugins/preauth/wpse/wpse_main.c b/src/plugins/preauth/wpse/wpse_main.c index 4da2c2f48..4f603474d 100644 --- a/src/plugins/preauth/wpse/wpse_main.c +++ b/src/plugins/preauth/wpse/wpse_main.c @@ -243,18 +243,17 @@ server_free_modreq(krb5_context kcontext, /* Obtain and return any preauthentication data (which is destined for the * client) which matches type data->pa_type. */ -static krb5_error_code +static void server_get_edata(krb5_context kcontext, krb5_kdc_req *request, krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata, - krb5_pa_data *data) + krb5_preauthtype pa_type, + krb5_kdcpreauth_edata_respond_fn respond, + void *arg) { - /* Return zero bytes of data. */ - data->length = 0; - data->contents = NULL; - return 0; + (*respond)(arg, 0, NULL); } /* Verify a request from a client. */ -- 2.26.2