From: Isaac Boukris Date: Tue, 12 Mar 2019 19:59:55 +0000 (+0200) Subject: S4U2Proxy evidence tickets needn't be forwardable X-Git-Tag: krb5-1.18-beta1~68 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e131d339b81a22bfc91ab96990c3be9e7779200e;p=thirdparty%2Fkrb5.git S4U2Proxy evidence tickets needn't be forwardable With the introduction of resource-based constrained delegation, the absence of the forwardable flag no longer implies that a ticket cannot be used for constrained delegation requests. Instead, we should check in the PAC to see if the user is marked as sensitive, and error out in that case rather than making a failed request. But we don't always have access to the PAC and we currently do not have the code to retrieve this attribute from the PAC. Since krb5_get_credentials_for_proxy() no longer needs to look at the decrypted ticket, change kvno to not require a keytab for constrained delegation. [ghudson@mit.edu: made minor style changes and commit message edits; updated documentation] ticket: 8479 --- diff --git a/doc/appdev/gssapi.rst b/doc/appdev/gssapi.rst index f574b608b1..66561fd0e1 100644 --- a/doc/appdev/gssapi.rst +++ b/doc/appdev/gssapi.rst @@ -258,13 +258,13 @@ ticket-granting ticket, if the KDC is configured to allow it. To perform a constrained delegation operation, the intermediate service must submit to the KDC an "evidence ticket" from the client to -the intermediate service with the forwardable bit set. An evidence -ticket can be acquired when the client authenticates to the -intermediate service with Kerberos, or with an S4U2Self request if the -KDC allows it. The MIT krb5 GSSAPI library represents an evidence -ticket using a "proxy credential", which is a special kind of -gss_cred_id_t object whose underlying credential cache contains the -evidence ticket and a krbtgt ticket for the intermediate service. +the intermediate service. An evidence ticket can be acquired when the +client authenticates to the intermediate service with Kerberos, or +with an S4U2Self request if the KDC allows it. The MIT krb5 GSSAPI +library represents an evidence ticket using a "proxy credential", +which is a special kind of gss_cred_id_t object whose underlying +credential cache contains the evidence ticket and a krbtgt ticket for +the intermediate service. To acquire a proxy credential during client authentication, the service should first create an acceptor credential using the @@ -273,9 +273,9 @@ credential as the *acceptor_cred_handle* to gss_accept_sec_context_, and also pass a *delegated_cred_handle* output parameter to receive a proxy credential containing the evidence ticket. The output value of *delegated_cred_handle* may be a delegated ticket-granting ticket if -the client sent one, or a proxy credential if the client authenticated -with a forwardable service ticket, or **GSS_C_NO_CREDENTIAL** if -neither is the case. +the client sent one, or a proxy credential if not. If the library can +determine that the client's ticket is not a valid evidence ticket, it +will place **GSS_C_NO_CREDENTIAL** in *delegated_cred_handle*. To acquire a proxy credential using an S4U2Self request, the service can use the following GSSAPI extension:: @@ -296,17 +296,10 @@ request to the KDC for a ticket from *desired_name* to the intermediate service. Both *icred* and *desired_name* are required for this function; passing **GSS_C_NO_CREDENTIAL** or **GSS_C_NO_NAME** will cause the call to fail. *icred* must contain a -krbtgt ticket for the intermediate service. If the KDC returns a -forwardable ticket, the result of this operation is a proxy -credential; if it is not forwardable, the result is a regular -credential for *desired_name*. - -A recent KDC will usually allow any service to acquire a ticket from a -client to itself with an S4U2Self request, but the ticket will only be -forwardable if the service has a specific privilege. In the MIT krb5 -KDC, this privilege is determined by the **ok_to_auth_as_delegate** -bit on the intermediate service's principal entry, which can be -configured with :ref:`kadmin(1)`. +krbtgt ticket for the intermediate service. The result of this +operation is a proxy credential. (Prior to release 1.18, the result +of this operation may be a regular credential for *desired_name*, if +the KDC issues a non-forwardable ticket.) Once the intermediate service has a proxy credential, it can simply pass it to gss_init_sec_context_ as the *initiator_cred_handle* diff --git a/src/clients/kvno/kvno.c b/src/clients/kvno/kvno.c index 66ef4911a7..2472c0cfe7 100644 --- a/src/clients/kvno/kvno.c +++ b/src/clients/kvno/kvno.c @@ -148,11 +148,7 @@ main(int argc, char *argv[]) } if (proxy) { - if (keytab_name == NULL) { - fprintf(stderr, _("Option -P (constrained delegation) " - "requires keytab to be specified\n")); - xusage(); - } else if (!impersonate) { + if (!impersonate) { fprintf(stderr, _("Option -P (constrained delegation) requires " "option -I|-U|-F (protocol transition)\n")); xusage(); @@ -360,27 +356,29 @@ kvno(const char *name, krb5_ccache ccache, krb5_principal me, printf(_("%s: kvno = %d, keytab entry valid\n"), princ, ticket->enc_part.kvno); } - if (proxy) { - krb5_free_creds(context, out_creds); - out_creds = NULL; - - in_creds.client = ticket->enc_part2->client; - in_creds.server = server; - - ret = krb5_get_credentials_for_proxy(context, KRB5_GC_CANONICALIZE, - ccache, &in_creds, ticket, - &out_creds); - if (ret) { - com_err(prog, ret, _("%s: constrained delegation failed"), - princ); - goto cleanup; - } - } } else { if (!quiet) printf(_("%s: kvno = %d\n"), princ, ticket->enc_part.kvno); } + if (proxy) { + in_creds.client = out_creds->client; + out_creds->client = NULL; + krb5_free_creds(context, out_creds); + out_creds = NULL; + in_creds.server = server; + + ret = krb5_get_credentials_for_proxy(context, KRB5_GC_CANONICALIZE, + ccache, &in_creds, ticket, + &out_creds); + krb5_free_principal(context, in_creds.client); + if (ret) { + com_err(prog, ret, _("%s: constrained delegation failed"), + princ); + goto cleanup; + } + } + cleanup: krb5_free_principal(context, server); krb5_free_ticket(context, ticket); diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c index 439ae6aeb5..c821cc830d 100644 --- a/src/lib/gssapi/krb5/accept_sec_context.c +++ b/src/lib/gssapi/krb5/accept_sec_context.c @@ -945,8 +945,7 @@ kg_accept_krb5(minor_status, context_handle, if (delegated_cred_handle != NULL && deleg_cred == NULL && /* no unconstrained delegation */ - cred->usage == GSS_C_BOTH && - (ticket->enc_part2->flags & TKT_FLG_FORWARDABLE)) { + cred->usage == GSS_C_BOTH) { /* * Now, we always fabricate a delegated credentials handle * containing the service ticket to ourselves, which can be diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c index 6ce3ad1c0c..b48a85e248 100644 --- a/src/lib/gssapi/krb5/init_sec_context.c +++ b/src/lib/gssapi/krb5/init_sec_context.c @@ -196,7 +196,6 @@ static krb5_error_code get_credentials(context, cred, server, now, 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; diff --git a/src/lib/gssapi/krb5/s4u_gss_glue.c b/src/lib/gssapi/krb5/s4u_gss_glue.c index 10848c1df8..126ca973ca 100644 --- a/src/lib/gssapi/krb5/s4u_gss_glue.c +++ b/src/lib/gssapi/krb5/s4u_gss_glue.c @@ -261,17 +261,9 @@ kg_compose_deleg_cred(OM_uint32 *minor_status, if (code != 0) goto cleanup; - /* - * Only return a "proxy" credential for use with constrained - * delegation if the subject credentials are forwardable. - * Submitting non-forwardable credentials to the KDC for use - * with constrained delegation will only return an error. - */ - if (subject_creds->ticket_flags & TKT_FLG_FORWARDABLE) { - code = make_proxy_cred(context, cred, impersonator_cred); - if (code != 0) - goto cleanup; - } + code = make_proxy_cred(context, cred, impersonator_cred); + if (code != 0) + goto cleanup; code = krb5_cc_store_cred(context, cred->ccache, subject_creds); if (code != 0) diff --git a/src/lib/krb5/krb/s4u_creds.c b/src/lib/krb5/krb/s4u_creds.c index 71e2a08e62..8202fe9d3b 100644 --- a/src/lib/krb5/krb/s4u_creds.c +++ b/src/lib/krb5/krb/s4u_creds.c @@ -1150,27 +1150,22 @@ krb5_get_credentials_for_proxy(krb5_context context, *out_creds = NULL; - if (in_creds == NULL || in_creds->client == NULL || - evidence_tkt == NULL || evidence_tkt->enc_part2 == NULL) { + if (in_creds == NULL || in_creds->client == NULL || evidence_tkt == NULL) { code = EINVAL; goto cleanup; } /* * Caller should have set in_creds->client to match evidence - * ticket client + * ticket client. If we can, verify it before issuing the request. */ - if (!krb5_principal_compare(context, evidence_tkt->enc_part2->client, + if (evidence_tkt->enc_part2 != NULL && + !krb5_principal_compare(context, evidence_tkt->enc_part2->client, in_creds->client)) { code = EINVAL; goto cleanup; } - if ((evidence_tkt->enc_part2->flags & TKT_FLG_FORWARDABLE) == 0) { - code = KRB5_TKT_NOT_FORWARDABLE; - goto cleanup; - } - code = krb5int_construct_matching_creds(context, options, in_creds, &mcreds, &fields); if (code != 0) @@ -1213,8 +1208,7 @@ krb5_get_credentials_for_proxy(krb5_context context, * Check client name because we couldn't compare that inside * krb5_get_credentials() (enc_part2 is unavailable in clear) */ - if (!krb5_principal_compare(context, - evidence_tkt->enc_part2->client, + if (!krb5_principal_compare(context, in_creds->client, (*out_creds)->client)) { code = KRB5_KDCREP_MODIFIED; goto cleanup; diff --git a/src/tests/gssapi/t_s4u.py b/src/tests/gssapi/t_s4u.py index 63183aa688..dc871ae73e 100755 --- a/src/tests/gssapi/t_s4u.py +++ b/src/tests/gssapi/t_s4u.py @@ -47,23 +47,20 @@ if ('auth1: ' + realm.user_princ not in output or 'NOT_ALLOWED_TO_DELEGATE' not in output): fail('krb5 -> s4u2proxy (SPNEGO)') -# Try krb5 -> S4U2Proxy without forwardable user creds. This should -# result in no delegated credential being created by -# accept_sec_context. +# Try krb5 -> S4U2Proxy without forwardable user creds. realm.kinit(realm.user_princ, password('user'), ['-c', usercache]) -realm.run(['./t_s4u2proxy_krb5', usercache, storagecache, pservice1, - pservice1, pservice2], expected_msg='no credential delegated') +output = realm.run(['./t_s4u2proxy_krb5', usercache, storagecache, pservice1, + pservice1, pservice2], expected_code=1) +if ('auth1: ' + realm.user_princ not in output or + 'EVIDENCE_TKT_NOT_FORWARDABLE' not in output): + fail('krb5 -> s4u2proxy not-forwardable') -# Try S4U2Self. Ask for an S4U2Proxy step; this won't happen because +# Try S4U2Self. Ask for an S4U2Proxy step; this won't succeed because # service/1 isn't allowed to get a forwardable S4U2Self ticket. -output = realm.run(['./t_s4u', puser, pservice2]) -if ('Warning: no delegated cred handle' not in output or - 'Source name:\t' + realm.user_princ not in output): - fail('s4u2self') -output = realm.run(['./t_s4u', '--spnego', puser, pservice2]) -if ('Warning: no delegated cred handle' not in output or - 'Source name:\t' + realm.user_princ not in output): - fail('s4u2self (SPNEGO)') +realm.run(['./t_s4u', puser, pservice2], expected_code=1, + expected_msg='EVIDENCE_TKT_NOT_FORWARDABLE') +realm.run(['./t_s4u', '--spnego', puser, pservice2], expected_code=1, + expected_msg='EVIDENCE_TKT_NOT_FORWARDABLE') # Correct that problem and try again. As above, the S4U2Proxy step # won't actually succeed since we don't support that in DB2.