#define TRACE_INIT_CREDS_GAK(c, salt, s2kparams) \
TRACE(c, "Getting AS key, salt \"{data}\", params \"{data}\"", \
salt, s2kparams)
+#define TRACE_INIT_CREDS_IDENTIFIED_REALM(c, realm) \
+ TRACE(c, "Identified realm of client principal as {data}", realm)
#define TRACE_INIT_CREDS_KEYTAB_LOOKUP(c, etypes) \
TRACE(c, "Looked up etypes in keytab: {etypes}", etypes)
#define TRACE_INIT_CREDS_KEYTAB_LOOKUP_FAILED(c, code) \
if (code)
goto cleanup;
+ if (ctx->subject_cert != NULL) {
+ code = add_padata(&ctx->request->padata, KRB5_PADATA_S4U_X509_USER,
+ ctx->subject_cert->data, ctx->subject_cert->length);
+ if (code)
+ return code;
+ }
+
code = maybe_add_pac_request(context, ctx);
if (code)
goto cleanup;
* FAST upgrade. */
ctx->restarted = FALSE;
code = restart_init_creds_loop(context, ctx, FALSE);
+ } else if (ctx->identify_realm &&
+ (reply_code == KDC_ERR_PREAUTH_REQUIRED ||
+ reply_code == KDC_ERR_KEY_EXP)) {
+ /* The client exists in this realm; we can stop. */
+ ctx->complete = TRUE;
+ goto cleanup;
} else if (reply_code == KDC_ERR_PREAUTH_REQUIRED && retry) {
note_req_timestamp(context, ctx, ctx->err_reply->stime,
ctx->err_reply->susec);
if (code != 0)
goto cleanup;
+ if (ctx->identify_realm) {
+ /* Just getting a reply means the client exists in this realm. */
+ ctx->complete = TRUE;
+ goto cleanup;
+ }
+
code = sort_krb5_padata_sequence(context, &ctx->request->client->realm,
ctx->reply->padata);
if (code != 0)
return code;
}
+krb5_error_code
+k5_identify_realm(krb5_context context, krb5_principal client,
+ const krb5_data *subject_cert, krb5_principal *client_out)
+{
+ krb5_error_code ret;
+ krb5_get_init_creds_opt *opts = NULL;
+ krb5_init_creds_context ctx = NULL;
+ int use_master = 0;
+
+ *client_out = NULL;
+
+ ret = krb5_get_init_creds_opt_alloc(context, &opts);
+ if (ret)
+ goto cleanup;
+ krb5_get_init_creds_opt_set_tkt_life(opts, 15);
+ krb5_get_init_creds_opt_set_renew_life(opts, 0);
+ krb5_get_init_creds_opt_set_forwardable(opts, 0);
+ krb5_get_init_creds_opt_set_proxiable(opts, 0);
+ krb5_get_init_creds_opt_set_canonicalize(opts, 1);
+
+ ret = krb5_init_creds_init(context, client, NULL, NULL, 0, opts, &ctx);
+ if (ret)
+ goto cleanup;
+
+ ctx->identify_realm = TRUE;
+ ctx->subject_cert = subject_cert;
+
+ ret = k5_init_creds_get(context, ctx, &use_master);
+ if (ret)
+ goto cleanup;
+
+ TRACE_INIT_CREDS_IDENTIFIED_REALM(context, &ctx->request->client->realm);
+ ret = krb5_copy_principal(context, ctx->request->client, client_out);
+
+cleanup:
+ krb5_get_init_creds_opt_free(context, opts);
+ krb5_init_creds_free(context, ctx);
+ return ret;
+}
+
krb5_error_code
k5_populate_gic_opt(krb5_context context, krb5_get_init_creds_opt **out,
krb5_flags options, krb5_address *const *addrs,
struct _krb5_init_creds_context {
krb5_get_init_creds_opt *opt;
krb5_get_init_creds_opt opt_storage;
+ krb5_boolean identify_realm;
+ const krb5_data *subject_cert;
char *in_tkt_service;
krb5_prompter_fct prompter;
void *prompter_data;
get_as_key_fn gak, void *gak_data, int *master,
krb5_kdc_rep **as_reply);
+/*
+ * Make AS requests with the canonicalize flag set, stopping when we get a
+ * message indicating which realm the client principal is in. Set *client_out
+ * to a copy of client with the canonical realm. If subject_cert is non-null,
+ * include PA_S4U_X509_USER pa-data with the subject certificate each request.
+ * (See [MS-SFU] 3.1.5.1.1.1 and 3.1.5.1.1.2.)
+ */
+krb5_error_code
+k5_identify_realm(krb5_context context, krb5_principal client,
+ const krb5_data *subject_cert, krb5_principal *client_out);
+
krb5_error_code
k5_populate_gic_opt(krb5_context context, krb5_get_init_creds_opt **opt,
krb5_flags options, krb5_address *const *addrs,
return ENOMEM;
}
-static krb5_error_code
-add_s4u_x509_user_padata(krb5_context context, krb5_s4u_userid *userid,
- krb5_principal client, krb5_pa_data ***out_pa_list,
- int *out_pa_list_size)
-{
- krb5_pa_data *s4u_padata;
- krb5_error_code code;
- krb5_principal client_copy;
-
- if (userid == NULL)
- return EINVAL;
- code = krb5_copy_principal(context, client, &client_copy);
- if (code != 0)
- return code;
- krb5_free_principal(context, userid->user);
- userid->user = client_copy;
-
- if (userid->subject_cert.length != 0) {
- s4u_padata = malloc(sizeof(*s4u_padata));
- if (s4u_padata == NULL)
- return ENOMEM;
-
- s4u_padata->magic = KV5M_PA_DATA;
- s4u_padata->pa_type = KRB5_PADATA_S4U_X509_USER;
- s4u_padata->contents = k5memdup(userid->subject_cert.data,
- userid->subject_cert.length, &code);
- if (s4u_padata->contents == NULL) {
- free(s4u_padata);
- return code;
- }
- s4u_padata->length = userid->subject_cert.length;
-
- code = grow_pa_list(out_pa_list, out_pa_list_size, &s4u_padata, 1);
- if (code) {
- free(s4u_padata->contents);
- free(s4u_padata);
- return code;
- }
- }
-
- return 0;
-}
-
/*
* If the module for pa_type can adjust its AS_REQ data using the contents of
* err and err_padata, return 0 with *padata_out set to a padata list for the
*padata_out = NULL;
*pa_type_out = KRB5_PADATA_NONE;
- if (in_padata == NULL)
+ /* We should never invoke preauth modules when identifying the realm. */
+ if (in_padata == NULL || ctx->identify_realm)
return 0;
TRACE_PREAUTH_INPUT(context, in_padata);
if (ret)
goto error;
- if (krb5int_find_pa_data(context, in_padata,
- KRB5_PADATA_S4U_X509_USER) != NULL) {
- /* Fulfill a private contract with krb5_get_credentials_for_user. */
- ret = add_s4u_x509_user_padata(context, ctx->gak_data,
- ctx->request->client,
- &out_pa_list, &out_pa_list_size);
- if (ret)
- goto error;
- }
-
/* If we can't initialize the preauth context, stop with what we have. */
k5_init_preauth_context(context);
if (context->preauth_context == NULL) {
* itself on behalf of an arbitrary principal.
*/
-static krb5_error_code
-krb5_get_as_key_noop(
- krb5_context context,
- krb5_principal client,
- krb5_enctype etype,
- krb5_prompter_fct prompter,
- void *prompter_data,
- krb5_data *salt,
- krb5_data *params,
- krb5_keyblock *as_key,
- void *gak_data,
- k5_response_items *ritems)
-{
- /* force a hard error, we don't actually have the key */
- return KRB5_PREAUTH_FAILED;
-}
-
static krb5_error_code
s4u_identify_user(krb5_context context,
krb5_creds *in_creds,
krb5_data *subject_cert,
krb5_principal *canon_user)
{
- krb5_error_code code;
- krb5_preauthtype ptypes[1] = { KRB5_PADATA_S4U_X509_USER };
- krb5_creds creds;
- int use_master = 0;
- krb5_get_init_creds_opt *opts = NULL;
krb5_principal_data client;
- krb5_s4u_userid userid;
*canon_user = NULL;
canon_user);
}
- memset(&creds, 0, sizeof(creds));
-
- memset(&userid, 0, sizeof(userid));
- if (subject_cert != NULL)
- userid.subject_cert = *subject_cert;
-
- code = krb5_get_init_creds_opt_alloc(context, &opts);
- if (code != 0)
- goto cleanup;
- krb5_get_init_creds_opt_set_tkt_life(opts, 15);
- krb5_get_init_creds_opt_set_renew_life(opts, 0);
- krb5_get_init_creds_opt_set_forwardable(opts, 0);
- krb5_get_init_creds_opt_set_proxiable(opts, 0);
- krb5_get_init_creds_opt_set_canonicalize(opts, 1);
- krb5_get_init_creds_opt_set_preauth_list(opts, ptypes, 1);
-
if (in_creds->client != NULL) {
client = *in_creds->client;
client.realm = in_creds->server->realm;
client.type = KRB5_NT_ENTERPRISE_PRINCIPAL;
}
- code = k5_get_init_creds(context, &creds, &client, NULL, NULL, 0, NULL,
- opts, krb5_get_as_key_noop, &userid, &use_master,
- NULL);
- if (!code || code == KRB5_PREAUTH_FAILED || code == KRB5KDC_ERR_KEY_EXP) {
- *canon_user = userid.user;
- userid.user = NULL;
- code = 0;
- }
-
-cleanup:
- krb5_free_cred_contents(context, &creds);
- if (opts != NULL)
- krb5_get_init_creds_opt_free(context, opts);
- if (userid.user != NULL)
- krb5_free_principal(context, userid.user);
-
- return code;
+ return k5_identify_realm(context, &client, subject_cert, canon_user);
}
static krb5_error_code
# that we start at the server realm.
mark('cross-realm S4U2Self with enterprise name')
msgs = ('Getting initial credentials for enterprise\\@abc@SREALM',
- 'Processing preauth types: PA-FOR-X509-USER (130)',
'Sending unauthenticated request',
'/Realm not local to KDC',
'Following referral to realm UREALM',
- 'Processing preauth types: PA-FOR-X509-USER (130)',
'Sending unauthenticated request',
'/Additional pre-authentication required',
- '/Generic preauthentication failure',
+ 'Identified realm of client principal as UREALM',
'Getting credentials enterprise\\@abc@UREALM -> user@SREALM',
'TGS reply is for enterprise\@abc@UREALM -> user@SREALM')
r1.run(['./t_s4u', 'e:enterprise@abc@NOREALM', '-', r1.keytab],