krb5 commit: S4U2Proxy evidence tickets needn't be forwardable

Greg Hudson ghudson at mit.edu
Mon Sep 9 10:33:17 EDT 2019


https://github.com/krb5/krb5/commit/e131d339b81a22bfc91ab96990c3be9e7779200e
commit e131d339b81a22bfc91ab96990c3be9e7779200e
Author: Isaac Boukris <iboukris at gmail.com>
Date:   Tue Mar 12 21:59:55 2019 +0200

    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 at mit.edu: made minor style changes and commit message edits;
    updated documentation]
    
    ticket: 8479

 doc/appdev/gssapi.rst                    |   35 ++++++++++---------------
 src/clients/kvno/kvno.c                  |   40 ++++++++++++++---------------
 src/lib/gssapi/krb5/accept_sec_context.c |    3 +-
 src/lib/gssapi/krb5/init_sec_context.c   |    1 -
 src/lib/gssapi/krb5/s4u_gss_glue.c       |   14 ++--------
 src/lib/krb5/krb/s4u_creds.c             |   16 +++--------
 src/tests/gssapi/t_s4u.py                |   25 ++++++++----------
 7 files changed, 53 insertions(+), 81 deletions(-)

diff --git a/doc/appdev/gssapi.rst b/doc/appdev/gssapi.rst
index f574b60..66561fd 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 66ef491..2472c0c 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 439ae6a..c821cc8 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 6ce3ad1..b48a85e 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 10848c1..126ca97 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 71e2a08..8202fe9 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 63183aa..dc871ae 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.


More information about the cvs-krb5 mailing list