d738b9
From d45e1ea83a6ed9eef0e7e6bfe86c8d4995a7402d Mon Sep 17 00:00:00 2001
d738b9
From: Greg Hudson <ghudson@mit.edu>
d738b9
Date: Sat, 14 Jan 2017 13:55:22 -0500
d738b9
Subject: [PATCH] Continue preauth after client-side failures
d738b9
d738b9
If the module for the selected preauth mechanism fails when processing
d738b9
a KDC_ERR_MORE_PREAUTH_DATA_REQUIRED error, or fails a tryagain
d738b9
operation, try again with a different preauth mech using the cached
d738b9
method data.
d738b9
d738b9
If optimistic preauth fails on the client side, send an
d738b9
unauthenticated request, allowing the mechanisms we tried
d738b9
optimistically to be tried again.
d738b9
d738b9
ticket: 8537
d738b9
(cherry picked from commit 644840a207917661a6ccf706e7830bec273e23b3)
d738b9
---
d738b9
 src/lib/krb5/krb/get_in_tkt.c | 49 +++++++++++++++++++++++------------
d738b9
 1 file changed, 32 insertions(+), 17 deletions(-)
d738b9
d738b9
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
d738b9
index 8c7919e65..8d0f964f9 100644
d738b9
--- a/src/lib/krb5/krb/get_in_tkt.c
d738b9
+++ b/src/lib/krb5/krb/get_in_tkt.c
d738b9
@@ -1307,6 +1307,7 @@ init_creds_step_request(krb5_context context,
d738b9
 {
d738b9
     krb5_error_code code;
d738b9
     krb5_preauthtype pa_type;
d738b9
+    struct errinfo save = EMPTY_ERRINFO;
d738b9
 
d738b9
     if (ctx->loopcount >= MAX_IN_TKT_LOOPS) {
d738b9
         code = KRB5_GET_IN_TKT_LOOP;
d738b9
@@ -1341,38 +1342,51 @@ init_creds_step_request(krb5_context context,
d738b9
     if (ctx->optimistic_padata != NULL) {
d738b9
         /* Our first attempt, using an optimistic padata list. */
d738b9
         TRACE_INIT_CREDS_PREAUTH_OPTIMISTIC(context);
d738b9
-        code = k5_preauth(context, ctx, ctx->optimistic_padata, FALSE,
d738b9
+        code = k5_preauth(context, ctx, ctx->optimistic_padata, TRUE,
d738b9
                           &ctx->request->padata, &ctx->selected_preauth_type);
d738b9
         krb5_free_pa_data(context, ctx->optimistic_padata);
d738b9
         ctx->optimistic_padata = NULL;
d738b9
-        if (code != 0)
d738b9
-            goto cleanup;
d738b9
+        if (code) {
d738b9
+            /* Make an unauthenticated request, and possibly try again using
d738b9
+             * the same mechanisms as we tried optimistically. */
d738b9
+            k5_reset_preauth_types_tried(ctx);
d738b9
+            krb5_clear_error_message(context);
d738b9
+            code = 0;
d738b9
+        }
d738b9
     } if (ctx->more_padata != NULL) {
d738b9
         /* Continuing after KDC_ERR_MORE_PREAUTH_DATA_REQUIRED. */
d738b9
         TRACE_INIT_CREDS_PREAUTH_MORE(context, ctx->selected_preauth_type);
d738b9
         code = k5_preauth(context, ctx, ctx->more_padata, TRUE,
d738b9
                           &ctx->request->padata, &pa_type);
d738b9
-        if (code != 0)
d738b9
-            goto cleanup;
d738b9
     } else if (ctx->err_reply != NULL &&
d738b9
-               ctx->err_reply->error == KDC_ERR_PREAUTH_REQUIRED) {
d738b9
-        /* Continuing after KDC_ERR_PREAUTH_REQUIRED, using method data. */
d738b9
-        TRACE_INIT_CREDS_PREAUTH(context);
d738b9
-        code = k5_preauth(context, ctx, ctx->method_padata, TRUE,
d738b9
-                          &ctx->request->padata, &ctx->selected_preauth_type);
d738b9
-        if (code != 0)
d738b9
-            goto cleanup;
d738b9
-    } else if (ctx->err_reply != NULL) {
d738b9
-        /* Retry after an error other than PREAUTH_REQUIRED, using error padata
d738b9
-         * to figure out what to change. */
d738b9
+               ctx->err_reply->error != KDC_ERR_PREAUTH_REQUIRED) {
d738b9
+        /* Retrying after an error (possibly mechanism-specific), using error
d738b9
+         * padata to figure out what to change. */
d738b9
         TRACE_INIT_CREDS_PREAUTH_TRYAGAIN(context, ctx->err_reply->error,
d738b9
                                           ctx->selected_preauth_type);
d738b9
         code = k5_preauth_tryagain(context, ctx, ctx->selected_preauth_type,
d738b9
                                    ctx->err_reply, ctx->err_padata,
d738b9
                                    &ctx->request->padata);
d738b9
-        if (code != 0) {
d738b9
-            /* couldn't come up with anything better */
d738b9
+        if (code) {
d738b9
+            krb5_clear_error_message(context);
d738b9
             code = ctx->err_reply->error + ERROR_TABLE_BASE_krb5;
d738b9
+        }
d738b9
+    }
d738b9
+    if (code) {
d738b9
+        /* See if we can try a different preauth mech before giving up. */
d738b9
+        k5_save_ctx_error(context, code, &save);
d738b9
+        ctx->selected_preauth_type = KRB5_PADATA_NONE;
d738b9
+    }
d738b9
+
d738b9
+    if (ctx->request->padata == NULL && ctx->method_padata != NULL) {
d738b9
+        /* Retrying after KDC_ERR_PREAUTH_REQUIRED, or trying again with a
d738b9
+         * different mechanism after a client-side failure. */
d738b9
+        TRACE_INIT_CREDS_PREAUTH(context);
d738b9
+        code = k5_preauth(context, ctx, ctx->method_padata, TRUE,
d738b9
+                          &ctx->request->padata, &ctx->selected_preauth_type);
d738b9
+        if (code) {
d738b9
+            if (save.code != 0)
d738b9
+                code = k5_restore_ctx_error(context, &save);
d738b9
             goto cleanup;
d738b9
         }
d738b9
     }
d738b9
@@ -1413,6 +1427,7 @@ init_creds_step_request(krb5_context context,
d738b9
 cleanup:
d738b9
     krb5_free_pa_data(context, ctx->request->padata);
d738b9
     ctx->request->padata = NULL;
d738b9
+    k5_clear_error(&save);
d738b9
     return code;
d738b9
 }
d738b9