]> git.ipfire.org Git - thirdparty/krb5.git/commitdiff
Add etype-info2 to MORE_PREAUTH_DATA_REQUIRED 316/head
authorGreg Hudson <ghudson@mit.edu>
Mon, 17 Aug 2015 22:26:36 +0000 (18:26 -0400)
committerGreg Hudson <ghudson@mit.edu>
Thu, 27 Aug 2015 15:55:42 +0000 (11:55 -0400)
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
src/plugins/preauth/test/cltest.c
src/plugins/preauth/test/kdctest.c
src/tests/etinfo.c
src/tests/t_etype_info.py

index 2509c38d5089fff59d2f11cbae5f3a0e9e703cc0..605fcb7addc68052341d19c459c545ebb6111b26 100644 (file)
@@ -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;
index 5244a7d8d18da1b88bbceb52651c52fecc85a875..4c31e1c0f8b2eadf5fd03627e85b886998c6ecfa 100644 (file)
@@ -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) {
index 8c1d01dab89d683123140c842d0b39c2951c7e3c..82a03b0e2e7b733bd72f2af97212f11411c9ed98 100644 (file)
@@ -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. */
index dc4563877ab1d4c02c5f2453dd86cbf36bff3524..3f0c42d3e5bdda70786faeea209cc54b14e1b575 100644 (file)
@@ -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);
index 8ff6ad666475868eb2fbac9f784ee385b52836e5..b2eb0f7affd063fd941a7862e92c38e26e68a10d 100644 (file)
@@ -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')