File 0010-Add-support-for-setting-max-ssf-0-to-GSS-SPNEGO.patch of Package cyrus-sasl.39256

From a82922f01d1de9827b64b395c99f46003a74fdfd Mon Sep 17 00:00:00 2001
From: Simo Sorce <simo@redhat.com>
Date: Thu, 19 Sep 2019 15:58:04 -0400
Subject: [PATCH 1/3] Add support for setting max ssf 0 to GSS-SPNEGO

This is needed to interop with Windows within a TLS channel.

Signed-off-by: Simo Sorce <simo@redhat.com>
(cherry picked from commit 9de4d7e885c96c68a155d2885c980e1d889129c7)
---
 m4/sasl2.m4      | 13 +++++++++++++
 plugins/gssapi.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 55 insertions(+), 1 deletion(-)

diff --git a/m4/sasl2.m4 b/m4/sasl2.m4
index 80371ef0..098c853a 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 5e0aa5ac..34e1baa5 100644
--- a/plugins/gssapi.c
+++ b/plugins/gssapi.c
@@ -1783,7 +1783,48 @@ 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;
-- 
2.46.0


From c3b7c7ff8132fb4f21d8d8e3dfbc9b7b7a6bb884 Mon Sep 17 00:00:00 2001
From: Simo Sorce <idra@samba.org>
Date: Wed, 15 Apr 2020 11:57:17 -0400
Subject: [PATCH 2/3] Test GSS-SPNEGO as well

Signed-off-by: Simo Sorce <simo@redhat.com>
(cherry picked from commit d95b0afef1289194148090874799428e9e4f4cff)
---
 tests/runtests.py    | 90 ++++++++++++++++++++++++++++++++++++++++----
 tests/t_common.c     | 13 ++++---
 tests/t_common.h     |  3 +-
 tests/t_gssapi_cli.c | 25 ++++++++++--
 tests/t_gssapi_srv.c | 25 ++++++++++--
 5 files changed, 136 insertions(+), 20 deletions(-)

diff --git a/tests/runtests.py b/tests/runtests.py
index fc9cf244..4caac2b0 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,19 +287,30 @@ 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 +326,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 478e6a1f..f56098ef 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 a10def17..be24a53d 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 a44a3f58..d9eafe1d 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 ef1217f6..4cad84fb 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,10 +99,20 @@ 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);
     if (r != SASL_OK && r != SASL_CONTINUE) {
@@ -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) {
-- 
2.46.0


From a9f8bddb4bdfc3766ca971bc46d6b740723de6c3 Mon Sep 17 00:00:00 2001
From: Simo Sorce <simo@redhat.com>
Date: Fri, 26 Jun 2020 11:23:55 -0400
Subject: [PATCH 3/3] Be more conformant to RFC4752

Although we need to be able to completly suppress Integrity and
Confidentiality flags in GSS-SPNEGO, we also need to be more conformant
to RFC4752 for the GSSAPI mechanism.

The RFC reuires to always set Integrity for SASL/GSSAPI, it also
requires MUTUAL/SEQUENCE flags to only be set if any Security Layer is
requested.

Finally Confidentiality should be set only when requested so change the
code that suppresses MIT krb5 setting CI flags not only in the SSF == 0
case but also when SSF == 1, the integrity flag in that case will be
explicitly set by our code and the NO_CI_FLAGS option will unset just
the CONF flag.

Signed-off-by: Simo Sorce <simo@redhat.com>
(cherry picked from commit c4c57d85c589d7e78bccdc67d705cdcdf85a2b02)
---
 plugins/gssapi.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/plugins/gssapi.c b/plugins/gssapi.c
index 34e1baa5..409a7d08 100644
--- a/plugins/gssapi.c
+++ b/plugins/gssapi.c
@@ -1773,9 +1773,15 @@ static int gssapi_client_mech_step(void *conn_context,
 
 	/* Setup req_flags properly */
 	req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG;
-	if (params->props.max_ssf > params->external_ssf) {
-	    /* We are requesting a security layer */
+        if (text->mech_type == NULL) {
+            /* in SASL/GSSAPI RFC4752 requires the INTEG flag MUST be set to
+             * TRUE in all cases */
 	    req_flags |= GSS_C_INTEG_FLAG;
+        }
+	/* We are requesting a security layer */
+	if (params->props.max_ssf > params->external_ssf) {
+            /* Add integrity if explicity requested */
+            req_flags |= GSS_C_INTEG_FLAG;
 	    /* Any SSF bigger than 1 is confidentiality. */
 	    /* Let's check if the client of the API requires confidentiality,
 	       and it wasn't already provided by an external layer */
@@ -1792,7 +1798,7 @@ static int gssapi_client_mech_step(void *conn_context,
          * 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) {
+	} else if (params->props.max_ssf <= 1) {
             gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
             if (client_creds == GSS_C_NO_CREDENTIAL) {
                 gss_OID_set_desc mechs = { 0 };
-- 
2.46.0

openSUSE Build Service is sponsored by