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
---------------------
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
#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
/* #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
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;
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,
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;
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 |
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;
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
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;
(*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;
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;
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. */
* 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;
}
}
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));
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')