diff --git a/SOURCES/cyrus-sasl-2.1.27-Add-Channel-Binding-support-for-GSSAPI-GSS-SPNEGO.patch b/SOURCES/cyrus-sasl-2.1.27-Add-Channel-Binding-support-for-GSSAPI-GSS-SPNEGO.patch
new file mode 100644
index 0000000..242b436
--- /dev/null
+++ b/SOURCES/cyrus-sasl-2.1.27-Add-Channel-Binding-support-for-GSSAPI-GSS-SPNEGO.patch
@@ -0,0 +1,444 @@
+From aa8b6b2275fd14ba2cca3d2339ae61c7e7ddfa70 Mon Sep 17 00:00:00 2001
+From: Simo Sorce <simo@redhat.com>
+Date: Tue, 5 May 2020 14:08:48 -0400
+Subject: [PATCH] Add Channel Binding support for GSSAPI/GSS-SPNEGO
+
+Backport of commit ids:
+829a6ed086432e26dafa9d1dcf892aef4c42cfbd
+944bd8a6205f840b105206ef83e8f6b9dff0138e
+
+Signed-off-by: Simo Sorce <simo@redhat.com>
+---
+ plugins/gssapi.c     | 30 +++++++++++---
+ tests/runtests.py    | 93 ++++++++++++++++++++++++++++++++++++++++----
+ tests/t_common.c     | 24 ++++++++----
+ tests/t_common.h     |  5 ++-
+ tests/t_gssapi_cli.c | 24 ++++++++++--
+ tests/t_gssapi_srv.c | 24 ++++++++++--
+ 6 files changed, 172 insertions(+), 28 deletions(-)
+
+diff --git a/plugins/gssapi.c b/plugins/gssapi.c
+index ff663da..5d900c5 100644
+--- a/plugins/gssapi.c
++++ b/plugins/gssapi.c
+@@ -830,7 +830,9 @@ gssapi_server_mech_authneg(context_t *text,
+     gss_buffer_desc name_without_realm;
+     gss_name_t client_name_MN = NULL, without = NULL;
+     gss_OID mech_type;
+-	
++    gss_channel_bindings_t bindings = GSS_C_NO_CHANNEL_BINDINGS;
++    struct gss_channel_bindings_struct cb = {0};
++
+     input_token = &real_input_token;
+     output_token = &real_output_token;
+     output_token->value = NULL; output_token->length = 0;
+@@ -902,6 +904,12 @@ gssapi_server_mech_authneg(context_t *text,
+ 	real_input_token.length = clientinlen;
+     }
+ 
++    if (params->cbinding != NULL) {
++        cb.application_data.length = params->cbinding->len;
++        cb.application_data.value = params->cbinding->data;
++        bindings = &cb;
++    }
++
+ 
+     GSS_LOCK_MUTEX_CTX(params->utils, text);
+     maj_stat =
+@@ -909,7 +917,7 @@ gssapi_server_mech_authneg(context_t *text,
+ 			       &(text->gss_ctx),
+ 			       server_creds,
+ 			       input_token,
+-			       GSS_C_NO_CHANNEL_BINDINGS,
++			       bindings,
+ 			       &text->client_name,
+ 			       &mech_type,
+ 			       output_token,
+@@ -1505,7 +1513,8 @@ static sasl_server_plug_t gssapi_server_plugins[] =
+ 	| SASL_SEC_PASS_CREDENTIALS,
+ 	SASL_FEAT_WANT_CLIENT_FIRST
+ 	| SASL_FEAT_ALLOWS_PROXY
+-	| SASL_FEAT_DONTUSE_USERPASSWD,	/* features */
++	| SASL_FEAT_DONTUSE_USERPASSWD
++	| SASL_FEAT_CHANNEL_BINDING,	/* features */
+ 	NULL,				/* glob_context */
+ 	&gssapi_server_mech_new,	/* mech_new */
+ 	&gssapi_server_mech_step,	/* mech_step */
+@@ -1529,6 +1538,7 @@ static sasl_server_plug_t gssapi_server_plugins[] =
+ 	SASL_FEAT_WANT_CLIENT_FIRST
+ 	| SASL_FEAT_ALLOWS_PROXY
+ 	| SASL_FEAT_DONTUSE_USERPASSWD
++	| SASL_FEAT_CHANNEL_BINDING
+ 	| SASL_FEAT_SUPPORTS_HTTP,	/* features */
+ 	&gss_spnego_oid,		/* glob_context */
+ 	&gssapi_server_mech_new,	/* mech_new */
+@@ -1662,6 +1672,8 @@ static int gssapi_client_mech_step(void *conn_context,
+     input_token->value = NULL; 
+     input_token->length = 0;
+     gss_cred_id_t client_creds = (gss_cred_id_t)params->gss_creds;
++    gss_channel_bindings_t bindings = GSS_C_NO_CHANNEL_BINDINGS;
++    struct gss_channel_bindings_struct cb = {0};
+ 
+     if (clientout)
+         *clientout = NULL;
+@@ -1777,6 +1789,12 @@ static int gssapi_client_mech_step(void *conn_context,
+ 	    req_flags = req_flags |  GSS_C_DELEG_FLAG;
+ 	}
+ 
++        if (params->cbinding != NULL) {
++            cb.application_data.length = params->cbinding->len;
++            cb.application_data.value = params->cbinding->data;
++            bindings = &cb;
++        }
++
+ 	GSS_LOCK_MUTEX_CTX(params->utils, text);
+ 	maj_stat = gss_init_sec_context(&min_stat,
+ 					client_creds, /* GSS_C_NO_CREDENTIAL */
+@@ -1785,7 +1803,7 @@ static int gssapi_client_mech_step(void *conn_context,
+ 					text->mech_type,
+ 					req_flags,
+ 					0,
+-					GSS_C_NO_CHANNEL_BINDINGS,
++					bindings,
+ 					input_token,
+ 					NULL,
+ 					output_token,
+@@ -2190,7 +2208,8 @@ static sasl_client_plug_t gssapi_client_plugins[] =
+ 	| SASL_SEC_PASS_CREDENTIALS,    /* security_flags */
+ 	SASL_FEAT_NEEDSERVERFQDN
+ 	| SASL_FEAT_WANT_CLIENT_FIRST
+-	| SASL_FEAT_ALLOWS_PROXY,	/* features */
++	| SASL_FEAT_ALLOWS_PROXY
++	| SASL_FEAT_CHANNEL_BINDING,	/* features */
+ 	gssapi_required_prompts,	/* required_prompts */
+ 	GSS_C_NO_OID,			/* glob_context */
+ 	&gssapi_client_mech_new,	/* mech_new */
+@@ -2213,6 +2232,7 @@ static sasl_client_plug_t gssapi_client_plugins[] =
+ 	SASL_FEAT_NEEDSERVERFQDN
+ 	| SASL_FEAT_WANT_CLIENT_FIRST
+ 	| SASL_FEAT_ALLOWS_PROXY
++	| SASL_FEAT_CHANNEL_BINDING
+ 	| SASL_FEAT_SUPPORTS_HTTP,	/* features */
+ 	gssapi_required_prompts,	/* required_prompts */
+ 	&gss_spnego_oid,		/* glob_context */
+diff --git a/tests/runtests.py b/tests/runtests.py
+index f645adf..fc9cf24 100755
+--- a/tests/runtests.py
++++ b/tests/runtests.py
+@@ -1,6 +1,7 @@
+ #!/usr/bin/python3
+ 
+ import argparse
++import base64
+ import os
+ import shutil
+ import signal
+@@ -126,14 +127,7 @@ def setup_kdc(testdir, env):
+ 
+     return kdc, env
+ 
+-
+-def gssapi_tests(testdir):
+-    """ SASL/GSSAPI Tests """
+-    env = setup_socket_wrappers(testdir)
+-    kdc, kenv = setup_kdc(testdir, env)
+-    #print("KDC: {}, ENV: {}".format(kdc, kenv))
+-    kenv['KRB5_TRACE'] = os.path.join(testdir, 'trace.log')
+-
++def gssapi_basic_test(kenv):
+     try:
+         srv = subprocess.Popen(["../tests/t_gssapi_srv"],
+                                stdout=subprocess.PIPE,
+@@ -155,11 +149,94 @@ def gssapi_tests(testdir):
+                 srv.returncode, srv.stderr.read().decode('utf-8')))
+     except Exception as e:
+         print("FAIL: {}".format(e))
++        return
++
++    print("PASS: CLI({}) SRV({})".format(
++        cli.stdout.read().decode('utf-8').strip(),
++        srv.stdout.read().decode('utf-8').strip()))
++
++def gssapi_channel_binding_test(kenv):
++    try:
++        bindings = base64.b64encode("MATCHING CBS".encode('utf-8'))
++        srv = subprocess.Popen(["../tests/t_gssapi_srv", "-c", bindings],
++                               stdout=subprocess.PIPE,
++                               stderr=subprocess.PIPE, env=kenv)
++        srv.stdout.readline() # Wait for srv to say it is ready
++        cli = subprocess.Popen(["../tests/t_gssapi_cli", "-c", bindings],
++                               stdout=subprocess.PIPE,
++                               stderr=subprocess.PIPE, env=kenv)
++        try:
++            cli.wait(timeout=5)
++            srv.wait(timeout=5)
++        except Exception as e:
++            print("Failed on {}".format(e));
++            cli.kill()
++            srv.kill()
++        if cli.returncode != 0 or srv.returncode != 0:
++            raise Exception("CLI ({}): {} --> SRV ({}): {}".format(
++                cli.returncode, cli.stderr.read().decode('utf-8'),
++                srv.returncode, srv.stderr.read().decode('utf-8')))
++    except Exception as e:
++        print("FAIL: {}".format(e))
++        return
+ 
+     print("PASS: CLI({}) SRV({})".format(
+         cli.stdout.read().decode('utf-8').strip(),
+         srv.stdout.read().decode('utf-8').strip()))
+ 
++def gssapi_channel_binding_mismatch_test(kenv):
++    result = "FAIL"
++    try:
++        bindings = base64.b64encode("SRV CBS".encode('utf-8'))
++        srv = subprocess.Popen(["../tests/t_gssapi_srv", "-c", bindings],
++                               stdout=subprocess.PIPE,
++                               stderr=subprocess.PIPE, env=kenv)
++        srv.stdout.readline() # Wait for srv to say it is ready
++        bindings = base64.b64encode("CLI CBS".encode('utf-8'))
++        cli = subprocess.Popen(["../tests/t_gssapi_cli", "-c", bindings],
++                               stdout=subprocess.PIPE,
++                               stderr=subprocess.PIPE, env=kenv)
++        try:
++            cli.wait(timeout=5)
++            srv.wait(timeout=5)
++        except Exception as e:
++            print("Failed on {}".format(e));
++            cli.kill()
++            srv.kill()
++        if cli.returncode != 0 or srv.returncode != 0:
++            cli_err = cli.stderr.read().decode('utf-8').strip()
++            srv_err = srv.stderr.read().decode('utf-8').strip()
++            if "authentication failure" in srv_err:
++                result = "PASS"
++            raise Exception("CLI ({}): {} --> SRV ({}): {}".format(
++                cli.returncode, cli_err, srv.returncode, srv_err))
++    except Exception as e:
++        print("{}: {}".format(result, e))
++        return
++
++    print("FAIL: This test should fail [CLI({}) SRV({})]".format(
++        cli.stdout.read().decode('utf-8').strip(),
++        srv.stdout.read().decode('utf-8').strip()))
++
++def gssapi_tests(testdir):
++    """ SASL/GSSAPI Tests """
++    env = setup_socket_wrappers(testdir)
++    kdc, kenv = setup_kdc(testdir, env)
++    #print("KDC: {}, ENV: {}".format(kdc, kenv))
++    kenv['KRB5_TRACE'] = os.path.join(testdir, 'trace.log')
++
++    print('GSSAPI BASIC:')
++    print('    ', end='')
++    gssapi_basic_test(kenv)
++
++    print('GSSAPI CHANNEL BINDING:')
++    print('    ', end='')
++    gssapi_channel_binding_test(kenv)
++
++    print('GSSAPI CHANNEL BINDING MISMTACH:')
++    print('    ', end='')
++    gssapi_channel_binding_mismatch_test(kenv)
++
+     os.killpg(kdc.pid, signal.SIGTERM)
+ 
+ 
+diff --git a/tests/t_common.c b/tests/t_common.c
+index 7168b2f..478e6a1 100644
+--- a/tests/t_common.c
++++ b/tests/t_common.c
+@@ -1,4 +1,5 @@
+-/* TBD, add (C) */
++/* Copyright (C) Simo Sorce <simo@redhat.com>
++ * See COPYING file for License */
+ 
+ #include <t_common.h>
+ 
+@@ -13,9 +14,6 @@ void send_string(int sd, const char *s, unsigned int l)
+ {
+     ssize_t ret;
+ 
+-fprintf(stderr, "s:%u ", l);
+-fflush(stderr);
+-
+     ret = send(sd, &l, sizeof(l), 0);
+     if (ret != sizeof(l)) s_error("send size", ret, sizeof(l), errno);
+ 
+@@ -34,8 +32,6 @@ void recv_string(int sd, char *buf, unsigned int *buflen)
+     if (ret != sizeof(l)) s_error("recv size", ret, sizeof(l), errno);
+ 
+     if (l == 0) {
+-fprintf(stderr, "r:0 ");
+-fflush(stderr);
+         *buflen = 0;
+         return;
+     }
+@@ -45,8 +41,6 @@ fflush(stderr);
+     ret = recv(sd, buf, l, 0);
+     if (ret != l) s_error("recv data", ret, l, errno);
+ 
+-fprintf(stderr, "r:%ld ", ret);
+-fflush(stderr);
+     *buflen = ret;
+ }
+ 
+@@ -65,4 +59,18 @@ int getpath(void *context __attribute__((unused)), const char **path)
+     return SASL_OK;
+ }
+ 
++void parse_cb(sasl_channel_binding_t *cb, char *buf, unsigned max, char *in)
++{
++    unsigned len;
++    int r;
+ 
++    r = sasl_decode64(in, strlen(in), buf, max, &len);
++    if (r != SASL_OK) {
++        saslerr(r, "failed to parse channel bindings");
++        exit(-1);
++    }
++    cb->name = "TEST BINDINGS";
++    cb->critical = 0;
++    cb->data = (unsigned char *)buf;
++    cb->len = len;
++}
+diff --git a/tests/t_common.h b/tests/t_common.h
+index 4ee1976..a10def1 100644
+--- a/tests/t_common.h
++++ b/tests/t_common.h
+@@ -1,4 +1,5 @@
+-/* TBD, add (C) */
++/* Copyright (C) Simo Sorce <simo@redhat.com>
++ * See COPYING file for License */
+ 
+ #include "config.h"
+ 
+@@ -7,9 +8,11 @@
+ #include <sys/socket.h>
+ 
+ #include <sasl.h>
++#include <saslutil.h>
+ 
+ void s_error(const char *hdr, ssize_t ret, ssize_t len, int err);
+ void send_string(int sd, const char *s, unsigned int l);
+ void recv_string(int sd, char *buf, unsigned int *buflen);
+ void saslerr(int why, const char *what);
+ int getpath(void *context __attribute__((unused)), const char **path);
++void parse_cb(sasl_channel_binding_t *cb, char *buf, unsigned max, char *in);
+diff --git a/tests/t_gssapi_cli.c b/tests/t_gssapi_cli.c
+index c833c05..a44a3f5 100644
+--- a/tests/t_gssapi_cli.c
++++ b/tests/t_gssapi_cli.c
+@@ -1,4 +1,5 @@
+-/* TBD, add (C) */
++/* Copyright (C) Simo Sorce <simo@redhat.com>
++ * See COPYING file for License */
+ 
+ #include "t_common.h"
+ 
+@@ -13,6 +14,7 @@
+ 
+ #include <arpa/inet.h>
+ #include <saslplug.h>
++#include <saslutil.h>
+ 
+ static int setup_socket(void)
+ {
+@@ -32,7 +34,7 @@ static int setup_socket(void)
+     return sock;
+ }
+ 
+-int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
++int main(int argc, char *argv[])
+ {
+     sasl_callback_t callbacks[2] = {};
+     char buf[8192];
+@@ -40,8 +42,20 @@ int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
+     sasl_conn_t *conn;
+     const char *data;
+     unsigned int len;
++    sasl_channel_binding_t cb = {0};
++    char cb_buf[256];
+     int sd;
+-    int r;
++    int c, r;
++
++    while ((c = getopt(argc, argv, "c:")) != EOF) {
++        switch (c) {
++        case 'c':
++            parse_cb(&cb, cb_buf, 256, optarg);
++            break;
++        default:
++            break;
++        }
++    }
+ 
+     /* initialize the sasl library */
+     callbacks[0].id = SASL_CB_GETPATH;
+@@ -60,6 +74,10 @@ int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
+         exit(-1);
+     }
+ 
++    if (cb.name) {
++        sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb);
++    }
++
+     r = sasl_client_start(conn, "GSSAPI", NULL, &data, &len, &chosenmech);
+     if (r != SASL_OK && r != SASL_CONTINUE) {
+ 	saslerr(r, "starting SASL negotiation");
+diff --git a/tests/t_gssapi_srv.c b/tests/t_gssapi_srv.c
+index 29f538d..ef1217f 100644
+--- a/tests/t_gssapi_srv.c
++++ b/tests/t_gssapi_srv.c
+@@ -1,4 +1,5 @@
+-/* TBD, add (C) */
++/* Copyright (C) Simo Sorce <simo@redhat.com>
++ * See COPYING file for License */
+ 
+ #include "t_common.h"
+ 
+@@ -44,15 +45,28 @@ static int setup_socket(void)
+     return sd;
+ }
+ 
+-int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
++int main(int argc, char *argv[])
+ {
+     sasl_callback_t callbacks[2] = {};
+     char buf[8192];
+     sasl_conn_t *conn;
+     const char *data;
+     unsigned int len;
++    sasl_channel_binding_t cb = {0};
++    unsigned char cb_buf[256];
+     int sd;
+-    int r;
++    int c, r;
++
++    while ((c = getopt(argc, argv, "c:")) != EOF) {
++        switch (c) {
++        case 'c':
++            parse_cb(&cb, cb_buf, 256, optarg);
++            break;
++        default:
++            break;
++        }
++    }
++
+ 
+     /* initialize the sasl library */
+     callbacks[0].id = SASL_CB_GETPATH;
+@@ -72,6 +86,10 @@ int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
+         exit(-1);
+     }
+ 
++    if (cb.name) {
++        sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb);
++    }
++
+     sd = setup_socket();
+ 
+     len = 8192;
+-- 
+2.18.2
+
diff --git a/SOURCES/cyrus-sasl-2.1.27-Add-basic-test-infrastructure.patch b/SOURCES/cyrus-sasl-2.1.27-Add-basic-test-infrastructure.patch
new file mode 100644
index 0000000..2f6a35c
--- /dev/null
+++ b/SOURCES/cyrus-sasl-2.1.27-Add-basic-test-infrastructure.patch
@@ -0,0 +1,641 @@
+From 82e299e970461c153a036bb1fbc84e808f926e12 Mon Sep 17 00:00:00 2001
+From: Simo Sorce <simo@redhat.com>
+Date: Tue, 5 May 2020 14:06:57 -0400
+Subject: [PATCH] Add basic test infrastructure
+
+First test is for SASL/GSSAPI
+
+Backport of upstream commit id:
+18ff41d5d18f61c2ded7235dad1d9618aa84784b
+
+Signed-off-by: Simo Sorce <simo@redhat.com>
+---
+ Makefile.am          |   2 +-
+ configure.ac         |   3 +-
+ tests/Makefile.am    |  79 +++++++++++++++++++
+ tests/runtests.py    | 179 +++++++++++++++++++++++++++++++++++++++++++
+ tests/t_common.c     |  68 ++++++++++++++++
+ tests/t_common.h     |  15 ++++
+ tests/t_gssapi_cli.c |  95 +++++++++++++++++++++++
+ tests/t_gssapi_srv.c | 111 +++++++++++++++++++++++++++
+ 8 files changed, 550 insertions(+), 2 deletions(-)
+ create mode 100644 tests/Makefile.am
+ create mode 100755 tests/runtests.py
+ create mode 100644 tests/t_common.c
+ create mode 100644 tests/t_common.h
+ create mode 100644 tests/t_gssapi_cli.c
+ create mode 100644 tests/t_gssapi_srv.c
+
+diff --git a/Makefile.am b/Makefile.am
+index 83dae6f..fc24509 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -70,7 +70,7 @@ else
+ INSTALLOSX = 
+ endif
+ 
+-SUBDIRS=include sasldb common lib plugins utils $(PWC) $(SAM) $(JAV) $(SAD)
++SUBDIRS=include sasldb common lib plugins utils $(PWC) $(SAM) $(JAV) $(SAD) tests
+ EXTRA_DIST=config doc docsrc win32 mac dlcompat-20010505 NTMakefile \
+     INSTALL.TXT libsasl2.pc.in
+ 
+diff --git a/configure.ac b/configure.ac
+index ca5936a..c1d2182 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1575,7 +1575,8 @@ java/javax/Makefile
+ java/javax/security/Makefile
+ java/javax/security/auth/Makefile
+ java/javax/security/auth/callback/Makefile
+-pwcheck/Makefile)
++pwcheck/Makefile
++tests/Makefile)
+ 
+ AC_MSG_NOTICE([
+ 
+diff --git a/tests/Makefile.am b/tests/Makefile.am
+new file mode 100644
+index 0000000..1edf010
+--- /dev/null
++++ b/tests/Makefile.am
+@@ -0,0 +1,79 @@
++# Makefile.am -- automake input for cyrus-sasl tests
++# Simo Sorce
++#
++################################################################
++# Copyright (c) 2000 Carnegie Mellon University.  All rights reserved.
++#
++# Redistribution and use in source and binary forms, with or without
++# modification, are permitted provided that the following conditions
++# are met:
++#
++# 1. Redistributions of source code must retain the above copyright
++#    notice, this list of conditions and the following disclaimer.
++#
++# 2. 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.
++#
++# 3. The name "Carnegie Mellon University" must not be used to
++#    endorse or promote products derived from this software without
++#    prior written permission. For permission or any other legal
++#    details, please contact
++#      Office of Technology Transfer
++#      Carnegie Mellon University
++#      5000 Forbes Avenue
++#      Pittsburgh, PA  15213-3890
++#      (412) 268-4387, fax: (412) 268-7395
++#      tech-transfer@andrew.cmu.edu
++#
++# 4. Redistributions of any form whatsoever must retain the following
++#    acknowledgment:
++#    "This product includes software developed by Computing Services
++#     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
++#
++# CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
++# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
++# AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
++# FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
++# AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
++# OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++#
++################################################################
++
++AM_CPPFLAGS=-I$(top_srcdir)/include -DPLUGINDIR='"${top_srcdir}/plugins/.libs"'
++
++COMMON_LDADD = ../lib/libsasl2.la $(GSSAPIBASE_LIBS) $(GSSAPI_LIBS) $(LIB_SOCKET)
++
++t_gssapi_cli_SOURCES = \
++	t_common.c \
++	t_gssapi_cli.c
++
++t_gssapi_cli_LDADD = $(COMMON_LDADD)
++
++t_gssapi_srv_SOURCES = \
++	t_common.c \
++	t_gssapi_srv.c
++
++t_gssapi_srv_LDADD = $(COMMON_LDADD)
++
++check_PROGRAMS = \
++	t_gssapi_cli \
++	t_gssapi_srv \
++	$(NULL)
++
++noinst_PROGRAMS = $(check_PROGRAMS)
++
++EXTRA_DIST = \
++	runtests.py \
++	$(NULL)
++
++all: $(check_PROGRAMS)
++
++check:
++if MACOSX
++# skip Mac OSX for now
++else
++	$(srcdir)/runtests.py $(CHECKARGS)
++endif
+diff --git a/tests/runtests.py b/tests/runtests.py
+new file mode 100755
+index 0000000..f645adf
+--- /dev/null
++++ b/tests/runtests.py
+@@ -0,0 +1,179 @@
++#!/usr/bin/python3
++
++import argparse
++import os
++import shutil
++import signal
++import subprocess
++import time
++from string import Template
++
++
++def setup_socket_wrappers(testdir):
++    """ Try to set up socket wrappers """
++    wrapdir = os.path.join(testdir, 'w')
++    os.makedirs(wrapdir)
++
++    wrappers = subprocess.Popen(['pkg-config', '--exists', 'socket_wrapper'])
++    wrappers.wait()
++    if wrappers.returncode != 0:
++        raise Exception('Socket Wrappers not available')
++
++    wrappers = subprocess.Popen(['pkg-config', '--exists', 'nss_wrapper'])
++    wrappers.wait()
++    if wrappers.returncode != 0:
++        raise Exception('NSS Wrappers not available')
++
++    hosts = os.path.join(wrapdir, 'hosts')
++    with open(hosts, 'w+') as conffile:
++        conffile.write('127.0.0.9 host.realm.test')
++
++    return {'LD_PRELOAD': 'libsocket_wrapper.so libnss_wrapper.so',
++            'SOCKET_WRAPPER_DIR': wrapdir,
++            'SOCKET_WRAPPER_DEFAULT_IFACE': '9',
++            'NSS_WRAPPER_HOSTNAME': 'host.realm.test',
++            'NSS_WRAPPER_HOSTS': hosts}
++
++
++KERBEROS_CONF = '''
++[libdefaults]
++  default_realm = REALM.TEST
++  dns_lookup_realm = false
++  dns_lookup_kdc = false
++  rdns = false
++  ticket_lifetime = 24h
++  forwardable = yes
++  default_ccache_name = FILE://${TESTDIR}/ccache
++  udp_preference_limit = 1
++
++[domain_realm]
++  .realm.test = REALM.TEST
++  realm.test = REALM.TEST
++
++[realms]
++ REALM.TEST = {
++  kdc = 127.0.0.9
++  admin_server = 127.0.0.9
++  acl_file = ${TESTDIR}/kadm.acl
++  dict_file = /usr/share/dict/words
++  admin_keytab = ${TESTDIR}/kadm.keytab
++  database_name = ${TESTDIR}/kdc.db
++  key_stash_file = ${TESTDIR}/kdc.stash
++ }
++
++[kdcdefaults]
++ kdc_ports = 88
++ kdc_tcp_ports = 88
++
++[logging]
++  kdc = FILE:${TESTDIR}/kdc.log
++  admin_server = FILE:${TESTDIR}/kadm.log
++  default = FILE:${TESTDIR}/krb5.log
++'''
++
++
++def setup_kdc(testdir, env):
++    """ Setup KDC and start process """
++    krbconf = os.path.join(testdir, 'krb.conf')
++    env['KRB5_CONFIG'] = krbconf
++
++    kenv = {'KRB5_KDC_PROFILE': krbconf,
++            'PATH': '/sbin:/bin:/usr/sbin:/usr/bin'}
++    kenv.update(env)
++
++    # KDC/KRB5 CONFIG
++    templ = Template(KERBEROS_CONF)
++    text = templ.substitute({'TESTDIR': testdir})
++    with open(krbconf, 'w+') as conffile:
++        conffile.write(text)
++
++    testlog = os.path.join(testdir, 'kdc.log')
++    log = open(testlog, 'a')
++
++    subprocess.check_call([
++        "kdb5_util", "create",
++        "-r", "REALM.TEST", "-s", "-P", "password"
++        ], stdout=log, stderr=log, env=kenv, timeout=5)
++
++    kdc = subprocess.Popen(['krb5kdc', '-n'], env=kenv, preexec_fn=os.setsid)
++    time.sleep(5)
++
++    # Add a user and genrate a keytab
++    keytab = os.path.join(testdir, "user.keytab")
++    subprocess.check_call([
++        "kadmin.local", "-q",
++        "addprinc -randkey user"
++        ], stdout=log, stderr=log, env=kenv, timeout=5)
++
++    subprocess.check_call([
++        "kadmin.local", "-q",
++        "ktadd -k {} user".format(keytab)
++        ], stdout=log, stderr=log, env=kenv, timeout=5)
++    env['KRB5_CLIENT_KTNAME'] = keytab
++
++    # Add a service and genrate a keytab
++    keytab = os.path.join(testdir, "test.keytab")
++    subprocess.check_call([
++        "kadmin.local", "-q",
++        "addprinc -randkey test/host.realm.test"
++        ], stdout=log, stderr=log, env=kenv, timeout=5)
++
++    subprocess.check_call([
++        "kadmin.local", "-q",
++        "ktadd -k {} test/host.realm.test".format(keytab)
++        ], stdout=log, stderr=log, env=kenv, timeout=5)
++    env['KRB5_KTNAME'] = keytab
++
++    return kdc, env
++
++
++def gssapi_tests(testdir):
++    """ SASL/GSSAPI Tests """
++    env = setup_socket_wrappers(testdir)
++    kdc, kenv = setup_kdc(testdir, env)
++    #print("KDC: {}, ENV: {}".format(kdc, kenv))
++    kenv['KRB5_TRACE'] = os.path.join(testdir, 'trace.log')
++
++    try:
++        srv = subprocess.Popen(["../tests/t_gssapi_srv"],
++                               stdout=subprocess.PIPE,
++                               stderr=subprocess.PIPE, env=kenv)
++        srv.stdout.readline() # Wait for srv to say it is ready
++        cli = subprocess.Popen(["../tests/t_gssapi_cli"],
++                               stdout=subprocess.PIPE,
++                               stderr=subprocess.PIPE, env=kenv)
++        try:
++            cli.wait(timeout=5)
++            srv.wait(timeout=5)
++        except Exception as e:
++            print("Failed on {}".format(e));
++            cli.kill()
++            srv.kill()
++        if cli.returncode != 0 or srv.returncode != 0:
++            raise Exception("CLI ({}): {} --> SRV ({}): {}".format(
++                cli.returncode, cli.stderr.read().decode('utf-8'),
++                srv.returncode, srv.stderr.read().decode('utf-8')))
++    except Exception as e:
++        print("FAIL: {}".format(e))
++
++    print("PASS: CLI({}) SRV({})".format(
++        cli.stdout.read().decode('utf-8').strip(),
++        srv.stdout.read().decode('utf-8').strip()))
++
++    os.killpg(kdc.pid, signal.SIGTERM)
++
++
++if __name__ == "__main__":
++
++    P = argparse.ArgumentParser(description='Cyrus SASL Tests')
++    P.add_argument('--testdir', default=os.path.join(os.getcwd(), '.tests'),
++                   help="Directory for running tests")
++    A = vars(P.parse_args())
++
++    T = A['testdir']
++
++    if os.path.exists(T):
++        shutil.rmtree(T)
++    os.makedirs(T)
++
++    gssapi_tests(T)
+diff --git a/tests/t_common.c b/tests/t_common.c
+new file mode 100644
+index 0000000..7168b2f
+--- /dev/null
++++ b/tests/t_common.c
+@@ -0,0 +1,68 @@
++/* TBD, add (C) */
++
++#include <t_common.h>
++
++void s_error(const char *hdr, ssize_t ret, ssize_t len, int err)
++{
++    fprintf(stderr, "%s l:%ld/%ld [%d] %s",
++            hdr, ret, len, err, strerror(err));
++    exit(-1);
++}
++
++void send_string(int sd, const char *s, unsigned int l)
++{
++    ssize_t ret;
++
++fprintf(stderr, "s:%u ", l);
++fflush(stderr);
++
++    ret = send(sd, &l, sizeof(l), 0);
++    if (ret != sizeof(l)) s_error("send size", ret, sizeof(l), errno);
++
++    if (l == 0) return;
++
++    ret = send(sd, s, l, 0);
++    if (ret != l) s_error("send data", ret, l, errno);
++}
++
++void recv_string(int sd, char *buf, unsigned int *buflen)
++{
++    unsigned int l;
++    ssize_t ret;
++
++    ret = recv(sd, &l, sizeof(l), MSG_WAITALL);
++    if (ret != sizeof(l)) s_error("recv size", ret, sizeof(l), errno);
++
++    if (l == 0) {
++fprintf(stderr, "r:0 ");
++fflush(stderr);
++        *buflen = 0;
++        return;
++    }
++
++    if (*buflen < l) s_error("recv len", l, *buflen, E2BIG);
++
++    ret = recv(sd, buf, l, 0);
++    if (ret != l) s_error("recv data", ret, l, errno);
++
++fprintf(stderr, "r:%ld ", ret);
++fflush(stderr);
++    *buflen = ret;
++}
++
++void saslerr(int why, const char *what)
++{
++    fprintf(stderr, "%s: %s", what, sasl_errstring(why, NULL, NULL));
++}
++
++int getpath(void *context __attribute__((unused)), const char **path)
++{
++    if (! path) {
++        return SASL_BADPARAM;
++    }
++
++    *path = PLUGINDIR;
++    return SASL_OK;
++}
++
++
+diff --git a/tests/t_common.h b/tests/t_common.h
+new file mode 100644
+index 0000000..4ee1976
+--- /dev/null
++++ b/tests/t_common.h
+@@ -0,0 +1,15 @@
++/* TBD, add (C) */
++
++#include "config.h"
++
++#include <errno.h>
++#include <stdio.h>
++#include <sys/socket.h>
++
++#include <sasl.h>
++
++void s_error(const char *hdr, ssize_t ret, ssize_t len, int err);
++void send_string(int sd, const char *s, unsigned int l);
++void recv_string(int sd, char *buf, unsigned int *buflen);
++void saslerr(int why, const char *what);
++int getpath(void *context __attribute__((unused)), const char **path);
+diff --git a/tests/t_gssapi_cli.c b/tests/t_gssapi_cli.c
+new file mode 100644
+index 0000000..c833c05
+--- /dev/null
++++ b/tests/t_gssapi_cli.c
+@@ -0,0 +1,95 @@
++/* TBD, add (C) */
++
++#include "t_common.h"
++
++#include <stdlib.h>
++#include <stdarg.h>
++#include <ctype.h>
++#include <string.h>
++
++#ifdef HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++
++#include <arpa/inet.h>
++#include <saslplug.h>
++
++static int setup_socket(void)
++{
++    struct sockaddr_in addr;
++    int sock, ret;
++
++    sock = socket(AF_INET, SOCK_STREAM, 0);
++    if (sock < 0) s_error("socket", 0, 0, errno);
++
++    addr.sin_family = AF_INET;
++    addr.sin_addr.s_addr = inet_addr("127.0.0.9");
++    addr.sin_port = htons(9000);
++
++    ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
++    if (ret != 0) s_error("connect", 0, 0, errno);
++
++    return sock;
++}
++
++int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
++{
++    sasl_callback_t callbacks[2] = {};
++    char buf[8192];
++    const char *chosenmech;
++    sasl_conn_t *conn;
++    const char *data;
++    unsigned int len;
++    int sd;
++    int r;
++
++    /* initialize the sasl library */
++    callbacks[0].id = SASL_CB_GETPATH;
++    callbacks[0].proc = (sasl_callback_ft)&getpath;
++    callbacks[0].context = NULL;
++    callbacks[1].id = SASL_CB_LIST_END;
++    callbacks[1].proc = NULL;
++    callbacks[1].context = NULL;
++
++    r = sasl_client_init(callbacks);
++    if (r != SASL_OK) exit(-1);
++
++    r = sasl_client_new("test", "host.realm.test", NULL, NULL, NULL, 0, &conn);
++    if (r != SASL_OK) {
++        saslerr(r, "allocating connection state");
++        exit(-1);
++    }
++
++    r = sasl_client_start(conn, "GSSAPI", NULL, &data, &len, &chosenmech);
++    if (r != SASL_OK && r != SASL_CONTINUE) {
++	saslerr(r, "starting SASL negotiation");
++	printf("\n%s\n", sasl_errdetail(conn));
++	exit(-1);
++    }
++
++    sd = setup_socket();
++
++    while (r == SASL_CONTINUE) {
++        send_string(sd, data, len);
++        len = 8192;
++        recv_string(sd, buf, &len);
++
++	r = sasl_client_step(conn, buf, len, NULL, &data, &len);
++	if (r != SASL_OK && r != SASL_CONTINUE) {
++	    saslerr(r, "performing SASL negotiation");
++	    printf("\n%s\n", sasl_errdetail(conn));
++	    exit(-1);
++        }
++    }
++
++    if (r != SASL_OK) exit(-1);
++
++    if (len > 0) {
++        send_string(sd, data, len);
++    }
++
++    fprintf(stdout, "DONE\n");
++    fflush(stdout);
++    return 0;
++}
++
+diff --git a/tests/t_gssapi_srv.c b/tests/t_gssapi_srv.c
+new file mode 100644
+index 0000000..29f538d
+--- /dev/null
++++ b/tests/t_gssapi_srv.c
+@@ -0,0 +1,111 @@
++/* TBD, add (C) */
++
++#include "t_common.h"
++
++#include <stdlib.h>
++#include <stdarg.h>
++#include <ctype.h>
++#include <string.h>
++
++#ifdef HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++
++#include <arpa/inet.h>
++#include <saslplug.h>
++
++static int setup_socket(void)
++{
++    struct sockaddr_in addr;
++    int sock, ret, sd;
++
++    sock = socket(AF_INET, SOCK_STREAM, 0);
++    if (sock < 0) s_error("socket", 0, 0, errno);
++
++    addr.sin_family = AF_INET;
++    addr.sin_addr.s_addr = inet_addr("127.0.0.9");
++    addr.sin_port = htons(9000);
++
++    ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
++    if (ret != 0) s_error("bind", 0, 0, errno);
++
++    ret = listen(sock, 1);
++    if (ret != 0) s_error("listen", 0, 0, errno);
++
++    /* signal we are ready */
++    fprintf(stdout, "READY\n");
++    fflush(stdout);
++
++    /* block until the client connects */
++    sd = accept(sock, NULL, NULL);
++    if (sd < 0) s_error("accept", 0, 0, errno);
++
++    close(sock);
++    return sd;
++}
++
++int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
++{
++    sasl_callback_t callbacks[2] = {};
++    char buf[8192];
++    sasl_conn_t *conn;
++    const char *data;
++    unsigned int len;
++    int sd;
++    int r;
++
++    /* initialize the sasl library */
++    callbacks[0].id = SASL_CB_GETPATH;
++    callbacks[0].proc = (sasl_callback_ft)&getpath;
++    callbacks[0].context = NULL;
++    callbacks[1].id = SASL_CB_LIST_END;
++    callbacks[1].proc = NULL;
++    callbacks[1].context = NULL;
++
++    r = sasl_server_init(callbacks, "t_gssapi_srv");
++    if (r != SASL_OK) exit(-1);
++
++    r = sasl_server_new("test", "host.realm.test", NULL, NULL, NULL,
++                        callbacks, 0, &conn);
++    if (r != SASL_OK) {
++        saslerr(r, "allocating connection state");
++        exit(-1);
++    }
++
++    sd = setup_socket();
++
++    len = 8192;
++    recv_string(sd, buf, &len);
++
++    r = sasl_server_start(conn, "GSSAPI", buf, len, &data, &len);
++    if (r != SASL_OK && r != SASL_CONTINUE) {
++	saslerr(r, "starting SASL negotiation");
++	printf("\n%s\n", sasl_errdetail(conn));
++	exit(-1);
++    }
++
++    while (r == SASL_CONTINUE) {
++        send_string(sd, data, len);
++        len = 8192;
++        recv_string(sd, buf, &len);
++
++	r = sasl_server_step(conn, buf, len, &data, &len);
++	if (r != SASL_OK && r != SASL_CONTINUE) {
++	    saslerr(r, "performing SASL negotiation");
++	    printf("\n%s\n", sasl_errdetail(conn));
++	    exit(-1);
++	}
++
++    }
++
++    if (r != SASL_OK) exit(-1);
++
++    if (len > 0) {
++        send_string(sd, data, len);
++    }
++
++    fprintf(stdout, "DONE\n");
++    fflush(stdout);
++    return 0;
++}
++
+-- 
+2.18.2
+
diff --git a/SOURCES/cyrus-sasl-2.1.27-Add-support-for-setting-max-ssf-0-to-GSS-SPNEGO.patch b/SOURCES/cyrus-sasl-2.1.27-Add-support-for-setting-max-ssf-0-to-GSS-SPNEGO.patch
new file mode 100644
index 0000000..c8c4a79
--- /dev/null
+++ b/SOURCES/cyrus-sasl-2.1.27-Add-support-for-setting-max-ssf-0-to-GSS-SPNEGO.patch
@@ -0,0 +1,435 @@
+From 49e965f41257a0ed299c58a7cf1c120ddf944aaa Mon Sep 17 00:00:00 2001
+From: Simo Sorce <simo@redhat.com>
+Date: Tue, 5 May 2020 14:51:36 -0400
+Subject: [PATCH] Add support for setting max ssf 0 to GSS-SPNEGO
+
+Bacport form this proposed PR (still open at bacport time):
+https://github.com/cyrusimap/cyrus-sasl/pull/603
+
+Signed-off-by: Simo Sorce <simo@redhat.com>
+---
+ m4/sasl2.m4          | 13 +++++++
+ plugins/gssapi.c     | 44 ++++++++++++++++++++-
+ tests/runtests.py    | 91 ++++++++++++++++++++++++++++++++++++++++----
+ tests/t_common.c     | 13 ++++---
+ tests/t_common.h     |  3 +-
+ tests/t_gssapi_cli.c | 25 ++++++++++--
+ tests/t_gssapi_srv.c | 28 +++++++++++---
+ 7 files changed, 194 insertions(+), 23 deletions(-)
+
+diff --git a/m4/sasl2.m4 b/m4/sasl2.m4
+index 56e0504..6effe99 100644
+--- a/m4/sasl2.m4
++++ b/m4/sasl2.m4
+@@ -287,6 +287,19 @@ if test "$gssapi" != no; then
+   AC_CHECK_FUNCS(gss_oid_equal)
+   LIBS="$cmu_save_LIBS"
+ 
++  cmu_save_LIBS="$LIBS"
++  LIBS="$LIBS $GSSAPIBASE_LIBS"
++  if test "$ac_cv_header_gssapi_gssapi_krb5_h" = "yes"; then
++    AC_CHECK_DECL(GSS_KRB5_CRED_NO_CI_FLAGS_X,
++                  [AC_DEFINE(HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X,1,
++                             [Define if your GSSAPI implementation supports GSS_KRB5_CRED_NO_CI_FLAGS_X])],,
++                  [
++                    AC_INCLUDES_DEFAULT
++                    #include <gssapi/gssapi_krb5.h>
++                    ])
++  fi
++  LIBS="$cmu_save_LIBS"
++
+   cmu_save_LIBS="$LIBS"
+   LIBS="$LIBS $GSSAPIBASE_LIBS"
+   AC_CHECK_FUNCS(gss_get_name_attribute)
+diff --git a/plugins/gssapi.c b/plugins/gssapi.c
+index 5d900c5..7480316 100644
+--- a/plugins/gssapi.c
++++ b/plugins/gssapi.c
+@@ -1783,7 +1783,49 @@ static int gssapi_client_mech_step(void *conn_context,
+ 		/* We want to try for privacy */
+ 		req_flags |= GSS_C_CONF_FLAG;
+ 	    }
+-	}
++#ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
++        /* The krb5 mechanism automatically adds INTEG and CONF flags even when
++         * not specified, this has the effect of rendering explicit requests
++         * of no confidentiality and integrity via setting maxssf 0 moot.
++         * However to interoperate with Windows machines it needs to be
++         * possible to unset these flags as Windows machines refuse to allow
++         * two layers (say TLS and GSSAPI) to both provide these services.
++         * So if we do not suppress these flags a SASL/GSS-SPNEGO negotiation
++         * over, say, LDAPS will fail against Windows Servers */
++	} else if (params->props.max_ssf == 0) {
++            gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
++            if (client_creds == GSS_C_NO_CREDENTIAL) {
++                gss_OID_set_desc mechs = { 0 };
++                gss_OID_set desired_mechs = GSS_C_NO_OID_SET;
++                if (text->mech_type != GSS_C_NO_OID) {
++                    mechs.count = 1;
++                    mechs.elements = text->mech_type;
++                    desired_mechs = &mechs;
++                }
++
++                maj_stat = gss_acquire_cred(&min_stat, GSS_C_NO_NAME,
++                                            GSS_C_INDEFINITE, desired_mechs,
++                                            GSS_C_INITIATE,
++                                            &text->client_creds, NULL, NULL);
++                if (GSS_ERROR(maj_stat)) {
++                    sasl_gss_seterror(text->utils, maj_stat, min_stat);
++                    sasl_gss_free_context_contents(text);
++                    return SASL_FAIL;
++                }
++                client_creds = text->client_creds;
++            }
++
++            maj_stat = gss_set_cred_option(&min_stat, &client_creds,
++                                           (gss_OID)GSS_KRB5_CRED_NO_CI_FLAGS_X,
++                                            &empty_buffer);
++            if (GSS_ERROR(maj_stat)) {
++                sasl_gss_seterror(text->utils, maj_stat, min_stat);
++                sasl_gss_free_context_contents(text);
++                return SASL_FAIL;
++            }
++#endif
++        }
++
+ 
+ 	if (params->props.security_flags & SASL_SEC_PASS_CREDENTIALS) {
+ 	    req_flags = req_flags |  GSS_C_DELEG_FLAG;
+diff --git a/tests/runtests.py b/tests/runtests.py
+index fc9cf24..4106401 100755
+--- a/tests/runtests.py
++++ b/tests/runtests.py
+@@ -6,6 +6,7 @@ import os
+ import shutil
+ import signal
+ import subprocess
++import sys
+ import time
+ from string import Template
+ 
+@@ -149,11 +150,12 @@ def gssapi_basic_test(kenv):
+                 srv.returncode, srv.stderr.read().decode('utf-8')))
+     except Exception as e:
+         print("FAIL: {}".format(e))
+-        return
++        return 1
+ 
+     print("PASS: CLI({}) SRV({})".format(
+         cli.stdout.read().decode('utf-8').strip(),
+         srv.stdout.read().decode('utf-8').strip()))
++    return 0
+ 
+ def gssapi_channel_binding_test(kenv):
+     try:
+@@ -178,11 +180,12 @@ def gssapi_channel_binding_test(kenv):
+                 srv.returncode, srv.stderr.read().decode('utf-8')))
+     except Exception as e:
+         print("FAIL: {}".format(e))
+-        return
++        return 1
+ 
+     print("PASS: CLI({}) SRV({})".format(
+         cli.stdout.read().decode('utf-8').strip(),
+         srv.stdout.read().decode('utf-8').strip()))
++    return 0
+ 
+ def gssapi_channel_binding_mismatch_test(kenv):
+     result = "FAIL"
+@@ -212,11 +215,70 @@ def gssapi_channel_binding_mismatch_test(kenv):
+                 cli.returncode, cli_err, srv.returncode, srv_err))
+     except Exception as e:
+         print("{}: {}".format(result, e))
+-        return
++        return 0
+ 
+     print("FAIL: This test should fail [CLI({}) SRV({})]".format(
+         cli.stdout.read().decode('utf-8').strip(),
+         srv.stdout.read().decode('utf-8').strip()))
++    return 1
++
++def gss_spnego_basic_test(kenv):
++    try:
++        srv = subprocess.Popen(["../tests/t_gssapi_srv", "-N"],
++                               stdout=subprocess.PIPE,
++                               stderr=subprocess.PIPE, env=kenv)
++        srv.stdout.readline() # Wait for srv to say it is ready
++        cli = subprocess.Popen(["../tests/t_gssapi_cli", "-N"],
++                               stdout=subprocess.PIPE,
++                               stderr=subprocess.PIPE, env=kenv)
++        try:
++            cli.wait(timeout=5)
++            srv.wait(timeout=5)
++        except Exception as e:
++            print("Failed on {}".format(e));
++            cli.kill()
++            srv.kill()
++        if cli.returncode != 0 or srv.returncode != 0:
++            raise Exception("CLI ({}): {} --> SRV ({}): {}".format(
++                cli.returncode, cli.stderr.read().decode('utf-8'),
++                srv.returncode, srv.stderr.read().decode('utf-8')))
++    except Exception as e:
++        print("FAIL: {}".format(e))
++        return 1
++
++    print("PASS: CLI({}) SRV({})".format(
++        cli.stdout.read().decode('utf-8').strip(),
++        srv.stdout.read().decode('utf-8').strip()))
++    return 0
++
++def gss_spnego_zeromaxssf_test(kenv):
++    try:
++        srv = subprocess.Popen(["../tests/t_gssapi_srv", "-N", "-z"],
++                               stdout=subprocess.PIPE,
++                               stderr=subprocess.PIPE, env=kenv)
++        srv.stdout.readline() # Wait for srv to say it is ready
++        cli = subprocess.Popen(["../tests/t_gssapi_cli", "-N", "-z"],
++                               stdout=subprocess.PIPE,
++                               stderr=subprocess.PIPE, env=kenv)
++        try:
++            cli.wait(timeout=5)
++            srv.wait(timeout=5)
++        except Exception as e:
++            print("Failed on {}".format(e));
++            cli.kill()
++            srv.kill()
++        if cli.returncode != 0 or srv.returncode != 0:
++            raise Exception("CLI ({}): {} --> SRV ({}): {}".format(
++                cli.returncode, cli.stderr.read().decode('utf-8'),
++                srv.returncode, srv.stderr.read().decode('utf-8')))
++    except Exception as e:
++        print("FAIL: {}".format(e))
++        return 1
++
++    print("PASS: CLI({}) SRV({})".format(
++        cli.stdout.read().decode('utf-8').strip(),
++        srv.stdout.read().decode('utf-8').strip()))
++    return 0
+ 
+ def gssapi_tests(testdir):
+     """ SASL/GSSAPI Tests """
+@@ -225,20 +287,32 @@ def gssapi_tests(testdir):
+     #print("KDC: {}, ENV: {}".format(kdc, kenv))
+     kenv['KRB5_TRACE'] = os.path.join(testdir, 'trace.log')
+ 
++    err = 0
++
+     print('GSSAPI BASIC:')
+     print('    ', end='')
+-    gssapi_basic_test(kenv)
++    err += gssapi_basic_test(kenv)
+ 
+     print('GSSAPI CHANNEL BINDING:')
+     print('    ', end='')
+-    gssapi_channel_binding_test(kenv)
++    err += gssapi_channel_binding_test(kenv)
+ 
+     print('GSSAPI CHANNEL BINDING MISMTACH:')
+     print('    ', end='')
+-    gssapi_channel_binding_mismatch_test(kenv)
++    err += gssapi_channel_binding_mismatch_test(kenv)
++
++    print('GSS-SPNEGO BASIC:')
++    print('    ', end='')
++    err += gss_spnego_basic_test(kenv)
++
++    print('GSS-SPNEGO 0 MAXSSF:')
++    print('    ', end='')
++    err += gss_spnego_zeromaxssf_test(kenv)
+ 
+     os.killpg(kdc.pid, signal.SIGTERM)
+ 
++    return err
++
+ 
+ if __name__ == "__main__":
+ 
+@@ -253,4 +327,7 @@ if __name__ == "__main__":
+         shutil.rmtree(T)
+     os.makedirs(T)
+ 
+-    gssapi_tests(T)
++    err = gssapi_tests(T)
++    if err != 0:
++        print('{} test(s) FAILED'.format(err))
++        sys.exit(-1)
+diff --git a/tests/t_common.c b/tests/t_common.c
+index 478e6a1..f56098e 100644
+--- a/tests/t_common.c
++++ b/tests/t_common.c
+@@ -23,20 +23,21 @@ void send_string(int sd, const char *s, unsigned int l)
+     if (ret != l) s_error("send data", ret, l, errno);
+ }
+ 
+-void recv_string(int sd, char *buf, unsigned int *buflen)
++void recv_string(int sd, char *buf, unsigned int *buflen, bool allow_eof)
+ {
++    unsigned int bufsize = *buflen;
+     unsigned int l;
+     ssize_t ret;
+ 
++    *buflen = 0;
++
+     ret = recv(sd, &l, sizeof(l), MSG_WAITALL);
++    if (allow_eof && ret == 0) return;
+     if (ret != sizeof(l)) s_error("recv size", ret, sizeof(l), errno);
+ 
+-    if (l == 0) {
+-        *buflen = 0;
+-        return;
+-    }
++    if (l == 0) return;
+ 
+-    if (*buflen < l) s_error("recv len", l, *buflen, E2BIG);
++    if (bufsize < l) s_error("recv len", l, bufsize, E2BIG);
+ 
+     ret = recv(sd, buf, l, 0);
+     if (ret != l) s_error("recv data", ret, l, errno);
+diff --git a/tests/t_common.h b/tests/t_common.h
+index a10def1..be24a53 100644
+--- a/tests/t_common.h
++++ b/tests/t_common.h
+@@ -4,6 +4,7 @@
+ #include "config.h"
+ 
+ #include <errno.h>
++#include <stdbool.h>
+ #include <stdio.h>
+ #include <sys/socket.h>
+ 
+@@ -12,7 +13,7 @@
+ 
+ void s_error(const char *hdr, ssize_t ret, ssize_t len, int err);
+ void send_string(int sd, const char *s, unsigned int l);
+-void recv_string(int sd, char *buf, unsigned int *buflen);
++void recv_string(int sd, char *buf, unsigned int *buflen, bool allow_eof);
+ void saslerr(int why, const char *what);
+ int getpath(void *context __attribute__((unused)), const char **path);
+ void parse_cb(sasl_channel_binding_t *cb, char *buf, unsigned max, char *in);
+diff --git a/tests/t_gssapi_cli.c b/tests/t_gssapi_cli.c
+index a44a3f5..d9eafe1 100644
+--- a/tests/t_gssapi_cli.c
++++ b/tests/t_gssapi_cli.c
+@@ -46,12 +46,21 @@ int main(int argc, char *argv[])
+     char cb_buf[256];
+     int sd;
+     int c, r;
++    const char *sasl_mech = "GSSAPI";
++    bool spnego = false;
++    bool zeromaxssf = false;
+ 
+-    while ((c = getopt(argc, argv, "c:")) != EOF) {
++    while ((c = getopt(argc, argv, "c:zN")) != EOF) {
+         switch (c) {
+         case 'c':
+             parse_cb(&cb, cb_buf, 256, optarg);
+             break;
++        case 'z':
++            zeromaxssf = true;
++            break;
++        case 'N':
++            spnego = true;
++            break;
+         default:
+             break;
+         }
+@@ -78,7 +87,17 @@ int main(int argc, char *argv[])
+         sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb);
+     }
+ 
+-    r = sasl_client_start(conn, "GSSAPI", NULL, &data, &len, &chosenmech);
++    if (spnego) {
++        sasl_mech = "GSS-SPNEGO";
++    }
++
++    if (zeromaxssf) {
++        /* set all security properties to 0 including maxssf */
++        sasl_security_properties_t secprops = { 0 };
++        sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
++    }
++
++    r = sasl_client_start(conn, sasl_mech, NULL, &data, &len, &chosenmech);
+     if (r != SASL_OK && r != SASL_CONTINUE) {
+ 	saslerr(r, "starting SASL negotiation");
+ 	printf("\n%s\n", sasl_errdetail(conn));
+@@ -90,7 +109,7 @@ int main(int argc, char *argv[])
+     while (r == SASL_CONTINUE) {
+         send_string(sd, data, len);
+         len = 8192;
+-        recv_string(sd, buf, &len);
++        recv_string(sd, buf, &len, false);
+ 
+ 	r = sasl_client_step(conn, buf, len, NULL, &data, &len);
+ 	if (r != SASL_OK && r != SASL_CONTINUE) {
+diff --git a/tests/t_gssapi_srv.c b/tests/t_gssapi_srv.c
+index ef1217f..448a218 100644
+--- a/tests/t_gssapi_srv.c
++++ b/tests/t_gssapi_srv.c
+@@ -56,12 +56,21 @@ int main(int argc, char *argv[])
+     unsigned char cb_buf[256];
+     int sd;
+     int c, r;
++    const char *sasl_mech = "GSSAPI";
++    bool spnego = false;
++    bool zeromaxssf = false;
+ 
+-    while ((c = getopt(argc, argv, "c:")) != EOF) {
++    while ((c = getopt(argc, argv, "c:zN")) != EOF) {
+         switch (c) {
+         case 'c':
+             parse_cb(&cb, cb_buf, 256, optarg);
+             break;
++        case 'z':
++            zeromaxssf = true;
++            break;
++        case 'N':
++            spnego = true;
++            break;
+         default:
+             break;
+         }
+@@ -90,12 +99,22 @@ int main(int argc, char *argv[])
+         sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb);
+     }
+ 
++    if (spnego) {
++        sasl_mech = "GSS-SPNEGO";
++    }
++
++    if (zeromaxssf) {
++        /* set all security properties to 0 including maxssf */
++        sasl_security_properties_t secprops = { 0 };
++        sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
++    }
++
+     sd = setup_socket();
+ 
+     len = 8192;
+-    recv_string(sd, buf, &len);
++    recv_string(sd, buf, &len, false);
+ 
+-    r = sasl_server_start(conn, "GSSAPI", buf, len, &data, &len);
++    r = sasl_server_start(conn, sasl_mech, buf, len, &data, &len);
+     if (r != SASL_OK && r != SASL_CONTINUE) {
+ 	saslerr(r, "starting SASL negotiation");
+ 	printf("\n%s\n", sasl_errdetail(conn));
+@@ -105,7 +124,7 @@ int main(int argc, char *argv[])
+     while (r == SASL_CONTINUE) {
+         send_string(sd, data, len);
+         len = 8192;
+-        recv_string(sd, buf, &len);
++        recv_string(sd, buf, &len, true);
+ 
+ 	r = sasl_server_step(conn, buf, len, &data, &len);
+ 	if (r != SASL_OK && r != SASL_CONTINUE) {
+@@ -113,7 +132,6 @@ int main(int argc, char *argv[])
+ 	    printf("\n%s\n", sasl_errdetail(conn));
+ 	    exit(-1);
+ 	}
+-
+     }
+ 
+     if (r != SASL_OK) exit(-1);
+-- 
+2.18.2
+
diff --git a/SOURCES/cyrus-sasl-2.1.27-CVE-2019-19906.patch b/SOURCES/cyrus-sasl-2.1.27-CVE-2019-19906.patch
new file mode 100644
index 0000000..ad278aa
--- /dev/null
+++ b/SOURCES/cyrus-sasl-2.1.27-CVE-2019-19906.patch
@@ -0,0 +1,29 @@
+From bcb6c06ec17728f7c9c492dc257b1e541a6830da Mon Sep 17 00:00:00 2001
+From: Simo Sorce <simo@redhat.com>
+Date: Tue, 5 May 2020 14:41:06 -0400
+Subject: [PATCH] CVE-2019-19906
+
+Backport of commit id:
+dcc9f51cbd4ed622cfb0f9b1c141eb2ffe3b12f1
+
+Signed-off-by: Simo Sorce <simo@redhat.com>
+---
+ lib/common.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/lib/common.c b/lib/common.c
+index bc3bf1d..9969d6a 100644
+--- a/lib/common.c
++++ b/lib/common.c
+@@ -190,7 +190,7 @@ int _sasl_add_string(char **out, size_t *alloclen,
+ 
+   if (add==NULL) add = "(null)";
+ 
+-  addlen=strlen(add); /* only compute once */
++  addlen=strlen(add)+1; /* only compute once */
+   if (_buf_alloc(out, alloclen, (*outlen)+addlen)!=SASL_OK)
+     return SASL_NOMEM;
+ 
+-- 
+2.18.2
+
diff --git a/SOURCES/cyrus-sasl-2.1.27-Emit-debug-log-only-in-case-of-errors.patch b/SOURCES/cyrus-sasl-2.1.27-Emit-debug-log-only-in-case-of-errors.patch
new file mode 100644
index 0000000..d5e1334
--- /dev/null
+++ b/SOURCES/cyrus-sasl-2.1.27-Emit-debug-log-only-in-case-of-errors.patch
@@ -0,0 +1,42 @@
+From ec070b2e83a4ee698c08d6d68c205aea4d90b0bb Mon Sep 17 00:00:00 2001
+From: Simo Sorce <simo@redhat.com>
+Date: Tue, 5 May 2020 14:31:10 -0400
+Subject: [PATCH] Emit debug log only in case of errors
+
+Backport of commit id:
+ccc5e547d4b40ee2b182a9945f8f6cc10b4fdf48
+
+Signed-off-by: Simo Sorce <simo@redhat.com>
+---
+ plugins/gssapi.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/plugins/gssapi.c b/plugins/gssapi.c
+index 7480316..6bcd78e 100644
+--- a/plugins/gssapi.c
++++ b/plugins/gssapi.c
+@@ -1444,9 +1444,6 @@ gssapi_server_mech_step(void *conn_context,
+ 
+     if (text == NULL) return SASL_BADPROT;
+ 
+-    params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
+-		       "GSSAPI server step %d\n", text->state);
+-
+     switch (text->state) {
+ 
+     case SASL_GSSAPI_STATE_AUTHNEG:
+@@ -1496,8 +1493,10 @@ gssapi_server_mech_step(void *conn_context,
+ 	}
+ 
+ 	oparams->doneflag = 1;
++    } else {
++        params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
++		           "GSSAPI server step failed: %d\n", text->state);
+     }
+-    
+     return ret;
+ }
+ 
+-- 
+2.18.2
+
diff --git a/SPECS/cyrus-sasl.spec b/SPECS/cyrus-sasl.spec
index 103a782..e667c2c 100644
--- a/SPECS/cyrus-sasl.spec
+++ b/SPECS/cyrus-sasl.spec
@@ -8,7 +8,7 @@
 Summary: The Cyrus SASL library
 Name: cyrus-sasl
 Version: 2.1.27
-Release: 1%{?dist}
+Release: 5%{?dist}
 License: BSD with advertising
 Group: System Environment/Libraries
 # Source0 originally comes from https://www.cyrusimap.org/releases/;
@@ -30,6 +30,11 @@ Patch23: cyrus-sasl-2.1.23-man.patch
 Patch24: cyrus-sasl-2.1.21-sizes.patch
 Patch49: cyrus-sasl-2.1.26-md5global.patch
 Patch60: cyrus-sasl-pr559-RC4-openssl.patch
+Patch830: cyrus-sasl-2.1.27-CVE-2019-19906.patch
+Patch831: cyrus-sasl-2.1.27-Add-basic-test-infrastructure.patch
+Patch832: cyrus-sasl-2.1.27-Add-Channel-Binding-support-for-GSSAPI-GSS-SPNEGO.patch
+Patch833: cyrus-sasl-2.1.27-Add-support-for-setting-max-ssf-0-to-GSS-SPNEGO.patch
+Patch834: cyrus-sasl-2.1.27-Emit-debug-log-only-in-case-of-errors.patch
 
 Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 BuildRequires: autoconf, automake, libtool, gdbm-devel, groff
@@ -39,6 +44,8 @@ BuildRequires: libdb-devel
 %if ! %{bootstrap_cyrus_sasl}
 BuildRequires: openldap-devel
 %endif
+#build reqs for make check
+BuildRequires: python36 nss_wrapper socket_wrapper krb5-server
 %{?systemd_requires}
 Requires(post): chkconfig
 Requires(pre): /usr/sbin/useradd /usr/sbin/groupadd
@@ -161,6 +168,11 @@ the GS2 authentication scheme.
 %patch24 -p1 -b .sizes
 %patch49 -p1 -b .md5global.h
 %patch60 -p1 -b .openssl_rc4
+%patch830 -p1 -b .CVE-2019-19906
+%patch831 -p1 -b .tests
+%patch832 -p1 -b .gssapi_cbs
+%patch833 -p1 -b .maxssf0
+%patch834 -p1 -b .nolog
 
 %build
 # reconfigure
@@ -300,6 +312,8 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/sasl2/*.la
 rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
 rm -f $RPM_BUILD_ROOT%{_mandir}/cat8/saslauthd.8
 
+%check
+make check %{?_smp_mflags}
 
 %pre
 getent group %{username} >/dev/null || groupadd -g 76 -r %{username}
@@ -392,6 +406,22 @@ getent passwd %{username} >/dev/null || useradd -r -g %{username} -d %{homedir} 
 %{_sbindir}/sasl2-shared-mechlist
 
 %changelog
+* Tue May  5 2020 Simo Sorce <simo@redhat.com> - 2.1.27-5
+- Reduce excessive GSSAPI plugin logging
+- Resolves: rhbz#1274734
+
+* Tue May  5 2020 Simo Sorce <simo@redhat.com> - 2.1.27-4
+- Add support for setting maxssf=0 in GSS-SPNEGO
+- Resolves: rhbz#1822133
+
+* Tue May  5 2020 Simo Sorce <simo@redhat.com> - 2.1.27-3
+- Backport GSSAPI Channel Bindings support
+- Resolves: rhbz#1817054
+
+* Tue May  5 2020 Simo Sorce <simo@redhat.com> - 2.1.27-2
+- Backport fix for CVE-2019-19906
+- Resolves: rhbz#1804036
+
 * Fri Jun 14 2019 Simo Sorce <simo@redhat.com> - 2.1.27-1
 - Rc7 to final source
 - Resovles bz#1618744