aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2024-03-01 14:23:47 +0100
committerGreg Hudson <ghudson@mit.edu>2024-05-06 17:40:31 -0400
commit6b74b6c18feab1f3d72d00ae412a93c6bfa4a00a (patch)
tree0aaf2d8be6557c8de905758c7df2eea858113c9f
parent0a3acc20564e82ba33741248cf25ca4d085d777f (diff)
downloadkrb5-6b74b6c18feab1f3d72d00ae412a93c6bfa4a00a.zip
krb5-6b74b6c18feab1f3d72d00ae412a93c6bfa4a00a.tar.gz
krb5-6b74b6c18feab1f3d72d00ae412a93c6bfa4a00a.tar.bz2
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)
-rw-r--r--doc/appdev/gssapi.rst36
-rw-r--r--doc/appdev/refs/macros/index.rst1
-rw-r--r--src/include/krb5/krb5.hin2
-rw-r--r--src/lib/gssapi/krb5/init_sec_context.c11
-rw-r--r--src/lib/krb5/krb/mk_req_ext.c21
-rw-r--r--src/tests/gssapi/t_bindings.c31
-rw-r--r--src/tests/gssapi/t_bindings.py18
7 files changed, 99 insertions, 21 deletions
diff --git a/doc/appdev/gssapi.rst b/doc/appdev/gssapi.rst
index 339fd6c..b58f412 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 45fe160..ee9fe2c 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 4e09ed3..7496383 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 0397fe1..3cc9d4c 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 0850486..3eae2e7 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 e890671..3d6a70a 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 f377977..1bb4966 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')