Blame SOURCES/Add-test-cases-for-preauth-fallback-behavior.patch

d738b9
From e1448b09ac4e94ff8e66a7cf0315841c38c48c37 Mon Sep 17 00:00:00 2001
d738b9
From: Greg Hudson <ghudson@mit.edu>
d738b9
Date: Fri, 20 Jan 2017 12:44:12 -0500
d738b9
Subject: [PATCH] Add test cases for preauth fallback behavior
d738b9
d738b9
Add options to icred for performing optimistic preauth and setting
d738b9
preauth options, and for choosing between the normal and stepwise
d738b9
interfaces.  Add options to the test preauth module to allow induced
d738b9
failures at several points in processing, factoring out some padata
d738b9
manipulation functions into a new file to avoid repeating too much
d738b9
code.  Add test cases to t_preauth.py using the new facilities to
d738b9
exercise and verify several preauth fallback scenarios.  Amend the
d738b9
tryagain test case in t_pkinit.py to look for more trace log messages.
d738b9
d738b9
ticket: 8537
d738b9
(cherry picked from commit 748beda1e36d76bed8b06b272ecb72988eede94b)
d738b9
[rharwood@redhat.com: more expected_trace]
d738b9
---
d738b9
 src/plugins/preauth/test/Makefile.in |   4 +-
d738b9
 src/plugins/preauth/test/cltest.c    |  86 ++++++++++-----
d738b9
 src/plugins/preauth/test/common.c    |  61 +++++++++++
d738b9
 src/plugins/preauth/test/common.h    |  41 +++++++
d738b9
 src/plugins/preauth/test/deps        |  14 ++-
d738b9
 src/plugins/preauth/test/kdctest.c   |  96 ++++++++++------
d738b9
 src/tests/icred.c                    |  69 +++++++++---
d738b9
 src/tests/t_general.py               |   1 +
d738b9
 src/tests/t_pkinit.py                |  12 +-
d738b9
 src/tests/t_preauth.py               | 158 ++++++++++++++++++++++++++-
d738b9
 10 files changed, 452 insertions(+), 90 deletions(-)
d738b9
 create mode 100644 src/plugins/preauth/test/common.c
d738b9
 create mode 100644 src/plugins/preauth/test/common.h
d738b9
d738b9
diff --git a/src/plugins/preauth/test/Makefile.in b/src/plugins/preauth/test/Makefile.in
d738b9
index ac3cb8155..77321b60f 100644
d738b9
--- a/src/plugins/preauth/test/Makefile.in
d738b9
+++ b/src/plugins/preauth/test/Makefile.in
d738b9
@@ -9,9 +9,9 @@ RELDIR=../plugins/preauth/test
d738b9
 SHLIB_EXPDEPS=$(KRB5_BASE_DEPLIBS)
d738b9
 SHLIB_EXPLIBS=$(KRB5_BASE_LIBS)
d738b9
 
d738b9
-STLIBOBJS=cltest.o kdctest.o
d738b9
+STLIBOBJS=cltest.o kdctest.o common.o
d738b9
 
d738b9
-SRCS= $(srcdir)/cltest.c $(srcdir)/kdctest.c
d738b9
+SRCS= $(srcdir)/cltest.c $(srcdir)/kdctest.c $(srcdir)/common.c
d738b9
 
d738b9
 all-unix: all-liblinks
d738b9
 install-unix: install-libs
d738b9
diff --git a/src/plugins/preauth/test/cltest.c b/src/plugins/preauth/test/cltest.c
d738b9
index 4c31e1c0f..f5f7c5aba 100644
d738b9
--- a/src/plugins/preauth/test/cltest.c
d738b9
+++ b/src/plugins/preauth/test/cltest.c
d738b9
@@ -1,7 +1,7 @@
d738b9
 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
d738b9
 /* plugins/preauth/test/cltest.c - Test clpreauth module */
d738b9
 /*
d738b9
- * Copyright (C) 2015 by the Massachusetts Institute of Technology.
d738b9
+ * Copyright (C) 2015, 2017 by the Massachusetts Institute of Technology.
d738b9
  * All rights reserved.
d738b9
  *
d738b9
  * Redistribution and use in source and binary forms, with or without
d738b9
@@ -32,7 +32,7 @@
d738b9
 
d738b9
 /*
d738b9
  * This module is used to test preauth interface features.  At this time, the
d738b9
- * clpreauth module does two things:
d738b9
+ * clpreauth module does the following:
d738b9
  *
d738b9
  * - It decrypts a message from the initial KDC pa-data using the reply key and
d738b9
  *   prints it to stdout.  (The unencrypted message "no key" can also be
d738b9
@@ -45,17 +45,27 @@
d738b9
  *   it to the server, instructing the kdcpreauth module to assert one or more
d738b9
  *   space-separated authentication indicators.  (This string is sent on both
d738b9
  *   round trips if a second round trip is requested.)
d738b9
+ *
d738b9
+ * - If a KDC_ERR_ENCTYPE_NOSUPP error with e-data is received, it prints the
d738b9
+ *   accompanying error padata and sends a follow-up request containing
d738b9
+ *   "tryagain".
d738b9
+ *
d738b9
+ * - If the "fail_optimistic", "fail_2rt", or "fail_tryagain" gic options are
d738b9
+ *   set, it fails with a recognizable error string at the requested point in
d738b9
+ *   processing.
d738b9
  */
d738b9
 
d738b9
 #include "k5-int.h"
d738b9
 #include <krb5/clpreauth_plugin.h>
d738b9
-
d738b9
-#define TEST_PA_TYPE -123
d738b9
+#include "common.h"
d738b9
 
d738b9
 static krb5_preauthtype pa_types[] = { TEST_PA_TYPE, 0 };
d738b9
 
d738b9
 struct client_state {
d738b9
     char *indicators;
d738b9
+    krb5_boolean fail_optimistic;
d738b9
+    krb5_boolean fail_2rt;
d738b9
+    krb5_boolean fail_tryagain;
d738b9
 };
d738b9
 
d738b9
 struct client_request_state {
d738b9
@@ -70,6 +80,7 @@ test_init(krb5_context context, krb5_clpreauth_moddata *moddata_out)
d738b9
     st = malloc(sizeof(*st));
d738b9
     assert(st != NULL);
d738b9
     st->indicators = NULL;
d738b9
+    st->fail_optimistic = st->fail_2rt = st->fail_tryagain = FALSE;
d738b9
     *moddata_out = (krb5_clpreauth_moddata)st;
d738b9
     return 0;
d738b9
 }
d738b9
@@ -114,7 +125,6 @@ test_process(krb5_context context, krb5_clpreauth_moddata moddata,
d738b9
     struct client_state *st = (struct client_state *)moddata;
d738b9
     struct client_request_state *reqst = (struct client_request_state *)modreq;
d738b9
     krb5_error_code ret;
d738b9
-    krb5_pa_data **list, *pa;
d738b9
     krb5_keyblock *k;
d738b9
     krb5_enc_data enc;
d738b9
     krb5_data plain;
d738b9
@@ -123,20 +133,18 @@ test_process(krb5_context context, krb5_clpreauth_moddata moddata,
d738b9
     if (pa_data->length == 0) {
d738b9
         /* This is an optimistic preauth test.  Send a recognizable padata
d738b9
          * value so the KDC knows not to expect a cookie. */
d738b9
-        list = k5calloc(2, sizeof(*list), &ret;;
d738b9
-        assert(!ret);
d738b9
-        pa = k5alloc(sizeof(*pa), &ret;;
d738b9
-        assert(!ret);
d738b9
-        pa->pa_type = TEST_PA_TYPE;
d738b9
-        pa->contents = (uint8_t *)strdup("optimistic");
d738b9
-        assert(pa->contents != NULL);
d738b9
-        pa->length = 10;
d738b9
-        list[0] = pa;
d738b9
-        list[1] = NULL;
d738b9
-        *out_pa_data = list;
d738b9
+        if (st->fail_optimistic) {
d738b9
+            k5_setmsg(context, KRB5_PREAUTH_FAILED, "induced optimistic fail");
d738b9
+            return KRB5_PREAUTH_FAILED;
d738b9
+        }
d738b9
+        *out_pa_data = make_pa_list("optimistic", 10);
d738b9
         return 0;
d738b9
     } else if (reqst->second_round_trip) {
d738b9
         printf("2rt: %.*s\n", pa_data->length, pa_data->contents);
d738b9
+        if (st->fail_2rt) {
d738b9
+            k5_setmsg(context, KRB5_PREAUTH_FAILED, "induced 2rt fail");
d738b9
+            return KRB5_PREAUTH_FAILED;
d738b9
+        }
d738b9
     } else if (pa_data->length == 6 &&
d738b9
                memcmp(pa_data->contents, "no key", 6) == 0) {
d738b9
         printf("no key\n");
d738b9
@@ -157,17 +165,34 @@ test_process(krb5_context context, krb5_clpreauth_moddata moddata,
d738b9
     reqst->second_round_trip = TRUE;
d738b9
 
d738b9
     indstr = (st->indicators != NULL) ? st->indicators : "";
d738b9
-    list = k5calloc(2, sizeof(*list), &ret;;
d738b9
-    assert(!ret);
d738b9
-    pa = k5alloc(sizeof(*pa), &ret;;
d738b9
-    assert(!ret);
d738b9
-    pa->pa_type = TEST_PA_TYPE;
d738b9
-    pa->contents = (uint8_t *)strdup(indstr);
d738b9
-    assert(pa->contents != NULL);
d738b9
-    pa->length = strlen(indstr);
d738b9
-    list[0] = pa;
d738b9
-    list[1] = NULL;
d738b9
-    *out_pa_data = list;
d738b9
+    *out_pa_data = make_pa_list(indstr, strlen(indstr));
d738b9
+    return 0;
d738b9
+}
d738b9
+
d738b9
+static krb5_error_code
d738b9
+test_tryagain(krb5_context context, krb5_clpreauth_moddata moddata,
d738b9
+              krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
d738b9
+              krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
d738b9
+              krb5_kdc_req *request, krb5_data *enc_req, krb5_data *enc_prev,
d738b9
+              krb5_preauthtype pa_type, krb5_error *error,
d738b9
+              krb5_pa_data **padata, krb5_prompter_fct prompter,
d738b9
+              void *prompter_data, krb5_pa_data ***padata_out)
d738b9
+{
d738b9
+    struct client_state *st = (struct client_state *)moddata;
d738b9
+    int i;
d738b9
+
d738b9
+    *padata_out = NULL;
d738b9
+    if (st->fail_tryagain) {
d738b9
+        k5_setmsg(context, KRB5_PREAUTH_FAILED, "induced tryagain fail");
d738b9
+        return KRB5_PREAUTH_FAILED;
d738b9
+    }
d738b9
+    if (error->error != KDC_ERR_ENCTYPE_NOSUPP)
d738b9
+        return KRB5_PREAUTH_FAILED;
d738b9
+    for (i = 0; padata[i] != NULL; i++) {
d738b9
+        if (padata[i]->pa_type == TEST_PA_TYPE)
d738b9
+            printf("tryagain: %.*s\n", padata[i]->length, padata[i]->contents);
d738b9
+    }
d738b9
+    *padata_out = make_pa_list("tryagain", 8);
d738b9
     return 0;
d738b9
 }
d738b9
 
d738b9
@@ -181,6 +206,12 @@ test_gic_opt(krb5_context kcontext, krb5_clpreauth_moddata moddata,
d738b9
         free(st->indicators);
d738b9
         st->indicators = strdup(value);
d738b9
         assert(st->indicators != NULL);
d738b9
+    } else if (strcmp(attr, "fail_optimistic") == 0) {
d738b9
+        st->fail_optimistic = TRUE;
d738b9
+    } else if (strcmp(attr, "fail_2rt") == 0) {
d738b9
+        st->fail_2rt = TRUE;
d738b9
+    } else if (strcmp(attr, "fail_tryagain") == 0) {
d738b9
+        st->fail_tryagain = TRUE;
d738b9
     }
d738b9
     return 0;
d738b9
 }
d738b9
@@ -205,6 +236,7 @@ clpreauth_test_initvt(krb5_context context, int maj_ver,
d738b9
     vt->request_init = test_request_init;
d738b9
     vt->request_fini = test_request_fini;
d738b9
     vt->process = test_process;
d738b9
+    vt->tryagain = test_tryagain;
d738b9
     vt->gic_opts = test_gic_opt;
d738b9
     return 0;
d738b9
 }
d738b9
diff --git a/src/plugins/preauth/test/common.c b/src/plugins/preauth/test/common.c
d738b9
new file mode 100644
d738b9
index 000000000..4d1f49dfa
d738b9
--- /dev/null
d738b9
+++ b/src/plugins/preauth/test/common.c
d738b9
@@ -0,0 +1,61 @@
d738b9
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
d738b9
+/* plugins/preauth/test/common.c - common functions for test preauth module */
d738b9
+/*
d738b9
+ * Copyright (C) 2017 by the Massachusetts Institute of Technology.
d738b9
+ * All rights reserved.
d738b9
+ *
d738b9
+ * Redistribution and use in source and binary forms, with or without
d738b9
+ * modification, are permitted provided that the following conditions
d738b9
+ * are met:
d738b9
+ *
d738b9
+ * * Redistributions of source code must retain the above copyright
d738b9
+ *   notice, this list of conditions and the following disclaimer.
d738b9
+ *
d738b9
+ * * Redistributions in binary form must reproduce the above copyright
d738b9
+ *   notice, this list of conditions and the following disclaimer in
d738b9
+ *   the documentation and/or other materials provided with the
d738b9
+ *   distribution.
d738b9
+ *
d738b9
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
d738b9
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
d738b9
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
d738b9
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
d738b9
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
d738b9
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
d738b9
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
d738b9
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
d738b9
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
d738b9
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
d738b9
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
d738b9
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
d738b9
+ */
d738b9
+
d738b9
+#include "k5-int.h"
d738b9
+#include "common.h"
d738b9
+
d738b9
+krb5_pa_data *
d738b9
+make_pa(const char *contents, size_t len)
d738b9
+{
d738b9
+    krb5_error_code ret;
d738b9
+    krb5_pa_data *pa;
d738b9
+
d738b9
+    pa = calloc(1, sizeof(*pa));
d738b9
+    assert(pa != NULL);
d738b9
+    pa->pa_type = TEST_PA_TYPE;
d738b9
+    pa->contents = k5memdup(contents, len, &ret;;
d738b9
+    assert(!ret);
d738b9
+    pa->length = len;
d738b9
+    return pa;
d738b9
+}
d738b9
+
d738b9
+/* Make a one-element padata list of type TEST_PA_TYPE. */
d738b9
+krb5_pa_data **
d738b9
+make_pa_list(const char *contents, size_t len)
d738b9
+{
d738b9
+    krb5_pa_data **list;
d738b9
+
d738b9
+    list = calloc(2, sizeof(*list));
d738b9
+    assert(list != NULL);
d738b9
+    list[0] = make_pa(contents, len);
d738b9
+    return list;
d738b9
+}
d738b9
diff --git a/src/plugins/preauth/test/common.h b/src/plugins/preauth/test/common.h
d738b9
new file mode 100644
d738b9
index 000000000..b748e0874
d738b9
--- /dev/null
d738b9
+++ b/src/plugins/preauth/test/common.h
d738b9
@@ -0,0 +1,41 @@
d738b9
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
d738b9
+/* plugins/preauth/test/common.h - Declarations for test preauth module */
d738b9
+/*
d738b9
+ * Copyright (C) 2017 by the Massachusetts Institute of Technology.
d738b9
+ * All rights reserved.
d738b9
+ *
d738b9
+ * Redistribution and use in source and binary forms, with or without
d738b9
+ * modification, are permitted provided that the following conditions
d738b9
+ * are met:
d738b9
+ *
d738b9
+ * * Redistributions of source code must retain the above copyright
d738b9
+ *   notice, this list of conditions and the following disclaimer.
d738b9
+ *
d738b9
+ * * Redistributions in binary form must reproduce the above copyright
d738b9
+ *   notice, this list of conditions and the following disclaimer in
d738b9
+ *   the documentation and/or other materials provided with the
d738b9
+ *   distribution.
d738b9
+ *
d738b9
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
d738b9
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
d738b9
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
d738b9
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
d738b9
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
d738b9
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
d738b9
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
d738b9
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
d738b9
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
d738b9
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
d738b9
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
d738b9
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
d738b9
+ */
d738b9
+
d738b9
+#ifndef COMMON_H
d738b9
+#define COMMON_H
d738b9
+
d738b9
+#define TEST_PA_TYPE -123
d738b9
+
d738b9
+krb5_pa_data *make_pa(const char *contents, size_t len);
d738b9
+krb5_pa_data **make_pa_list(const char *contents, size_t len);
d738b9
+
d738b9
+#endif /* COMMON_H */
d738b9
diff --git a/src/plugins/preauth/test/deps b/src/plugins/preauth/test/deps
d738b9
index b48f00032..b1429e9e1 100644
d738b9
--- a/src/plugins/preauth/test/deps
d738b9
+++ b/src/plugins/preauth/test/deps
d738b9
@@ -11,7 +11,7 @@ cltest.so cltest.po $(OUTPRE)cltest.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
d738b9
   $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
d738b9
   $(top_srcdir)/include/krb5/clpreauth_plugin.h $(top_srcdir)/include/krb5/plugin.h \
d738b9
   $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
d738b9
-  cltest.c
d738b9
+  cltest.c common.h
d738b9
 kdctest.so kdctest.po $(OUTPRE)kdctest.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
d738b9
   $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
d738b9
   $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
d738b9
@@ -22,4 +22,14 @@ kdctest.so kdctest.po $(OUTPRE)kdctest.$(OBJEXT): $(BUILDTOP)/include/autoconf.h
d738b9
   $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
d738b9
   $(top_srcdir)/include/krb5/kdcpreauth_plugin.h $(top_srcdir)/include/krb5/plugin.h \
d738b9
   $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
d738b9
-  kdctest.c
d738b9
+  common.h kdctest.c
d738b9
+common.so common.po $(OUTPRE)common.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
d738b9
+  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
d738b9
+  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
d738b9
+  $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
d738b9
+  $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
d738b9
+  $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
d738b9
+  $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
d738b9
+  $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
d738b9
+  $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
d738b9
+  $(top_srcdir)/include/socket-utils.h common.c common.h
d738b9
diff --git a/src/plugins/preauth/test/kdctest.c b/src/plugins/preauth/test/kdctest.c
d738b9
index 026dc680d..66b77969a 100644
d738b9
--- a/src/plugins/preauth/test/kdctest.c
d738b9
+++ b/src/plugins/preauth/test/kdctest.c
d738b9
@@ -1,7 +1,7 @@
d738b9
 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
d738b9
 /* plugins/preauth/test/kdctest.c - Test kdcpreauth module */
d738b9
 /*
d738b9
- * Copyright (C) 2015 by the Massachusetts Institute of Technology.
d738b9
+ * Copyright (C) 2015, 2017 by the Massachusetts Institute of Technology.
d738b9
  * All rights reserved.
d738b9
  *
d738b9
  * Redistribution and use in source and binary forms, with or without
d738b9
@@ -40,10 +40,20 @@
d738b9
  *   key; the encrypted message "no attr" is sent if there is no string
d738b9
  *   attribute.)  It also sets a cookie containing "method-data".
d738b9
  *
d738b9
- * - It retrieves the "2rt" attribute from the client principal.  If set, the
d738b9
- *   verify method sends the client a KDC_ERR_MORE_PREAUTH_DATA_REQUIRED error
d738b9
- *   with the contents of the 2rt attribute as pa-data, and sets a cookie
d738b9
- *   containing "more".
d738b9
+ * - If the "err" attribute is set on the client principal, the verify method
d738b9
+ *   returns an KDC_ERR_ETYPE_NOSUPP error on the first try, with the contents
d738b9
+ *   of the err attribute as pa-data.  If the client tries again with the
d738b9
+ *   padata value "tryagain", the verify method preuthenticates successfully
d738b9
+ *   with no additional processing.
d738b9
+ *
d738b9
+ * - If the "failopt" attribute is set on the client principal, the verify
d738b9
+ *   method returns KDC_ERR_PREAUTH_FAILED on optimistic preauth attempts.
d738b9
+ *
d738b9
+ * - If the "2rt" attribute is set on client principal, the verify method sends
d738b9
+ *   the client a KDC_ERR_MORE_PREAUTH_DATA_REQUIRED error with the contents of
d738b9
+ *   the 2rt attribute as pa-data, and sets a cookie containing "more".  If the
d738b9
+ *   "fail2rt" attribute is set on the client principal, the client's second
d738b9
+ *   try results in a KDC_ERR_PREAUTH_FAILED error.
d738b9
  *
d738b9
  * - It receives a space-separated list from the clpreauth module and asserts
d738b9
  *   each string as an authentication indicator.  It always succeeds in
d738b9
@@ -52,6 +62,7 @@
d738b9
 
d738b9
 #include "k5-int.h"
d738b9
 #include <krb5/kdcpreauth_plugin.h>
d738b9
+#include "common.h"
d738b9
 
d738b9
 #define TEST_PA_TYPE -123
d738b9
 
d738b9
@@ -73,11 +84,6 @@ test_edata(krb5_context context, krb5_kdc_req *req,
d738b9
 
d738b9
     ret = cb->get_string(context, rock, "teststring", &attr);
d738b9
     assert(!ret);
d738b9
-    pa = k5alloc(sizeof(*pa), &ret;;
d738b9
-    assert(!ret);
d738b9
-    if (pa == NULL)
d738b9
-        abort();
d738b9
-    pa->pa_type = TEST_PA_TYPE;
d738b9
     if (k != NULL) {
d738b9
         d = string2data((attr != NULL) ? attr : "no attr");
d738b9
         ret = krb5_c_encrypt_length(context, k->enctype, d.length, &enclen);
d738b9
@@ -86,12 +92,10 @@ test_edata(krb5_context context, krb5_kdc_req *req,
d738b9
         assert(!ret);
d738b9
         ret = krb5_c_encrypt(context, k, 1024, NULL, &d, &enc;;
d738b9
         assert(!ret);
d738b9
-        pa->contents = (uint8_t *)enc.ciphertext.data;
d738b9
-        pa->length = enc.ciphertext.length;
d738b9
+        pa = make_pa(enc.ciphertext.data, enc.ciphertext.length);
d738b9
+        free(enc.ciphertext.data);
d738b9
     } else {
d738b9
-        pa->contents = (uint8_t *)strdup("no key");
d738b9
-        assert(pa->contents != NULL);
d738b9
-        pa->length = 6;
d738b9
+        pa = make_pa("no key", 6);
d738b9
     }
d738b9
 
d738b9
     /* Exercise setting a cookie information from the edata method. */
d738b9
@@ -111,12 +115,19 @@ test_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
d738b9
             krb5_kdcpreauth_verify_respond_fn respond, void *arg)
d738b9
 {
d738b9
     krb5_error_code ret;
d738b9
-    krb5_boolean second_round_trip = FALSE;
d738b9
-    krb5_pa_data **list;
d738b9
+    krb5_boolean second_round_trip = FALSE, optimistic = FALSE;
d738b9
+    krb5_pa_data **list = NULL;
d738b9
     krb5_data cookie_data, d;
d738b9
-    char *str, *ind, *attr, *toksave = NULL;
d738b9
+    char *str, *ind, *toksave = NULL;
d738b9
+    char *attr_err, *attr_2rt, *attr_fail2rt, *attr_failopt;
d738b9
 
d738b9
-    ret = cb->get_string(context, rock, "2rt", &attr);
d738b9
+    ret = cb->get_string(context, rock, "err", &attr_err);
d738b9
+    assert(!ret);
d738b9
+    ret = cb->get_string(context, rock, "2rt", &attr_2rt);
d738b9
+    assert(!ret);
d738b9
+    ret = cb->get_string(context, rock, "fail2rt", &attr_fail2rt);
d738b9
+    assert(!ret);
d738b9
+    ret = cb->get_string(context, rock, "failopt", &attr_failopt);
d738b9
     assert(!ret);
d738b9
 
d738b9
     /* Check the incoming cookie value. */
d738b9
@@ -124,13 +135,36 @@ test_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
d738b9
         /* Make sure we are seeing optimistic preauth and not a lost cookie. */
d738b9
         d = make_data(data->contents, data->length);
d738b9
         assert(data_eq_string(d, "optimistic"));
d738b9
+        optimistic = TRUE;
d738b9
     } else if (data_eq_string(cookie_data, "more")) {
d738b9
         second_round_trip = TRUE;
d738b9
     } else {
d738b9
-        assert(data_eq_string(cookie_data, "method-data"));
d738b9
+        assert(data_eq_string(cookie_data, "method-data") ||
d738b9
+               data_eq_string(cookie_data, "err"));
d738b9
     }
d738b9
 
d738b9
-    if (attr == NULL || second_round_trip) {
d738b9
+    if (attr_err != NULL) {
d738b9
+        d = make_data(data->contents, data->length);
d738b9
+        if (data_eq_string(d, "tryagain")) {
d738b9
+            /* Authenticate successfully. */
d738b9
+            enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
d738b9
+        } else {
d738b9
+            d = string2data("err");
d738b9
+            ret = cb->set_cookie(context, rock, TEST_PA_TYPE, &d);
d738b9
+            assert(!ret);
d738b9
+            ret = KRB5KDC_ERR_ETYPE_NOSUPP;
d738b9
+            list = make_pa_list(attr_err, strlen(attr_err));
d738b9
+        }
d738b9
+    } else if (attr_2rt != NULL && !second_round_trip) {
d738b9
+        d = string2data("more");
d738b9
+        ret = cb->set_cookie(context, rock, TEST_PA_TYPE, &d);
d738b9
+        assert(!ret);
d738b9
+        ret = KRB5KDC_ERR_MORE_PREAUTH_DATA_REQUIRED;
d738b9
+        list = make_pa_list(attr_2rt, strlen(attr_2rt));
d738b9
+    } else if ((attr_fail2rt != NULL && second_round_trip) ||
d738b9
+               (attr_failopt != NULL && optimistic)) {
d738b9
+        ret = KRB5KDC_ERR_PREAUTH_FAILED;
d738b9
+    } else {
d738b9
         /* Parse and assert the indicators. */
d738b9
         str = k5memdup0(data->contents, data->length, &ret;;
d738b9
         if (ret)
d738b9
@@ -142,21 +176,13 @@ test_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
d738b9
         }
d738b9
         free(str);
d738b9
         enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
d738b9
-        cb->free_string(context, rock, attr);
d738b9
-        (*respond)(arg, 0, NULL, NULL, NULL);
d738b9
-    } else {
d738b9
-        d = string2data("more");
d738b9
-        ret = cb->set_cookie(context, rock, TEST_PA_TYPE, &d);
d738b9
-        list = k5calloc(2, sizeof(*list), &ret;;
d738b9
-        assert(!ret);
d738b9
-        list[0] = k5alloc(sizeof(*list[0]), &ret;;
d738b9
-        assert(!ret);
d738b9
-        list[0]->pa_type = TEST_PA_TYPE;
d738b9
-        list[0]->contents = (uint8_t *)attr;
d738b9
-        list[0]->length = strlen(attr);
d738b9
-        (*respond)(arg, KRB5KDC_ERR_MORE_PREAUTH_DATA_REQUIRED, NULL, list,
d738b9
-                   NULL);
d738b9
     }
d738b9
+
d738b9
+    cb->free_string(context, rock, attr_err);
d738b9
+    cb->free_string(context, rock, attr_2rt);
d738b9
+    cb->free_string(context, rock, attr_fail2rt);
d738b9
+    cb->free_string(context, rock, attr_failopt);
d738b9
+    (*respond)(arg, ret, NULL, list, NULL);
d738b9
 }
d738b9
 
d738b9
 static krb5_error_code
d738b9
diff --git a/src/tests/icred.c b/src/tests/icred.c
d738b9
index 071f91c80..55f929cd7 100644
d738b9
--- a/src/tests/icred.c
d738b9
+++ b/src/tests/icred.c
d738b9
@@ -35,8 +35,8 @@
d738b9
  * it is very simplistic, but it can be extended as needed.
d738b9
  */
d738b9
 
d738b9
+#include "k5-platform.h"
d738b9
 #include <krb5.h>
d738b9
-#include <stdio.h>
d738b9
 
d738b9
 static krb5_context ctx;
d738b9
 
d738b9
@@ -59,29 +59,64 @@ main(int argc, char **argv)
d738b9
     const char *princstr, *password;
d738b9
     krb5_principal client;
d738b9
     krb5_init_creds_context icc;
d738b9
+    krb5_get_init_creds_opt *opt;
d738b9
     krb5_creds creds;
d738b9
-
d738b9
-    if (argc != 3) {
d738b9
-        fprintf(stderr, "Usage: icred princname password\n");
d738b9
-        exit(1);
d738b9
-    }
d738b9
-    princstr = argv[1];
d738b9
-    password = argv[2];
d738b9
+    krb5_boolean stepwise = FALSE;
d738b9
+    krb5_preauthtype ptypes[64];
d738b9
+    int c, nptypes = 0;
d738b9
+    char *val;
d738b9
 
d738b9
     check(krb5_init_context(&ctx));
d738b9
+    check(krb5_get_init_creds_opt_alloc(ctx, &opt));
d738b9
+
d738b9
+    while ((c = getopt(argc, argv, "so:X:")) != -1) {
d738b9
+        switch (c) {
d738b9
+        case 's':
d738b9
+            stepwise = TRUE;
d738b9
+            break;
d738b9
+        case 'o':
d738b9
+            assert(nptypes < 64);
d738b9
+            ptypes[nptypes++] = atoi(optarg);
d738b9
+            break;
d738b9
+        case 'X':
d738b9
+            val = strchr(optarg, '=');
d738b9
+            if (val != NULL)
d738b9
+                *val++ = '\0';
d738b9
+            else
d738b9
+                val = "yes";
d738b9
+            check(krb5_get_init_creds_opt_set_pa(ctx, opt, optarg, val));
d738b9
+            break;
d738b9
+        default:
d738b9
+            abort();
d738b9
+        }
d738b9
+    }
d738b9
+
d738b9
+    argc -= optind;
d738b9
+    argv += optind;
d738b9
+    if (argc != 2)
d738b9
+        abort();
d738b9
+    princstr = argv[0];
d738b9
+    password = argv[1];
d738b9
+
d738b9
     check(krb5_parse_name(ctx, princstr, &client));
d738b9
 
d738b9
-    /* Try once with the traditional interface. */
d738b9
-    check(krb5_get_init_creds_password(ctx, &creds, client, password, NULL,
d738b9
-                                       NULL, 0, NULL, NULL));
d738b9
-    krb5_free_cred_contents(ctx, &creds);
d738b9
+    if (nptypes > 0)
d738b9
+        krb5_get_init_creds_opt_set_preauth_list(opt, ptypes, nptypes);
d738b9
 
d738b9
-    /* Try again with the step interface. */
d738b9
-    check(krb5_init_creds_init(ctx, client, NULL, NULL, 0, NULL, &icc));
d738b9
-    check(krb5_init_creds_set_password(ctx, icc, password));
d738b9
-    check(krb5_init_creds_get(ctx, icc));
d738b9
-    krb5_init_creds_free(ctx, icc);
d738b9
+    if (stepwise) {
d738b9
+        /* Use the stepwise interface. */
d738b9
+        check(krb5_init_creds_init(ctx, client, NULL, NULL, 0, NULL, &icc));
d738b9
+        check(krb5_init_creds_set_password(ctx, icc, password));
d738b9
+        check(krb5_init_creds_get(ctx, icc));
d738b9
+        krb5_init_creds_free(ctx, icc);
d738b9
+    } else {
d738b9
+        /* Use the traditional one-shot interface. */
d738b9
+        check(krb5_get_init_creds_password(ctx, &creds, client, password, NULL,
d738b9
+                                           NULL, 0, NULL, opt));
d738b9
+        krb5_free_cred_contents(ctx, &creds);
d738b9
+    }
d738b9
 
d738b9
+    krb5_get_init_creds_opt_free(ctx, opt);
d738b9
     krb5_free_principal(ctx, client);
d738b9
     krb5_free_context(ctx);
d738b9
     return 0;
d738b9
diff --git a/src/tests/t_general.py b/src/tests/t_general.py
d738b9
index 6d523fe45..b16cffa37 100755
d738b9
--- a/src/tests/t_general.py
d738b9
+++ b/src/tests/t_general.py
d738b9
@@ -30,6 +30,7 @@ conf={'plugins': {'pwqual': {'disable': 'empty'}}}
d738b9
 realm = K5Realm(create_user=False, create_host=False, krb5_conf=conf)
d738b9
 realm.run([kadminl, 'addprinc', '-pw', '', 'user'])
d738b9
 realm.run(['./icred', 'user', ''])
d738b9
+realm.run(['./icred', '-s', 'user', ''])
d738b9
 realm.stop()
d738b9
 
d738b9
 realm = K5Realm(create_host=False)
d738b9
diff --git a/src/tests/t_pkinit.py b/src/tests/t_pkinit.py
d738b9
index 38424932b..c25475096 100755
d738b9
--- a/src/tests/t_pkinit.py
d738b9
+++ b/src/tests/t_pkinit.py
d738b9
@@ -176,14 +176,20 @@ realm.klist(realm.user_princ)
d738b9
 
d738b9
 # Test a DH parameter renegotiation by temporarily setting a 4096-bit
d738b9
 # minimum on the KDC.  (Preauth type 16 is PKINIT PA_PK_AS_REQ;
d738b9
-# 133 is FAST PA-FX-COOKIE.)
d738b9
+# 109 is PKINIT TD_DH_PARAMETERS; 133 is FAST PA-FX-COOKIE.)
d738b9
 minbits_kdc_conf = {'realms': {'$realm': {'pkinit_dh_min_bits': '4096'}}}
d738b9
 minbits_env = realm.special_env('restrict', True, kdc_conf=minbits_kdc_conf)
d738b9
 realm.stop_kdc()
d738b9
 realm.start_kdc(env=minbits_env)
d738b9
-expected_trace = ('Key parameters not accepted',
d738b9
-                  'Preauth tryagain input types',
d738b9
+expected_trace = ('Sending unauthenticated request',
d738b9
+                  '/Additional pre-authentication required',
d738b9
+                  'Preauthenticating using KDC method data',
d738b9
+                  'Preauth module pkinit (16) (real) returned: 0/Success',
d738b9
+                  'Produced preauth for next request: 133, 16',
d738b9
+                  '/Key parameters not accepted',
d738b9
+                  'Preauth tryagain input types (16): 109, 133',
d738b9
                   'trying again with KDC-provided parameters',
d738b9
+                  'Preauth module pkinit (16) tryagain returned: 0/Success',
d738b9
                   'Followup preauth for next request: 16, 133')
d738b9
 realm.kinit(realm.user_princ,
d738b9
             flags=['-X', 'X509_user_identity=%s' % file_identity],
d738b9
diff --git a/src/tests/t_preauth.py b/src/tests/t_preauth.py
d738b9
index 9b6da5a96..7d4d299dc 100644
d738b9
--- a/src/tests/t_preauth.py
d738b9
+++ b/src/tests/t_preauth.py
d738b9
@@ -18,11 +18,161 @@ out = realm.run([kinit, 'nokeyuser'], input=password('user')+'\n',
d738b9
 if 'no key' not in out:
d738b9
     fail('Expected "no key" message not in kinit output')
d738b9
 
d738b9
-# Exercise KDC_ERR_MORE_PREAUTH_DATA_REQUIRED and secure cookies.
d738b9
+# Preauth type -123 is the test preauth module type; 133 is FAST
d738b9
+# PA-FX-COOKIE; 2 is encrypted timestamp.
d738b9
+
d738b9
+# Test normal preauth flow.
d738b9
+expected_trace = ('Sending unauthenticated request',
d738b9
+                  '/Additional pre-authentication required',
d738b9
+                  'Preauthenticating using KDC method data',
d738b9
+                  'Processing preauth types:',
d738b9
+                  'Preauth module test (-123) (real) returned: 0/Success',
d738b9
+                  'Produced preauth for next request: 133, -123',
d738b9
+                  'Decrypted AS reply')
d738b9
+realm.run(['./icred', realm.user_princ, password('user')],
d738b9
+          expected_msg='testval', expected_trace=expected_trace)
d738b9
+
d738b9
+# Test successful optimistic preauth.
d738b9
+expected_trace = ('Attempting optimistic preauth',
d738b9
+                  'Processing preauth types: -123',
d738b9
+                  'Preauth module test (-123) (real) returned: 0/Success',
d738b9
+                  'Produced preauth for next request: -123',
d738b9
+                  'Decrypted AS reply')
d738b9
+realm.run(['./icred', '-o', '-123', realm.user_princ, password('user')],
d738b9
+          expected_trace=expected_trace)
d738b9
+
d738b9
+# Test optimistic preauth failing on client, followed by successful
d738b9
+# preauth using the same module.
d738b9
+expected_trace = ('Attempting optimistic preauth',
d738b9
+                  'Processing preauth types: -123',
d738b9
+                  '/induced optimistic fail',
d738b9
+                  'Sending unauthenticated request',
d738b9
+                  '/Additional pre-authentication required',
d738b9
+                  'Preauthenticating using KDC method data',
d738b9
+                  'Processing preauth types:',
d738b9
+                  'Preauth module test (-123) (real) returned: 0/Success',
d738b9
+                  'Produced preauth for next request: 133, -123',
d738b9
+                  'Decrypted AS reply')
d738b9
+realm.run(['./icred', '-o', '-123', '-X', 'fail_optimistic', realm.user_princ,
d738b9
+           password('user')], expected_msg='testval',
d738b9
+          expected_trace=expected_trace)
d738b9
+
d738b9
+# Test optimistic preauth failing on KDC, followed by successful preauth
d738b9
+# using the same module.
d738b9
+realm.run([kadminl, 'setstr', realm.user_princ, 'failopt', 'yes'])
d738b9
+expected_trace = ('Attempting optimistic preauth',
d738b9
+                  'Processing preauth types: -123',
d738b9
+                  'Preauth module test (-123) (real) returned: 0/Success',
d738b9
+                  'Produced preauth for next request: -123',
d738b9
+                  '/Preauthentication failed',
d738b9
+                  'Preauthenticating using KDC method data',
d738b9
+                  'Processing preauth types:',
d738b9
+                  'Preauth module test (-123) (real) returned: 0/Success',
d738b9
+                  'Produced preauth for next request: 133, -123',
d738b9
+                  'Decrypted AS reply')
d738b9
+realm.run(['./icred', '-o', '-123', realm.user_princ, password('user')],
d738b9
+          expected_msg='testval', expected_trace=expected_trace)
d738b9
+realm.run([kadminl, 'delstr', realm.user_princ, 'failopt'])
d738b9
+
d738b9
+# Test KDC_ERR_MORE_PREAUTH_DATA_REQUIRED and secure cookies.
d738b9
 realm.run([kadminl, 'setstr', realm.user_princ, '2rt', 'secondtrip'])
d738b9
-out = realm.run([kinit, realm.user_princ], input=password('user')+'\n')
d738b9
-if '2rt: secondtrip' not in out:
d738b9
-    fail('multi round-trip cookie test')
d738b9
+expected_trace = ('Sending unauthenticated request',
d738b9
+                  '/Additional pre-authentication required',
d738b9
+                  'Preauthenticating using KDC method data',
d738b9
+                  'Processing preauth types:',
d738b9
+                  'Preauth module test (-123) (real) returned: 0/Success',
d738b9
+                  'Produced preauth for next request: 133, -123',
d738b9
+                  '/More preauthentication data is required',
d738b9
+                  'Continuing preauth mech -123',
d738b9
+                  'Processing preauth types: -123, 133',
d738b9
+                  'Produced preauth for next request: 133, -123',
d738b9
+                  'Decrypted AS reply')
d738b9
+realm.run(['./icred', realm.user_princ, password('user')],
d738b9
+          expected_msg='2rt: secondtrip', expected_trace=expected_trace)
d738b9
+
d738b9
+# Test client-side failure after KDC_ERR_MORE_PREAUTH_DATA_REQUIRED,
d738b9
+# falling back to encrypted timestamp.
d738b9
+expected_trace = ('Sending unauthenticated request',
d738b9
+                  '/Additional pre-authentication required',
d738b9
+                  'Preauthenticating using KDC method data',
d738b9
+                  'Processing preauth types:',
d738b9
+                  'Preauth module test (-123) (real) returned: 0/Success',
d738b9
+                  'Produced preauth for next request: 133, -123',
d738b9
+                  '/More preauthentication data is required',
d738b9
+                  'Continuing preauth mech -123',
d738b9
+                  'Processing preauth types: -123, 133',
d738b9
+                  '/induced 2rt fail',
d738b9
+                  'Preauthenticating using KDC method data',
d738b9
+                  'Processing preauth types:',
d738b9
+                  'Encrypted timestamp (for ',
d738b9
+                  'module encrypted_timestamp (2) (real) returned: 0/Success',
d738b9
+                  'Produced preauth for next request: 133, 2',
d738b9
+                  'Decrypted AS reply')
d738b9
+realm.run(['./icred', '-X', 'fail_2rt', realm.user_princ, password('user')],
d738b9
+          expected_msg='2rt: secondtrip', expected_trace=expected_trace)
d738b9
+
d738b9
+# Test KDC-side failure after KDC_ERR_MORE_PREAUTH_DATA_REQUIRED,
d738b9
+# falling back to encrypted timestamp.
d738b9
+realm.run([kadminl, 'setstr', realm.user_princ, 'fail2rt', 'yes'])
d738b9
+expected_trace = ('Sending unauthenticated request',
d738b9
+                  '/Additional pre-authentication required',
d738b9
+                  'Preauthenticating using KDC method data',
d738b9
+                  'Processing preauth types:',
d738b9
+                  'Preauth module test (-123) (real) returned: 0/Success',
d738b9
+                  'Produced preauth for next request: 133, -123',
d738b9
+                  '/More preauthentication data is required',
d738b9
+                  'Continuing preauth mech -123',
d738b9
+                  'Processing preauth types: -123, 133',
d738b9
+                  'Preauth module test (-123) (real) returned: 0/Success',
d738b9
+                  'Produced preauth for next request: 133, -123',
d738b9
+                  '/Preauthentication failed',
d738b9
+                  'Preauthenticating using KDC method data',
d738b9
+                  'Processing preauth types:',
d738b9
+                  'Encrypted timestamp (for ',
d738b9
+                  'module encrypted_timestamp (2) (real) returned: 0/Success',
d738b9
+                  'Produced preauth for next request: 133, 2',
d738b9
+                  'Decrypted AS reply')
d738b9
+realm.run(['./icred', realm.user_princ, password('user')],
d738b9
+          expected_msg='2rt: secondtrip', expected_trace=expected_trace)
d738b9
+realm.run([kadminl, 'delstr', realm.user_princ, 'fail2rt'])
d738b9
+
d738b9
+# Test tryagain flow by inducing a KDC_ERR_ENCTYPE_NOSUPP error on the KDC.
d738b9
+realm.run([kadminl, 'setstr', realm.user_princ, 'err', 'testagain'])
d738b9
+expected_trace = ('Sending unauthenticated request',
d738b9
+                  '/Additional pre-authentication required',
d738b9
+                  'Preauthenticating using KDC method data',
d738b9
+                  'Processing preauth types:',
d738b9
+                  'Preauth module test (-123) (real) returned: 0/Success',
d738b9
+                  'Produced preauth for next request: 133, -123',
d738b9
+                  '/KDC has no support for encryption type',
d738b9
+                  'Recovering from KDC error 14 using preauth mech -123',
d738b9
+                  'Preauth tryagain input types (-123): -123, 133',
d738b9
+                  'Preauth module test (-123) tryagain returned: 0/Success',
d738b9
+                  'Followup preauth for next request: -123, 133',
d738b9
+                  'Decrypted AS reply')
d738b9
+realm.run(['./icred', realm.user_princ, password('user')],
d738b9
+          expected_msg='tryagain: testagain', expected_trace=expected_trace)
d738b9
+
d738b9
+# Test a client-side tryagain failure, falling back to encrypted
d738b9
+# timestamp.
d738b9
+expected_trace = ('Sending unauthenticated request',
d738b9
+                  '/Additional pre-authentication required',
d738b9
+                  'Preauthenticating using KDC method data',
d738b9
+                  'Processing preauth types:',
d738b9
+                  'Preauth module test (-123) (real) returned: 0/Success',
d738b9
+                  'Produced preauth for next request: 133, -123',
d738b9
+                  '/KDC has no support for encryption type',
d738b9
+                  'Recovering from KDC error 14 using preauth mech -123',
d738b9
+                  'Preauth tryagain input types (-123): -123, 133',
d738b9
+                  '/induced tryagain fail',
d738b9
+                  'Preauthenticating using KDC method data',
d738b9
+                  'Processing preauth types:',
d738b9
+                  'Encrypted timestamp (for ',
d738b9
+                  'module encrypted_timestamp (2) (real) returned: 0/Success',
d738b9
+                  'Produced preauth for next request: 133, 2',
d738b9
+                  'Decrypted AS reply')
d738b9
+realm.run(['./icred', '-X', 'fail_tryagain', realm.user_princ,
d738b9
+           password('user')], expected_trace=expected_trace)
d738b9
 
d738b9
 # Test that multiple stepwise initial creds operations can be
d738b9
 # performed with the same krb5_context, with proper tracking of