From 75b375abcec69421c430a0241e5c72cafd96cb7f Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Mon, 16 Jan 2017 15:09:32 -0500 Subject: [PATCH] Continue after KDC_ERR_PREAUTH_FAILED If the KDC sends KDC_ERR_PREAUTH_FAILED, try another mechanism, or send an unauthenticated request if optimistic preauth failed. ticket: 8537 (cherry picked from commit 52d2de31bc4728dbc2f59c6033dcdab86da919e9) --- src/lib/krb5/krb/get_in_tkt.c | 45 ++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c index 8d0f964f9..c7d7bfe74 100644 --- a/src/lib/krb5/krb/get_in_tkt.c +++ b/src/lib/krb5/krb/get_in_tkt.c @@ -1308,6 +1308,7 @@ init_creds_step_request(krb5_context context, krb5_error_code code; krb5_preauthtype pa_type; struct errinfo save = EMPTY_ERRINFO; + uint32_t rcode = (ctx->err_reply == NULL) ? 0 : ctx->err_reply->error; if (ctx->loopcount >= MAX_IN_TKT_LOOPS) { code = KRB5_GET_IN_TKT_LOOP; @@ -1358,8 +1359,10 @@ init_creds_step_request(krb5_context context, TRACE_INIT_CREDS_PREAUTH_MORE(context, ctx->selected_preauth_type); code = k5_preauth(context, ctx, ctx->more_padata, TRUE, &ctx->request->padata, &pa_type); - } else if (ctx->err_reply != NULL && - ctx->err_reply->error != KDC_ERR_PREAUTH_REQUIRED) { + } else if (rcode == KDC_ERR_PREAUTH_FAILED) { + /* Report the KDC-side failure code if we can't try another mech. */ + code = KRB5KDC_ERR_PREAUTH_FAILED; + } else if (rcode && rcode != KDC_ERR_PREAUTH_REQUIRED) { /* Retrying after an error (possibly mechanism-specific), using error * padata to figure out what to change. */ TRACE_INIT_CREDS_PREAUTH_TRYAGAIN(context, ctx->err_reply->error, @@ -1380,7 +1383,7 @@ init_creds_step_request(krb5_context context, if (ctx->request->padata == NULL && ctx->method_padata != NULL) { /* Retrying after KDC_ERR_PREAUTH_REQUIRED, or trying again with a - * different mechanism after a client-side failure. */ + * different mechanism after a failure. */ TRACE_INIT_CREDS_PREAUTH(context); code = k5_preauth(context, ctx, ctx->method_padata, TRUE, &ctx->request->padata, &ctx->selected_preauth_type); @@ -1480,6 +1483,18 @@ is_referral(krb5_context context, krb5_error *err, krb5_principal client) return !krb5_realm_compare(context, err->client, client); } +/* Transfer error padata to method data in ctx and sort it according to + * configuration. */ +static krb5_error_code +accept_method_data(krb5_context context, krb5_init_creds_context ctx) +{ + krb5_free_pa_data(context, ctx->method_padata); + ctx->method_padata = ctx->err_padata; + ctx->err_padata = NULL; + return sort_krb5_padata_sequence(context, &ctx->request->client->realm, + ctx->method_padata); +} + static krb5_error_code init_creds_step_reply(krb5_context context, krb5_init_creds_context ctx, @@ -1538,14 +1553,26 @@ init_creds_step_reply(krb5_context context, ctx->restarted = FALSE; code = restart_init_creds_loop(context, ctx, FALSE); } else if (reply_code == KDC_ERR_PREAUTH_REQUIRED && retry) { - krb5_free_pa_data(context, ctx->method_padata); - ctx->method_padata = ctx->err_padata; - ctx->err_padata = NULL; note_req_timestamp(context, ctx, ctx->err_reply->stime, ctx->err_reply->susec); - code = sort_krb5_padata_sequence(context, - &ctx->request->client->realm, - ctx->method_padata); + code = accept_method_data(context, ctx); + } else if (reply_code == KDC_ERR_PREAUTH_FAILED && retry) { + note_req_timestamp(context, ctx, ctx->err_reply->stime, + ctx->err_reply->susec); + if (ctx->method_padata == NULL) { + /* Optimistic preauth failed on the KDC. Allow all mechanisms + * to be tried again using method data. */ + k5_reset_preauth_types_tried(ctx); + } else { + /* Don't try again with the mechanism that failed. */ + code = k5_preauth_note_failed(ctx, ctx->selected_preauth_type); + if (code) + goto cleanup; + } + ctx->selected_preauth_type = KRB5_PADATA_NONE; + /* Accept or update method data if the KDC sent it. */ + if (ctx->err_padata != NULL) + code = accept_method_data(context, ctx); } else if (reply_code == KDC_ERR_MORE_PREAUTH_DATA_REQUIRED && retry) { ctx->more_padata = ctx->err_padata; ctx->err_padata = NULL;