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