diff options
Diffstat (limited to 'src/lib/krb5/krb/preauth2.c')
-rw-r--r-- | src/lib/krb5/krb/preauth2.c | 311 |
1 files changed, 196 insertions, 115 deletions
diff --git a/src/lib/krb5/krb/preauth2.c b/src/lib/krb5/krb/preauth2.c index ca26fb0e3fa5..6b96fa135e1a 100644 --- a/src/lib/krb5/krb/preauth2.c +++ b/src/lib/krb5/krb/preauth2.c @@ -46,14 +46,18 @@ typedef struct { struct krb5_clpreauth_vtable_st vt; krb5_clpreauth_moddata data; - krb5_clpreauth_modreq req; } *clpreauth_handle; struct krb5_preauth_context_st { - krb5_preauthtype *tried; clpreauth_handle *handles; }; +struct krb5_preauth_req_context_st { + krb5_context orig_context; + krb5_preauthtype *failed; + krb5_clpreauth_modreq *modreqs; +}; + /* Release the memory used by a list of handles. */ static void free_handles(krb5_context context, clpreauth_handle *handles) @@ -71,21 +75,44 @@ free_handles(krb5_context context, clpreauth_handle *handles) free(handles); } -/* Find the handle in handles which can process pa_type. */ -static clpreauth_handle -find_module(clpreauth_handle *handles, krb5_preauthtype pa_type) +/* Return an index into handles which can process pa_type, or -1 if none is + * found found. */ +static int +search_module_list(clpreauth_handle *handles, krb5_preauthtype pa_type) { - clpreauth_handle *hp, h; - krb5_preauthtype *tp; + clpreauth_handle h; + int i, j; - for (hp = handles; *hp != NULL; hp++) { - h = *hp; - for (tp = h->vt.pa_type_list; *tp != 0; tp++) { - if (*tp == pa_type) - return h; + for (i = 0; handles[i] != NULL; i++) { + h = handles[i]; + for (j = 0; h->vt.pa_type_list[j] != 0; j++) { + if (h->vt.pa_type_list[j] == pa_type) + return i; } } - return FALSE; + return -1; +} + +/* Find the handle which can process pa_type, or NULL if none is found. On + * success, set *modreq_out to the corresponding per-request module data. */ +static clpreauth_handle +find_module(krb5_context context, krb5_init_creds_context ctx, + krb5_preauthtype pa_type, krb5_clpreauth_modreq *modreq_out) +{ + krb5_preauth_context pctx = context->preauth_context; + krb5_preauth_req_context reqctx = ctx->preauth_reqctx; + int i; + + *modreq_out = NULL; + if (pctx == NULL || reqctx == NULL) + return NULL; + + i = search_module_list(pctx->handles, pa_type); + if (i == -1) + return NULL; + + *modreq_out = reqctx->modreqs[i]; + return pctx->handles[i]; } /* Initialize the preauth state for a krb5 context. */ @@ -93,7 +120,8 @@ void k5_init_preauth_context(krb5_context context) { krb5_plugin_initvt_fn *modules = NULL, *mod; - clpreauth_handle *list = NULL, h, h2; + clpreauth_handle *list = NULL, h; + int i; size_t count; krb5_preauthtype *tp; @@ -140,9 +168,10 @@ k5_init_preauth_context(krb5_context context) /* Check for a preauth type conflict with an existing module. */ for (tp = h->vt.pa_type_list; *tp != 0; tp++) { - h2 = find_module(list, *tp); - if (h2 != NULL) { - TRACE_PREAUTH_CONFLICT(context, h->vt.name, h2->vt.name, *tp); + i = search_module_list(list, *tp); + if (i != -1) { + TRACE_PREAUTH_CONFLICT(context, h->vt.name, list[i]->vt.name, + *tp); break; } } @@ -161,10 +190,9 @@ k5_init_preauth_context(krb5_context context) list[count] = NULL; /* Place the constructed preauth context into the krb5 context. */ - context->preauth_context = malloc(sizeof(struct krb5_preauth_context_st)); + context->preauth_context = malloc(sizeof(*context->preauth_context)); if (context->preauth_context == NULL) goto cleanup; - context->preauth_context->tried = NULL; context->preauth_context->handles = list; list = NULL; @@ -173,22 +201,35 @@ cleanup: free_handles(context, list); } -/* - * Reset the memory of which preauth types we have already tried, because we - * are entering a new phase of padata processing (such as the padata in an - * AS-REP). - */ +/* Reset the memory of which preauth types we have already tried. */ void -k5_reset_preauth_types_tried(krb5_context context) +k5_reset_preauth_types_tried(krb5_init_creds_context ctx) { - struct krb5_preauth_context_st *pctx = context->preauth_context; + krb5_preauth_req_context reqctx = ctx->preauth_reqctx; - if (pctx == NULL) + if (reqctx == NULL) return; - free(pctx->tried); - pctx->tried = NULL; + free(reqctx->failed); + reqctx->failed = NULL; } +/* Add pa_type to the list of types which has previously failed. */ +krb5_error_code +k5_preauth_note_failed(krb5_init_creds_context ctx, krb5_preauthtype pa_type) +{ + krb5_preauth_req_context reqctx = ctx->preauth_reqctx; + krb5_preauthtype *newptr; + size_t i; + + for (i = 0; reqctx->failed != NULL && reqctx->failed[i] != 0; i++); + newptr = realloc(reqctx->failed, (i + 2) * sizeof(*newptr)); + if (newptr == NULL) + return ENOMEM; + reqctx->failed = newptr; + reqctx->failed[i] = pa_type; + reqctx->failed[i + 1] = 0; + return 0; +} /* Free the per-krb5_context preauth_context. This means clearing any * plugin-specific context which may have been created, and then @@ -196,11 +237,10 @@ k5_reset_preauth_types_tried(krb5_context context) void k5_free_preauth_context(krb5_context context) { - struct krb5_preauth_context_st *pctx = context->preauth_context; + krb5_preauth_context pctx = context->preauth_context; if (pctx == NULL) return; - free(pctx->tried); free_handles(context, pctx->handles); free(pctx); context->preauth_context = NULL; @@ -209,10 +249,13 @@ k5_free_preauth_context(krb5_context context) /* Initialize the per-AS-REQ context. This means calling the client_req_init * function to give the plugin a chance to allocate a per-request context. */ void -k5_preauth_request_context_init(krb5_context context) +k5_preauth_request_context_init(krb5_context context, + krb5_init_creds_context ctx) { - struct krb5_preauth_context_st *pctx = context->preauth_context; - clpreauth_handle *hp, h; + krb5_preauth_context pctx = context->preauth_context; + clpreauth_handle h; + krb5_preauth_req_context reqctx; + size_t count, i; if (pctx == NULL) { k5_init_preauth_context(context); @@ -220,30 +263,63 @@ k5_preauth_request_context_init(krb5_context context) if (pctx == NULL) return; } - k5_reset_preauth_types_tried(context); - for (hp = pctx->handles; *hp != NULL; hp++) { - h = *hp; + + reqctx = calloc(1, sizeof(*reqctx)); + if (reqctx == NULL) + return; + reqctx->orig_context = context; + + /* Create an array of per-request module data objects corresponding to the + * preauth context's array of handles. */ + for (count = 0; pctx->handles[count] != NULL; count++); + reqctx->modreqs = calloc(count, sizeof(*reqctx->modreqs)); + for (i = 0; i < count; i++) { + h = pctx->handles[i]; if (h->vt.request_init != NULL) - h->vt.request_init(context, h->data, &h->req); + h->vt.request_init(context, h->data, &reqctx->modreqs[i]); } + ctx->preauth_reqctx = reqctx; } /* Free the per-AS-REQ context. This means clearing any request-specific * context which the plugin may have created. */ void -k5_preauth_request_context_fini(krb5_context context) +k5_preauth_request_context_fini(krb5_context context, + krb5_init_creds_context ctx) { - struct krb5_preauth_context_st *pctx = context->preauth_context; - clpreauth_handle *hp, h; + krb5_preauth_context pctx = context->preauth_context; + krb5_preauth_req_context reqctx = ctx->preauth_reqctx; + size_t i; + clpreauth_handle h; - if (pctx == NULL) + if (reqctx == NULL) return; - for (hp = pctx->handles; *hp != NULL; hp++) { - h = *hp; - if (h->req != NULL && h->vt.request_fini != NULL) - h->vt.request_fini(context, h->data, h->req); - h->req = NULL; + if (reqctx->orig_context == context && pctx != NULL) { + for (i = 0; pctx->handles[i] != NULL; i++) { + h = pctx->handles[i]; + if (reqctx->modreqs[i] != NULL && h->vt.request_fini != NULL) + h->vt.request_fini(context, h->data, reqctx->modreqs[i]); + } + } else { + TRACE_PREAUTH_WRONG_CONTEXT(context); + } + free(reqctx->modreqs); + free(reqctx->failed); + free(reqctx); + ctx->preauth_reqctx = NULL; +} + +krb5_error_code +k5_preauth_check_context(krb5_context context, krb5_init_creds_context ctx) +{ + krb5_preauth_req_context reqctx = ctx->preauth_reqctx; + + if (reqctx != NULL && reqctx->orig_context != context) { + k5_setmsg(context, EINVAL, + _("krb5_init_creds calls must use same library context")); + return EINVAL; } + return 0; } /* Return 1 if pa_type is a real preauthentication mechanism according to the @@ -259,6 +335,7 @@ clpreauth_is_real(krb5_context context, clpreauth_handle h, static krb5_error_code clpreauth_prep_questions(krb5_context context, clpreauth_handle h, + krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt, krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock, krb5_kdc_req *req, krb5_data *req_body, @@ -266,35 +343,35 @@ clpreauth_prep_questions(krb5_context context, clpreauth_handle h, { if (h->vt.prep_questions == NULL) return 0; - return h->vt.prep_questions(context, h->data, h->req, opt, cb, rock, req, + return h->vt.prep_questions(context, h->data, modreq, opt, cb, rock, req, req_body, prev_req, pa_data); } static krb5_error_code clpreauth_process(krb5_context context, clpreauth_handle h, - krb5_get_init_creds_opt *opt, krb5_clpreauth_callbacks cb, - krb5_clpreauth_rock rock, krb5_kdc_req *req, - krb5_data *req_body, krb5_data *prev_req, + krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt, + krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock, + krb5_kdc_req *req, krb5_data *req_body, krb5_data *prev_req, krb5_pa_data *pa_data, krb5_prompter_fct prompter, void *prompter_data, krb5_pa_data ***pa_data_out) { - return h->vt.process(context, h->data, h->req, opt, cb, rock, req, + return h->vt.process(context, h->data, modreq, opt, cb, rock, req, req_body, prev_req, pa_data, prompter, prompter_data, pa_data_out); } static krb5_error_code clpreauth_tryagain(krb5_context context, clpreauth_handle h, - krb5_get_init_creds_opt *opt, krb5_clpreauth_callbacks cb, - krb5_clpreauth_rock rock, krb5_kdc_req *req, - krb5_data *req_body, krb5_data *prev_req, + krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt, + krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock, + krb5_kdc_req *req, krb5_data *req_body, krb5_data *prev_req, krb5_preauthtype pa_type, krb5_error *error, krb5_pa_data **error_padata, krb5_prompter_fct prompter, void *prompter_data, krb5_pa_data ***pa_data_out) { if (h->vt.tryagain == NULL) return 0; - return h->vt.tryagain(context, h->data, h->req, opt, cb, rock, req, + return h->vt.tryagain(context, h->data, modreq, opt, cb, rock, req, req_body, prev_req, pa_type, error, error_padata, prompter, prompter_data, pa_data_out); } @@ -420,7 +497,7 @@ responder_get_answer(krb5_context context, krb5_clpreauth_rock rock, krb5_init_creds_context ctx = (krb5_init_creds_context)rock; /* Don't let plugins get the raw password. */ - if (question && strcmp(KRB5_RESPONDER_QUESTION_PASSWORD, question) == 0) + if (strcmp(KRB5_RESPONDER_QUESTION_PASSWORD, question) == 0) return NULL; return k5_response_items_get_answer(ctx->rctx.items, question); } @@ -495,7 +572,7 @@ void k5_preauth_prepare_request(krb5_context context, krb5_get_init_creds_opt *opt, krb5_kdc_req *req) { - struct krb5_preauth_context_st *pctx = context->preauth_context; + krb5_preauth_context pctx = context->preauth_context; clpreauth_handle *hp, h; krb5_enctype *ep; @@ -548,28 +625,17 @@ pa_type_allowed(krb5_init_creds_context ctx, krb5_preauthtype pa_type) pa_type == ctx->allowed_preauth_type; } -/* - * If pa_type has already been tried as a real preauth type for this - * authentication, return true. Otherwise ass pa_type to the list of tried - * types and return false. - */ +/* Return true if pa_type previously failed during this authentication. */ static krb5_boolean -already_tried(krb5_context context, krb5_preauthtype pa_type) +previously_failed(krb5_init_creds_context ctx, krb5_preauthtype pa_type) { - struct krb5_preauth_context_st *pctx = context->preauth_context; - size_t count; - krb5_preauthtype *newptr; + krb5_preauth_req_context reqctx = ctx->preauth_reqctx; + size_t i; - for (count = 0; pctx->tried != NULL && pctx->tried[count] != 0; count++) { - if (pctx->tried[count] == pa_type) + for (i = 0; reqctx->failed != NULL && reqctx->failed[i] != 0; i++) { + if (reqctx->failed[i] == pa_type) return TRUE; } - newptr = realloc(pctx->tried, (count + 2) * sizeof(*newptr)); - if (newptr == NULL) - return FALSE; - pctx->tried = newptr; - pctx->tried[count] = pa_type; - pctx->tried[count + 1] = ENCTYPE_NULL; return FALSE; } @@ -580,16 +646,13 @@ process_pa_data(krb5_context context, krb5_init_creds_context ctx, krb5_pa_data ***out_pa_list, int *out_pa_list_size, krb5_preauthtype *out_type) { - struct krb5_preauth_context_st *pctx = context->preauth_context; struct errinfo save = EMPTY_ERRINFO; krb5_pa_data *pa, **pa_ptr, **mod_pa; krb5_error_code ret = 0; + krb5_clpreauth_modreq modreq; clpreauth_handle h; int real, i; - if (pctx == NULL) - return ENOENT; - /* Process all informational padata types, then the first real preauth type * we succeed on. */ for (real = 0; real <= 1; real++) { @@ -598,17 +661,17 @@ process_pa_data(krb5_context context, krb5_init_creds_context ctx, /* Restrict real mechanisms to the chosen one if we have one. */ if (real && !pa_type_allowed(ctx, pa->pa_type)) continue; - h = find_module(pctx->handles, pa->pa_type); + h = find_module(context, ctx, pa->pa_type, &modreq); if (h == NULL) continue; /* Make sure this type is for the current pass. */ if (clpreauth_is_real(context, h, pa->pa_type) != real) continue; - /* Only try a real mechanism once per authentication. */ - if (real && already_tried(context, pa->pa_type)) + /* Don't try a real mechanism again after failure. */ + if (real && previously_failed(ctx, pa->pa_type)) continue; mod_pa = NULL; - ret = clpreauth_process(context, h, ctx->opt, &callbacks, + ret = clpreauth_process(context, h, modreq, ctx->opt, &callbacks, (krb5_clpreauth_rock)ctx, ctx->request, ctx->inner_request_body, ctx->encoded_previous_request, pa, @@ -625,6 +688,9 @@ process_pa_data(krb5_context context, krb5_init_creds_context ctx, } free(mod_pa); } + /* Don't continue to try mechanisms after a keyboard interrupt. */ + if (ret == KRB5_LIBOS_PWDINTR) + goto cleanup; if (ret == 0 && real) { /* Stop now and record which real padata type we answered. */ *out_type = pa->pa_type; @@ -633,6 +699,12 @@ process_pa_data(krb5_context context, krb5_init_creds_context ctx, /* Save the first error we get from a real preauth type. */ k5_save_ctx_error(context, ret, &save); } + if (real && ret) { + /* Don't try this mechanism again for this authentication. */ + ret = k5_preauth_note_failed(ctx, pa->pa_type); + if (ret) + goto cleanup; + } } } @@ -850,45 +922,54 @@ add_s4u_x509_user_padata(krb5_context context, krb5_s4u_userid *userid, } /* - * If one of the modules can adjust its AS_REQ data using the contents of the - * err_reply, return 0. If it's the sort of correction which requires that we - * ask the user another question, we let the calling application deal with it. + * If the module for pa_type can adjust its AS_REQ data using the contents of + * err and err_padata, return 0 with *padata_out set to a padata list for the + * next request. If it's the sort of correction which requires that we ask the + * user another question, we let the calling application deal with it. */ krb5_error_code k5_preauth_tryagain(krb5_context context, krb5_init_creds_context ctx, - krb5_pa_data **in_padata, krb5_pa_data ***padata_out) + krb5_preauthtype pa_type, krb5_error *err, + krb5_pa_data **err_padata, krb5_pa_data ***padata_out) { - struct krb5_preauth_context_st *pctx = context->preauth_context; krb5_error_code ret; krb5_pa_data **mod_pa; + krb5_clpreauth_modreq modreq; clpreauth_handle h; - int i; + int count; *padata_out = NULL; - if (pctx == NULL) - return KRB5KRB_ERR_GENERIC; - TRACE_PREAUTH_TRYAGAIN_INPUT(context, in_padata); + TRACE_PREAUTH_TRYAGAIN_INPUT(context, pa_type, err_padata); - for (i = 0; in_padata[i] != NULL; i++) { - h = find_module(pctx->handles, in_padata[i]->pa_type); - if (h == NULL) - continue; - mod_pa = NULL; - ret = clpreauth_tryagain(context, h, ctx->opt, &callbacks, - (krb5_clpreauth_rock)ctx, ctx->request, - ctx->inner_request_body, - ctx->encoded_previous_request, - in_padata[i]->pa_type, - ctx->err_reply, ctx->err_padata, - ctx->prompter, ctx->prompter_data, &mod_pa); - if (ret == 0 && mod_pa != NULL) { - TRACE_PREAUTH_TRYAGAIN_OUTPUT(context, mod_pa); - *padata_out = mod_pa; - return 0; - } + h = find_module(context, ctx, pa_type, &modreq); + if (h == NULL) + return KRB5KRB_ERR_GENERIC; + mod_pa = NULL; + ret = clpreauth_tryagain(context, h, modreq, ctx->opt, &callbacks, + (krb5_clpreauth_rock)ctx, ctx->request, + ctx->inner_request_body, + ctx->encoded_previous_request, pa_type, err, + err_padata, ctx->prompter, ctx->prompter_data, + &mod_pa); + TRACE_PREAUTH_TRYAGAIN(context, h->vt.name, pa_type, ret); + if (!ret && mod_pa == NULL) + ret = KRB5KRB_ERR_GENERIC; + if (ret) { + k5_preauth_note_failed(ctx, pa_type); + return ret; } - return KRB5KRB_ERR_GENERIC; + + for (count = 0; mod_pa[count] != NULL; count++); + ret = copy_cookie(context, err_padata, &mod_pa, &count); + if (ret) { + krb5_free_pa_data(context, mod_pa); + return ret; + } + + TRACE_PREAUTH_TRYAGAIN_OUTPUT(context, mod_pa); + *padata_out = mod_pa; + return 0; } /* Compile the set of response items for in_padata by invoke each module's @@ -897,9 +978,9 @@ static krb5_error_code fill_response_items(krb5_context context, krb5_init_creds_context ctx, krb5_pa_data **in_padata) { - struct krb5_preauth_context_st *pctx = context->preauth_context; krb5_error_code ret; krb5_pa_data *pa; + krb5_clpreauth_modreq modreq; clpreauth_handle h; int i; @@ -908,11 +989,11 @@ fill_response_items(krb5_context context, krb5_init_creds_context ctx, pa = in_padata[i]; if (!pa_type_allowed(ctx, pa->pa_type)) continue; - h = find_module(pctx->handles, pa->pa_type); + h = find_module(context, ctx, pa->pa_type, &modreq); if (h == NULL) continue; - ret = clpreauth_prep_questions(context, h, ctx->opt, &callbacks, - (krb5_clpreauth_rock)ctx, + ret = clpreauth_prep_questions(context, h, modreq, ctx->opt, + &callbacks, (krb5_clpreauth_rock)ctx, ctx->request, ctx->inner_request_body, ctx->encoded_previous_request, pa); if (ret) @@ -1004,7 +1085,7 @@ krb5_preauth_supply_preauth_data(krb5_context context, krb5_get_init_creds_opt *opt, const char *attr, const char *value) { - struct krb5_preauth_context_st *pctx = context->preauth_context; + krb5_preauth_context pctx = context->preauth_context; clpreauth_handle *hp, h; krb5_error_code ret; |