Blame SOURCES/cyrus-sasl-2.1.27-Add-Channel-Binding-support-for-GSSAPI-GSS-SPNEGO.patch

df6e3e
From aa8b6b2275fd14ba2cca3d2339ae61c7e7ddfa70 Mon Sep 17 00:00:00 2001
df6e3e
From: Simo Sorce <simo@redhat.com>
df6e3e
Date: Tue, 5 May 2020 14:08:48 -0400
df6e3e
Subject: [PATCH] Add Channel Binding support for GSSAPI/GSS-SPNEGO
df6e3e
df6e3e
Backport of commit ids:
df6e3e
829a6ed086432e26dafa9d1dcf892aef4c42cfbd
df6e3e
944bd8a6205f840b105206ef83e8f6b9dff0138e
df6e3e
df6e3e
Signed-off-by: Simo Sorce <simo@redhat.com>
df6e3e
---
df6e3e
 plugins/gssapi.c     | 30 +++++++++++---
df6e3e
 tests/runtests.py    | 93 ++++++++++++++++++++++++++++++++++++++++----
df6e3e
 tests/t_common.c     | 24 ++++++++----
df6e3e
 tests/t_common.h     |  5 ++-
df6e3e
 tests/t_gssapi_cli.c | 24 ++++++++++--
df6e3e
 tests/t_gssapi_srv.c | 24 ++++++++++--
df6e3e
 6 files changed, 172 insertions(+), 28 deletions(-)
df6e3e
df6e3e
diff --git a/plugins/gssapi.c b/plugins/gssapi.c
df6e3e
index ff663da..5d900c5 100644
df6e3e
--- a/plugins/gssapi.c
df6e3e
+++ b/plugins/gssapi.c
df6e3e
@@ -830,7 +830,9 @@ gssapi_server_mech_authneg(context_t *text,
df6e3e
     gss_buffer_desc name_without_realm;
df6e3e
     gss_name_t client_name_MN = NULL, without = NULL;
df6e3e
     gss_OID mech_type;
df6e3e
-	
df6e3e
+    gss_channel_bindings_t bindings = GSS_C_NO_CHANNEL_BINDINGS;
df6e3e
+    struct gss_channel_bindings_struct cb = {0};
df6e3e
+
df6e3e
     input_token = &real_input_token;
df6e3e
     output_token = &real_output_token;
df6e3e
     output_token->value = NULL; output_token->length = 0;
df6e3e
@@ -902,6 +904,12 @@ gssapi_server_mech_authneg(context_t *text,
df6e3e
 	real_input_token.length = clientinlen;
df6e3e
     }
df6e3e
 
df6e3e
+    if (params->cbinding != NULL) {
df6e3e
+        cb.application_data.length = params->cbinding->len;
df6e3e
+        cb.application_data.value = params->cbinding->data;
df6e3e
+        bindings = &cb;
df6e3e
+    }
df6e3e
+
df6e3e
 
df6e3e
     GSS_LOCK_MUTEX_CTX(params->utils, text);
df6e3e
     maj_stat =
df6e3e
@@ -909,7 +917,7 @@ gssapi_server_mech_authneg(context_t *text,
df6e3e
 			       &(text->gss_ctx),
df6e3e
 			       server_creds,
df6e3e
 			       input_token,
df6e3e
-			       GSS_C_NO_CHANNEL_BINDINGS,
df6e3e
+			       bindings,
df6e3e
 			       &text->client_name,
df6e3e
 			       &mech_type,
df6e3e
 			       output_token,
df6e3e
@@ -1505,7 +1513,8 @@ static sasl_server_plug_t gssapi_server_plugins[] =
df6e3e
 	| SASL_SEC_PASS_CREDENTIALS,
df6e3e
 	SASL_FEAT_WANT_CLIENT_FIRST
df6e3e
 	| SASL_FEAT_ALLOWS_PROXY
df6e3e
-	| SASL_FEAT_DONTUSE_USERPASSWD,	/* features */
df6e3e
+	| SASL_FEAT_DONTUSE_USERPASSWD
df6e3e
+	| SASL_FEAT_CHANNEL_BINDING,	/* features */
df6e3e
 	NULL,				/* glob_context */
df6e3e
 	&gssapi_server_mech_new,	/* mech_new */
df6e3e
 	&gssapi_server_mech_step,	/* mech_step */
df6e3e
@@ -1529,6 +1538,7 @@ static sasl_server_plug_t gssapi_server_plugins[] =
df6e3e
 	SASL_FEAT_WANT_CLIENT_FIRST
df6e3e
 	| SASL_FEAT_ALLOWS_PROXY
df6e3e
 	| SASL_FEAT_DONTUSE_USERPASSWD
df6e3e
+	| SASL_FEAT_CHANNEL_BINDING
df6e3e
 	| SASL_FEAT_SUPPORTS_HTTP,	/* features */
df6e3e
 	&gss_spnego_oid,		/* glob_context */
df6e3e
 	&gssapi_server_mech_new,	/* mech_new */
df6e3e
@@ -1662,6 +1672,8 @@ static int gssapi_client_mech_step(void *conn_context,
df6e3e
     input_token->value = NULL; 
df6e3e
     input_token->length = 0;
df6e3e
     gss_cred_id_t client_creds = (gss_cred_id_t)params->gss_creds;
df6e3e
+    gss_channel_bindings_t bindings = GSS_C_NO_CHANNEL_BINDINGS;
df6e3e
+    struct gss_channel_bindings_struct cb = {0};
df6e3e
 
df6e3e
     if (clientout)
df6e3e
         *clientout = NULL;
df6e3e
@@ -1777,6 +1789,12 @@ static int gssapi_client_mech_step(void *conn_context,
df6e3e
 	    req_flags = req_flags |  GSS_C_DELEG_FLAG;
df6e3e
 	}
df6e3e
 
df6e3e
+        if (params->cbinding != NULL) {
df6e3e
+            cb.application_data.length = params->cbinding->len;
df6e3e
+            cb.application_data.value = params->cbinding->data;
df6e3e
+            bindings = &cb;
df6e3e
+        }
df6e3e
+
df6e3e
 	GSS_LOCK_MUTEX_CTX(params->utils, text);
df6e3e
 	maj_stat = gss_init_sec_context(&min_stat,
df6e3e
 					client_creds, /* GSS_C_NO_CREDENTIAL */
df6e3e
@@ -1785,7 +1803,7 @@ static int gssapi_client_mech_step(void *conn_context,
df6e3e
 					text->mech_type,
df6e3e
 					req_flags,
df6e3e
 					0,
df6e3e
-					GSS_C_NO_CHANNEL_BINDINGS,
df6e3e
+					bindings,
df6e3e
 					input_token,
df6e3e
 					NULL,
df6e3e
 					output_token,
df6e3e
@@ -2190,7 +2208,8 @@ static sasl_client_plug_t gssapi_client_plugins[] =
df6e3e
 	| SASL_SEC_PASS_CREDENTIALS,    /* security_flags */
df6e3e
 	SASL_FEAT_NEEDSERVERFQDN
df6e3e
 	| SASL_FEAT_WANT_CLIENT_FIRST
df6e3e
-	| SASL_FEAT_ALLOWS_PROXY,	/* features */
df6e3e
+	| SASL_FEAT_ALLOWS_PROXY
df6e3e
+	| SASL_FEAT_CHANNEL_BINDING,	/* features */
df6e3e
 	gssapi_required_prompts,	/* required_prompts */
df6e3e
 	GSS_C_NO_OID,			/* glob_context */
df6e3e
 	&gssapi_client_mech_new,	/* mech_new */
df6e3e
@@ -2213,6 +2232,7 @@ static sasl_client_plug_t gssapi_client_plugins[] =
df6e3e
 	SASL_FEAT_NEEDSERVERFQDN
df6e3e
 	| SASL_FEAT_WANT_CLIENT_FIRST
df6e3e
 	| SASL_FEAT_ALLOWS_PROXY
df6e3e
+	| SASL_FEAT_CHANNEL_BINDING
df6e3e
 	| SASL_FEAT_SUPPORTS_HTTP,	/* features */
df6e3e
 	gssapi_required_prompts,	/* required_prompts */
df6e3e
 	&gss_spnego_oid,		/* glob_context */
df6e3e
diff --git a/tests/runtests.py b/tests/runtests.py
df6e3e
index f645adf..fc9cf24 100755
df6e3e
--- a/tests/runtests.py
df6e3e
+++ b/tests/runtests.py
df6e3e
@@ -1,6 +1,7 @@
df6e3e
 #!/usr/bin/python3
df6e3e
 
df6e3e
 import argparse
df6e3e
+import base64
df6e3e
 import os
df6e3e
 import shutil
df6e3e
 import signal
df6e3e
@@ -126,14 +127,7 @@ def setup_kdc(testdir, env):
df6e3e
 
df6e3e
     return kdc, env
df6e3e
 
df6e3e
-
df6e3e
-def gssapi_tests(testdir):
df6e3e
-    """ SASL/GSSAPI Tests """
df6e3e
-    env = setup_socket_wrappers(testdir)
df6e3e
-    kdc, kenv = setup_kdc(testdir, env)
df6e3e
-    #print("KDC: {}, ENV: {}".format(kdc, kenv))
df6e3e
-    kenv['KRB5_TRACE'] = os.path.join(testdir, 'trace.log')
df6e3e
-
df6e3e
+def gssapi_basic_test(kenv):
df6e3e
     try:
df6e3e
         srv = subprocess.Popen(["../tests/t_gssapi_srv"],
df6e3e
                                stdout=subprocess.PIPE,
df6e3e
@@ -155,11 +149,94 @@ def gssapi_tests(testdir):
df6e3e
                 srv.returncode, srv.stderr.read().decode('utf-8')))
df6e3e
     except Exception as e:
df6e3e
         print("FAIL: {}".format(e))
df6e3e
+        return
df6e3e
+
df6e3e
+    print("PASS: CLI({}) SRV({})".format(
df6e3e
+        cli.stdout.read().decode('utf-8').strip(),
df6e3e
+        srv.stdout.read().decode('utf-8').strip()))
df6e3e
+
df6e3e
+def gssapi_channel_binding_test(kenv):
df6e3e
+    try:
df6e3e
+        bindings = base64.b64encode("MATCHING CBS".encode('utf-8'))
df6e3e
+        srv = subprocess.Popen(["../tests/t_gssapi_srv", "-c", bindings],
df6e3e
+                               stdout=subprocess.PIPE,
df6e3e
+                               stderr=subprocess.PIPE, env=kenv)
df6e3e
+        srv.stdout.readline() # Wait for srv to say it is ready
df6e3e
+        cli = subprocess.Popen(["../tests/t_gssapi_cli", "-c", bindings],
df6e3e
+                               stdout=subprocess.PIPE,
df6e3e
+                               stderr=subprocess.PIPE, env=kenv)
df6e3e
+        try:
df6e3e
+            cli.wait(timeout=5)
df6e3e
+            srv.wait(timeout=5)
df6e3e
+        except Exception as e:
df6e3e
+            print("Failed on {}".format(e));
df6e3e
+            cli.kill()
df6e3e
+            srv.kill()
df6e3e
+        if cli.returncode != 0 or srv.returncode != 0:
df6e3e
+            raise Exception("CLI ({}): {} --> SRV ({}): {}".format(
df6e3e
+                cli.returncode, cli.stderr.read().decode('utf-8'),
df6e3e
+                srv.returncode, srv.stderr.read().decode('utf-8')))
df6e3e
+    except Exception as e:
df6e3e
+        print("FAIL: {}".format(e))
df6e3e
+        return
df6e3e
 
df6e3e
     print("PASS: CLI({}) SRV({})".format(
df6e3e
         cli.stdout.read().decode('utf-8').strip(),
df6e3e
         srv.stdout.read().decode('utf-8').strip()))
df6e3e
 
df6e3e
+def gssapi_channel_binding_mismatch_test(kenv):
df6e3e
+    result = "FAIL"
df6e3e
+    try:
df6e3e
+        bindings = base64.b64encode("SRV CBS".encode('utf-8'))
df6e3e
+        srv = subprocess.Popen(["../tests/t_gssapi_srv", "-c", bindings],
df6e3e
+                               stdout=subprocess.PIPE,
df6e3e
+                               stderr=subprocess.PIPE, env=kenv)
df6e3e
+        srv.stdout.readline() # Wait for srv to say it is ready
df6e3e
+        bindings = base64.b64encode("CLI CBS".encode('utf-8'))
df6e3e
+        cli = subprocess.Popen(["../tests/t_gssapi_cli", "-c", bindings],
df6e3e
+                               stdout=subprocess.PIPE,
df6e3e
+                               stderr=subprocess.PIPE, env=kenv)
df6e3e
+        try:
df6e3e
+            cli.wait(timeout=5)
df6e3e
+            srv.wait(timeout=5)
df6e3e
+        except Exception as e:
df6e3e
+            print("Failed on {}".format(e));
df6e3e
+            cli.kill()
df6e3e
+            srv.kill()
df6e3e
+        if cli.returncode != 0 or srv.returncode != 0:
df6e3e
+            cli_err = cli.stderr.read().decode('utf-8').strip()
df6e3e
+            srv_err = srv.stderr.read().decode('utf-8').strip()
df6e3e
+            if "authentication failure" in srv_err:
df6e3e
+                result = "PASS"
df6e3e
+            raise Exception("CLI ({}): {} --> SRV ({}): {}".format(
df6e3e
+                cli.returncode, cli_err, srv.returncode, srv_err))
df6e3e
+    except Exception as e:
df6e3e
+        print("{}: {}".format(result, e))
df6e3e
+        return
df6e3e
+
df6e3e
+    print("FAIL: This test should fail [CLI({}) SRV({})]".format(
df6e3e
+        cli.stdout.read().decode('utf-8').strip(),
df6e3e
+        srv.stdout.read().decode('utf-8').strip()))
df6e3e
+
df6e3e
+def gssapi_tests(testdir):
df6e3e
+    """ SASL/GSSAPI Tests """
df6e3e
+    env = setup_socket_wrappers(testdir)
df6e3e
+    kdc, kenv = setup_kdc(testdir, env)
df6e3e
+    #print("KDC: {}, ENV: {}".format(kdc, kenv))
df6e3e
+    kenv['KRB5_TRACE'] = os.path.join(testdir, 'trace.log')
df6e3e
+
df6e3e
+    print('GSSAPI BASIC:')
df6e3e
+    print('    ', end='')
df6e3e
+    gssapi_basic_test(kenv)
df6e3e
+
df6e3e
+    print('GSSAPI CHANNEL BINDING:')
df6e3e
+    print('    ', end='')
df6e3e
+    gssapi_channel_binding_test(kenv)
df6e3e
+
df6e3e
+    print('GSSAPI CHANNEL BINDING MISMTACH:')
df6e3e
+    print('    ', end='')
df6e3e
+    gssapi_channel_binding_mismatch_test(kenv)
df6e3e
+
df6e3e
     os.killpg(kdc.pid, signal.SIGTERM)
df6e3e
 
df6e3e
 
df6e3e
diff --git a/tests/t_common.c b/tests/t_common.c
df6e3e
index 7168b2f..478e6a1 100644
df6e3e
--- a/tests/t_common.c
df6e3e
+++ b/tests/t_common.c
df6e3e
@@ -1,4 +1,5 @@
df6e3e
-/* TBD, add (C) */
df6e3e
+/* Copyright (C) Simo Sorce <simo@redhat.com>
df6e3e
+ * See COPYING file for License */
df6e3e
 
df6e3e
 #include <t_common.h>
df6e3e
 
df6e3e
@@ -13,9 +14,6 @@ void send_string(int sd, const char *s, unsigned int l)
df6e3e
 {
df6e3e
     ssize_t ret;
df6e3e
 
df6e3e
-fprintf(stderr, "s:%u ", l);
df6e3e
-fflush(stderr);
df6e3e
-
df6e3e
     ret = send(sd, &l, sizeof(l), 0);
df6e3e
     if (ret != sizeof(l)) s_error("send size", ret, sizeof(l), errno);
df6e3e
 
df6e3e
@@ -34,8 +32,6 @@ void recv_string(int sd, char *buf, unsigned int *buflen)
df6e3e
     if (ret != sizeof(l)) s_error("recv size", ret, sizeof(l), errno);
df6e3e
 
df6e3e
     if (l == 0) {
df6e3e
-fprintf(stderr, "r:0 ");
df6e3e
-fflush(stderr);
df6e3e
         *buflen = 0;
df6e3e
         return;
df6e3e
     }
df6e3e
@@ -45,8 +41,6 @@ fflush(stderr);
df6e3e
     ret = recv(sd, buf, l, 0);
df6e3e
     if (ret != l) s_error("recv data", ret, l, errno);
df6e3e
 
df6e3e
-fprintf(stderr, "r:%ld ", ret);
df6e3e
-fflush(stderr);
df6e3e
     *buflen = ret;
df6e3e
 }
df6e3e
 
df6e3e
@@ -65,4 +59,18 @@ int getpath(void *context __attribute__((unused)), const char **path)
df6e3e
     return SASL_OK;
df6e3e
 }
df6e3e
 
df6e3e
+void parse_cb(sasl_channel_binding_t *cb, char *buf, unsigned max, char *in)
df6e3e
+{
df6e3e
+    unsigned len;
df6e3e
+    int r;
df6e3e
 
df6e3e
+    r = sasl_decode64(in, strlen(in), buf, max, &len;;
df6e3e
+    if (r != SASL_OK) {
df6e3e
+        saslerr(r, "failed to parse channel bindings");
df6e3e
+        exit(-1);
df6e3e
+    }
df6e3e
+    cb->name = "TEST BINDINGS";
df6e3e
+    cb->critical = 0;
df6e3e
+    cb->data = (unsigned char *)buf;
df6e3e
+    cb->len = len;
df6e3e
+}
df6e3e
diff --git a/tests/t_common.h b/tests/t_common.h
df6e3e
index 4ee1976..a10def1 100644
df6e3e
--- a/tests/t_common.h
df6e3e
+++ b/tests/t_common.h
df6e3e
@@ -1,4 +1,5 @@
df6e3e
-/* TBD, add (C) */
df6e3e
+/* Copyright (C) Simo Sorce <simo@redhat.com>
df6e3e
+ * See COPYING file for License */
df6e3e
 
df6e3e
 #include "config.h"
df6e3e
 
df6e3e
@@ -7,9 +8,11 @@
df6e3e
 #include <sys/socket.h>
df6e3e
 
df6e3e
 #include <sasl.h>
df6e3e
+#include <saslutil.h>
df6e3e
 
df6e3e
 void s_error(const char *hdr, ssize_t ret, ssize_t len, int err);
df6e3e
 void send_string(int sd, const char *s, unsigned int l);
df6e3e
 void recv_string(int sd, char *buf, unsigned int *buflen);
df6e3e
 void saslerr(int why, const char *what);
df6e3e
 int getpath(void *context __attribute__((unused)), const char **path);
df6e3e
+void parse_cb(sasl_channel_binding_t *cb, char *buf, unsigned max, char *in);
df6e3e
diff --git a/tests/t_gssapi_cli.c b/tests/t_gssapi_cli.c
df6e3e
index c833c05..a44a3f5 100644
df6e3e
--- a/tests/t_gssapi_cli.c
df6e3e
+++ b/tests/t_gssapi_cli.c
df6e3e
@@ -1,4 +1,5 @@
df6e3e
-/* TBD, add (C) */
df6e3e
+/* Copyright (C) Simo Sorce <simo@redhat.com>
df6e3e
+ * See COPYING file for License */
df6e3e
 
df6e3e
 #include "t_common.h"
df6e3e
 
df6e3e
@@ -13,6 +14,7 @@
df6e3e
 
df6e3e
 #include <arpa/inet.h>
df6e3e
 #include <saslplug.h>
df6e3e
+#include <saslutil.h>
df6e3e
 
df6e3e
 static int setup_socket(void)
df6e3e
 {
df6e3e
@@ -32,7 +34,7 @@ static int setup_socket(void)
df6e3e
     return sock;
df6e3e
 }
df6e3e
 
df6e3e
-int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
df6e3e
+int main(int argc, char *argv[])
df6e3e
 {
df6e3e
     sasl_callback_t callbacks[2] = {};
df6e3e
     char buf[8192];
df6e3e
@@ -40,8 +42,20 @@ int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
df6e3e
     sasl_conn_t *conn;
df6e3e
     const char *data;
df6e3e
     unsigned int len;
df6e3e
+    sasl_channel_binding_t cb = {0};
df6e3e
+    char cb_buf[256];
df6e3e
     int sd;
df6e3e
-    int r;
df6e3e
+    int c, r;
df6e3e
+
df6e3e
+    while ((c = getopt(argc, argv, "c:")) != EOF) {
df6e3e
+        switch (c) {
df6e3e
+        case 'c':
df6e3e
+            parse_cb(&cb, cb_buf, 256, optarg);
df6e3e
+            break;
df6e3e
+        default:
df6e3e
+            break;
df6e3e
+        }
df6e3e
+    }
df6e3e
 
df6e3e
     /* initialize the sasl library */
df6e3e
     callbacks[0].id = SASL_CB_GETPATH;
df6e3e
@@ -60,6 +74,10 @@ int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
df6e3e
         exit(-1);
df6e3e
     }
df6e3e
 
df6e3e
+    if (cb.name) {
df6e3e
+        sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb;;
df6e3e
+    }
df6e3e
+
df6e3e
     r = sasl_client_start(conn, "GSSAPI", NULL, &data, &len, &chosenmech);
df6e3e
     if (r != SASL_OK && r != SASL_CONTINUE) {
df6e3e
 	saslerr(r, "starting SASL negotiation");
df6e3e
diff --git a/tests/t_gssapi_srv.c b/tests/t_gssapi_srv.c
df6e3e
index 29f538d..ef1217f 100644
df6e3e
--- a/tests/t_gssapi_srv.c
df6e3e
+++ b/tests/t_gssapi_srv.c
df6e3e
@@ -1,4 +1,5 @@
df6e3e
-/* TBD, add (C) */
df6e3e
+/* Copyright (C) Simo Sorce <simo@redhat.com>
df6e3e
+ * See COPYING file for License */
df6e3e
 
df6e3e
 #include "t_common.h"
df6e3e
 
df6e3e
@@ -44,15 +45,28 @@ static int setup_socket(void)
df6e3e
     return sd;
df6e3e
 }
df6e3e
 
df6e3e
-int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
df6e3e
+int main(int argc, char *argv[])
df6e3e
 {
df6e3e
     sasl_callback_t callbacks[2] = {};
df6e3e
     char buf[8192];
df6e3e
     sasl_conn_t *conn;
df6e3e
     const char *data;
df6e3e
     unsigned int len;
df6e3e
+    sasl_channel_binding_t cb = {0};
df6e3e
+    unsigned char cb_buf[256];
df6e3e
     int sd;
df6e3e
-    int r;
df6e3e
+    int c, r;
df6e3e
+
df6e3e
+    while ((c = getopt(argc, argv, "c:")) != EOF) {
df6e3e
+        switch (c) {
df6e3e
+        case 'c':
df6e3e
+            parse_cb(&cb, cb_buf, 256, optarg);
df6e3e
+            break;
df6e3e
+        default:
df6e3e
+            break;
df6e3e
+        }
df6e3e
+    }
df6e3e
+
df6e3e
 
df6e3e
     /* initialize the sasl library */
df6e3e
     callbacks[0].id = SASL_CB_GETPATH;
df6e3e
@@ -72,6 +86,10 @@ int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
df6e3e
         exit(-1);
df6e3e
     }
df6e3e
 
df6e3e
+    if (cb.name) {
df6e3e
+        sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb;;
df6e3e
+    }
df6e3e
+
df6e3e
     sd = setup_socket();
df6e3e
 
df6e3e
     len = 8192;
df6e3e
-- 
df6e3e
2.18.2
df6e3e