]> git.ipfire.org Git - thirdparty/krb5.git/commitdiff
S4U2Proxy evidence tickets needn't be forwardable
authorIsaac Boukris <iboukris@gmail.com>
Tue, 12 Mar 2019 19:59:55 +0000 (21:59 +0200)
committerGreg Hudson <ghudson@mit.edu>
Mon, 9 Sep 2019 04:04:33 +0000 (00:04 -0400)
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

doc/appdev/gssapi.rst
src/clients/kvno/kvno.c
src/lib/gssapi/krb5/accept_sec_context.c
src/lib/gssapi/krb5/init_sec_context.c
src/lib/gssapi/krb5/s4u_gss_glue.c
src/lib/krb5/krb/s4u_creds.c
src/tests/gssapi/t_s4u.py

index f574b608b14b00fd13364cd60675bf18e8f66129..66561fd0e168960a602cffd0aa17c5ad79e63e6f 100644 (file)
@@ -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*
index 66ef4911a76974f4ccfdf679f7afb9dfc8fd62da..2472c0cfe743c89785905a6d75c2d9cbcfcfb752 100644 (file)
@@ -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);
index 439ae6aeb5aa9195e68320519940cc501634c507..c821cc830dae1428532557fa2511cbc045ac5421 100644 (file)
@@ -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
index 6ce3ad1c0c931c2570dca627b018a14b4dbda556..b48a85e248772ebc7e6732ae9cc85a549ed2c036 100644 (file)
@@ -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;
index 10848c1df810c75c3dfae27771721b36768d118f..126ca973cae959c0fe639e8b3beebbee34793845 100644 (file)
@@ -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)
index 71e2a08e62513f25fb4929a3d93c0f7763b9a100..8202fe9d3b723cbc9b8d330d6b341e59049561d2 100644 (file)
@@ -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;
index 63183aa68819056b63c94b057237ed613320028d..dc871ae73e7a6ea9d2907845368e4e307028d185 100755 (executable)
@@ -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.