diff --git a/SOURCES/Add-channel-bindings-tests.patch b/SOURCES/Add-channel-bindings-tests.patch
new file mode 100644
index 0000000..92c803d
--- /dev/null
+++ b/SOURCES/Add-channel-bindings-tests.patch
@@ -0,0 +1,420 @@
+From 6af3fd382e99a9724413929af7eee7c86326ffd9 Mon Sep 17 00:00:00 2001
+From: Isaac Boukris <iboukris@gmail.com>
+Date: Fri, 20 Mar 2020 00:17:28 +0100
+Subject: [PATCH] Add channel bindings tests
+
+[ghudson@mit.edu: adjusted test program to output channel-bound state
+instead of optionally enforcing it; adjusted tests to check program
+output; split out tests into separate Python script; made cosmetic
+changes]
+
+ticket: 8900
+(cherry picked from commit b0b21b6d25b06f3e2b365dfe9dd4c99b3d43bf57)
+[rharwood@redhat.com: .gitignore]
+(cherry picked from commit 3e92520c1417f22447751cd9172d5ab30c2e0ad8)
+---
+ src/plugins/gssapi/negoextest/main.c |  18 +++++
+ src/tests/gssapi/Makefile.in         |  49 ++++++------
+ src/tests/gssapi/common.c            |  25 ++++--
+ src/tests/gssapi/common.h            |   9 +++
+ src/tests/gssapi/deps                |   4 +
+ src/tests/gssapi/t_bindings.c        | 111 +++++++++++++++++++++++++++
+ src/tests/gssapi/t_bindings.py       |  43 +++++++++++
+ src/tests/gssapi/t_negoex.py         |   7 ++
+ 8 files changed, 237 insertions(+), 29 deletions(-)
+ create mode 100644 src/tests/gssapi/t_bindings.c
+ create mode 100644 src/tests/gssapi/t_bindings.py
+
+diff --git a/src/plugins/gssapi/negoextest/main.c b/src/plugins/gssapi/negoextest/main.c
+index 6c340f41b..72fc5273a 100644
+--- a/src/plugins/gssapi/negoextest/main.c
++++ b/src/plugins/gssapi/negoextest/main.c
+@@ -57,6 +57,15 @@ gss_init_sec_context(OM_uint32 *minor_status,
+     const char *envstr;
+     uint8_t hops, mech_last_octet;
+ 
++    envstr = getenv("GSS_INIT_BINDING");
++    if (envstr != NULL) {
++        assert(strlen(envstr) > 0);
++        assert(input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS);
++        assert(strlen(envstr) == input_chan_bindings->application_data.length);
++        assert(strcmp((char *)input_chan_bindings->application_data.value,
++                      envstr) == 0);
++    }
++
+     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
+         envstr = getenv("HOPS");
+         hops = (envstr != NULL) ? atoi(envstr) : 1;
+@@ -112,6 +121,15 @@ gss_accept_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,
+     uint8_t hops, mech_last_octet;
+     const char *envstr;
+ 
++    envstr = getenv("GSS_ACCEPT_BINDING");
++    if (envstr != NULL) {
++        assert(strlen(envstr) > 0);
++        assert(input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS);
++        assert(strlen(envstr) == input_chan_bindings->application_data.length);
++        assert(strcmp((char *)input_chan_bindings->application_data.value,
++                      envstr) == 0);
++    }
++
+     /*
+      * The unwrapped token sits at the end and is just one byte giving the
+      * remaining number of hops.  The final octet of the mech encoding should
+diff --git a/src/tests/gssapi/Makefile.in b/src/tests/gssapi/Makefile.in
+index 5cc1e0f58..68c132b79 100644
+--- a/src/tests/gssapi/Makefile.in
++++ b/src/tests/gssapi/Makefile.in
+@@ -9,33 +9,33 @@ LOCALINCLUDES = -I$(srcdir)/../../lib/gssapi/mechglue \
+ 	-I../../lib/gssapi/generic
+ 
+ SRCS=	$(srcdir)/ccinit.c $(srcdir)/ccrefresh.c $(srcdir)/common.c \
+-	$(srcdir)/t_accname.c $(srcdir)/t_add_cred.c $(srcdir)/t_ccselect.c \
+-	$(srcdir)/t_ciflags.c $(srcdir)/t_context.c $(srcdir)/t_credstore.c \
+-	$(srcdir)/t_enctypes.c $(srcdir)/t_err.c $(srcdir)/t_export_cred.c \
+-	$(srcdir)/t_export_name.c $(srcdir)/t_gssexts.c \
+-	$(srcdir)/t_imp_cred.c $(srcdir)/t_imp_name.c $(srcdir)/t_invalid.c \
+-	$(srcdir)/t_inq_cred.c $(srcdir)/t_inq_ctx.c \
++	$(srcdir)/t_accname.c $(srcdir)/t_add_cred.c $(srcdir)/t_bindings.c \
++	$(srcdir)/t_ccselect.c $(srcdir)/t_ciflags.c $(srcdir)/t_context.c \
++	$(srcdir)/t_credstore.c $(srcdir)/t_enctypes.c $(srcdir)/t_err.c \
++	$(srcdir)/t_export_cred.c $(srcdir)/t_export_name.c \
++	$(srcdir)/t_gssexts.c $(srcdir)/t_imp_cred.c $(srcdir)/t_imp_name.c \
++	$(srcdir)/t_invalid.c $(srcdir)/t_inq_cred.c $(srcdir)/t_inq_ctx.c \
+ 	$(srcdir)/t_inq_mechs_name.c $(srcdir)/t_iov.c \
+ 	$(srcdir)/t_lifetime.c $(srcdir)/t_namingexts.c $(srcdir)/t_oid.c \
+ 	$(srcdir)/t_pcontok.c $(srcdir)/t_prf.c $(srcdir)/t_s4u.c \
+ 	$(srcdir)/t_s4u2proxy_krb5.c $(srcdir)/t_saslname.c \
+ 	$(srcdir)/t_spnego.c $(srcdir)/t_srcattrs.c
+ 
+-OBJS=	ccinit.o ccrefresh.o common.o t_accname.o t_add_cred.o t_ccselect.o \
+-	t_ciflags.o t_context.o t_credstore.o t_enctypes.o t_err.o \
+-	t_export_cred.o t_export_name.o t_gssexts.o t_imp_cred.o t_imp_name.o \
+-	t_invalid.o t_inq_cred.o t_inq_ctx.o t_inq_mechs_name.o t_iov.o \
+-	t_lifetime.o t_namingexts.o t_oid.o t_pcontok.o t_prf.o t_s4u.o \
+-	t_s4u2proxy_krb5.o t_saslname.o t_spnego.o t_srcattrs.o
++OBJS=	ccinit.o ccrefresh.o common.o t_accname.o t_add_cred.o t_bindings.o \
++	t_ccselect.o t_ciflags.o t_context.o t_credstore.o t_enctypes.o \
++	t_err.o t_export_cred.o t_export_name.o t_gssexts.o t_imp_cred.o \
++	t_imp_name.o t_invalid.o t_inq_cred.o t_inq_ctx.o t_inq_mechs_name.o \
++	t_iov.o t_lifetime.o t_namingexts.o t_oid.o t_pcontok.o t_prf.o \
++	t_s4u.o t_s4u2proxy_krb5.o t_saslname.o t_spnego.o t_srcattrs.o
+ 
+ COMMON_DEPS= common.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+ COMMON_LIBS= common.o $(GSS_LIBS) $(KRB5_BASE_LIBS)
+ 
+-all: ccinit ccrefresh t_accname t_add_cred t_ccselect t_ciflags t_context \
+-	t_credstore t_enctypes t_err t_export_cred t_export_name t_gssexts \
+-	t_imp_cred t_imp_name t_invalid t_inq_cred t_inq_ctx t_inq_mechs_name \
+-	t_iov t_lifetime t_namingexts t_oid t_pcontok t_prf t_s4u \
+-	t_s4u2proxy_krb5 t_saslname t_spnego t_srcattrs
++all: ccinit ccrefresh t_accname t_add_cred t_bindings t_ccselect t_ciflags \
++	t_context t_credstore t_enctypes t_err t_export_cred t_export_name \
++	t_gssexts t_imp_cred t_imp_name t_invalid t_inq_cred t_inq_ctx \
++	t_inq_mechs_name t_iov t_lifetime t_namingexts t_oid t_pcontok t_prf \
++	t_s4u t_s4u2proxy_krb5 t_saslname t_spnego t_srcattrs
+ 
+ check-unix: t_oid
+ 	$(RUN_TEST) ./t_invalid
+@@ -43,11 +43,12 @@ check-unix: t_oid
+ 	$(RUN_TEST) ./t_prf
+ 	$(RUN_TEST) ./t_imp_name
+ 
+-check-pytests: ccinit ccrefresh t_accname t_add_cred t_ccselect t_ciflags \
+-	t_context t_credstore t_enctypes t_err t_export_cred t_export_name \
+-	t_imp_cred t_inq_cred t_inq_ctx t_inq_mechs_name t_iov t_lifetime \
+-	t_pcontok t_s4u t_s4u2proxy_krb5 t_spnego t_srcattrs
++check-pytests: ccinit ccrefresh t_accname t_add_cred t_bindings t_ccselect \
++	t_ciflags t_context t_credstore t_enctypes t_err t_export_cred \
++	t_export_name t_imp_cred t_inq_cred t_inq_ctx t_inq_mechs_name t_iov \
++	t_lifetime t_pcontok t_s4u t_s4u2proxy_krb5 t_spnego t_srcattrs
+ 	$(RUNPYTEST) $(srcdir)/t_gssapi.py $(PYTESTFLAGS)
++	$(RUNPYTEST) $(srcdir)/t_bindings.py $(PYTESTFLAGS)
+ 	$(RUNPYTEST) $(srcdir)/t_ccselect.py $(PYTESTFLAGS)
+ 	$(RUNPYTEST) $(srcdir)/t_client_keytab.py $(PYTESTFLAGS)
+ 	$(RUNPYTEST) $(srcdir)/t_enctypes.py $(PYTESTFLAGS)
+@@ -64,6 +65,8 @@ t_accname: t_accname.o $(COMMON_DEPS)
+ 	$(CC_LINK) -o $@ t_accname.o $(COMMON_LIBS)
+ t_add_cred: t_add_cred.o $(COMMON_DEPS)
+ 	$(CC_LINK) -o $@ t_add_cred.o $(COMMON_LIBS)
++t_bindings: t_bindings.o $(COMMON_DEPS)
++	$(CC_LINK) -o $@ t_bindings.o $(COMMON_LIBS)
+ t_ccselect: t_ccselect.o $(COMMON_DEPS)
+ 	$(CC_LINK) -o $@ t_ccselect.o $(COMMON_LIBS)
+ t_ciflags: t_ciflags.o $(COMMON_DEPS)
+@@ -118,8 +121,8 @@ t_srcattrs: t_srcattrs.o $(COMMON_DEPS)
+ 	$(CC_LINK) -o $@ t_srcattrs.o $(COMMON_LIBS)
+ 
+ clean:
+-	$(RM) ccinit ccrefresh t_accname t_add_cred t_ccselect t_ciflags
+-	$(RM) t_context t_credstore t_enctypes t_err t_export_cred
++	$(RM) ccinit ccrefresh t_accname t_add_cred t_bindings t_ccselect
++	$(RM) t_ciflags t_context t_credstore t_enctypes t_err t_export_cred
+ 	$(RM) t_export_name t_gssexts t_imp_cred t_imp_name t_invalid
+ 	$(RM) t_inq_cred t_inq_ctx t_inq_mechs_name t_iov t_lifetime
+ 	$(RM) t_namingexts t_oid t_pcontok t_prf t_s4u t_s4u2proxy_krb5
+diff --git a/src/tests/gssapi/common.c b/src/tests/gssapi/common.c
+index 83e9d9bb8..7ba72f7b2 100644
+--- a/src/tests/gssapi/common.c
++++ b/src/tests/gssapi/common.c
+@@ -115,6 +115,20 @@ establish_contexts(gss_OID imech, gss_cred_id_t icred, gss_cred_id_t acred,
+                    gss_name_t tname, OM_uint32 flags, gss_ctx_id_t *ictx,
+                    gss_ctx_id_t *actx, gss_name_t *src_name, gss_OID *amech,
+                    gss_cred_id_t *deleg_cred)
++{
++    return establish_contexts_ex(imech, icred, acred, tname, flags, ictx, actx,
++                                 GSS_C_NO_CHANNEL_BINDINGS,
++                                 GSS_C_NO_CHANNEL_BINDINGS, NULL, src_name,
++                                 amech, deleg_cred);
++}
++
++void
++establish_contexts_ex(gss_OID imech, gss_cred_id_t icred, gss_cred_id_t acred,
++                      gss_name_t tname, OM_uint32 flags, gss_ctx_id_t *ictx,
++                      gss_ctx_id_t *actx, gss_channel_bindings_t icb,
++                      gss_channel_bindings_t acb, OM_uint32 *aret_flags,
++                      gss_name_t *src_name, gss_OID *amech,
++                      gss_cred_id_t *deleg_cred)
+ {
+     OM_uint32 minor, imaj, amaj;
+     gss_buffer_desc itok, atok;
+@@ -126,17 +140,16 @@ establish_contexts(gss_OID imech, gss_cred_id_t icred, gss_cred_id_t acred,
+     for (;;) {
+         (void)gss_release_buffer(&minor, &itok);
+         imaj = gss_init_sec_context(&minor, icred, ictx, tname, imech, flags,
+-                                    GSS_C_INDEFINITE,
+-                                    GSS_C_NO_CHANNEL_BINDINGS, &atok, NULL,
+-                                    &itok, NULL, NULL);
++                                    GSS_C_INDEFINITE, icb, &atok, NULL, &itok,
++                                    NULL, NULL);
+         check_gsserr("gss_init_sec_context", imaj, minor);
+         if (amaj == GSS_S_COMPLETE)
+             break;
+ 
+         (void)gss_release_buffer(&minor, &atok);
+-        amaj = gss_accept_sec_context(&minor, actx, acred, &itok,
+-                                      GSS_C_NO_CHANNEL_BINDINGS, src_name,
+-                                      amech, &atok, NULL, NULL, deleg_cred);
++        amaj = gss_accept_sec_context(&minor, actx, acred, &itok, acb,
++                                      src_name, amech, &atok, aret_flags, NULL,
++                                      deleg_cred);
+         check_gsserr("gss_accept_sec_context", amaj, minor);
+         (void)gss_release_buffer(&minor, &itok);
+         if (imaj == GSS_S_COMPLETE)
+diff --git a/src/tests/gssapi/common.h b/src/tests/gssapi/common.h
+index ae11b51d4..a5c8f87e6 100644
+--- a/src/tests/gssapi/common.h
++++ b/src/tests/gssapi/common.h
+@@ -62,6 +62,15 @@ void establish_contexts(gss_OID imech, gss_cred_id_t icred,
+                         gss_name_t *src_name, gss_OID *amech,
+                         gss_cred_id_t *deleg_cred);
+ 
++/* Establish contexts with channel bindings. */
++void establish_contexts_ex(gss_OID imech, gss_cred_id_t icred,
++                           gss_cred_id_t acred, gss_name_t tname,
++                           OM_uint32 flags, gss_ctx_id_t *ictx,
++                           gss_ctx_id_t *actx, gss_channel_bindings_t icb,
++                           gss_channel_bindings_t acb, OM_uint32 *aret_flags,
++                           gss_name_t *src_name, gss_OID *amech,
++                           gss_cred_id_t *deleg_cred);
++
+ /* Export *cred to a token, then release *cred and replace it by re-importing
+  * the token. */
+ void export_import_cred(gss_cred_id_t *cred);
+diff --git a/src/tests/gssapi/deps b/src/tests/gssapi/deps
+index acd0e96f8..73e4d9a74 100644
+--- a/src/tests/gssapi/deps
++++ b/src/tests/gssapi/deps
+@@ -33,6 +33,10 @@ $(OUTPRE)t_add_cred.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+   $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+   $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+   common.h t_add_cred.c
++$(OUTPRE)t_bindings.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
++  $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
++  $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
++  common.h t_bindings.c
+ $(OUTPRE)t_ccselect.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+   $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \
+   $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+diff --git a/src/tests/gssapi/t_bindings.c b/src/tests/gssapi/t_bindings.c
+new file mode 100644
+index 000000000..e8906715b
+--- /dev/null
++++ b/src/tests/gssapi/t_bindings.c
+@@ -0,0 +1,111 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/*
++ * Copyright (C) 2020 by Red Hat, Inc.
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * * Redistributions of source code must retain the above copyright
++ *   notice, this list of conditions and the following disclaimer.
++ *
++ * * Redistributions in binary form must reproduce the above copyright
++ *   notice, this list of conditions and the following disclaimer in
++ *   the documentation and/or other materials provided with the
++ *   distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
++ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
++ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
++ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
++ * OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include <stdio.h>
++#include <string.h>
++#include <assert.h>
++
++#include "common.h"
++
++/*
++ * Establish contexts (without and with GSS_C_DCE_STYLE) with the default
++ * initiator name, a specified principal name as target name, initiator
++ * bindings, and acceptor bindings.  If any call is unsuccessful, display an
++ * error message.  Output "yes" or "no" to indicate whether the contexts were
++ * 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
++ *
++ * An icb or abc value of "-" will not specify channel bindings.
++ */
++
++int
++main(int argc, char *argv[])
++{
++    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;
++
++    argv++;
++    argc--;
++    if (*argv != NULL && strcmp(*argv, "-s") == 0) {
++        mech = &mech_spnego;
++        argv++;
++        argc--;
++    } else {
++        mech = &mech_krb5;
++    }
++
++    if (argc != 3) {
++        fprintf(stderr, "Usage: t_bindings [-s] targetname icb acb\n");
++        return 1;
++    }
++
++    target_name = import_name(argv[0]);
++
++    if (strcmp(argv[1], "-") != 0) {
++        icb_data.application_data.length = strlen(argv[1]);
++        icb_data.application_data.value = argv[1];
++        icb = &icb_data;
++    }
++
++    if (strcmp(argv[2], "-") != 0) {
++        acb_data.application_data.length = strlen(argv[2]);
++        acb_data.application_data.value = argv[2];
++        acb = &acb_data;
++    }
++
++    establish_contexts_ex(mech, GSS_C_NO_CREDENTIAL, GSS_C_NO_CREDENTIAL,
++                          target_name, 0, &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);
++
++    establish_contexts_ex(mech, GSS_C_NO_CREDENTIAL, GSS_C_NO_CREDENTIAL,
++                          target_name, GSS_C_DCE_STYLE, &ictx, &actx, icb, acb,
++                          &flags2, NULL, NULL, NULL);
++    assert((flags1 & GSS_C_CHANNEL_BOUND_FLAG) ==
++           (flags2 & GSS_C_CHANNEL_BOUND_FLAG));
++    printf("%s\n", (flags1 & GSS_C_CHANNEL_BOUND_FLAG) ? "yes" : "no");
++
++    (void)gss_delete_sec_context(&minor, &ictx, NULL);
++    (void)gss_delete_sec_context(&minor, &actx, NULL);
++    (void)gss_release_name(&minor, &target_name);
++
++    return 0;
++}
+diff --git a/src/tests/gssapi/t_bindings.py b/src/tests/gssapi/t_bindings.py
+new file mode 100644
+index 000000000..f377977b6
+--- /dev/null
++++ b/src/tests/gssapi/t_bindings.py
+@@ -0,0 +1,43 @@
++from k5test import *
++
++realm = K5Realm()
++server = 'p:' + realm.host_princ
++
++mark('krb5 channel bindings')
++realm.run(['./t_bindings', server, '-', '-'], expected_msg='no')
++realm.run(['./t_bindings', server, 'a', '-'], expected_msg='no')
++realm.run(['./t_bindings', server, 'a', 'a'], expected_msg='yes')
++realm.run(['./t_bindings', server, '-', 'a'], expected_msg='no')
++realm.run(['./t_bindings', server, 'a', 'x'],
++          expected_code=1, expected_msg='Incorrect channel bindings')
++
++mark('SPNEGO channel bindings')
++realm.run(['./t_bindings', '-s', server, '-', '-'], expected_msg='no')
++realm.run(['./t_bindings', '-s', server, 'a', '-'], expected_msg='no')
++realm.run(['./t_bindings', '-s', server, 'a', 'a'], expected_msg='yes')
++realm.run(['./t_bindings', '-s', server, '-', 'a'], expected_msg='no')
++realm.run(['./t_bindings', '-s', server, 'a', 'x'],
++          expected_code=1, expected_msg='Incorrect channel bindings')
++
++client_aware_conf = {'libdefaults': {'client_aware_channel_bindings': 'true'}}
++e = realm.special_env('cb_aware', False, krb5_conf=client_aware_conf)
++
++mark('krb5 client_aware_channel_bindings')
++realm.run(['./t_bindings', server, '-', '-'], env=e, expected_msg='no')
++realm.run(['./t_bindings', server, 'a', '-'], env=e, expected_msg='no')
++realm.run(['./t_bindings', server, 'a', 'a'], env=e, expected_msg='yes')
++realm.run(['./t_bindings', server, '-', 'a'], env=e,
++          expected_code=1, expected_msg='Incorrect channel bindings')
++realm.run(['./t_bindings', server, 'a', 'x'], env=e,
++          expected_code=1, expected_msg='Incorrect channel bindings')
++
++mark('SPNEGO client_aware_channel_bindings')
++realm.run(['./t_bindings', '-s', server, '-', '-'], env=e, expected_msg='no')
++realm.run(['./t_bindings', '-s', server, 'a', '-'], env=e, expected_msg='no')
++realm.run(['./t_bindings', '-s', server, 'a', 'a'], env=e, expected_msg='yes')
++realm.run(['./t_bindings', '-s', server, '-', 'a'], env=e,
++          expected_code=1, expected_msg='Incorrect channel bindings')
++realm.run(['./t_bindings', '-s', server, 'a', 'x'], env=e,
++          expected_code=1, expected_msg='Incorrect channel bindings')
++
++success('channel bindings tests')
+diff --git a/src/tests/gssapi/t_negoex.py b/src/tests/gssapi/t_negoex.py
+index 88470d2fa..a218899c4 100644
+--- a/src/tests/gssapi/t_negoex.py
++++ b/src/tests/gssapi/t_negoex.py
+@@ -139,4 +139,11 @@ msgs = ('sending [3]AP_REQUEST', 'sending [7]CHALLENGE', 'sending [8]VERIFY',
+         'sending [11]CHALLENGE', 'sending [12]VERIFY', 'sending [13]VERIFY')
+ test({'HOPS': '4', 'KEY': 'accept-always'}, expected_trace=())
+ 
++mark('channel bindings')
++e = realm.env.copy()
++e.update({'HOPS': '1', 'GSS_INIT_BINDING': 'a', 'GSS_ACCEPT_BINDING': 'b'})
++# The test mech will verify that the bindings are communicated to the
++# mech, but does not set the channel-bound flag.
++realm.run(['./t_bindings', '-s', 'h:host', 'a', 'b'], env=e, expected_msg='no')
++
+ success('NegoEx tests')
diff --git a/SOURCES/Add-client_aware_channel_bindings-option.patch b/SOURCES/Add-client_aware_channel_bindings-option.patch
new file mode 100644
index 0000000..b8faf33
--- /dev/null
+++ b/SOURCES/Add-client_aware_channel_bindings-option.patch
@@ -0,0 +1,265 @@
+From fe50c57f6428d7512868663bd226bdc9007148a9 Mon Sep 17 00:00:00 2001
+From: Isaac Boukris <iboukris@gmail.com>
+Date: Tue, 10 Mar 2020 13:13:17 +0100
+Subject: [PATCH] Add client_aware_channel_bindings option
+
+Add client support for KERB_AP_OPTIONS_CBT in the form of a profile
+option "client_aware_gss_bindings".  Adjust the make_etype_list()
+helper so that enctype negotiation and AP_OPTIONS can be included in
+the same IF-RELEVANT wrapper.
+
+[ghudson@mit.edu: refactored; edited documentation; wrote commit
+message]
+
+ticket: 8900
+(cherry picked from commit 225e6ef7f021cd1a8ef2a054af0ca58b7288fd81)
+(cherry picked from commit 2a08fe3d2d1972df4ffe37d4bb64b161889ff988)
+---
+ doc/admin/conf_files/krb5_conf.rst |   6 +
+ src/include/k5-int.h               |   1 +
+ src/lib/krb5/krb/mk_req_ext.c      | 177 +++++++++++++++--------------
+ 3 files changed, 98 insertions(+), 86 deletions(-)
+
+diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
+index 1d2aa7f68..1d8ffc1e4 100644
+--- a/doc/admin/conf_files/krb5_conf.rst
++++ b/doc/admin/conf_files/krb5_conf.rst
+@@ -383,6 +383,12 @@ The libdefaults section may contain any of the following relations:
+     credentials will fail if the client machine does not have a
+     keytab.  The default value is false.
+ 
++**client_aware_channel_bindings**
++    If this flag is true, then all application protocol authentication
++    requests will be flagged to indicate that the application supports
++    channel bindings when operating over a secure channel.  The
++    default value is false.
++
+ .. _realms:
+ 
+ [realms]
+diff --git a/src/include/k5-int.h b/src/include/k5-int.h
+index 0d9af3d95..eb18a4cd6 100644
+--- a/src/include/k5-int.h
++++ b/src/include/k5-int.h
+@@ -299,6 +299,7 @@ typedef unsigned char   u_char;
+ #define KRB5_CONF_V4_INSTANCE_CONVERT          "v4_instance_convert"
+ #define KRB5_CONF_V4_REALM                     "v4_realm"
+ #define KRB5_CONF_VERIFY_AP_REQ_NOFAIL         "verify_ap_req_nofail"
++#define KRB5_CONF_CLIENT_AWARE_GSS_BINDINGS    "client_aware_channel_bindings"
+ 
+ /* Cache configuration variables */
+ #define KRB5_CC_CONF_FAST_AVAIL                "fast_avail"
+diff --git a/src/lib/krb5/krb/mk_req_ext.c b/src/lib/krb5/krb/mk_req_ext.c
+index 9fc6a0e52..08504860c 100644
+--- a/src/lib/krb5/krb/mk_req_ext.c
++++ b/src/lib/krb5/krb/mk_req_ext.c
+@@ -68,10 +68,9 @@
+ */
+ 
+ static krb5_error_code
+-make_etype_list(krb5_context context,
+-                krb5_enctype *desired_etypes,
+-                krb5_enctype tkt_enctype,
+-                krb5_authdata ***authdata);
++make_ap_authdata(krb5_context context, krb5_enctype *desired_enctypes,
++                 krb5_enctype tkt_enctype, krb5_boolean client_aware_cb,
++                 krb5_authdata ***authdata_out);
+ 
+ static krb5_error_code
+ generate_authenticator(krb5_context,
+@@ -263,7 +262,8 @@ generate_authenticator(krb5_context context, krb5_authenticator *authent,
+                        krb5_enctype tkt_enctype)
+ {
+     krb5_error_code retval;
+-    krb5_authdata **ext_authdata = NULL;
++    krb5_authdata **ext_authdata = NULL, **ap_authdata, **combined;
++    int client_aware_cb;
+ 
+     authent->client = client;
+     authent->checksum = cksum;
+@@ -297,99 +297,104 @@ generate_authenticator(krb5_context context, krb5_authenticator *authent,
+         krb5_free_authdata(context, ext_authdata);
+     }
+ 
+-    /* Only send EtypeList if we prefer another enctype to tkt_enctype */
+-    if (desired_etypes != NULL && desired_etypes[0] != tkt_enctype) {
+-        TRACE_MK_REQ_ETYPES(context, desired_etypes);
+-        retval = make_etype_list(context, desired_etypes, tkt_enctype,
+-                                 &authent->authorization_data);
++    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. */
++    retval = make_ap_authdata(context, desired_etypes, tkt_enctype,
++                              client_aware_cb, &ap_authdata);
++    if (retval)
++        return retval;
++    if (ap_authdata != NULL) {
++        retval = krb5_merge_authdata(context, ap_authdata,
++                                     authent->authorization_data, &combined);
++        krb5_free_authdata(context, ap_authdata);
+         if (retval)
+             return retval;
++        krb5_free_authdata(context, authent->authorization_data);
++        authent->authorization_data = combined;
+     }
+ 
+     return(krb5_us_timeofday(context, &authent->ctime, &authent->cusec));
+ }
+ 
+-/* RFC 4537 */
++/* Set *out to a DER-encoded RFC 4537 etype list, or to NULL if no etype list
++ * should be sent. */
+ static krb5_error_code
+-make_etype_list(krb5_context context,
+-                krb5_enctype *desired_etypes,
+-                krb5_enctype tkt_enctype,
+-                krb5_authdata ***authdata)
++make_etype_list(krb5_context context, krb5_enctype *desired_enctypes,
++                krb5_enctype tkt_enctype, krb5_data **out)
+ {
+-    krb5_error_code code;
+-    krb5_etype_list etypes;
+-    krb5_data *enc_etype_list;
+-    krb5_data *ad_if_relevant;
+-    krb5_authdata *etype_adata[2], etype_adatum, **adata;
+-    int i;
++    krb5_etype_list etlist;
++    int count;
+ 
+-    etypes.etypes = desired_etypes;
++    *out = NULL;
+ 
+-    for (etypes.length = 0;
+-         etypes.etypes[etypes.length] != ENCTYPE_NULL;
+-         etypes.length++)
+-    {
+-        /*
+-         * RFC 4537:
+-         *
+-         *   If the enctype of the ticket session key is included in the enctype
+-         *   list sent by the client, it SHOULD be the last on the list;
+-         */
+-        if (etypes.length && etypes.etypes[etypes.length - 1] == tkt_enctype)
++    /* Only send a list if we prefer another enctype to tkt_enctype. */
++    if (desired_enctypes == NULL || desired_enctypes[0] == tkt_enctype)
++        return 0;
++
++    /* Count elements of desired_etypes, stopping at tkt_enctypes if present.
++     * (Per RFC 4537, it must be the last option if it is included.) */
++    for (count = 0; desired_enctypes[count] != ENCTYPE_NULL; count++) {
++        if (count > 0 && desired_enctypes[count - 1] == tkt_enctype)
+             break;
+     }
+ 
+-    code = encode_krb5_etype_list(&etypes, &enc_etype_list);
+-    if (code) {
+-        return code;
+-    }
+-
+-    etype_adatum.magic = KV5M_AUTHDATA;
+-    etype_adatum.ad_type = KRB5_AUTHDATA_ETYPE_NEGOTIATION;
+-    etype_adatum.length = enc_etype_list->length;
+-    etype_adatum.contents = (krb5_octet *)enc_etype_list->data;
+-
+-    etype_adata[0] = &etype_adatum;
+-    etype_adata[1] = NULL;
+-
+-    /* Wrap in AD-IF-RELEVANT container */
+-    code = encode_krb5_authdata(etype_adata, &ad_if_relevant);
+-    if (code) {
+-        krb5_free_data(context, enc_etype_list);
+-        return code;
+-    }
+-
+-    krb5_free_data(context, enc_etype_list);
+-
+-    adata = *authdata;
+-    if (adata == NULL) {
+-        adata = (krb5_authdata **)calloc(2, sizeof(krb5_authdata *));
+-        i = 0;
+-    } else {
+-        for (i = 0; adata[i] != NULL; i++)
+-            ;
+-
+-        adata = (krb5_authdata **)realloc(*authdata,
+-                                          (i + 2) * sizeof(krb5_authdata *));
+-    }
+-    if (adata == NULL) {
+-        krb5_free_data(context, ad_if_relevant);
+-        return ENOMEM;
+-    }
+-    *authdata = adata;
+-
+-    adata[i] = (krb5_authdata *)malloc(sizeof(krb5_authdata));
+-    if (adata[i] == NULL) {
+-        krb5_free_data(context, ad_if_relevant);
+-        return ENOMEM;
+-    }
+-    adata[i]->magic = KV5M_AUTHDATA;
+-    adata[i]->ad_type = KRB5_AUTHDATA_IF_RELEVANT;
+-    adata[i]->length = ad_if_relevant->length;
+-    adata[i]->contents = (krb5_octet *)ad_if_relevant->data;
+-    free(ad_if_relevant); /* contents owned by adata[i] */
+-
+-    adata[i + 1] = NULL;
+-
+-    return 0;
++    etlist.etypes = desired_enctypes;
++    etlist.length = count;
++    return encode_krb5_etype_list(&etlist, out);
++}
++
++/* Set *authdata_out to appropriate authenticator authdata for the request,
++ * encoded in a single AD_IF_RELEVANT element. */
++static krb5_error_code
++make_ap_authdata(krb5_context context, krb5_enctype *desired_enctypes,
++                 krb5_enctype tkt_enctype, krb5_boolean client_aware_cb,
++                 krb5_authdata ***authdata_out)
++{
++    krb5_error_code ret;
++    krb5_authdata etypes_ad, flags_ad, *list[3];
++    krb5_data *der_etypes = NULL;
++    size_t count = 0;
++    uint8_t flagbuf[4];
++    const uint32_t KERB_AP_OPTIONS_CBT = 0x4000;
++
++    *authdata_out = NULL;
++
++    /* Include an ETYPE_NEGOTIATION element if appropriate. */
++    ret = make_etype_list(context, desired_enctypes, tkt_enctype, &der_etypes);
++    if (ret)
++        goto cleanup;
++    if (der_etypes != NULL) {
++        etypes_ad.magic = KV5M_AUTHDATA;
++        etypes_ad.ad_type = KRB5_AUTHDATA_ETYPE_NEGOTIATION;
++        etypes_ad.length = der_etypes->length;
++        etypes_ad.contents = (uint8_t *)der_etypes->data;
++        list[count++] = &etypes_ad;
++    }
++
++    /* Include an AP_OPTIONS element if the CBT flag is configured. */
++    if (client_aware_cb != 0) {
++        store_32_le(KERB_AP_OPTIONS_CBT, flagbuf);
++        flags_ad.magic = KV5M_AUTHDATA;
++        flags_ad.ad_type = KRB5_AUTHDATA_AP_OPTIONS;
++        flags_ad.length = 4;
++        flags_ad.contents = flagbuf;
++        list[count++] = &flags_ad;
++    }
++
++    if (count > 0) {
++        list[count] = NULL;
++        ret = krb5_encode_authdata_container(context,
++                                             KRB5_AUTHDATA_IF_RELEVANT,
++                                             list, authdata_out);
++    }
++
++cleanup:
++    krb5_free_data(context, der_etypes);
++    return ret;
+ }
diff --git a/SOURCES/Implement-GSS_C_CHANNEL_BOUND_FLAG.patch b/SOURCES/Implement-GSS_C_CHANNEL_BOUND_FLAG.patch
new file mode 100644
index 0000000..347c0fb
--- /dev/null
+++ b/SOURCES/Implement-GSS_C_CHANNEL_BOUND_FLAG.patch
@@ -0,0 +1,92 @@
+From 3c15e9724dae95a4bf0899a8b8efc3e9e3f486ab Mon Sep 17 00:00:00 2001
+From: Alexander Scheel <ascheel@redhat.com>
+Date: Wed, 5 Jul 2017 11:38:30 -0400
+Subject: [PATCH] Implement GSS_C_CHANNEL_BOUND_FLAG
+
+Define a new channel-bound GSS return flag, and set it in the krb5
+mech if the initiator sent channel bindings matching the acceptor's.
+Do not error out if the acceptor specifies channel bindings and the
+initiator does not send them.
+
+[ghudson@mit.edu: simplified code changes; fleshed out commit message]
+
+[iboukris: cherry-picked from another PR and reduced in scope]
+
+ticket: 8899 (new)
+(cherry picked from commit 429a31146083fac21958631c2af572b08ec91022)
+(cherry picked from commit 3ea1d6296ced3a998e79356f9be212e4c5e6a5d5)
+---
+ src/lib/gssapi/generic/gssapi_ext.h      |  2 ++
+ src/lib/gssapi/krb5/accept_sec_context.c | 18 +++++++++++++-----
+ 2 files changed, 15 insertions(+), 5 deletions(-)
+
+diff --git a/src/lib/gssapi/generic/gssapi_ext.h b/src/lib/gssapi/generic/gssapi_ext.h
+index 218456e44..c675e8ebb 100644
+--- a/src/lib/gssapi/generic/gssapi_ext.h
++++ b/src/lib/gssapi/generic/gssapi_ext.h
+@@ -595,6 +595,8 @@ gss_store_cred_into(
+  * attribute (along with any applicable RFC 5587 attributes).
+  */
+ 
++#define GSS_C_CHANNEL_BOUND_FLAG 2048 /* 0x00000800 */
++
+ OM_uint32 KRB5_CALLCONV
+ gssspi_query_meta_data(
+     OM_uint32 *minor_status,
+diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
+index 70dd7fc0c..9d3e2f4fe 100644
+--- a/src/lib/gssapi/krb5/accept_sec_context.c
++++ b/src/lib/gssapi/krb5/accept_sec_context.c
+@@ -427,6 +427,9 @@ kg_process_extension(krb5_context context,
+                          GSS_C_SEQUENCE_FLAG | GSS_C_DCE_STYLE |        \
+                          GSS_C_IDENTIFY_FLAG | GSS_C_EXTENDED_ERROR_FLAG)
+ 
++/* A zero-value channel binding, for comparison */
++static const uint8_t null_cb[CB_MD5_LEN];
++
+ /*
+  * The krb5 GSS mech appropriates the authenticator checksum field from RFC
+  * 4120 to store structured data instead of a checksum, indicated with checksum
+@@ -435,9 +438,10 @@ kg_process_extension(krb5_context context,
+  *
+  * Interpret the checksum.  Read delegated creds into *deleg_out if it is not
+  * NULL.  Set *flags_out to the allowed subset of token flags, plus
+- * GSS_C_DELEG_FLAG if a delegated credential was present.  Process any
+- * extensions found using exts.  On error, set *code_out to a krb5_error code
+- * for use as a minor status value.
++ * GSS_C_DELEG_FLAG if a delegated credential was present and
++ * GSS_C_CHANNEL_BOUND_FLAG if matching channel bindings are present.  Process
++ * any extensions found using exts.  On error, set *code_out to a krb5_error
++ * code for use as a minor status value.
+  */
+ static OM_uint32
+ process_checksum(OM_uint32 *minor_status, krb5_context context,
+@@ -450,7 +454,7 @@ process_checksum(OM_uint32 *minor_status, krb5_context context,
+     krb5_error_code code = 0;
+     OM_uint32 status, option_id, token_flags;
+     size_t cb_len, option_len;
+-    krb5_boolean valid;
++    krb5_boolean valid, token_cb_present = FALSE, cb_match = FALSE;
+     krb5_key subkey;
+     krb5_data option, empty = empty_data();
+     krb5_checksum cb_cksum;
+@@ -516,7 +520,9 @@ process_checksum(OM_uint32 *minor_status, krb5_context context,
+                 goto fail;
+             }
+             assert(cb_cksum.length == cb_len);
+-            if (k5_bcmp(token_cb, cb_cksum.contents, cb_len) != 0) {
++            token_cb_present = (k5_bcmp(token_cb, null_cb, cb_len) != 0);
++            cb_match = (k5_bcmp(token_cb, cb_cksum.contents, cb_len) == 0);
++            if (token_cb_present && !cb_match) {
+                 status = GSS_S_BAD_BINDINGS;
+                 goto fail;
+             }
+@@ -525,6 +531,8 @@ process_checksum(OM_uint32 *minor_status, krb5_context context,
+         /* Read the token flags and accept some of them as context flags. */
+         token_flags = k5_input_get_uint32_le(&in);
+         *flags_out = token_flags & INITIATOR_FLAGS;
++        if (cb_match)
++            *flags_out |= GSS_C_CHANNEL_BOUND_FLAG;
+ 
+         /* Read the delegated credential if present. */
+         if (in.len >= 4 && (token_flags & GSS_C_DELEG_FLAG)) {
diff --git a/SOURCES/Implement-KERB_AP_OPTIONS_CBT-server-side.patch b/SOURCES/Implement-KERB_AP_OPTIONS_CBT-server-side.patch
new file mode 100644
index 0000000..156088b
--- /dev/null
+++ b/SOURCES/Implement-KERB_AP_OPTIONS_CBT-server-side.patch
@@ -0,0 +1,103 @@
+From 7aea9fc73fb508e3168581990eb2e2ff7a1aea31 Mon Sep 17 00:00:00 2001
+From: Isaac Boukris <iboukris@gmail.com>
+Date: Mon, 9 Mar 2020 16:04:21 +0100
+Subject: [PATCH] Implement KERB_AP_OPTIONS_CBT (server side)
+
+Add server support for Microsoft's KERB_AP_OPTIONS_CBT as described in
+MS-KILE.  If the client includes the AP option in the authenticator
+authdata and the server passed channel bindings, require the bindings
+to match.
+
+[ghudson@mit.edu: refactored to put more logic in the helper function;
+added a comment; clarified commit message]
+
+ticket: 8900 (new)
+(cherry picked from commit 4f7c77b64a048ca5e3199b26b31493698c777a9c)
+(cherry picked from commit 6407bf087fe53088d91efd09df736e979cd4e8db)
+---
+ src/include/krb5/krb5.hin                |  1 +
+ src/lib/gssapi/krb5/accept_sec_context.c | 45 +++++++++++++++++++++++-
+ 2 files changed, 45 insertions(+), 1 deletion(-)
+
+diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
+index f8269fb17..9264bede1 100644
+--- a/src/include/krb5/krb5.hin
++++ b/src/include/krb5/krb5.hin
+@@ -1915,6 +1915,7 @@ krb5_verify_checksum(krb5_context context, krb5_cksumtype ctype,
+ #define KRB5_AUTHDATA_SIGNTICKET        512     /**< formerly 142 in krb5 1.8 */
+ #define KRB5_AUTHDATA_FX_ARMOR 71
+ #define KRB5_AUTHDATA_AUTH_INDICATOR 97
++#define KRB5_AUTHDATA_AP_OPTIONS 143
+ /** @} */ /* end of KRB5_AUTHDATA group */
+ 
+ /* password change constants */
+diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
+index 9d3e2f4fe..175a24c4e 100644
+--- a/src/lib/gssapi/krb5/accept_sec_context.c
++++ b/src/lib/gssapi/krb5/accept_sec_context.c
+@@ -430,6 +430,32 @@ kg_process_extension(krb5_context context,
+ /* A zero-value channel binding, for comparison */
+ static const uint8_t null_cb[CB_MD5_LEN];
+ 
++/* Look for AP_OPTIONS in authdata.  If present and the options include
++ * KERB_AP_OPTIONS_CBT, set *cbt_out to true. */
++static krb5_error_code
++check_cbt(krb5_context context, krb5_authdata **authdata,
++          krb5_boolean *cbt_out)
++{
++    krb5_error_code code;
++    uint32_t ad_ap_options;
++    const uint32_t KERB_AP_OPTIONS_CBT = 0x4000;
++
++    *cbt_out = FALSE;
++
++    code = krb5_find_authdata(context, NULL, authdata,
++                              KRB5_AUTHDATA_AP_OPTIONS, &authdata);
++    if (code || authdata == NULL)
++        return code;
++    if (authdata[1] != NULL || authdata[0]->length != 4)
++        return KRB5KRB_AP_ERR_MSG_TYPE;
++
++    ad_ap_options = load_32_le(authdata[0]->contents);
++    if (ad_ap_options & KERB_AP_OPTIONS_CBT)
++        *cbt_out = TRUE;
++
++    return 0;
++}
++
+ /*
+  * The krb5 GSS mech appropriates the authenticator checksum field from RFC
+  * 4120 to store structured data instead of a checksum, indicated with checksum
+@@ -454,7 +480,7 @@ process_checksum(OM_uint32 *minor_status, krb5_context context,
+     krb5_error_code code = 0;
+     OM_uint32 status, option_id, token_flags;
+     size_t cb_len, option_len;
+-    krb5_boolean valid, token_cb_present = FALSE, cb_match = FALSE;
++    krb5_boolean valid, client_cbt, token_cb_present = FALSE, cb_match = FALSE;
+     krb5_key subkey;
+     krb5_data option, empty = empty_data();
+     krb5_checksum cb_cksum;
+@@ -582,6 +608,23 @@ process_checksum(OM_uint32 *minor_status, krb5_context context,
+         }
+     }
+ 
++    /*
++     * If the client asserts the KERB_AP_OPTIONS_CBT flag (from MS-KILE) in the
++     * authenticator authdata, and the acceptor passed channel bindings,
++     * require matching channel bindings from the client.  The intent is to
++     * prevent an authenticator generated for use outside of a TLS channel from
++     * being used inside of one.
++     */
++    code = check_cbt(context, authenticator->authorization_data, &client_cbt);
++    if (code) {
++        status = GSS_S_FAILURE;
++        goto fail;
++    }
++    if (client_cbt && acceptor_cb != GSS_C_NO_CHANNEL_BINDINGS && !cb_match) {
++        status = GSS_S_BAD_BINDINGS;
++        goto fail;
++    }
++
+     status = GSS_S_COMPLETE;
+ 
+ fail:
diff --git a/SOURCES/Improve-negoex_parse_token-code-hygiene.patch b/SOURCES/Improve-negoex_parse_token-code-hygiene.patch
new file mode 100644
index 0000000..c7136e1
--- /dev/null
+++ b/SOURCES/Improve-negoex_parse_token-code-hygiene.patch
@@ -0,0 +1,31 @@
+From ca72aa3a2e4ca8bc1b1c33e46ca59ed4b3f20393 Mon Sep 17 00:00:00 2001
+From: Robbie Harwood <rharwood@redhat.com>
+Date: Tue, 9 Jun 2020 16:23:37 -0400
+Subject: [PATCH] Improve negoex_parse_token() code hygiene
+
+If the while loop in negoex_parse_token() runs for zero iterations,
+major will be used initialized.  Currently this cannot happen, but
+only because both of the call sites check for zero-length tokens.
+Initialize major for safety.
+
+[ghudson@mit.edu: rewrote commit message]
+
+(cherry picked from commit 4f91b6f8fa6fe1de662b3fdac0d59b7758ec642a)
+(cherry picked from commit c726a72c68244129eb08b840b92144acfa776573)
+---
+ src/lib/gssapi/spnego/negoex_util.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/lib/gssapi/spnego/negoex_util.c b/src/lib/gssapi/spnego/negoex_util.c
+index 700368456..99580fd79 100644
+--- a/src/lib/gssapi/spnego/negoex_util.c
++++ b/src/lib/gssapi/spnego/negoex_util.c
+@@ -454,7 +454,7 @@ negoex_parse_token(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
+                    gss_const_buffer_t token,
+                    struct negoex_message **messages_out, size_t *count_out)
+ {
+-    OM_uint32 major;
++    OM_uint32 major = GSS_S_COMPLETE;
+     size_t count = 0;
+     struct k5input in;
+     struct negoex_message *messages = NULL, *newptr;
diff --git a/SOURCES/Omit-PA_FOR_USER-if-we-can-t-compute-its-checksum.patch b/SOURCES/Omit-PA_FOR_USER-if-we-can-t-compute-its-checksum.patch
new file mode 100644
index 0000000..a852fa7
--- /dev/null
+++ b/SOURCES/Omit-PA_FOR_USER-if-we-can-t-compute-its-checksum.patch
@@ -0,0 +1,35 @@
+From 4c4c22639eb2794e563370a2ee48a34dbdddc639 Mon Sep 17 00:00:00 2001
+From: Isaac Boukris <iboukris@gmail.com>
+Date: Sat, 6 Jun 2020 11:03:37 +0200
+Subject: [PATCH] Omit PA_FOR_USER if we can't compute its checksum
+
+OpenSSL in FIPS mode will refuse to perform hmac-md5.  Omit the legacy
+PA_FOR_USER element in this case rather than failing out.
+
+[ghudson@mit.edu: minor code and comment edits; wrote commit message]
+
+ticket: 8912 (new)
+(cherry picked from commit 03f122bdb22cfa53c7d855ed929c9541e56365e0)
+(cherry picked from commit 086de78292b8ae89aba8a72926831124da44205d)
+---
+ src/lib/krb5/krb/s4u_creds.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/lib/krb5/krb/s4u_creds.c b/src/lib/krb5/krb/s4u_creds.c
+index fc5c886d6..d8f486dc6 100644
+--- a/src/lib/krb5/krb/s4u_creds.c
++++ b/src/lib/krb5/krb/s4u_creds.c
+@@ -534,6 +534,13 @@ krb5_get_self_cred_from_kdc(krb5_context context,
+         if (s4u_user.user_id.user != NULL && s4u_user.user_id.user->length) {
+             code = build_pa_for_user(context, tgtptr, &s4u_user.user_id,
+                                      &in_padata[1]);
++            /*
++             * If we couldn't compute the hmac-md5 checksum, send only the
++             * KRB5_PADATA_S4U_X509_USER; this will still work against modern
++             * Windows and MIT KDCs.
++             */
++            if (code == KRB5_CRYPTO_INTERNAL)
++                code = 0;
+             if (code != 0) {
+                 krb5_free_pa_data(context, in_padata);
+                 goto cleanup;
diff --git a/SOURCES/Pass-channel-bindings-through-SPNEGO.patch b/SOURCES/Pass-channel-bindings-through-SPNEGO.patch
new file mode 100644
index 0000000..8ab4bbf
--- /dev/null
+++ b/SOURCES/Pass-channel-bindings-through-SPNEGO.patch
@@ -0,0 +1,257 @@
+From d98f8867f8245b3c9dd506271897d0f03d69ae49 Mon Sep 17 00:00:00 2001
+From: Isaac Boukris <iboukris@gmail.com>
+Date: Tue, 28 Apr 2020 18:15:55 +0200
+Subject: [PATCH] Pass channel bindings through SPNEGO
+
+ticket: 8907 (new)
+(cherry picked from commit d16325a24c34ec9a5f6fb4910987f162e0d4d9cd)
+(cherry picked from commit ee79bd43005245d3e5a2d3ec6d61146945e77717)
+---
+ src/lib/gssapi/spnego/gssapiP_negoex.h |  8 ++---
+ src/lib/gssapi/spnego/negoex_ctx.c     | 34 +++++++++++----------
+ src/lib/gssapi/spnego/spnego_mech.c    | 41 +++++++++++++-------------
+ 3 files changed, 43 insertions(+), 40 deletions(-)
+
+diff --git a/src/lib/gssapi/spnego/gssapiP_negoex.h b/src/lib/gssapi/spnego/gssapiP_negoex.h
+index 44b08f523..489ab7c42 100644
+--- a/src/lib/gssapi/spnego/gssapiP_negoex.h
++++ b/src/lib/gssapi/spnego/gssapiP_negoex.h
+@@ -201,10 +201,10 @@ negoex_restrict_auth_schemes(spnego_gss_ctx_id_t ctx,
+ OM_uint32
+ negoex_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
+             gss_name_t target_name, OM_uint32 req_flags, OM_uint32 time_req,
+-            gss_buffer_t input_token, gss_buffer_t output_token,
+-            OM_uint32 *time_rec);
++            gss_buffer_t input_token, gss_channel_bindings_t bindings,
++            gss_buffer_t output_token, OM_uint32 *time_rec);
+ 
+ OM_uint32
+ negoex_accept(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
+-              gss_buffer_t input_token, gss_buffer_t output_token,
+-              OM_uint32 *time_rec);
++              gss_buffer_t input_token, gss_channel_bindings_t bindings,
++              gss_buffer_t output_token, OM_uint32 *time_rec);
+diff --git a/src/lib/gssapi/spnego/negoex_ctx.c b/src/lib/gssapi/spnego/negoex_ctx.c
+index 18d9d4147..8848ee4db 100644
+--- a/src/lib/gssapi/spnego/negoex_ctx.c
++++ b/src/lib/gssapi/spnego/negoex_ctx.c
+@@ -276,7 +276,8 @@ static OM_uint32
+ mech_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
+           gss_name_t target, OM_uint32 req_flags, OM_uint32 time_req,
+           struct negoex_message *messages, size_t nmessages,
+-          gss_buffer_t output_token, OM_uint32 *time_rec)
++          gss_channel_bindings_t bindings, gss_buffer_t output_token,
++          OM_uint32 *time_rec)
+ {
+     OM_uint32 major, first_major = 0, first_minor = 0;
+     struct negoex_auth_mech *mech = NULL;
+@@ -316,10 +317,9 @@ mech_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
+         mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
+ 
+         major = gss_init_sec_context(minor, cred, &mech->mech_context, target,
+-                                     mech->oid, req_flags, time_req,
+-                                     GSS_C_NO_CHANNEL_BINDINGS, input_token,
+-                                     &ctx->actual_mech, output_token,
+-                                     &ctx->ctx_flags, time_rec);
++                                     mech->oid, req_flags, time_req, bindings,
++                                     input_token, &ctx->actual_mech,
++                                     output_token, &ctx->ctx_flags, time_rec);
+ 
+         if (major == GSS_S_COMPLETE)
+             mech->complete = 1;
+@@ -351,7 +351,8 @@ mech_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
+ static OM_uint32
+ mech_accept(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
+             gss_cred_id_t cred, struct negoex_message *messages,
+-            size_t nmessages, gss_buffer_t output_token, OM_uint32 *time_rec)
++            size_t nmessages, gss_channel_bindings_t bindings,
++            gss_buffer_t output_token, OM_uint32 *time_rec)
+ {
+     OM_uint32 major, tmpmin;
+     struct negoex_auth_mech *mech;
+@@ -395,10 +396,10 @@ mech_accept(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
+         gss_release_cred(&tmpmin, &ctx->deleg_cred);
+ 
+     major = gss_accept_sec_context(minor, &mech->mech_context, cred,
+-                                   &msg->token, GSS_C_NO_CHANNEL_BINDINGS,
+-                                   &ctx->internal_name, &ctx->actual_mech,
+-                                   output_token, &ctx->ctx_flags,
+-                                   time_rec, &ctx->deleg_cred);
++                                   &msg->token, bindings, &ctx->internal_name,
++                                   &ctx->actual_mech, output_token,
++                                   &ctx->ctx_flags, time_rec,
++                                   &ctx->deleg_cred);
+ 
+     if (major == GSS_S_COMPLETE)
+         mech->complete = 1;
+@@ -609,8 +610,8 @@ make_output_token(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
+ OM_uint32
+ negoex_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
+             gss_name_t target_name, OM_uint32 req_flags, OM_uint32 time_req,
+-            gss_buffer_t input_token, gss_buffer_t output_token,
+-            OM_uint32 *time_rec)
++            gss_buffer_t input_token, gss_channel_bindings_t bindings,
++            gss_buffer_t output_token, OM_uint32 *time_rec)
+ {
+     OM_uint32 major, tmpmin;
+     gss_buffer_desc mech_output_token = GSS_C_EMPTY_BUFFER;
+@@ -663,7 +664,8 @@ negoex_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
+     /* Process the input token and/or produce an output token.  This may prune
+      * the mech list, but on success there will be at least one mech entry. */
+     major = mech_init(minor, ctx, cred, target_name, req_flags, time_req,
+-                      messages, nmessages, &mech_output_token, time_rec);
++                      messages, nmessages, bindings, &mech_output_token,
++                      time_rec);
+     if (major != GSS_S_COMPLETE)
+         goto cleanup;
+     assert(!K5_TAILQ_EMPTY(&ctx->negoex_mechs));
+@@ -701,8 +703,8 @@ cleanup:
+ 
+ OM_uint32
+ negoex_accept(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
+-              gss_buffer_t input_token, gss_buffer_t output_token,
+-              OM_uint32 *time_rec)
++              gss_buffer_t input_token, gss_channel_bindings_t bindings,
++              gss_buffer_t output_token, OM_uint32 *time_rec)
+ {
+     OM_uint32 major, tmpmin;
+     gss_buffer_desc mech_output_token = GSS_C_EMPTY_BUFFER;
+@@ -754,7 +756,7 @@ negoex_accept(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
+      * prune the list to a single mech.  Continue on error if an output token
+      * is generated, so that we send the token to the initiator.
+      */
+-    major = mech_accept(minor, ctx, cred, messages, nmessages,
++    major = mech_accept(minor, ctx, cred, messages, nmessages, bindings,
+                         &mech_output_token, time_rec);
+     if (major != GSS_S_COMPLETE && mech_output_token.length == 0)
+         goto cleanup;
+diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c
+index 594fc5894..4cf011143 100644
+--- a/src/lib/gssapi/spnego/spnego_mech.c
++++ b/src/lib/gssapi/spnego/spnego_mech.c
+@@ -130,6 +130,7 @@ init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
+ static OM_uint32
+ init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
+ 		   OM_uint32, gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
++		   gss_channel_bindings_t,
+ 		   gss_buffer_t, OM_uint32 *, send_token_flag *);
+ 
+ static OM_uint32
+@@ -144,8 +145,8 @@ acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
+ 		OM_uint32 *, send_token_flag *);
+ static OM_uint32
+ acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
+-		 gss_buffer_t, gss_buffer_t, OM_uint32 *, OM_uint32 *,
+-		 send_token_flag *);
++		 gss_buffer_t, gss_channel_bindings_t, gss_buffer_t,
++		 OM_uint32 *, OM_uint32 *, send_token_flag *);
+ 
+ static gss_OID
+ negotiate_mech(spnego_gss_ctx_id_t, gss_OID_set, OM_uint32 *);
+@@ -905,6 +906,7 @@ init_ctx_call_init(OM_uint32 *minor_status,
+ 		   OM_uint32 req_flags,
+ 		   OM_uint32 time_req,
+ 		   gss_buffer_t mechtok_in,
++		   gss_channel_bindings_t bindings,
+ 		   gss_buffer_t mechtok_out,
+ 		   OM_uint32 *time_rec,
+ 		   send_token_flag *send_token)
+@@ -921,15 +923,14 @@ init_ctx_call_init(OM_uint32 *minor_status,
+ 	if (gss_oid_equal(sc->internal_mech, &negoex_mech)) {
+ 		ret = negoex_init(minor_status, sc, mcred, target_name,
+ 				  mech_req_flags, time_req, mechtok_in,
+-				  mechtok_out, time_rec);
++				  bindings, mechtok_out, time_rec);
+ 	} else {
+ 		ret = gss_init_sec_context(minor_status, mcred,
+ 					   &sc->ctx_handle, target_name,
+ 					   sc->internal_mech, mech_req_flags,
+-					   time_req, GSS_C_NO_CHANNEL_BINDINGS,
+-					   mechtok_in, &sc->actual_mech,
+-					   mechtok_out, &sc->ctx_flags,
+-					   time_rec);
++					   time_req, bindings, mechtok_in,
++					   &sc->actual_mech, mechtok_out,
++					   &sc->ctx_flags, time_rec);
+ 	}
+ 
+ 	/* Bail out if the acceptor gave us an error token but the mech didn't
+@@ -981,8 +982,8 @@ init_ctx_call_init(OM_uint32 *minor_status,
+ 	gss_delete_sec_context(&tmpmin, &sc->ctx_handle, GSS_C_NO_BUFFER);
+ 	tmpret = init_ctx_call_init(&tmpmin, sc, spcred, acc_negState,
+ 				    target_name, req_flags, time_req,
+-				    mechtok_in, mechtok_out, time_rec,
+-				    send_token);
++				    mechtok_in, bindings, mechtok_out,
++				    time_rec, send_token);
+ 	if (HARD_ERROR(tmpret))
+ 		goto fail;
+ 	*minor_status = tmpmin;
+@@ -1004,7 +1005,7 @@ spnego_gss_init_sec_context(
+ 			gss_OID mech_type,
+ 			OM_uint32 req_flags,
+ 			OM_uint32 time_req,
+-			gss_channel_bindings_t input_chan_bindings,
++			gss_channel_bindings_t bindings,
+ 			gss_buffer_t input_token,
+ 			gss_OID *actual_mech,
+ 			gss_buffer_t output_token,
+@@ -1084,8 +1085,8 @@ spnego_gss_init_sec_context(
+ 	if (!spnego_ctx->mech_complete) {
+ 		ret = init_ctx_call_init(minor_status, spnego_ctx, spcred,
+ 					 acc_negState, target_name, req_flags,
+-					 time_req, mechtok_in, &mechtok_out,
+-					 time_rec, &send_token);
++					 time_req, mechtok_in, bindings,
++					 &mechtok_out, time_rec, &send_token);
+ 		if (ret != GSS_S_COMPLETE)
+ 			goto cleanup;
+ 
+@@ -1542,8 +1543,9 @@ cleanup:
+ static OM_uint32
+ acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
+ 		 spnego_gss_cred_id_t spcred, gss_buffer_t mechtok_in,
+-		 gss_buffer_t mechtok_out, OM_uint32 *time_rec,
+-		 OM_uint32 *negState, send_token_flag *tokflag)
++		 gss_channel_bindings_t bindings, gss_buffer_t mechtok_out,
++		 OM_uint32 *time_rec, OM_uint32 *negState,
++		 send_token_flag *tokflag)
+ {
+ 	OM_uint32 ret, tmpmin;
+ 	gss_OID_desc mechoid;
+@@ -1568,13 +1570,12 @@ acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
+ 	mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
+ 	if (negoex) {
+ 		ret = negoex_accept(minor_status, sc, mcred, mechtok_in,
+-				    mechtok_out, time_rec);
++				    bindings, mechtok_out, time_rec);
+ 	} else {
+ 		(void) gss_release_name(&tmpmin, &sc->internal_name);
+ 		(void) gss_release_cred(&tmpmin, &sc->deleg_cred);
+ 		ret = gss_accept_sec_context(minor_status, &sc->ctx_handle,
+-					     mcred, mechtok_in,
+-					     GSS_C_NO_CHANNEL_BINDINGS,
++					     mcred, mechtok_in, bindings,
+ 					     &sc->internal_name,
+ 					     &sc->actual_mech, mechtok_out,
+ 					     &sc->ctx_flags, time_rec,
+@@ -1620,7 +1621,7 @@ spnego_gss_accept_sec_context(
+ 			    gss_ctx_id_t *context_handle,
+ 			    gss_cred_id_t verifier_cred_handle,
+ 			    gss_buffer_t input_token,
+-			    gss_channel_bindings_t input_chan_bindings,
++			    gss_channel_bindings_t bindings,
+ 			    gss_name_t *src_name,
+ 			    gss_OID *mech_type,
+ 			    gss_buffer_t output_token,
+@@ -1734,8 +1735,8 @@ spnego_gss_accept_sec_context(
+ 	 */
+ 	if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
+ 		ret = acc_ctx_call_acc(minor_status, sc, spcred, mechtok_in,
+-				       &mechtok_out, time_rec, &negState,
+-				       &return_token);
++				       bindings, &mechtok_out, time_rec,
++				       &negState, &return_token);
+ 	}
+ 
+ 	/* Step 3: process or generate the MIC, if the negotiated mech is
diff --git a/SOURCES/Refactor-krb5-GSS-checksum-handling.patch b/SOURCES/Refactor-krb5-GSS-checksum-handling.patch
new file mode 100644
index 0000000..8e97028
--- /dev/null
+++ b/SOURCES/Refactor-krb5-GSS-checksum-handling.patch
@@ -0,0 +1,480 @@
+From c4a49f5b42916fdbb34c72a11adb42ff879c50c3 Mon Sep 17 00:00:00 2001
+From: Alexander Scheel <ascheel@redhat.com>
+Date: Fri, 30 Jun 2017 16:03:01 -0400
+Subject: [PATCH] Refactor krb5 GSS checksum handling
+
+Separate out checksum handling from kg_accept_krb5() into a new helper
+process_checksum().
+
+[ghudson@mit.edu: simplified checksum processing and made it use
+k5-input.h instead of TREAD_ macros; moved more flag handling into
+helper]
+
+[iboukris: adjusted helper function arguments, allowing access to the
+full authenticator for subsequent changes]
+
+(cherry picked from commit 64d56233f9816a2a93f6e8d3030c8ed6ce397735)
+[rharwood@redhat.com: problem with typo fix commit, I think]
+(cherry picked from commit a34b7c50e62c19f80d39ece6a72017dac781df64)
+---
+ src/lib/gssapi/krb5/accept_sec_context.c | 383 +++++++++++------------
+ 1 file changed, 179 insertions(+), 204 deletions(-)
+
+diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
+index c5bddb1e8..70dd7fc0c 100644
+--- a/src/lib/gssapi/krb5/accept_sec_context.c
++++ b/src/lib/gssapi/krb5/accept_sec_context.c
+@@ -98,6 +98,7 @@
+  */
+ 
+ #include "k5-int.h"
++#include "k5-input.h"
+ #include "gssapiP_krb5.h"
+ #ifdef HAVE_MEMORY_H
+ #include <memory.h>
+@@ -413,6 +414,174 @@ kg_process_extension(krb5_context context,
+     return code;
+ }
+ 
++/* The length of the MD5 channel bindings in an 0x8003 checksum */
++#define CB_MD5_LEN 16
++
++/* The minimum length of an 0x8003 checksum value (4-byte channel bindings
++ * length, 16-byte channel bindings, 4-byte flags) */
++#define MIN_8003_LEN (4 + CB_MD5_LEN + 4)
++
++/* The flags we accept from the initiator's authenticator checksum. */
++#define INITIATOR_FLAGS (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |           \
++                         GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |        \
++                         GSS_C_SEQUENCE_FLAG | GSS_C_DCE_STYLE |        \
++                         GSS_C_IDENTIFY_FLAG | GSS_C_EXTENDED_ERROR_FLAG)
++
++/*
++ * The krb5 GSS mech appropriates the authenticator checksum field from RFC
++ * 4120 to store structured data instead of a checksum, indicated with checksum
++ * type 0x8003 (see RFC 4121 section 4.1.1).  Some implementations instead send
++ * no checksum, or a regular checksum over empty data.
++ *
++ * Interpret the checksum.  Read delegated creds into *deleg_out if it is not
++ * NULL.  Set *flags_out to the allowed subset of token flags, plus
++ * GSS_C_DELEG_FLAG if a delegated credential was present.  Process any
++ * extensions found using exts.  On error, set *code_out to a krb5_error code
++ * for use as a minor status value.
++ */
++static OM_uint32
++process_checksum(OM_uint32 *minor_status, krb5_context context,
++                 gss_channel_bindings_t acceptor_cb,
++                 krb5_auth_context auth_context, krb5_flags ap_req_options,
++                 krb5_authenticator *authenticator, krb5_gss_ctx_ext_t exts,
++                 krb5_gss_cred_id_t *deleg_out, krb5_ui_4 *flags_out,
++                 krb5_error_code *code_out)
++{
++    krb5_error_code code = 0;
++    OM_uint32 status, option_id, token_flags;
++    size_t cb_len, option_len;
++    krb5_boolean valid;
++    krb5_key subkey;
++    krb5_data option, empty = empty_data();
++    krb5_checksum cb_cksum;
++    const uint8_t *token_cb, *option_bytes;
++    struct k5input in;
++    const krb5_checksum *cksum = authenticator->checksum;
++
++    cb_cksum.contents = NULL;
++
++    if (cksum == NULL) {
++        /*
++         * Some SMB client implementations use handcrafted GSSAPI code that
++         * does not provide a checksum.  MS-KILE documents that the Microsoft
++         * implementation considers a missing checksum acceptable; the server
++         * assumes all flags are unset in this case, and does not check channel
++         * bindings.
++         */
++        *flags_out = 0;
++    } else if (cksum->checksum_type != CKSUMTYPE_KG_CB) {
++        /* Samba sends a regular checksum. */
++        code = krb5_auth_con_getkey_k(context, auth_context, &subkey);
++        if (code) {
++            status = GSS_S_FAILURE;
++            goto fail;
++        }
++
++        /* Verifying the checksum ensures that this authenticator wasn't
++         * replayed from one with a checksum over actual data. */
++        code = krb5_k_verify_checksum(context, subkey,
++                                      KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM, &empty,
++                                      cksum, &valid);
++        krb5_k_free_key(context, subkey);
++        if (code || !valid) {
++            status = GSS_S_BAD_SIG;
++            goto fail;
++        }
++
++        /* Use ap_options from the request to guess the mutual flag. */
++        *flags_out = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
++        if (ap_req_options & AP_OPTS_MUTUAL_REQUIRED)
++            *flags_out |= GSS_C_MUTUAL_FLAG;
++    } else {
++        /* The checksum must contain at least a fixed 24-byte part. */
++        if (cksum->length < MIN_8003_LEN) {
++            status = GSS_S_BAD_BINDINGS;
++            goto fail;
++        }
++
++        k5_input_init(&in, cksum->contents, cksum->length);
++        cb_len = k5_input_get_uint32_le(&in);
++        if (cb_len != CB_MD5_LEN) {
++            code = KG_BAD_LENGTH;
++            status = GSS_S_FAILURE;
++            goto fail;
++        }
++
++        token_cb = k5_input_get_bytes(&in, cb_len);
++        if (acceptor_cb != GSS_C_NO_CHANNEL_BINDINGS) {
++            code = kg_checksum_channel_bindings(context, acceptor_cb,
++                                                &cb_cksum);
++            if (code) {
++                status = GSS_S_BAD_BINDINGS;
++                goto fail;
++            }
++            assert(cb_cksum.length == cb_len);
++            if (k5_bcmp(token_cb, cb_cksum.contents, cb_len) != 0) {
++                status = GSS_S_BAD_BINDINGS;
++                goto fail;
++            }
++        }
++
++        /* Read the token flags and accept some of them as context flags. */
++        token_flags = k5_input_get_uint32_le(&in);
++        *flags_out = token_flags & INITIATOR_FLAGS;
++
++        /* Read the delegated credential if present. */
++        if (in.len >= 4 && (token_flags & GSS_C_DELEG_FLAG)) {
++            option_id = k5_input_get_uint16_le(&in);
++            option_len = k5_input_get_uint16_le(&in);
++            option_bytes = k5_input_get_bytes(&in, option_len);
++            option = make_data((uint8_t *)option_bytes, option_len);
++            if (in.status) {
++                code = KG_BAD_LENGTH;
++                status = GSS_S_FAILURE;
++                goto fail;
++            }
++            if (option_id != KRB5_GSS_FOR_CREDS_OPTION) {
++                status = GSS_S_FAILURE;
++                goto fail;
++            }
++
++            /* Store the delegated credential. */
++            code = rd_and_store_for_creds(context, auth_context, &option,
++                                          deleg_out);
++            if (code) {
++                status = GSS_S_FAILURE;
++                goto fail;
++            }
++            *flags_out |= GSS_C_DELEG_FLAG;
++        }
++
++        /* Process any extensions at the end of the checksum.  Extensions use
++         * 4-byte big-endian tag and length instead of 2-byte little-endian. */
++        while (in.len > 0) {
++            option_id = k5_input_get_uint32_be(&in);
++            option_len = k5_input_get_uint32_be(&in);
++            option_bytes = k5_input_get_bytes(&in, option_len);
++            option = make_data((uint8_t *)option_bytes, option_len);
++            if (in.status) {
++                code = KG_BAD_LENGTH;
++                status = GSS_S_FAILURE;
++                goto fail;
++            }
++
++            code = kg_process_extension(context, auth_context, option_id,
++                                        &option, exts);
++            if (code) {
++                status = GSS_S_FAILURE;
++                goto fail;
++            }
++        }
++    }
++
++    status = GSS_S_COMPLETE;
++
++fail:
++    free(cb_cksum.contents);
++    *code_out = code;
++    return status;
++}
++
+ static OM_uint32
+ kg_accept_krb5(minor_status, context_handle,
+                verifier_cred_handle, input_token,
+@@ -433,17 +602,13 @@ kg_accept_krb5(minor_status, context_handle,
+     krb5_gss_ctx_ext_t exts;
+ {
+     krb5_context context;
+-    unsigned char *ptr, *ptr2;
++    unsigned char *ptr;
+     char *sptr;
+-    OM_uint32 tmp;
+-    size_t md5len;
+     krb5_gss_cred_id_t cred = 0;
+     krb5_data ap_rep, ap_req;
+-    unsigned int i;
+     krb5_error_code code;
+     krb5_address addr, *paddr;
+     krb5_authenticator *authdat = 0;
+-    krb5_checksum reqcksum;
+     krb5_gss_name_t name = NULL;
+     krb5_ui_4 gss_flags = 0;
+     krb5_gss_ctx_id_rec *ctx = NULL;
+@@ -451,8 +616,6 @@ kg_accept_krb5(minor_status, context_handle,
+     gss_buffer_desc token;
+     krb5_auth_context auth_context = NULL;
+     krb5_ticket * ticket = NULL;
+-    int option_id;
+-    krb5_data option;
+     const gss_OID_desc *mech_used = NULL;
+     OM_uint32 major_status = GSS_S_FAILURE;
+     OM_uint32 tmp_minor_status;
+@@ -463,7 +626,6 @@ kg_accept_krb5(minor_status, context_handle,
+     krb5int_access kaccess;
+     int cred_rcache = 0;
+     int no_encap = 0;
+-    int token_deleg_flag = 0;
+     krb5_flags ap_req_options = 0;
+     krb5_enctype negotiated_etype;
+     krb5_authdata_context ad_context = NULL;
+@@ -489,7 +651,6 @@ kg_accept_krb5(minor_status, context_handle,
+     output_token->length = 0;
+     output_token->value = NULL;
+     token.value = 0;
+-    reqcksum.contents = 0;
+     ap_req.data = 0;
+     ap_rep.data = 0;
+ 
+@@ -654,195 +815,16 @@ kg_accept_krb5(minor_status, context_handle,
+ 
+     krb5_auth_con_getauthenticator(context, auth_context, &authdat);
+ 
+-    if (authdat->checksum == NULL) {
+-        /*
+-         * Some SMB client implementations use handcrafted GSSAPI code that
+-         * does not provide a checksum.  MS-KILE documents that the Microsoft
+-         * implementation considers a missing checksum acceptable; the server
+-         * assumes all flags are unset in this case, and does not check channel
+-         * bindings.
+-         */
+-        gss_flags = 0;
+-    } else if (authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) {
+-        /* Samba does not send 0x8003 GSS-API checksums */
+-        krb5_boolean valid;
+-        krb5_key subkey;
+-        krb5_data zero;
++    major_status = process_checksum(minor_status, context, input_chan_bindings,
++                                    auth_context, ap_req_options,
++                                    authdat, exts,
++                                    delegated_cred_handle ? &deleg_cred : NULL,
++                                    &gss_flags, &code);
+ 
+-        code = krb5_auth_con_getkey_k(context, auth_context, &subkey);
+-        if (code) {
+-            major_status = GSS_S_FAILURE;
+-            goto fail;
+-        }
++    if (major_status != GSS_S_COMPLETE)
++        goto fail;
+ 
+-        zero.length = 0;
+-        zero.data = "";
+-
+-        code = krb5_k_verify_checksum(context,
+-                                      subkey,
+-                                      KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM,
+-                                      &zero,
+-                                      authdat->checksum,
+-                                      &valid);
+-        krb5_k_free_key(context, subkey);
+-        if (code || !valid) {
+-            major_status = GSS_S_BAD_SIG;
+-            goto fail;
+-        }
+-
+-        /* Use ap_options from the request to guess the mutual flag. */
+-        gss_flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
+-        if (ap_req_options & AP_OPTS_MUTUAL_REQUIRED)
+-            gss_flags |= GSS_C_MUTUAL_FLAG;
+-    } else {
+-        /* gss krb5 v1 */
+-
+-        /* stash this now, for later. */
+-        code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &md5len);
+-        if (code) {
+-            major_status = GSS_S_FAILURE;
+-            goto fail;
+-        }
+-
+-        /* verify that the checksum is correct */
+-
+-        /*
+-          The checksum may be either exactly 24 bytes, in which case
+-          no options are specified, or greater than 24 bytes, in which case
+-          one or more options are specified. Currently, the only valid
+-          option is KRB5_GSS_FOR_CREDS_OPTION ( = 1 ).
+-        */
+-
+-        if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) ||
+-            (authdat->checksum->length < 24)) {
+-            code = 0;
+-            major_status = GSS_S_BAD_BINDINGS;
+-            goto fail;
+-        }
+-
+-        ptr = (unsigned char *) authdat->checksum->contents;
+-
+-        TREAD_INT(ptr, tmp, 0);
+-
+-        if (tmp != md5len) {
+-            code = KG_BAD_LENGTH;
+-            major_status = GSS_S_FAILURE;
+-            goto fail;
+-        }
+-
+-        /*
+-          The following section of code attempts to implement the
+-          optional channel binding facility as described in RFC2743.
+-
+-          Since this facility is optional channel binding may or may
+-          not have been provided by either the client or the server.
+-
+-          If the server has specified input_chan_bindings equal to
+-          GSS_C_NO_CHANNEL_BINDINGS then we skip the check.  If
+-          the server does provide channel bindings then we compute
+-          a checksum and compare against those provided by the
+-          client.         */
+-
+-        if ((code = kg_checksum_channel_bindings(context,
+-                                                 input_chan_bindings,
+-                                                 &reqcksum))) {
+-            major_status = GSS_S_BAD_BINDINGS;
+-            goto fail;
+-        }
+-
+-        /* Always read the clients bindings - eventhough we might ignore them */
+-        TREAD_STR(ptr, ptr2, reqcksum.length);
+-
+-        if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS ) {
+-            if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
+-                xfree(reqcksum.contents);
+-                reqcksum.contents = 0;
+-                code = 0;
+-                major_status = GSS_S_BAD_BINDINGS;
+-                goto fail;
+-            }
+-
+-        }
+-
+-        xfree(reqcksum.contents);
+-        reqcksum.contents = 0;
+-
+-        /* Read the token flags.  Remember if GSS_C_DELEG_FLAG was set, but
+-         * mask it out until we actually read a delegated credential. */
+-        TREAD_INT(ptr, gss_flags, 0);
+-        token_deleg_flag = (gss_flags & GSS_C_DELEG_FLAG);
+-        gss_flags &= ~GSS_C_DELEG_FLAG;
+-
+-        /* if the checksum length > 24, there are options to process */
+-
+-        i = authdat->checksum->length - 24;
+-        if (i && token_deleg_flag) {
+-            if (i >= 4) {
+-                TREAD_INT16(ptr, option_id, 0);
+-                TREAD_INT16(ptr, option.length, 0);
+-                i -= 4;
+-
+-                if (i < option.length) {
+-                    code = KG_BAD_LENGTH;
+-                    major_status = GSS_S_FAILURE;
+-                    goto fail;
+-                }
+-
+-                /* have to use ptr2, since option.data is wrong type and
+-                   macro uses ptr as both lvalue and rvalue */
+-
+-                TREAD_STR(ptr, ptr2, option.length);
+-                option.data = (char *) ptr2;
+-
+-                i -= option.length;
+-
+-                if (option_id != KRB5_GSS_FOR_CREDS_OPTION) {
+-                    major_status = GSS_S_FAILURE;
+-                    goto fail;
+-                }
+-
+-                /* store the delegated credential */
+-
+-                code = rd_and_store_for_creds(context, auth_context, &option,
+-                                              (delegated_cred_handle) ?
+-                                              &deleg_cred : NULL);
+-                if (code) {
+-                    major_status = GSS_S_FAILURE;
+-                    goto fail;
+-                }
+-
+-                gss_flags |= GSS_C_DELEG_FLAG;
+-            } /* if i >= 4 */
+-            /* ignore any additional trailing data, for now */
+-        }
+-        while (i > 0) {
+-            /* Process Type-Length-Data options */
+-            if (i < 8) {
+-                code = KG_BAD_LENGTH;
+-                major_status = GSS_S_FAILURE;
+-                goto fail;
+-            }
+-            TREAD_INT(ptr, option_id, 1);
+-            TREAD_INT(ptr, option.length, 1);
+-            i -= 8;
+-            if (i < option.length) {
+-                code = KG_BAD_LENGTH;
+-                major_status = GSS_S_FAILURE;
+-                goto fail;
+-            }
+-            TREAD_STR(ptr, ptr2, option.length);
+-            option.data = (char *)ptr2;
+-
+-            i -= option.length;
+-
+-            code = kg_process_extension(context, auth_context,
+-                                        option_id, &option, exts);
+-            if (code != 0) {
+-                major_status = GSS_S_FAILURE;
+-                goto fail;
+-            }
+-        }
+-    }
++    major_status = GSS_S_FAILURE;
+ 
+     if (exts->iakerb.conv && !exts->iakerb.verified) {
+         major_status = GSS_S_BAD_SIG;
+@@ -869,12 +851,7 @@ kg_accept_krb5(minor_status, context_handle,
+     ctx->mech_used = (gss_OID) mech_used;
+     ctx->auth_context = auth_context;
+     ctx->initiate = 0;
+-    ctx->gss_flags = (GSS_C_TRANS_FLAG |
+-                      ((gss_flags) & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |
+-                                      GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
+-                                      GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG |
+-                                      GSS_C_DCE_STYLE | GSS_C_IDENTIFY_FLAG |
+-                                      GSS_C_EXTENDED_ERROR_FLAG)));
++    ctx->gss_flags = gss_flags | GSS_C_TRANS_FLAG;
+     ctx->seed_init = 0;
+     ctx->cred_rcache = cred_rcache;
+ 
+@@ -1161,8 +1138,6 @@ fail:
+ 
+         krb5_auth_con_free(context, auth_context);
+     }
+-    if (reqcksum.contents)
+-        xfree(reqcksum.contents);
+     if (ap_rep.data)
+         krb5_free_data_contents(context, &ap_rep);
+     if (major_status == GSS_S_COMPLETE ||
diff --git a/SPECS/krb5.spec b/SPECS/krb5.spec
index f87f2de..3f0049c 100644
--- a/SPECS/krb5.spec
+++ b/SPECS/krb5.spec
@@ -18,7 +18,7 @@ Summary: The Kerberos network authentication system
 Name: krb5
 Version: 1.18.2
 # for prerelease, should be e.g., 0.% {prerelease}.1% { ?dist } (without spaces)
-Release: 2%{?dist}
+Release: 3%{?dist}
 
 # lookaside-cached sources; two downloads and a build artifact
 Source0: https://web.mit.edu/kerberos/dist/krb5/1.17/krb5-%{version}%{prerelease}.tar.gz
@@ -63,6 +63,14 @@ Patch117: Do-expiration-warnings-for-all-init_creds-APIs.patch
 Patch118: Pass-gss_localname-through-SPNEGO.patch
 Patch119: Omit-KDC-indicator-check-for-S4U2Self-requests.patch
 Patch120: Fix-typo-in-in-in-the-ksu-man-page.patch
+Patch121: Omit-PA_FOR_USER-if-we-can-t-compute-its-checksum.patch
+Patch122: Improve-negoex_parse_token-code-hygiene.patch
+Patch123: Refactor-krb5-GSS-checksum-handling.patch
+Patch124: Implement-GSS_C_CHANNEL_BOUND_FLAG.patch
+Patch125: Implement-KERB_AP_OPTIONS_CBT-server-side.patch
+Patch126: Add-client_aware_channel_bindings-option.patch
+Patch127: Pass-channel-bindings-through-SPNEGO.patch
+Patch128: Add-channel-bindings-tests.patch
 
 License: MIT
 URL: http://web.mit.edu/kerberos/www/
@@ -673,6 +681,11 @@ exit 0
 %{_libdir}/libkadm5srv_mit.so.*
 
 %changelog
+* Mon Jun 15 2020 Robbie Harwood <rharwood@redhat.com> - 1.18.2-3
+- Match Heimdal behavior for channel bindings
+- Code hygiene + test stability fix included
+- Resolves: #1840518
+
 * Wed May 27 2020 Robbie Harwood <rharwood@redhat.com> - 1.18.2-2
 - Drop DES3 from sample kdc.conf
 - Resolves: #1802334