krb5 commit: Add etype-info2 to MORE_PREAUTH_DATA_REQUIRED

Greg Hudson ghudson at mit.edu
Thu Aug 27 12:06:53 EDT 2015


https://github.com/krb5/krb5/commit/1b4bd4e388faa5685aa483fdc2bded02c95350bc
commit 1b4bd4e388faa5685aa483fdc2bded02c95350bc
Author: Greg Hudson <ghudson at mit.edu>
Date:   Mon Aug 17 18:26:36 2015 -0400

    Add etype-info2 to MORE_PREAUTH_DATA_REQUIRED
    
    A multi-round-trip preauth mechanism may require key information, but
    not for the initial message from the client.  To support optimistic
    preauth for such mechanisms, make the KDC include etype-info2
    information in a MORE_PREAUTH_DATA_REQUIRED error if the client didn't
    include a PA-FX-COOKIE in its request.
    
    Add optimistic preauth support to the test preauth module and to
    etinfo.c, and add a test case to t_etype_info.py to verify that
    etype-info2 is included in the optimistic multi-hop scenario.
    
    ticket: 8234 (new)

 src/kdc/kdc_preauth.c              |   52 ++++++++++++++++++++++++++++++++++++
 src/plugins/preauth/test/cltest.c  |   17 +++++++++++-
 src/plugins/preauth/test/kdctest.c |   11 +++++---
 src/tests/etinfo.c                 |   22 +++++++++++---
 src/tests/t_etype_info.py          |   12 ++++++++
 5 files changed, 104 insertions(+), 10 deletions(-)

diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
index 2509c38..605fcb7 100644
--- a/src/kdc/kdc_preauth.c
+++ b/src/kdc/kdc_preauth.c
@@ -101,6 +101,11 @@ typedef struct preauth_system_st {
     krb5_kdcpreauth_loop_fn loop;
 } preauth_system;
 
+static krb5_error_code
+make_etype_info(krb5_context context, krb5_preauthtype pa_type,
+                krb5_principal client, krb5_key_data *client_key,
+                krb5_enctype enctype, krb5_pa_data **pa_out);
+
 static void
 get_etype_info(krb5_context context, krb5_kdc_req *request,
                krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
@@ -985,6 +990,47 @@ filter_preauth_error(krb5_error_code code)
     }
 }
 
+/*
+ * If the client performed optimistic pre-authentication for a multi-round-trip
+ * mechanism, it may need key information to complete the exchange, so send it
+ * a PA-ETYPE-INFO2 element in addition to the pa-data from the module.
+ */
+static krb5_error_code
+maybe_add_etype_info2(struct padata_state *state, krb5_error_code code)
+{
+    krb5_context context = state->context;
+    krb5_kdcpreauth_rock rock = state->rock;
+    krb5_pa_data **list = state->pa_e_data;
+    size_t count;
+
+    /* Only add key information when requesting another preauth round trip. */
+    if (code != KRB5KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
+        return 0;
+
+    /* Don't try to add key information when there is no key. */
+    if (rock->client_key == NULL)
+        return 0;
+
+    /* If the client sent a cookie, it has already seen a KDC response with key
+     * information. */
+    if (krb5int_find_pa_data(context, state->request->padata,
+                             KRB5_PADATA_FX_COOKIE) != NULL)
+        return 0;
+
+    /* Reallocate state->pa_e_data to make room for the etype-info2 element. */
+    for (count = 0; list != NULL && list[count] != NULL; count++);
+    list = realloc(list, (count + 2) * sizeof(*list));
+    if (list == NULL)
+        return ENOMEM;
+    list[count] = list[count + 1] = NULL;
+    state->pa_e_data = list;
+
+    /* Generate an etype-info2 element in the new slot. */
+    return make_etype_info(context, KRB5_PADATA_ETYPE_INFO2,
+                           rock->client->princ, rock->client_key,
+                           rock->client_keyblock->enctype, &list[count]);
+}
+
 /* Release state and respond to the AS-REQ processing code with the result of
  * checking pre-authentication data. */
 static void
@@ -1000,6 +1046,12 @@ finish_check_padata(struct padata_state *state, krb5_error_code code)
         goto cleanup;
     }
 
+    /* Add key information to the saved error pa-data if required. */
+    if (maybe_add_etype_info2(state, code) != 0) {
+        code = KRB5KDC_ERR_PREAUTH_FAILED;
+        goto cleanup;
+    }
+
     /* Return any saved error pa-data, stealing the pointer from state. */
     *state->e_data_out = state->pa_e_data;
     *state->typed_e_data_out = state->typed_e_data_flag;
diff --git a/src/plugins/preauth/test/cltest.c b/src/plugins/preauth/test/cltest.c
index 5244a7d..4c31e1c 100644
--- a/src/plugins/preauth/test/cltest.c
+++ b/src/plugins/preauth/test/cltest.c
@@ -120,7 +120,22 @@ test_process(krb5_context context, krb5_clpreauth_moddata moddata,
     krb5_data plain;
     const char *indstr;
 
-    if (reqst->second_round_trip) {
+    if (pa_data->length == 0) {
+        /* This is an optimistic preauth test.  Send a recognizable padata
+         * value so the KDC knows not to expect a cookie. */
+        list = k5calloc(2, sizeof(*list), &ret);
+        assert(!ret);
+        pa = k5alloc(sizeof(*pa), &ret);
+        assert(!ret);
+        pa->pa_type = TEST_PA_TYPE;
+        pa->contents = (uint8_t *)strdup("optimistic");
+        assert(pa->contents != NULL);
+        pa->length = 10;
+        list[0] = pa;
+        list[1] = NULL;
+        *out_pa_data = list;
+        return 0;
+    } else if (reqst->second_round_trip) {
         printf("2rt: %.*s\n", pa_data->length, pa_data->contents);
     } else if (pa_data->length == 6 &&
                memcmp(pa_data->contents, "no key", 6) == 0) {
diff --git a/src/plugins/preauth/test/kdctest.c b/src/plugins/preauth/test/kdctest.c
index 8c1d01d..82a03b0 100644
--- a/src/plugins/preauth/test/kdctest.c
+++ b/src/plugins/preauth/test/kdctest.c
@@ -120,12 +120,15 @@ test_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
     assert(!ret);
 
     /* Check the incoming cookie value. */
-    if (!cb->get_cookie(context, rock, TEST_PA_TYPE, &cookie_data))
-        abort();
-    if (data_eq_string(cookie_data, "more"))
+    if (!cb->get_cookie(context, rock, TEST_PA_TYPE, &cookie_data)) {
+        /* Make sure we are seeing optimistic preauth and not a lost cookie. */
+        d = make_data(data->contents, data->length);
+        assert(data_eq_string(d, "optimistic"));
+    } else if (data_eq_string(cookie_data, "more")) {
         second_round_trip = TRUE;
-    else
+    } else {
         assert(data_eq_string(cookie_data, "method-data"));
+    }
 
     if (attr == NULL || second_round_trip) {
         /* Parse and assert the indicators. */
diff --git a/src/tests/etinfo.c b/src/tests/etinfo.c
index dc45638..3f0c42d 100644
--- a/src/tests/etinfo.c
+++ b/src/tests/etinfo.c
@@ -113,17 +113,19 @@ int
 main(int argc, char **argv)
 {
     krb5_principal client;
+    krb5_get_init_creds_opt *opt;
     krb5_init_creds_context icc;
     krb5_data reply, request, realm;
     krb5_error *error;
     krb5_kdc_rep *asrep;
     krb5_pa_data **padata;
     krb5_enctype *enctypes, def[] = { ENCTYPE_NULL };
+    krb5_preauthtype pa_type = KRB5_PADATA_NONE;
     unsigned int flags;
     int master = 0;
 
-    if (argc < 2 && argc > 3) {
-        fprintf(stderr, "Usage: %s princname [enctypes]\n", argv[0]);
+    if (argc < 2 && argc > 4) {
+        fprintf(stderr, "Usage: %s princname [enctypes] [patype]\n", argv[0]);
         exit(1);
     }
     check(krb5_init_context(&ctx));
@@ -133,8 +135,14 @@ main(int argc, char **argv)
         krb5_set_default_in_tkt_ktypes(ctx, enctypes);
         free(enctypes);
     }
+    if (argc >= 4)
+        pa_type = atoi(argv[3]);
 
-    check(krb5_init_creds_init(ctx, client, NULL, NULL, 0, NULL, &icc));
+    check(krb5_get_init_creds_opt_alloc(ctx, &opt));
+    if (pa_type != KRB5_PADATA_NONE)
+        krb5_get_init_creds_opt_set_preauth_list(opt, &pa_type, 1);
+
+    check(krb5_init_creds_init(ctx, client, NULL, NULL, 0, opt, &icc));
     reply = empty_data();
     check(krb5_init_creds_step(ctx, icc, &reply, &request, &realm, &flags));
     assert(flags == KRB5_INIT_CREDS_STEP_FLAG_CONTINUE);
@@ -142,11 +150,14 @@ main(int argc, char **argv)
 
     if (decode_krb5_error(&reply, &error) == 0) {
         decode_krb5_padata_sequence(&error->e_data, &padata);
-        if (error->error != KDC_ERR_PREAUTH_REQUIRED) {
+        if (error->error == KDC_ERR_PREAUTH_REQUIRED) {
+            display_padata(padata, "error");
+        } else if (error->error == KDC_ERR_MORE_PREAUTH_DATA_REQUIRED) {
+            display_padata(padata, "more");
+        } else {
             fprintf(stderr, "Unexpected error %d\n", (int)error->error);
             return 1;
         }
-        display_padata(padata, "error");
         krb5_free_pa_data(ctx, padata);
         krb5_free_error(ctx, error);
     } else if (decode_krb5_as_rep(&reply, &asrep) == 0) {
@@ -159,6 +170,7 @@ main(int argc, char **argv)
     krb5_free_data_contents(ctx, &request);
     krb5_free_data_contents(ctx, &reply);
     krb5_free_data_contents(ctx, &realm);
+    krb5_get_init_creds_opt_free(ctx, opt);
     krb5_init_creds_free(ctx, icc);
     krb5_free_principal(ctx, client);
     krb5_free_context(ctx);
diff --git a/src/tests/t_etype_info.py b/src/tests/t_etype_info.py
index 8ff6ad6..b2eb0f7 100644
--- a/src/tests/t_etype_info.py
+++ b/src/tests/t_etype_info.py
@@ -73,4 +73,16 @@ test_etinfo('user', 'des-cbc-md5 rc4',
 test_etinfo('rc4user', 'des3', [])
 test_etinfo('nokeyuser', 'des3', [])
 
+# Verify that etype-info2 is included in a MORE_PREAUTH_DATA_REQUIRED
+# error if the client does optimistic preauth.
+realm.stop()
+testpreauth = os.path.join(buildtop, 'plugins', 'preauth', 'test', 'test.so')
+conf = {'plugins': {'kdcpreauth': {'module': 'test:' + testpreauth},
+                    'clpreauth': {'module': 'test:' + testpreauth}}}
+realm = K5Realm(create_host=False, get_creds=False, krb5_conf=conf)
+realm.run([kadminl, 'setstr', realm.user_princ, '2rt', '2rtval'])
+out = realm.run(['./etinfo', realm.user_princ, 'aes128-cts', '-123'])
+if out != 'more etype_info2 aes128-cts KRBTEST.COMuser\n':
+    fail('Unexpected output for MORE_PREAUTH_DATA_REQUIRED test')
+
 success('KDC etype-info tests')


More information about the cvs-krb5 mailing list