krb5 commit: Move S4U2Proxy client code to s4u_creds.c

Greg Hudson <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.cvs
Message-ID <[email protected]>
https://github.com/krb5/krb5/commit/2b29619aa27c2e63fea80cac60b5607a3fce972f
commit 2b29619aa27c2e63fea80cac60b5607a3fce972f
Author: Isaac Boukris <[email protected]>
Date:   Mon Jun 17 00:21:38 2019 +0300

    Move S4U2Proxy client code to s4u_creds.c
    
    Add an internal libkrb5 interface k5_get_proxy_cred_from_kdc(), which
    implements S4U2Proxy requests synchronously.  Call it from
    krb5_get_credentials() if constrained delegation is requested.
    
    [[email protected]: rewrote commit message; made style changes]
    
    ticket: 8479

 src/lib/krb5/krb/get_creds.c |   28 +++-------
 src/lib/krb5/krb/int-proto.h |   10 +++
 src/lib/krb5/krb/s4u_creds.c |  130 ++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 143 insertions(+), 25 deletions(-)

diff --git a/src/lib/krb5/krb/get_creds.c b/src/lib/krb5/krb/get_creds.c
index 0a04d68..e0a3b5c 100644
--- a/src/lib/krb5/krb/get_creds.c
+++ b/src/lib/krb5/krb/get_creds.c
@@ -332,8 +332,7 @@ make_request_for_service(krb5_context context, krb5_tkt_creds_context ctx,
     extra_options = ctx->req_kdcopt;
 
     /* Automatically set the enc-tkt-in-skey flag for user-to-user requests. */
-    if (ctx->in_creds->second_ticket.length != 0 &&
-        (extra_options & KDC_OPT_CNAME_IN_ADDL_TKT) == 0)
+    if (ctx->in_creds->second_ticket.length != 0)
         extra_options |= KDC_OPT_ENC_TKT_IN_SKEY;
 
     /* Set the canonicalize flag for referral requests. */
@@ -444,12 +443,6 @@ complete(krb5_context context, krb5_tkt_creds_context ctx)
         (void) krb5_cc_store_cred(context, ctx->ccache, ctx->reply_creds);
     }
 
-    /* If we were doing constrained delegation, make sure we got a forwardable
-     * ticket, or it won't work. */
-    if ((ctx->req_options & KRB5_GC_CONSTRAINED_DELEGATION)
-        && (ctx->reply_creds->ticket_flags & TKT_FLG_FORWARDABLE) == 0)
-        return KRB5_TKT_NOT_FORWARDABLE;
-
     ctx->state = STATE_COMPLETE;
     return 0;
 }
@@ -1023,11 +1016,6 @@ check_cache(krb5_context context, krb5_tkt_creds_context ctx)
     krb5_creds mcreds;
     krb5_flags fields;
 
-    /* For constrained delegation, the expected result is in second_ticket, so
-     * we can't really do a cache check here. */
-    if (ctx->req_options & KRB5_GC_CONSTRAINED_DELEGATION)
-        return (ctx->req_options & KRB5_GC_CACHED) ? KRB5_CC_NOTFOUND : 0;
-
     /* Perform the cache lookup. */
     code = krb5int_construct_matching_creds(context, ctx->req_options,
                                             ctx->in_creds, &mcreds, &fields);
@@ -1098,13 +1086,6 @@ krb5_tkt_creds_init(krb5_context context, krb5_ccache ccache,
         ctx->req_kdcopt |= KDC_OPT_FORWARDABLE;
     if (options & KRB5_GC_NO_TRANSIT_CHECK)
         ctx->req_kdcopt |= KDC_OPT_DISABLE_TRANSITED_CHECK;
-    if (options & KRB5_GC_CONSTRAINED_DELEGATION) {
-        if (options & KRB5_GC_USER_USER) {
-            code = EINVAL;
-            goto cleanup;
-        }
-        ctx->req_kdcopt |= KDC_OPT_FORWARDABLE | KDC_OPT_CNAME_IN_ADDL_TKT;
-    }
 
     ctx->state = STATE_BEGIN;
 
@@ -1284,6 +1265,13 @@ krb5_get_credentials(krb5_context context, krb5_flags options,
 
     *out_creds = NULL;
 
+    /* If S4U2Proxy is requested, use the synchronous implementation in
+     * s4u_creds.c. */
+    if (options & KRB5_GC_CONSTRAINED_DELEGATION) {
+        return k5_get_proxy_cred_from_kdc(context, options, ccache, in_creds,
+                                          out_creds);
+    }
+
     /* Allocate a container. */
     ncreds = k5alloc(sizeof(*ncreds), &code);
     if (ncreds == NULL)
diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h
index d45adab..fe61beb 100644
--- a/src/lib/krb5/krb/int-proto.h
+++ b/src/lib/krb5/krb/int-proto.h
@@ -376,4 +376,14 @@ krb5_error_code
 k5_get_etype_info(krb5_context context, krb5_init_creds_context ctx,
                   krb5_pa_data **padata);
 
+/*
+ * Make an S4U2Proxy (constrained delegation) request.  in_creds->client is the
+ * impersonator principal, and in_creds->second_ticket is the evidence
+ * ticket.
+ */
+krb5_error_code
+k5_get_proxy_cred_from_kdc(krb5_context context, krb5_flags options,
+                           krb5_ccache ccache, krb5_creds *in_creds,
+                           krb5_creds **out_creds);
+
 #endif /* KRB5_INT_FUNC_PROTO__ */
diff --git a/src/lib/krb5/krb/s4u_creds.c b/src/lib/krb5/krb/s4u_creds.c
index 283417a..26c15fe 100644
--- a/src/lib/krb5/krb/s4u_creds.c
+++ b/src/lib/krb5/krb/s4u_creds.c
@@ -725,6 +725,128 @@ cleanup:
     return code;
 }
 
+/* Set *tgt_out to a local TGT for the client realm retrieved from ccache. */
+static krb5_error_code
+get_client_tgt(krb5_context context, krb5_flags options, krb5_ccache ccache,
+               krb5_principal client, krb5_creds **tgt_out)
+{
+    krb5_error_code code;
+    krb5_principal tgs;
+    krb5_creds mcreds;
+
+    *tgt_out = NULL;
+
+    code = krb5int_tgtname(context, &client->realm, &client->realm, &tgs);
+    if (code)
+        return code;
+
+    memset(&mcreds, 0, sizeof(mcreds));
+    mcreds.client = client;
+    mcreds.server = tgs;
+    code = krb5_get_credentials(context, options, ccache, &mcreds, tgt_out);
+    krb5_free_principal(context, tgs);
+    return code;
+}
+
+/* Copy req_server to *out_server.  If req_server has the referral realm, set
+ * the realm of *out_server to realm. */
+static krb5_error_code
+normalize_server_princ(krb5_context context, const krb5_data *realm,
+                       krb5_principal req_server, krb5_principal *out_server)
+{
+    krb5_error_code code;
+    krb5_principal server;
+
+    *out_server = NULL;
+
+    code = krb5_copy_principal(context, req_server, &server);
+    if (code)
+        return code;
+
+    if (krb5_is_referral_realm(&server->realm)) {
+        krb5_free_data_contents(context, &server->realm);
+        code = krb5int_copy_data_contents(context, realm, &server->realm);
+        if (code) {
+            krb5_free_principal(context, server);
+            return code;
+        }
+    }
+
+    *out_server = server;
+    return 0;
+}
+
+krb5_error_code
+k5_get_proxy_cred_from_kdc(krb5_context context, krb5_flags options,
+                           krb5_ccache ccache, krb5_creds *in_creds,
+                           krb5_creds **out_creds)
+{
+    krb5_error_code code;
+    krb5_flags kdcopt, flags;
+    krb5_principal server = NULL;
+    krb5_creds mcreds, *tgt = NULL, *tkt = NULL;
+
+    *out_creds = NULL;
+
+    if (in_creds->second_ticket.length == 0 ||
+        (options & KRB5_GC_CONSTRAINED_DELEGATION) == 0)
+        return EINVAL;
+
+    options &= ~KRB5_GC_CONSTRAINED_DELEGATION;
+
+    code = get_client_tgt(context, options, ccache, in_creds->client, &tgt);
+    if (code)
+        goto cleanup;
+
+    code = normalize_server_princ(context, &in_creds->client->realm,
+                                  in_creds->server, &server);
+    if (code)
+        goto cleanup;
+
+    kdcopt = KDC_OPT_CNAME_IN_ADDL_TKT;
+    if (options & KRB5_GC_CANONICALIZE)
+        kdcopt |= KDC_OPT_CANONICALIZE;
+    if (options & KRB5_GC_FORWARDABLE)
+        kdcopt |= KDC_OPT_FORWARDABLE;
+    if (options & KRB5_GC_NO_TRANSIT_CHECK)
+        kdcopt |= KDC_OPT_DISABLE_TRANSITED_CHECK;
+
+    mcreds = *in_creds;
+    mcreds.server = server;
+
+    flags = kdcopt | FLAGS2OPTS(tgt->ticket_flags);
+    code = krb5_get_cred_via_tkt_ext(context, tgt, flags, tgt->addresses,
+                                     NULL, &mcreds, NULL, NULL, NULL, NULL,
+                                     &tkt, NULL);
+    if (code)
+        goto cleanup;
+
+    if (!krb5_principal_compare(context, server, tkt->server)) {
+        code = KRB5KRB_AP_WRONG_PRINC;
+        goto cleanup;
+    }
+
+    if (!krb5_principal_compare(context, in_creds->server, tkt->server)) {
+        krb5_free_principal(context, tkt->server);
+        tkt->server = NULL;
+        code = krb5_copy_principal(context, in_creds->server, &tkt->server);
+        if (code)
+            goto cleanup;
+    }
+
+    if (!(options & KRB5_GC_NO_STORE))
+        (void)krb5_cc_store_cred(context, ccache, tkt);
+
+    *out_creds = tkt;
+    tkt = NULL;
+
+cleanup:
+    krb5_free_creds(context, tgt);
+    krb5_free_creds(context, tkt);
+    krb5_free_principal(context, server);
+    return code;
+}
+
 /*
  * Exported API for constrained delegation (S4U2Proxy).
  *
@@ -801,11 +923,9 @@ krb5_get_credentials_for_proxy(krb5_context context,
     s4u_creds.client = evidence_tkt->server;
     s4u_creds.second_ticket = *evidence_tkt_data;
 
-    code = krb5_get_credentials(context,
-                                options | KRB5_GC_CONSTRAINED_DELEGATION,
-                                ccache,
-                                &s4u_creds,
-                                out_creds);
+    code = k5_get_proxy_cred_from_kdc(context,
+                                      options | KRB5_GC_CONSTRAINED_DELEGATION,
+                                      ccache, &s4u_creds, out_creds);
     if (code != 0)
         goto cleanup;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.