Skip to content

Commit

Permalink
S4U2Proxy evidence tickets needn't be forwardable
Browse files Browse the repository at this point in the history
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
  • Loading branch information
iboukris authored and greghudson committed Sep 9, 2019
1 parent c426ef2 commit e131d33
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 81 deletions.
35 changes: 14 additions & 21 deletions doc/appdev/gssapi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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::
Expand All @@ -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*
Expand Down
40 changes: 19 additions & 21 deletions src/clients/kvno/kvno.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 1 addition & 2 deletions src/lib/gssapi/krb5/accept_sec_context.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion src/lib/gssapi/krb5/init_sec_context.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
14 changes: 3 additions & 11 deletions src/lib/gssapi/krb5/s4u_gss_glue.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
16 changes: 5 additions & 11 deletions src/lib/krb5/krb/s4u_creds.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down
25 changes: 11 additions & 14 deletions src/tests/gssapi/t_s4u.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,20 @@
'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.
Expand Down

0 comments on commit e131d33

Please sign in to comment.