summaryrefslogtreecommitdiff
path: root/src/lib/krb5/krb/get_in_tkt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/krb5/krb/get_in_tkt.c')
-rw-r--r--src/lib/krb5/krb/get_in_tkt.c294
1 files changed, 162 insertions, 132 deletions
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
index 54badbbc32f8..47a00bf2c702 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -40,24 +40,6 @@ static krb5_error_code sort_krb5_padata_sequence(krb5_context context,
krb5_pa_data **padata);
/*
- * This function performs 32 bit bounded addition so we can generate
- * lifetimes without overflowing krb5_int32
- */
-static krb5_int32
-krb5int_addint32 (krb5_int32 x, krb5_int32 y)
-{
- if ((x > 0) && (y > (KRB5_INT32_MAX - x))) {
- /* sum will be be greater than KRB5_INT32_MAX */
- return KRB5_INT32_MAX;
- } else if ((x < 0) && (y < (KRB5_INT32_MIN - x))) {
- /* sum will be less than KRB5_INT32_MIN */
- return KRB5_INT32_MIN;
- }
-
- return x + y;
-}
-
-/*
* Decrypt the AS reply in ctx, populating ctx->reply->enc_part2. If
* strengthen_key is not null, combine it with the reply key as specified in
* RFC 6113 section 5.4.3. Place the key used in *key_out.
@@ -267,28 +249,28 @@ verify_as_reply(krb5_context context,
(request->from != 0) &&
(request->from != as_reply->enc_part2->times.starttime))
|| ((request->till != 0) &&
- (as_reply->enc_part2->times.endtime > request->till))
+ ts_after(as_reply->enc_part2->times.endtime, request->till))
|| ((request->kdc_options & KDC_OPT_RENEWABLE) &&
(request->rtime != 0) &&
- (as_reply->enc_part2->times.renew_till > request->rtime))
+ ts_after(as_reply->enc_part2->times.renew_till, request->rtime))
|| ((request->kdc_options & KDC_OPT_RENEWABLE_OK) &&
!(request->kdc_options & KDC_OPT_RENEWABLE) &&
(as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) &&
(request->till != 0) &&
- (as_reply->enc_part2->times.renew_till > request->till))
+ ts_after(as_reply->enc_part2->times.renew_till, request->till))
) {
return KRB5_KDCREP_MODIFIED;
}
if (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) {
- time_offset = as_reply->enc_part2->times.authtime - time_now;
+ time_offset = ts_delta(as_reply->enc_part2->times.authtime, time_now);
retval = krb5_set_time_offsets(context, time_offset, 0);
if (retval)
return retval;
} else {
if ((request->from == 0) &&
- (labs(as_reply->enc_part2->times.starttime - time_now)
- > context->clockskew))
+ !ts_within(as_reply->enc_part2->times.starttime, time_now,
+ context->clockskew))
return (KRB5_KDCREP_SKEW);
}
return 0;
@@ -583,7 +565,7 @@ krb5_init_creds_free(krb5_context context,
k5_response_items_free(ctx->rctx.items);
free(ctx->in_tkt_service);
zapfree(ctx->gakpw.storage.data, ctx->gakpw.storage.length);
- k5_preauth_request_context_fini(context);
+ k5_preauth_request_context_fini(context, ctx);
krb5_free_error(context, ctx->err_reply);
krb5_free_pa_data(context, ctx->err_padata);
krb5_free_cred_contents(context, &ctx->cred);
@@ -593,7 +575,9 @@ krb5_init_creds_free(krb5_context context,
krb5_free_data(context, ctx->inner_request_body);
krb5_free_data(context, ctx->encoded_previous_request);
krb5int_fast_free_state(context, ctx->fast_state);
- krb5_free_pa_data(context, ctx->preauth_to_use);
+ krb5_free_pa_data(context, ctx->optimistic_padata);
+ krb5_free_pa_data(context, ctx->method_padata);
+ krb5_free_pa_data(context, ctx->more_padata);
krb5_free_data_contents(context, &ctx->salt);
krb5_free_data_contents(context, &ctx->s2kparams);
krb5_free_keyblock_contents(context, &ctx->as_key);
@@ -760,23 +744,6 @@ k5_init_creds_current_time(krb5_context context, krb5_init_creds_context ctx,
}
}
-/* Choose a random nonce for ctx->request. */
-static krb5_error_code
-pick_nonce(krb5_context context, krb5_init_creds_context ctx)
-{
- krb5_error_code code = 0;
- unsigned char random_buf[4];
- krb5_data random_data = make_data(random_buf, 4);
-
- /* We incorrectly encode this as signed, so make sure we use an unsigned
- * value to avoid interoperability issues. */
- code = krb5_c_random_make_octets(context, &random_data);
- if (code != 0)
- return code;
- ctx->request->nonce = 0x7fffffff & load_32_n(random_buf);
- return 0;
-}
-
/* Set the timestamps for ctx->request based on the desired lifetimes. */
static krb5_error_code
set_request_times(krb5_context context, krb5_init_creds_context ctx)
@@ -790,16 +757,16 @@ set_request_times(krb5_context context, krb5_init_creds_context ctx)
return code;
/* Omit request start time unless the caller explicitly asked for one. */
- from = krb5int_addint32(now, ctx->start_time);
+ from = ts_incr(now, ctx->start_time);
if (ctx->start_time != 0)
ctx->request->from = from;
- ctx->request->till = krb5int_addint32(from, ctx->tkt_life);
+ ctx->request->till = ts_incr(from, ctx->tkt_life);
if (ctx->renew_life > 0) {
/* Don't ask for a smaller renewable time than the lifetime. */
- ctx->request->rtime = krb5int_addint32(from, ctx->renew_life);
- if (ctx->request->rtime < ctx->request->till)
+ ctx->request->rtime = ts_incr(from, ctx->renew_life);
+ if (ts_after(ctx->request->till, ctx->request->rtime))
ctx->request->rtime = ctx->request->till;
ctx->request->kdc_options &= ~KDC_OPT_RENEWABLE_OK;
} else {
@@ -809,6 +776,31 @@ set_request_times(krb5_context context, krb5_init_creds_context ctx)
return 0;
}
+static void
+read_allowed_preauth_type(krb5_context context, krb5_init_creds_context ctx)
+{
+ krb5_error_code ret;
+ krb5_data config;
+ char *tmp, *p;
+ krb5_ccache in_ccache = k5_gic_opt_get_in_ccache(ctx->opt);
+
+ ctx->allowed_preauth_type = KRB5_PADATA_NONE;
+ if (in_ccache == NULL)
+ return;
+ memset(&config, 0, sizeof(config));
+ if (krb5_cc_get_config(context, in_ccache, ctx->request->server,
+ KRB5_CC_CONF_PA_TYPE, &config) != 0)
+ return;
+ tmp = k5memdup0(config.data, config.length, &ret);
+ krb5_free_data_contents(context, &config);
+ if (tmp == NULL)
+ return;
+ ctx->allowed_preauth_type = strtol(tmp, &p, 10);
+ if (p == NULL || *p != '\0')
+ ctx->allowed_preauth_type = KRB5_PADATA_NONE;
+ free(tmp);
+}
+
/**
* Throw away any pre-authentication realm state and begin with a
* unauthenticated or optimistically authenticated request. If fast_upgrade is
@@ -820,11 +812,15 @@ restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
{
krb5_error_code code = 0;
- krb5_free_pa_data(context, ctx->preauth_to_use);
+ krb5_free_pa_data(context, ctx->optimistic_padata);
+ krb5_free_pa_data(context, ctx->method_padata);
+ krb5_free_pa_data(context, ctx->more_padata);
krb5_free_pa_data(context, ctx->err_padata);
krb5_free_error(context, ctx->err_reply);
- ctx->preauth_to_use = ctx->err_padata = NULL;
+ ctx->optimistic_padata = ctx->method_padata = ctx->more_padata = NULL;
+ ctx->err_padata = NULL;
ctx->err_reply = NULL;
+ ctx->selected_preauth_type = KRB5_PADATA_NONE;
krb5int_fast_free_state(context, ctx->fast_state);
ctx->fast_state = NULL;
@@ -834,14 +830,14 @@ restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
if (fast_upgrade)
ctx->fast_state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
- k5_preauth_request_context_fini(context);
- k5_preauth_request_context_init(context);
+ k5_preauth_request_context_fini(context, ctx);
+ k5_preauth_request_context_init(context, ctx);
krb5_free_data(context, ctx->outer_request_body);
ctx->outer_request_body = NULL;
if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
code = make_preauth_list(context, ctx->opt->preauth_list,
ctx->opt->preauth_list_length,
- &ctx->preauth_to_use);
+ &ctx->optimistic_padata);
if (code)
goto cleanup;
}
@@ -867,6 +863,11 @@ restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
&ctx->outer_request_body);
if (code != 0)
goto cleanup;
+
+ /* Read the allowed preauth type for this server principal from the input
+ * ccache, if the application supplied one. */
+ read_allowed_preauth_type(context, ctx);
+
cleanup:
return code;
}
@@ -1172,31 +1173,6 @@ init_creds_validate_reply(krb5_context context,
return 0;
}
-static void
-read_allowed_preauth_type(krb5_context context, krb5_init_creds_context ctx)
-{
- krb5_error_code ret;
- krb5_data config;
- char *tmp, *p;
- krb5_ccache in_ccache = k5_gic_opt_get_in_ccache(ctx->opt);
-
- ctx->allowed_preauth_type = KRB5_PADATA_NONE;
- if (in_ccache == NULL)
- return;
- memset(&config, 0, sizeof(config));
- if (krb5_cc_get_config(context, in_ccache, ctx->request->server,
- KRB5_CC_CONF_PA_TYPE, &config) != 0)
- return;
- tmp = k5memdup0(config.data, config.length, &ret);
- krb5_free_data_contents(context, &config);
- if (tmp == NULL)
- return;
- ctx->allowed_preauth_type = strtol(tmp, &p, 10);
- if (p == NULL || *p != '\0')
- ctx->allowed_preauth_type = KRB5_PADATA_NONE;
- free(tmp);
-}
-
static krb5_error_code
save_selected_preauth_type(krb5_context context, krb5_ccache ccache,
krb5_init_creds_context ctx)
@@ -1313,6 +1289,9 @@ init_creds_step_request(krb5_context context,
krb5_data *out)
{
krb5_error_code code;
+ krb5_preauthtype pa_type;
+ struct errinfo save = EMPTY_ERRINFO;
+ uint32_t rcode = (ctx->err_reply == NULL) ? 0 : ctx->err_reply->error;
if (ctx->loopcount >= MAX_IN_TKT_LOOPS) {
code = KRB5_GET_IN_TKT_LOOP;
@@ -1320,7 +1299,7 @@ init_creds_step_request(krb5_context context,
}
/* RFC 6113 requires a new nonce for the inner request on each try. */
- code = pick_nonce(context, ctx);
+ code = k5_generate_nonce(context, &ctx->request->nonce);
if (code != 0)
goto cleanup;
@@ -1335,11 +1314,6 @@ init_creds_step_request(krb5_context context,
if (code)
goto cleanup;
- /* Read the allowed patype for this server principal from the in_ccache,
- * if the application supplied one. */
- read_allowed_preauth_type(context, ctx);
- ctx->selected_preauth_type = KRB5_PADATA_NONE;
-
/*
* Read cached preauth configuration data for this server principal from
* the in_ccache, if the application supplied one, and delete any that was
@@ -1348,32 +1322,65 @@ init_creds_step_request(krb5_context context,
read_cc_config_in_data(context, ctx);
clear_cc_config_out_data(context, ctx);
- if (ctx->err_reply == NULL) {
- /* Either our first attempt, or retrying after KDC_ERR_PREAUTH_REQUIRED
- * or KDC_ERR_MORE_PREAUTH_DATA_REQUIRED. */
- code = k5_preauth(context, ctx, ctx->preauth_to_use,
- ctx->preauth_required, &ctx->request->padata,
- &ctx->selected_preauth_type);
- if (code != 0)
- goto cleanup;
- } else {
- if (ctx->preauth_to_use != NULL) {
- /*
- * Retry after an error other than PREAUTH_NEEDED,
- * using ctx->err_padata to figure out what to change.
- */
- code = k5_preauth_tryagain(context, ctx, ctx->preauth_to_use,
- &ctx->request->padata);
- } else {
- /* No preauth supplied, so can't query the plugins. */
- code = KRB5KRB_ERR_GENERIC;
+ ctx->request->padata = NULL;
+ if (ctx->optimistic_padata != NULL) {
+ /* Our first attempt, using an optimistic padata list. */
+ TRACE_INIT_CREDS_PREAUTH_OPTIMISTIC(context);
+ code = k5_preauth(context, ctx, ctx->optimistic_padata, TRUE,
+ &ctx->request->padata, &ctx->selected_preauth_type);
+ krb5_free_pa_data(context, ctx->optimistic_padata);
+ ctx->optimistic_padata = NULL;
+ if (code) {
+ /* Make an unauthenticated request, and possibly try again using
+ * the same mechanisms as we tried optimistically. */
+ k5_reset_preauth_types_tried(ctx);
+ krb5_clear_error_message(context);
+ code = 0;
}
- if (code != 0) {
- /* couldn't come up with anything better */
+ } if (ctx->more_padata != NULL) {
+ /* Continuing after KDC_ERR_MORE_PREAUTH_DATA_REQUIRED. */
+ TRACE_INIT_CREDS_PREAUTH_MORE(context, ctx->selected_preauth_type);
+ code = k5_preauth(context, ctx, ctx->more_padata, TRUE,
+ &ctx->request->padata, &pa_type);
+ } else if (rcode == KDC_ERR_PREAUTH_FAILED) {
+ /* Report the KDC-side failure code if we can't try another mech. */
+ code = KRB5KDC_ERR_PREAUTH_FAILED;
+ } else if (rcode && rcode != KDC_ERR_PREAUTH_REQUIRED) {
+ /* Retrying after an error (possibly mechanism-specific), using error
+ * padata to figure out what to change. */
+ TRACE_INIT_CREDS_PREAUTH_TRYAGAIN(context, ctx->err_reply->error,
+ ctx->selected_preauth_type);
+ code = k5_preauth_tryagain(context, ctx, ctx->selected_preauth_type,
+ ctx->err_reply, ctx->err_padata,
+ &ctx->request->padata);
+ if (code) {
+ krb5_clear_error_message(context);
code = ctx->err_reply->error + ERROR_TABLE_BASE_krb5;
+ }
+ }
+ /* Don't continue after a keyboard interrupt. */
+ if (code == KRB5_LIBOS_PWDINTR)
+ goto cleanup;
+ if (code) {
+ /* See if we can try a different preauth mech before giving up. */
+ k5_save_ctx_error(context, code, &save);
+ ctx->selected_preauth_type = KRB5_PADATA_NONE;
+ }
+
+ if (ctx->request->padata == NULL && ctx->method_padata != NULL) {
+ /* Retrying after KDC_ERR_PREAUTH_REQUIRED, or trying again with a
+ * different mechanism after a failure. */
+ TRACE_INIT_CREDS_PREAUTH(context);
+ code = k5_preauth(context, ctx, ctx->method_padata, TRUE,
+ &ctx->request->padata, &ctx->selected_preauth_type);
+ if (code) {
+ if (save.code != 0)
+ code = k5_restore_ctx_error(context, &save);
goto cleanup;
}
}
+ if (ctx->request->padata == NULL)
+ TRACE_INIT_CREDS_PREAUTH_NONE(context);
/* Remember when we sent this request (after any preauth delay). */
ctx->request_time = time(NULL);
@@ -1382,8 +1389,6 @@ init_creds_step_request(krb5_context context,
krb5_free_data(context, ctx->encoded_previous_request);
ctx->encoded_previous_request = NULL;
}
- if (ctx->request->padata)
- ctx->sent_nontrivial_preauth = TRUE;
if (ctx->enc_pa_rep_permitted) {
code = add_padata(&ctx->request->padata, KRB5_ENCPADATA_REQ_ENC_PA_REP,
NULL, 0);
@@ -1411,6 +1416,7 @@ init_creds_step_request(krb5_context context,
cleanup:
krb5_free_pa_data(context, ctx->request->padata);
ctx->request->padata = NULL;
+ k5_clear_error(&save);
return code;
}
@@ -1438,7 +1444,7 @@ note_req_timestamp(krb5_context context, krb5_init_creds_context ctx,
if (k5_time_with_offset(0, 0, &now, &usec) != 0)
return;
- ctx->pa_offset = kdc_time - now;
+ ctx->pa_offset = ts_delta(kdc_time, now);
ctx->pa_offset_usec = kdc_usec - usec;
ctx->pa_offset_state = (ctx->fast_state->armor_key != NULL) ?
AUTH_OFFSET : UNAUTH_OFFSET;
@@ -1463,6 +1469,18 @@ is_referral(krb5_context context, krb5_error *err, krb5_principal client)
return !krb5_realm_compare(context, err->client, client);
}
+/* Transfer error padata to method data in ctx and sort it according to
+ * configuration. */
+static krb5_error_code
+accept_method_data(krb5_context context, krb5_init_creds_context ctx)
+{
+ krb5_free_pa_data(context, ctx->method_padata);
+ ctx->method_padata = ctx->err_padata;
+ ctx->err_padata = NULL;
+ return sort_krb5_padata_sequence(context, &ctx->request->client->realm,
+ ctx->method_padata);
+}
+
static krb5_error_code
init_creds_step_reply(krb5_context context,
krb5_init_creds_context ctx,
@@ -1492,8 +1510,9 @@ init_creds_step_reply(krb5_context context,
ctx->request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL;
if (ctx->err_reply != NULL) {
+ krb5_free_pa_data(context, ctx->more_padata);
krb5_free_pa_data(context, ctx->err_padata);
- ctx->err_padata = NULL;
+ ctx->more_padata = ctx->err_padata = NULL;
code = krb5int_fast_process_error(context, ctx->fast_state,
&ctx->err_reply, &ctx->err_padata,
&retry);
@@ -1508,7 +1527,7 @@ init_creds_step_reply(krb5_context context,
ctx->restarted = TRUE;
code = restart_init_creds_loop(context, ctx, TRUE);
} else if (!ctx->restarted && reply_code == KDC_ERR_PREAUTH_FAILED &&
- !ctx->sent_nontrivial_preauth) {
+ ctx->selected_preauth_type == KRB5_PADATA_NONE) {
/* The KDC didn't like our informational padata (probably a pre-1.7
* MIT krb5 KDC). Retry without it. */
ctx->enc_pa_rep_permitted = FALSE;
@@ -1519,23 +1538,30 @@ init_creds_step_reply(krb5_context context,
* FAST upgrade. */
ctx->restarted = FALSE;
code = restart_init_creds_loop(context, ctx, FALSE);
- } else if ((reply_code == KDC_ERR_MORE_PREAUTH_DATA_REQUIRED ||
- reply_code == KDC_ERR_PREAUTH_REQUIRED) && retry) {
- /* reset the list of preauth types to try */
- k5_reset_preauth_types_tried(context);
- krb5_free_pa_data(context, ctx->preauth_to_use);
- ctx->preauth_to_use = ctx->err_padata;
- ctx->err_padata = NULL;
+ } else if (reply_code == KDC_ERR_PREAUTH_REQUIRED && retry) {
note_req_timestamp(context, ctx, ctx->err_reply->stime,
ctx->err_reply->susec);
- /* This will trigger a new call to k5_preauth(). */
- krb5_free_error(context, ctx->err_reply);
- ctx->err_reply = NULL;
- code = sort_krb5_padata_sequence(context,
- &ctx->request->client->realm,
- ctx->preauth_to_use);
- ctx->preauth_required = TRUE;
-
+ code = accept_method_data(context, ctx);
+ } else if (reply_code == KDC_ERR_PREAUTH_FAILED && retry) {
+ note_req_timestamp(context, ctx, ctx->err_reply->stime,
+ ctx->err_reply->susec);
+ if (ctx->method_padata == NULL) {
+ /* Optimistic preauth failed on the KDC. Allow all mechanisms
+ * to be tried again using method data. */
+ k5_reset_preauth_types_tried(ctx);
+ } else {
+ /* Don't try again with the mechanism that failed. */
+ code = k5_preauth_note_failed(ctx, ctx->selected_preauth_type);
+ if (code)
+ goto cleanup;
+ }
+ ctx->selected_preauth_type = KRB5_PADATA_NONE;
+ /* Accept or update method data if the KDC sent it. */
+ if (ctx->err_padata != NULL)
+ code = accept_method_data(context, ctx);
+ } else if (reply_code == KDC_ERR_MORE_PREAUTH_DATA_REQUIRED && retry) {
+ ctx->more_padata = ctx->err_padata;
+ ctx->err_padata = NULL;
} else if (canon_flag && is_referral(context, ctx->err_reply,
ctx->request->client)) {
TRACE_INIT_CREDS_REFERRAL(context, &ctx->err_reply->client->realm);
@@ -1548,14 +1574,13 @@ init_creds_step_reply(krb5_context context,
goto cleanup;
/* Reset per-realm negotiation state. */
ctx->restarted = FALSE;
- ctx->sent_nontrivial_preauth = FALSE;
ctx->enc_pa_rep_permitted = TRUE;
code = restart_init_creds_loop(context, ctx, FALSE);
} else {
- if (retry) {
+ if (retry && ctx->selected_preauth_type != KRB5_PADATA_NONE) {
code = 0;
} else {
- /* error + no hints = give up */
+ /* error + no hints (or no preauth mech) = give up */
code = (krb5_error_code)reply_code + ERROR_TABLE_BASE_krb5;
}
}
@@ -1573,7 +1598,6 @@ init_creds_step_reply(krb5_context context,
goto cleanup;
/* process any preauth data in the as_reply */
- k5_reset_preauth_types_tried(context);
code = krb5int_fast_process_response(context, ctx->fast_state,
ctx->reply, &strengthen_key);
if (code != 0)
@@ -1658,7 +1682,7 @@ init_creds_step_reply(krb5_context context,
k5_prependmsg(context, code, _("Failed to store credentials"));
}
- k5_preauth_request_context_fini(context);
+ k5_preauth_request_context_fini(context, ctx);
/* success */
ctx->complete = TRUE;
@@ -1685,7 +1709,7 @@ krb5_init_creds_step(krb5_context context,
krb5_data *realm,
unsigned int *flags)
{
- krb5_error_code code = 0, code2;
+ krb5_error_code code, code2;
*flags = 0;
@@ -1698,6 +1722,10 @@ krb5_init_creds_step(krb5_context context,
if (ctx->complete)
return EINVAL;
+ code = k5_preauth_check_context(context, ctx);
+ if (code)
+ return code;
+
if (in->length != 0) {
code = init_creds_step_reply(context, ctx, in);
if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG) {
@@ -1806,7 +1834,8 @@ k5_populate_gic_opt(krb5_context context, krb5_get_init_creds_opt **out,
krb5_creds *creds)
{
int i;
- krb5_int32 starttime;
+ krb5_timestamp starttime;
+ krb5_deltat lifetime;
krb5_get_init_creds_opt *opt;
krb5_error_code retval;
@@ -1838,7 +1867,8 @@ k5_populate_gic_opt(krb5_context context, krb5_get_init_creds_opt **out,
if (retval)
goto cleanup;
if (creds->times.starttime) starttime = creds->times.starttime;
- krb5_get_init_creds_opt_set_tkt_life(opt, creds->times.endtime - starttime);
+ lifetime = ts_delta(creds->times.endtime, starttime);
+ krb5_get_init_creds_opt_set_tkt_life(opt, lifetime);
}
*out = opt;
return 0;