From f149b9cc1e0f8c6e7cca2ee0a5fd8feff7deaf58 Mon Sep 17 00:00:00 2001 From: Isaac Boukris Date: Mon, 1 Feb 2016 18:08:24 +0200 Subject: [PATCH] Use cached S4U2Proxy tickets in GSSAPI Ticket #7047 allowed credentials obtain using S4U2Proxy through GSSAPI to be cached, but doesn't actually use the cached credentials. Modify get_credentials() to check the cache for the desired client name first, then to make an S4U2Proxy request if we don't find it. Test this change by adding code to t_s4u.c to repeat the constrained delegation request and verify that only three tickets are present in the cache. [ghudson@mit.edu: squash commits; commit message rewrite; minor style edits; changed test code to use gss_store_cred_into() to avoid the need to pick a principal to initialize the ccache with] ticket: 8372 (new) --- src/lib/gssapi/krb5/init_sec_context.c | 71 ++++++++++++++------------ src/tests/gssapi/t_s4u.c | 66 ++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 34 deletions(-) diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c index 4d05d782b2..70f7955ae1 100644 --- a/src/lib/gssapi/krb5/init_sec_context.c +++ b/src/lib/gssapi/krb5/init_sec_context.c @@ -127,7 +127,7 @@ static krb5_error_code get_credentials(context, cred, server, now, 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; @@ -139,37 +139,7 @@ static krb5_error_code get_credentials(context, cred, server, now, 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; @@ -188,12 +158,45 @@ static krb5_error_code get_credentials(context, cred, server, now, 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; diff --git a/src/tests/gssapi/t_s4u.c b/src/tests/gssapi/t_s4u.c index c33560f8fb..5bc1e4470b 100644 --- a/src/tests/gssapi/t_s4u.c +++ b/src/tests/gssapi/t_s4u.c @@ -122,6 +122,55 @@ init_accept_sec_context(gss_cred_id_t claimant_cred_handle, (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, @@ -162,9 +211,26 @@ constrained_delegate(gss_OID_set desired_mechs, gss_name_t target, &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 -- 2.47.2