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,
}
}
+/*
+ * 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
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;
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) {
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. */
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));
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);
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) {
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);
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')