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