[31314] in CVS-changelog-for-Kerberos-V5

home help back first fref pref prev next nref lref last post

krb5 commit: Add GSS flag to include KERB_AP_OPTIONS_CBT

daemon@ATHENA.MIT.EDU (ghudson@mit.edu)
Mon May 6 18:25:28 2024

From: ghudson@mit.edu
To: cvs-krb5@mit.edu
Message-Id: <20240506222522.F370C101A5A@krbdev.mit.edu>
Date: Mon,  6 May 2024 18:25:22 -0400 (EDT)
MIME-Version: 1.0
Reply-To: krbdev@mit.edu
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
Errors-To: cvs-krb5-bounces@mit.edu

https://github.com/krb5/krb5/commit/6b74b6c18feab1f3d72d00ae412a93c6bfa4a00a
commit 6b74b6c18feab1f3d72d00ae412a93c6bfa4a00a
Author: Stefan Metzmacher <metze@samba.org>
Date:   Fri Mar 1 14:23:47 2024 +0100

    Add GSS flag to include KERB_AP_OPTIONS_CBT
    
    The Microsoft KERB_AP_OPTIONS_CBT extension (defined in [MS-KILE]
    3.2.5.8) allows the client to request strict enforcement of GSS
    channel bindings.  Client support for this extension was added in
    commit 225e6ef7f021cd1a8ef2a054af0ca58b7288fd81 (ticket 8900) but it
    requires a configuration variable to be set.  The choice to include
    the extension should be made by the client application code, as it is
    a promise to include channel bindings when operating within TLS.
    
    In libkrb5, add an option AP_OPTS_CBT_FLAG to make
    krb5_mk_req[_extended]() include KERB_AP_OPTIONS_CBT.  In the GSS
    initiator code, set this flag when the GSS_C_CHANNEL_BOUND flag is
    included in the request options.  GSS_C_CHANNEL_BOUND was introduced
    in commit 429a31146083fac21958631c2af572b08ec91022 (ticket 8899) as an
    acceptor output flag.
    
    [ghudson@mit.edu: rewrote commit message; adjusted some names;
    simplified GSS initiator bookkeeping; added documentation]
    
    ticket: 9122 (new)

 doc/appdev/gssapi.rst                  | 36 ++++++++++++++++++++++++++++++++++
 doc/appdev/refs/macros/index.rst       |  1 +
 src/include/krb5/krb5.hin              |  2 +-
 src/lib/gssapi/krb5/init_sec_context.c | 11 ++++++++---
 src/lib/krb5/krb/mk_req_ext.c          | 21 ++++++++++++--------
 src/tests/gssapi/t_bindings.c          | 31 ++++++++++++++++++++---------
 src/tests/gssapi/t_bindings.py         | 18 +++++++++++++++++
 7 files changed, 99 insertions(+), 21 deletions(-)

diff --git a/doc/appdev/gssapi.rst b/doc/appdev/gssapi.rst
index 339fd6c7c..b58f4122b 100644
--- a/doc/appdev/gssapi.rst
+++ b/doc/appdev/gssapi.rst
@@ -424,6 +424,42 @@ set.  If the library does not support the query,
 gss_inquire_cred_by_oid will return **GSS_S_UNAVAILABLE**.
 
 
+Channel binding behavior and GSS_C_CHANNEL_BOUND_FLAG
+-----------------------------------------------------
+
+GSSAPI channel bindings can be used to limit the scope of a context
+establishment token to a particular protected channel or endpoint,
+such as a TLS channel or server certificate.  Channel bindings can be
+supplied via the *input_chan_bindings* parameter to either
+gss_init_sec_context() or gss_accept_sec_context().
+
+If both the initiator and acceptor of a GSSAPI exchange supply
+matching channel bindings, **GSS_C_CHANNEL_BOUND_FLAG** will be
+included in the gss_accept_sec_context() *ret_flags* result.  If
+either the initiator or acceptor (or both) do not supply channel
+bindings, the exchange will succeed, but **GSS_C_CHANNEL_BOUND_FLAG**
+will not be included in the return flags.  If the acceptor and
+initiator both inlude channel bindings but they do not match, the
+exchange will fail.
+
+If **GSS_C_CHANNEL_BOUND_FLAG** is included in the *req_flags*
+parameter of gss_init_sec_context(), the initiator will add the
+Microsoft KERB_AP_OPTIONS_CBT extension to the Kerberos authenticator.
+This extension requests that the acceptor strictly enforce channel
+bindings, causing the exchange to fail if the acceptor supplies
+channel bindings and the initiator does not.  The KERB_AP_OPTIONS_CBT
+extension will also be included if the
+**client_aware_channel_bindings** variable is set to ``true`` in
+:ref:`libdefaults`.
+
+Prior to release 1.19, **GSS_C_CHANNEL_BOUND_FLAG** is not
+implemented, and the exchange will fail if the acceptor supply channel
+bindings and the initiator does not (but not vice versa).  Between
+releases 1.19 and 1.21, **GSS_C_CHANNEL_BOUND_FLAG** is not recognized
+as an initiator flag, so **client_aware_channel_bindings** is the only
+way to cause KERB_AP_OPTIONS_CBT to be included.
+
+
 AEAD message wrapping
 ---------------------
 
diff --git a/doc/appdev/refs/macros/index.rst b/doc/appdev/refs/macros/index.rst
index 45fe160d7..ee9fe2c61 100644
--- a/doc/appdev/refs/macros/index.rst
+++ b/doc/appdev/refs/macros/index.rst
@@ -22,6 +22,7 @@ Public
    AD_TYPE_REGISTERED.rst
    AD_TYPE_RESERVED.rst
    AP_OPTS_ETYPE_NEGOTIATION.rst
+   AP_OPTS_CBT_FLAG.rst
    AP_OPTS_MUTUAL_REQUIRED.rst
    AP_OPTS_RESERVED.rst
    AP_OPTS_USE_SESSION_KEY.rst
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index 4e09ed345..74963832d 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -1658,6 +1658,7 @@ krb5_verify_checksum(krb5_context context, krb5_cksumtype ctype,
 #define AP_OPTS_USE_SESSION_KEY    0x40000000 /**< Use session key */
 #define AP_OPTS_MUTUAL_REQUIRED    0x20000000 /**< Perform a mutual
                                                  authentication exchange */
+#define AP_OPTS_CBT_FLAG           0x00000004 /* include KERB_AP_OPTIONS_CBT */
 #define AP_OPTS_ETYPE_NEGOTIATION  0x00000002
 #define AP_OPTS_USE_SUBKEY         0x00000001 /**< Generate a subsession key
                                                  from the current session key
@@ -1689,7 +1690,6 @@ krb5_verify_checksum(krb5_context context, krb5_cksumtype ctype,
 /* #define      AP_OPTS_RESERVED        0x00000020 */
 /* #define      AP_OPTS_RESERVED        0x00000010 */
 /* #define      AP_OPTS_RESERVED        0x00000008 */
-/* #define      AP_OPTS_RESERVED        0x00000004 */
 
 
 #define AP_OPTS_WIRE_MASK               0xfffffff0
diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c
index 0397fe1df..3cc9d4ceb 100644
--- a/src/lib/gssapi/krb5/init_sec_context.c
+++ b/src/lib/gssapi/krb5/init_sec_context.c
@@ -365,7 +365,7 @@ make_ap_req_v1(krb5_context context, krb5_gss_ctx_id_rec *ctx,
                krb5_gss_cred_id_t cred, krb5_creds *k_cred,
                krb5_authdata_context ad_context,
                gss_channel_bindings_t chan_bindings, gss_OID mech_type,
-               gss_buffer_t token, krb5_gss_ctx_ext_t exts)
+               int cbt_flag, gss_buffer_t token, krb5_gss_ctx_ext_t exts)
 {
     krb5_flags mk_req_flags = 0;
     krb5_error_code code;
@@ -400,6 +400,8 @@ make_ap_req_v1(krb5_context context, krb5_gss_ctx_id_rec *ctx,
 
     if (ctx->gss_flags & GSS_C_MUTUAL_FLAG)
         mk_req_flags |= AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_ETYPE_NEGOTIATION;
+    if (cbt_flag)
+        mk_req_flags |= AP_OPTS_CBT_FLAG;
 
     krb5_auth_con_set_authdata_context(context, ctx->auth_context, ad_context);
     code = krb5_mk_req_extended(context, &ctx->auth_context, mk_req_flags,
@@ -481,6 +483,7 @@ kg_new_connection(
     krb5_timestamp now;
     gss_buffer_desc token;
     krb5_keyblock *keyblock;
+    int cbt_flag = (req_flags & GSS_C_CHANNEL_BOUND_FLAG) != 0;
 
     k5_mutex_assert_locked(&cred->lock);
     major_status = GSS_S_FAILURE;
@@ -538,6 +541,8 @@ kg_new_connection(
         req_flags |= GSS_C_DELEG_POLICY_FLAG;
     }
 
+    /* Don't include GSS_C_CHANNEL_BOUND_FLAG here; we don't want to put it on
+     * the wire, and we only need to know about it for the first token. */
     ctx->gss_flags = req_flags & (GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG |
                                   GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
                                   GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG |
@@ -595,8 +600,8 @@ kg_new_connection(
         krb5_int32 seq_temp;
         if ((code = make_ap_req_v1(context, ctx,
                                    cred, k_cred, ctx->here->ad_context,
-                                   input_chan_bindings,
-                                   mech_type, &token, exts))) {
+                                   input_chan_bindings, mech_type, cbt_flag,
+                                   &token, exts))) {
             if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) ||
                 (code == KG_EMPTY_CCACHE))
                 major_status = GSS_S_NO_CRED;
diff --git a/src/lib/krb5/krb/mk_req_ext.c b/src/lib/krb5/krb/mk_req_ext.c
index 08504860c..3eae2e751 100644
--- a/src/lib/krb5/krb/mk_req_ext.c
+++ b/src/lib/krb5/krb/mk_req_ext.c
@@ -78,7 +78,7 @@ generate_authenticator(krb5_context,
                        krb5_checksum *, krb5_key,
                        krb5_ui_4, krb5_authdata **,
                        krb5_authdata_context ad_context,
-                       krb5_enctype *desired_etypes,
+                       krb5_enctype *desired_etypes, krb5_boolean cbt_flag,
                        krb5_enctype tkt_enctype);
 
 krb5_error_code KRB5_CALLCONV
@@ -95,6 +95,7 @@ krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context,
     krb5_ap_req request;
     krb5_data *scratch = 0;
     krb5_data *toutbuf;
+    krb5_boolean cbt_flag = (ap_req_options & AP_OPTS_CBT_FLAG) != 0;
 
     request.ap_options = ap_req_options & AP_OPTS_WIRE_MASK;
     request.authenticator.ciphertext.data = NULL;
@@ -201,7 +202,7 @@ krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context,
                                          (*auth_context)->local_seq_number,
                                          in_creds->authdata,
                                          (*auth_context)->ad_context,
-                                         desired_etypes,
+                                         desired_etypes, cbt_flag,
                                          in_creds->keyblock.enctype)))
         goto cleanup_cksum;
 
@@ -258,7 +259,7 @@ generate_authenticator(krb5_context context, krb5_authenticator *authent,
                        krb5_key key, krb5_ui_4 seq_number,
                        krb5_authdata **authorization,
                        krb5_authdata_context ad_context,
-                       krb5_enctype *desired_etypes,
+                       krb5_enctype *desired_etypes, krb5_boolean cbt_flag,
                        krb5_enctype tkt_enctype)
 {
     krb5_error_code retval;
@@ -297,11 +298,15 @@ generate_authenticator(krb5_context context, krb5_authenticator *authent,
         krb5_free_authdata(context, ext_authdata);
     }
 
-    retval = profile_get_boolean(context->profile, KRB5_CONF_LIBDEFAULTS,
-                                 KRB5_CONF_CLIENT_AWARE_GSS_BINDINGS, NULL,
-                                 FALSE, &client_aware_cb);
-    if (retval)
-        return retval;
+    if (cbt_flag) {
+        client_aware_cb = TRUE;
+    } else {
+        retval = profile_get_boolean(context->profile, KRB5_CONF_LIBDEFAULTS,
+                                     KRB5_CONF_CLIENT_AWARE_GSS_BINDINGS, NULL,
+                                     FALSE, &client_aware_cb);
+        if (retval)
+            return retval;
+    }
 
     /* Add etype negotiation or channel-binding awareness authdata to the
      * front, if appropriate. */
diff --git a/src/tests/gssapi/t_bindings.c b/src/tests/gssapi/t_bindings.c
index e8906715b..3d6a70acf 100644
--- a/src/tests/gssapi/t_bindings.c
+++ b/src/tests/gssapi/t_bindings.c
@@ -43,34 +43,46 @@
  * reported as channel-bound on the acceptor.  Exit with status 0 if all
  * operations are successful, or 1 if not.
  *
- * Usage: ./t_bindings [-s] targetname icb acb
+ * Usage: ./t_bindings [-s] [-b] targetname icb acb
  *
- * An icb or abc value of "-" will not specify channel bindings.
+ * An icb or abc value of "-" will not specify channel bindings.  The -s flag
+ * uses the SPNEGO mechanism instead of the krb5 mecanism.  The -b flag
+ * includes GSS_C_CHANNEL_BOUND in req_flags, which requests strict enforcement
+ * of channel bindings by the acceptor.
  */
 
 int
 main(int argc, char *argv[])
 {
+    OM_uint32 client_flags = 0;
     OM_uint32 minor, flags1, flags2;
     gss_name_t target_name;
     gss_ctx_id_t ictx, actx;
     struct gss_channel_bindings_struct icb_data = {0}, acb_data = {0};
     gss_channel_bindings_t icb = GSS_C_NO_CHANNEL_BINDINGS;
     gss_channel_bindings_t acb = GSS_C_NO_CHANNEL_BINDINGS;
-    gss_OID_desc *mech;
+    gss_OID_desc *mech = GSS_C_NO_OID;
 
     argv++;
     argc--;
+
     if (*argv != NULL && strcmp(*argv, "-s") == 0) {
         mech = &mech_spnego;
         argv++;
         argc--;
-    } else {
-        mech = &mech_krb5;
     }
 
+    if (*argv != NULL && strcmp(*argv, "-b") == 0) {
+        client_flags |= GSS_C_CHANNEL_BOUND_FLAG;
+        argv++;
+        argc--;
+    }
+
+    if (mech == GSS_C_NO_OID)
+        mech = &mech_krb5;
+
     if (argc != 3) {
-        fprintf(stderr, "Usage: t_bindings [-s] targetname icb acb\n");
+        fprintf(stderr, "Usage: t_bindings [-s] [-b] targetname icb acb\n");
         return 1;
     }
 
@@ -89,15 +101,16 @@ main(int argc, char *argv[])
     }
 
     establish_contexts_ex(mech, GSS_C_NO_CREDENTIAL, GSS_C_NO_CREDENTIAL,
-                          target_name, 0, &ictx, &actx, icb, acb, &flags1,
-                          NULL, NULL, NULL);
+                          target_name, client_flags, &ictx, &actx, icb, acb,
+                          &flags1, NULL, NULL, NULL);
 
     /* Try again with GSS_C_DCE_STYLE */
     (void)gss_delete_sec_context(&minor, &ictx, NULL);
     (void)gss_delete_sec_context(&minor, &actx, NULL);
 
+    client_flags |= GSS_C_DCE_STYLE;
     establish_contexts_ex(mech, GSS_C_NO_CREDENTIAL, GSS_C_NO_CREDENTIAL,
-                          target_name, GSS_C_DCE_STYLE, &ictx, &actx, icb, acb,
+                          target_name, client_flags, &ictx, &actx, icb, acb,
                           &flags2, NULL, NULL, NULL);
     assert((flags1 & GSS_C_CHANNEL_BOUND_FLAG) ==
            (flags2 & GSS_C_CHANNEL_BOUND_FLAG));
diff --git a/src/tests/gssapi/t_bindings.py b/src/tests/gssapi/t_bindings.py
index f377977b6..1bb496629 100644
--- a/src/tests/gssapi/t_bindings.py
+++ b/src/tests/gssapi/t_bindings.py
@@ -40,4 +40,22 @@ realm.run(['./t_bindings', '-s', server, '-', 'a'], env=e,
 realm.run(['./t_bindings', '-s', server, 'a', 'x'], env=e,
           expected_code=1, expected_msg='Incorrect channel bindings')
 
+mark('krb5 GSS_C_CHANNEL_BOUND_FLAG initiator input flag')
+realm.run(['./t_bindings', '-b', server, '-', '-'], expected_msg='no')
+realm.run(['./t_bindings', '-b', server, 'a', '-'], expected_msg='no')
+realm.run(['./t_bindings', '-b', server, 'a', 'a'], expected_msg='yes')
+realm.run(['./t_bindings', '-b', server, '-', 'a'],
+          expected_code=1, expected_msg='Incorrect channel bindings')
+realm.run(['./t_bindings', '-b', server, 'a', 'x'],
+          expected_code=1, expected_msg='Incorrect channel bindings')
+
+mark('SPNEGO GSS_C_CHANNEL_BOUND_FLAG initiator input flag')
+realm.run(['./t_bindings', '-s', '-b', server, '-', '-'], expected_msg='no')
+realm.run(['./t_bindings', '-s', '-b', server, 'a', '-'], expected_msg='no')
+realm.run(['./t_bindings', '-s', '-b', server, 'a', 'a'], expected_msg='yes')
+realm.run(['./t_bindings', '-s', '-b', server, '-', 'a'],
+          expected_code=1, expected_msg='Incorrect channel bindings')
+realm.run(['./t_bindings', '-s', '-b', server, 'a', 'x'],
+          expected_code=1, expected_msg='Incorrect channel bindings')
+
 success('channel bindings tests')
_______________________________________________
cvs-krb5 mailing list
cvs-krb5@mit.edu
https://mailman.mit.edu/mailman/listinfo/cvs-krb5

home help back first fref pref prev next nref lref last post