krb5_creds **out_creds;
{
krb5_error_code code;
- krb5_creds in_creds, evidence_creds, *result_creds = NULL;
+ krb5_creds in_creds, evidence_creds, mcreds, *result_creds = NULL;
krb5_flags flags = 0;
*out_creds = NULL;
assert(cred->name != NULL);
- /*
- * Do constrained delegation if we have proxy credentials and
- * we're not trying to get a ticket to ourselves (in which case
- * we can just use the S4U2Self or evidence ticket directly).
- */
- if (cred->impersonator &&
- !krb5_principal_compare(context, cred->impersonator, server->princ)) {
- krb5_creds mcreds;
-
- flags |= KRB5_GC_CANONICALIZE | KRB5_GC_CONSTRAINED_DELEGATION;
-
- memset(&mcreds, 0, sizeof(mcreds));
-
- mcreds.magic = KV5M_CREDS;
- mcreds.server = cred->impersonator;
- mcreds.client = cred->name->princ;
-
- code = krb5_cc_retrieve_cred(context, cred->ccache,
- KRB5_TC_MATCH_AUTHDATA, &mcreds,
- &evidence_creds);
- if (code)
- goto cleanup;
-
- assert(evidence_creds.ticket_flags & TKT_FLG_FORWARDABLE);
-
- in_creds.client = cred->impersonator;
- in_creds.second_ticket = evidence_creds.ticket;
- } else {
- in_creds.client = cred->name->princ;
- }
-
+ in_creds.client = cred->name->princ;
in_creds.server = server->princ;
in_creds.times.endtime = endtime;
in_creds.authdata = NULL;
goto cleanup;
}
- /* Don't go out over the network if we used IAKERB */
- if (cred->iakerb_mech)
+ /*
+ * For IAKERB or constrained delegation, only check the cache in this step.
+ * For IAKERB we will ask the server to make any necessary TGS requests;
+ * for constrained delegation we will adjust in_creds and make an S4U2Proxy
+ * request below if the cache lookup fails.
+ */
+ if (cred->impersonator != NULL || cred->iakerb_mech)
flags |= KRB5_GC_CACHED;
code = krb5_get_credentials(context, flags, cred->ccache,
&in_creds, &result_creds);
+
+ /*
+ * Try constrained delegation if we have proxy credentials, unless
+ * we are trying to get a ticket to ourselves (in which case we could
+ * just use the evidence ticket directly from cache).
+ */
+ if (code == KRB5_CC_NOTFOUND && cred->impersonator != NULL &&
+ !cred->iakerb_mech &&
+ !krb5_principal_compare(context, cred->impersonator, server->princ)) {
+
+ memset(&mcreds, 0, sizeof(mcreds));
+ mcreds.magic = KV5M_CREDS;
+ mcreds.server = cred->impersonator;
+ mcreds.client = cred->name->princ;
+ code = krb5_cc_retrieve_cred(context, cred->ccache,
+ KRB5_TC_MATCH_AUTHDATA, &mcreds,
+ &evidence_creds);
+ if (code)
+ goto cleanup;
+
+ assert(evidence_creds.ticket_flags & TKT_FLG_FORWARDABLE);
+ in_creds.client = cred->impersonator;
+ in_creds.second_ticket = evidence_creds.ticket;
+ flags = KRB5_GC_CANONICALIZE | KRB5_GC_CONSTRAINED_DELEGATION;
+ code = krb5_get_credentials(context, flags, cred->ccache,
+ &in_creds, &result_creds);
+ }
+
if (code)
goto cleanup;
(void)gss_delete_sec_context(&minor, &acceptor_context, NULL);
}
+static void
+check_ticket_count(gss_cred_id_t cred, int expected)
+{
+ krb5_error_code ret;
+ krb5_context context = NULL;
+ krb5_creds kcred;
+ krb5_cc_cursor cur;
+ krb5_ccache ccache;
+ int count = 0;
+ gss_key_value_set_desc store;
+ gss_key_value_element_desc elem;
+ OM_uint32 major, minor;
+ const char *ccname = "MEMORY:count";
+
+ store.count = 1;
+ store.elements = &elem;
+ elem.key = "ccache";
+ elem.value = ccname;
+ major = gss_store_cred_into(&minor, cred, GSS_C_INITIATE, &mech_krb5, 1, 0,
+ &store, NULL, NULL);
+ check_gsserr("gss_store_cred_into", major, minor);
+
+ ret = krb5_init_context(&context);
+ check_k5err(context, "krb5_init_context", ret);
+
+ ret = krb5_cc_resolve(context, ccname, &ccache);
+ check_k5err(context, "krb5_cc_resolve", ret);
+
+ ret = krb5_cc_start_seq_get(context, ccache, &cur);
+ check_k5err(context, "krb5_cc_start_seq_get", ret);
+
+ while (!krb5_cc_next_cred(context, ccache, &cur, &kcred)) {
+ if (!krb5_is_config_principal(context, kcred.server))
+ count++;
+ krb5_free_cred_contents(context, &kcred);
+ }
+
+ ret = krb5_cc_end_seq_get(context, ccache, &cur);
+ check_k5err(context, "krb5_cc_end_seq_get", ret);
+
+ if (expected != count) {
+ printf("Expected %d tickets but got %d\n", expected, count);
+ exit(1);
+ }
+
+ krb5_cc_destroy(context, ccache);
+ krb5_free_context(context);
+}
+
static void
constrained_delegate(gss_OID_set desired_mechs, gss_name_t target,
gss_cred_id_t delegated_cred_handle,
&time_rec);
check_gsserr("gss_init_sec_context", major, minor);
+ (void)gss_release_buffer(&minor, &token);
+ (void)gss_delete_sec_context(&minor, &initiator_context, NULL);
+
+ /* Ensure a second call does not acquire new ticket. */
+ major = gss_init_sec_context(&minor, delegated_cred_handle,
+ &initiator_context, target,
+ mechs ? &mechs->elements[0] : &mech_krb5,
+ GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
+ GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS,
+ GSS_C_NO_BUFFER, NULL, &token, NULL,
+ &time_rec);
+ check_gsserr("gss_init_sec_context", major, minor);
+
(void)gss_release_buffer(&minor, &token);
(void)gss_delete_sec_context(&minor, &initiator_context, NULL);
(void)gss_release_oid_set(&minor, &mechs);
+
+ /* We expect three tickets: our TGT, the evidence ticket, and the ticket to
+ * the target service. */
+ check_ticket_count(delegated_cred_handle, 3);
}
int