From becf43d690caee18f36daa30d81ac4c4468b40bd Mon Sep 17 00:00:00 2001 From: Funda Wang Date: Wed, 8 Mar 2023 13:02:26 +0800 Subject: [PATCH] Add various patches --- autogen.sh | 37 + cyrus-sasl-2.1.20-saslauthd.conf-path.patch | 26 + cyrus-sasl-2.1.21-sizes.patch | 119 +++ cyrus-sasl-2.1.23-man.patch | 24 + cyrus-sasl-2.1.25-no_rpath.patch | 20 + cyrus-sasl-2.1.26-md5global.patch | 24 + ...inding-support-for-GSSAPI-GSS-SPNEGO.patch | 475 +++++++++++ ...2.1.27-Add-basic-test-infrastructure.patch | 642 +++++++++++++++ ...-for-setting-max-ssf-0-to-GSS-SPNEGO.patch | 95 +++ cyrus-sasl-2.1.27-coverity.patch | 56 ++ cyrus-sasl-2.1.27-cumulative-digestmd5.patch | 554 +++++++++++++ cyrus-sasl-2.1.27-cumulative-ossl3.patch | 109 +++ cyrus-sasl-2.1.27-more-tests.patch | 761 ++++++++++++++++++ cyrus-sasl-2.1.27-nostrncpy.patch | 51 ++ cyrus-sasl.spec | 296 ++++--- sasl-mechlist.c | 99 +++ saslauth.sysusers | 4 + 17 files changed, 3272 insertions(+), 120 deletions(-) create mode 100644 autogen.sh create mode 100644 cyrus-sasl-2.1.20-saslauthd.conf-path.patch create mode 100644 cyrus-sasl-2.1.21-sizes.patch create mode 100644 cyrus-sasl-2.1.23-man.patch create mode 100644 cyrus-sasl-2.1.25-no_rpath.patch create mode 100644 cyrus-sasl-2.1.26-md5global.patch create mode 100644 cyrus-sasl-2.1.27-Add-Channel-Binding-support-for-GSSAPI-GSS-SPNEGO.patch create mode 100644 cyrus-sasl-2.1.27-Add-basic-test-infrastructure.patch create mode 100644 cyrus-sasl-2.1.27-Add-support-for-setting-max-ssf-0-to-GSS-SPNEGO.patch create mode 100644 cyrus-sasl-2.1.27-coverity.patch create mode 100644 cyrus-sasl-2.1.27-cumulative-digestmd5.patch create mode 100644 cyrus-sasl-2.1.27-cumulative-ossl3.patch create mode 100644 cyrus-sasl-2.1.27-more-tests.patch create mode 100644 cyrus-sasl-2.1.27-nostrncpy.patch create mode 100644 sasl-mechlist.c create mode 100644 saslauth.sysusers diff --git a/autogen.sh b/autogen.sh new file mode 100644 index 0000000..38e3be2 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. +test -n "$srcdir" || srcdir=`dirname "$0"` +test -n "$srcdir" || srcdir=. + +olddir=`pwd` + +cd $srcdir + +(test -f configure.ac) || { + echo "*** ERROR: Directory "\`$srcdir\'" does not look like the top-level project directory ***" + exit 1 +} + +PKG_NAME=`autoconf --trace 'AC_INIT:$1' configure.ac` + +if [ "$#" = 0 -a "x$NOCONFIGURE" = "x" ]; then + echo "*** WARNING: I am going to run \`configure' with no arguments." >&2 + echo "*** If you wish to pass any to it, please specify them on the" >&2 + echo "*** \`$0\' command line." >&2 + echo "" >&2 +fi + +aclocal --install || exit 1 +autoreconf --verbose --force --install -Wno-portability || exit 1 + +cd $olddir +if [ "$NOCONFIGURE" = "" ]; then + $srcdir/configure "$@" || exit 1 + + if [ "$1" = "--help" ]; then exit 0 else + echo "Now type \`make\' to compile $PKG_NAME" || exit 1 + fi +else + echo "Skipping configure process." +fi + diff --git a/cyrus-sasl-2.1.20-saslauthd.conf-path.patch b/cyrus-sasl-2.1.20-saslauthd.conf-path.patch new file mode 100644 index 0000000..1e414ff --- /dev/null +++ b/cyrus-sasl-2.1.20-saslauthd.conf-path.patch @@ -0,0 +1,26 @@ +diff -up cyrus-sasl-2.1.27/saslauthd/saslauthd.mdoc.path cyrus-sasl-2.1.27/saslauthd/saslauthd.mdoc +--- cyrus-sasl-2.1.27/saslauthd/saslauthd.mdoc.path 2015-10-15 15:44:43.000000000 +0200 ++++ cyrus-sasl-2.1.27/saslauthd/saslauthd.mdoc 2015-11-20 15:05:30.421377527 +0100 +@@ -221,7 +221,7 @@ instead. + .Em (All platforms that support OpenLDAP 2.0 or higher) + .Pp + Authenticate against an ldap server. The ldap configuration parameters are +-read from /usr/local/etc/saslauthd.conf. The location of this file can be ++read from /etc/saslauthd.conf. The location of this file can be + changed with the -O parameter. See the LDAP_SASLAUTHD file included with the + distribution for the list of available parameters. + .It Li sia +@@ -251,10 +251,10 @@ these ticket files can cause serious per + servers. (Kerberos + was never intended to be used in this manner, anyway.) + .Sh FILES +-.Bl -tag -width "/var/run/saslauthd/mux" +-.It Pa /var/run/saslauthd/mux ++.Bl -tag -width "/run/saslauthd/mux" ++.It Pa /run/saslauthd/mux + The default communications socket. +-.It Pa /usr/local/etc/saslauthd.conf ++.It Pa /etc/saslauthd.conf + The default configuration file for ldap support. + .El + .Sh SEE ALSO diff --git a/cyrus-sasl-2.1.21-sizes.patch b/cyrus-sasl-2.1.21-sizes.patch new file mode 100644 index 0000000..6373924 --- /dev/null +++ b/cyrus-sasl-2.1.21-sizes.patch @@ -0,0 +1,119 @@ +diff -up cyrus-sasl-2.1.27/configure.ac.sizes cyrus-sasl-2.1.27/configure.ac +--- cyrus-sasl-2.1.27/configure.ac.sizes 2015-11-18 09:46:24.000000000 +0100 ++++ cyrus-sasl-2.1.27/configure.ac 2015-11-20 15:11:20.474588247 +0100 +@@ -1312,6 +1312,10 @@ AC_HEADER_STDC + AC_HEADER_DIRENT + AC_HEADER_SYS_WAIT + AC_CHECK_HEADERS(crypt.h des.h dlfcn.h fcntl.h limits.h malloc.h paths.h strings.h sys/file.h sys/time.h syslog.h unistd.h inttypes.h sys/uio.h sys/param.h sysexits.h stdarg.h varargs.h krb5.h) ++AC_CHECK_TYPES([long long, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t],,,[ ++#ifdef HAVE_INTTYPES_H ++#include ++#endif]) + + IPv6_CHECK_SS_FAMILY() + IPv6_CHECK_SA_LEN() +diff -up cyrus-sasl-2.1.27/include/makemd5.c.sizes cyrus-sasl-2.1.27/include/makemd5.c +--- cyrus-sasl-2.1.27/include/makemd5.c.sizes 2015-10-15 15:44:43.000000000 +0200 ++++ cyrus-sasl-2.1.27/include/makemd5.c 2015-11-20 15:11:20.477588240 +0100 +@@ -82,12 +82,19 @@ + */ + + ++#ifdef HAVE_CONFIG_H ++#include "../config.h" ++#endif + + #include + #include + #include + #include + ++#ifdef HAVE_INTTYPES_H ++#include ++#endif ++ + + static void + my_strupr(char *s) +@@ -122,6 +129,18 @@ my_strupr(char *s) + static void + try_signed(FILE *f, int len) + { ++#ifdef HAVE_INT8_T ++ BITSIZE(int8_t); ++#endif ++#ifdef HAVE_INT16_T ++ BITSIZE(int16_t); ++#endif ++#ifdef HAVE_INT32_T ++ BITSIZE(int32_t); ++#endif ++#ifdef HAVE_INT64_T ++ BITSIZE(int64_t); ++#endif + BITSIZE(signed char); + BITSIZE(short); + BITSIZE(int); +@@ -135,6 +154,18 @@ try_signed(FILE *f, int len) + static void + try_unsigned(FILE *f, int len) + { ++#ifdef HAVE_UINT8_T ++ BITSIZE(uint8_t); ++#endif ++#ifdef HAVE_UINT16_T ++ BITSIZE(uint16_t); ++#endif ++#ifdef HAVE_UINT32_T ++ BITSIZE(uint32_t); ++#endif ++#ifdef HAVE_UINT64_T ++ BITSIZE(uint64_t); ++#endif + BITSIZE(unsigned char); + BITSIZE(unsigned short); + BITSIZE(unsigned int); +@@ -165,6 +196,11 @@ static int print_pre(FILE *f) + "/* POINTER defines a generic pointer type */\n" + "typedef unsigned char *POINTER;\n" + "\n" ++#ifdef HAVE_INTTYPES_H ++ "/* We try to define integer types for our use */\n" ++ "#include \n" ++ "\n" ++#endif + ); + return 1; + } +@@ -212,31 +248,15 @@ int main(int argc, char **argv) + + print_pre(f); + +-#ifndef HAVE_INT8_T + try_signed (f, 8); +-#endif /* HAVE_INT8_T */ +-#ifndef HAVE_INT16_T + try_signed (f, 16); +-#endif /* HAVE_INT16_T */ +-#ifndef HAVE_INT32_T + try_signed (f, 32); +-#endif /* HAVE_INT32_T */ +-#ifndef HAVE_INT64_T + try_signed (f, 64); +-#endif /* HAVE_INT64_T */ + +-#ifndef HAVE_U_INT8_T + try_unsigned (f, 8); +-#endif /* HAVE_INT8_T */ +-#ifndef HAVE_U_INT16_T + try_unsigned (f, 16); +-#endif /* HAVE_U_INT16_T */ +-#ifndef HAVE_U_INT32_T + try_unsigned (f, 32); +-#endif /* HAVE_U_INT32_T */ +-#ifndef HAVE_U_INT64_T + try_unsigned (f, 64); +-#endif /* HAVE_U_INT64_T */ + + print_post(f); + diff --git a/cyrus-sasl-2.1.23-man.patch b/cyrus-sasl-2.1.23-man.patch new file mode 100644 index 0000000..21c63cd --- /dev/null +++ b/cyrus-sasl-2.1.23-man.patch @@ -0,0 +1,24 @@ +diff -up cyrus-sasl-2.1.26/saslauthd/testsaslauthd.8.man cyrus-sasl-2.1.26/saslauthd/testsaslauthd.8 +--- cyrus-sasl-2.1.26/saslauthd/testsaslauthd.8.man 2013-09-03 15:25:26.818042047 +0200 ++++ cyrus-sasl-2.1.26/saslauthd/testsaslauthd.8 2013-09-03 15:25:26.818042047 +0200 +@@ -0,0 +1,20 @@ ++.\" Hey, EMACS: -*- nroff -*- ++.TH TESTSASLAUTHD 8 "14 October 2006" ++.SH NAME ++testsaslauthd \- test utility for the SASL authentication server ++.SH SYNOPSIS ++.B testsaslauthd ++.RI "[ " \(hyr " " realm " ] [ " \(hys " " servicename " ] [ " \(hyf " " socket " " path " ] [ " \(hyR " " repeatnum " ]" ++.SH DESCRIPTION ++This manual page documents briefly the ++.B testsaslauthd ++command. ++.PP ++.SH SEE ALSO ++.BR saslauthd (8). ++.br ++.SH AUTHOR ++testsaslauthd was written by Carnegie Mellon University. ++.PP ++This manual page was written by Roberto C. Sanchez , ++for the Debian project (but may be used by others). diff --git a/cyrus-sasl-2.1.25-no_rpath.patch b/cyrus-sasl-2.1.25-no_rpath.patch new file mode 100644 index 0000000..82e5aa7 --- /dev/null +++ b/cyrus-sasl-2.1.25-no_rpath.patch @@ -0,0 +1,20 @@ +diff -up cyrus-sasl-2.1.25/m4/cyrus.m4.no_rpath cyrus-sasl-2.1.25/m4/cyrus.m4 +--- cyrus-sasl-2.1.25/m4/cyrus.m4.no_rpath 2010-01-22 16:12:01.000000000 +0100 ++++ cyrus-sasl-2.1.25/m4/cyrus.m4 2012-12-06 14:59:47.956102057 +0100 +@@ -32,14 +32,5 @@ AC_DEFUN([CMU_ADD_LIBPATH_TO], [ + dnl runpath initialization + AC_DEFUN([CMU_GUESS_RUNPATH_SWITCH], [ + # CMU GUESS RUNPATH SWITCH +- AC_CACHE_CHECK(for runpath switch, andrew_cv_runpath_switch, [ +- # first, try -R +- SAVE_LDFLAGS="${LDFLAGS}" +- LDFLAGS="-R /usr/lib" +- AC_TRY_LINK([],[],[andrew_cv_runpath_switch="-R"], [ +- LDFLAGS="-Wl,-rpath,/usr/lib" +- AC_TRY_LINK([],[],[andrew_cv_runpath_switch="-Wl,-rpath,"], +- [andrew_cv_runpath_switch="none"]) +- ]) +- LDFLAGS="${SAVE_LDFLAGS}" +- ])]) ++ andrew_cv_runpath_switch="none" ++ ]) diff --git a/cyrus-sasl-2.1.26-md5global.patch b/cyrus-sasl-2.1.26-md5global.patch new file mode 100644 index 0000000..605c8ec --- /dev/null +++ b/cyrus-sasl-2.1.26-md5global.patch @@ -0,0 +1,24 @@ +diff -up cyrus-sasl-2.1.27/include/Makefile.am.md5global.h cyrus-sasl-2.1.27/include/Makefile.am +--- cyrus-sasl-2.1.27/include/Makefile.am.md5global.h 2018-05-17 13:33:49.588368350 +0200 ++++ cyrus-sasl-2.1.27/include/Makefile.am 2018-05-17 13:38:19.377316869 +0200 +@@ -49,20 +49,7 @@ saslinclude_HEADERS = hmac-md5.h md5.h m + + noinst_PROGRAMS = makemd5 + +-makemd5_SOURCES = makemd5.c +- +-makemd5$(BUILD_EXEEXT) $(makemd5_OBJECTS): CC=$(CC_FOR_BUILD) +-makemd5$(BUILD_EXEEXT) $(makemd5_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +-makemd5$(BUILD_EXEEXT): LDFLAGS=$(LDFLAGS_FOR_BUILD) +- +-md5global.h: makemd5$(BUILD_EXEEXT) Makefile +- -rm -f $@ +- ./$< $@ +- +-BUILT_SOURCES = md5global.h +- + EXTRA_DIST = NTMakefile +-DISTCLEANFILES = md5global.h + + if MACOSX + framedir = /Library/Frameworks/SASL2.framework diff --git a/cyrus-sasl-2.1.27-Add-Channel-Binding-support-for-GSSAPI-GSS-SPNEGO.patch b/cyrus-sasl-2.1.27-Add-Channel-Binding-support-for-GSSAPI-GSS-SPNEGO.patch new file mode 100644 index 0000000..b95d8f1 --- /dev/null +++ b/cyrus-sasl-2.1.27-Add-Channel-Binding-support-for-GSSAPI-GSS-SPNEGO.patch @@ -0,0 +1,475 @@ +Backport of commit ids (minor inline mods to make them apply): +975edbb69070eba6b035f08776de771a129cfb57 +ea8eb892e44129ac3890298da91c868d5592ed20 + +From 975edbb69070eba6b035f08776de771a129cfb57 Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Fri, 20 Mar 2020 14:51:04 -0400 +Subject: [PATCH] Add Channel Binding support for GSSAPI/GSS-SPNEGO + +Signed-off-by: Simo Sorce +--- + plugins/gssapi.c | 30 +++++++++++--- + tests/runtests.py | 93 ++++++++++++++++++++++++++++++++++++++++---- + tests/t_common.c | 14 +++++++ + tests/t_common.h | 2 + + tests/t_gssapi_cli.c | 21 +++++++++- + tests/t_gssapi_srv.c | 21 +++++++++- + 6 files changed, 164 insertions(+), 17 deletions(-) + +diff --git a/plugins/gssapi.c b/plugins/gssapi.c +index ff663da7..5d900c5e 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 f645adf4..fc9cf244 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 7168b2f1..87875b48 100644 +--- a/tests/t_common.c ++++ b/tests/t_common.c +@@ -65,4 +65,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 4ee1976c..0d08d8ba 100644 +--- a/tests/t_common.h ++++ b/tests/t_common.h +@@ -7,9 +7,11 @@ + #include + + #include ++#include + + 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 c833c055..6b5664eb 100644 +--- a/tests/t_gssapi_cli.c ++++ b/tests/t_gssapi_cli.c +@@ -13,6 +13,7 @@ + + #include + #include ++#include + + static int setup_socket(void) + { +@@ -32,7 +33,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 +41,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 +73,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 29f538dd..3a8a5d44 100644 +--- a/tests/t_gssapi_srv.c ++++ b/tests/t_gssapi_srv.c +@@ -44,15 +44,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 +85,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; + +From ea8eb892e44129ac3890298da91c868d5592ed20 Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Fri, 20 Mar 2020 14:52:15 -0400 +Subject: [PATCH] Fixup minor issues in previous PR. + +Remove spurious debugging code, this was left in by mistake. +Add C notices, this was omitted by mistake. + +Signed-off-by: Simo Sorce +--- + tests/t_common.c | 10 ++-------- + tests/t_common.h | 3 ++- + tests/t_gssapi_cli.c | 3 ++- + tests/t_gssapi_srv.c | 3 ++- + 4 files changed, 8 insertions(+), 11 deletions(-) + +diff --git a/tests/t_common.c b/tests/t_common.c +index 87875b48..478e6a1f 100644 +--- a/tests/t_common.c ++++ b/tests/t_common.c +@@ -1,4 +1,5 @@ +-/* TBD, add (C) */ ++/* Copyright (C) Simo Sorce ++ * See COPYING file for License */ + + #include + +@@ -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; + } + +diff --git a/tests/t_common.h b/tests/t_common.h +index 0d08d8ba..a10def17 100644 +--- a/tests/t_common.h ++++ b/tests/t_common.h +@@ -1,4 +1,5 @@ +-/* TBD, add (C) */ ++/* Copyright (C) Simo Sorce ++ * See COPYING file for License */ + + #include "config.h" + +diff --git a/tests/t_gssapi_cli.c b/tests/t_gssapi_cli.c +index 6b5664eb..a44a3f58 100644 +--- a/tests/t_gssapi_cli.c ++++ b/tests/t_gssapi_cli.c +@@ -1,4 +1,5 @@ +-/* TBD, add (C) */ ++/* Copyright (C) Simo Sorce ++ * See COPYING file for License */ + + #include "t_common.h" + +diff --git a/tests/t_gssapi_srv.c b/tests/t_gssapi_srv.c +index 3a8a5d44..ef1217f6 100644 +--- a/tests/t_gssapi_srv.c ++++ b/tests/t_gssapi_srv.c +@@ -1,4 +1,5 @@ +-/* TBD, add (C) */ ++/* Copyright (C) Simo Sorce ++ * See COPYING file for License */ + + #include "t_common.h" + + diff --git a/cyrus-sasl-2.1.27-Add-basic-test-infrastructure.patch b/cyrus-sasl-2.1.27-Add-basic-test-infrastructure.patch new file mode 100644 index 0000000..171b309 --- /dev/null +++ b/cyrus-sasl-2.1.27-Add-basic-test-infrastructure.patch @@ -0,0 +1,642 @@ +Backport of 18ff41d5d18f61c2ded7235dad1d9618aa84784b with minor inline mods to +make it apply. + +From 18ff41d5d18f61c2ded7235dad1d9618aa84784b Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Sat, 14 Mar 2020 10:50:19 -0400 +Subject: [PATCH] Add basic test infrastructure + +First test is for SASL/GSSAPI + +Signed-off-by: Simo Sorce + +:x +--- + 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 +++++++++++++++++++++++++++ + 10 files changed, 559 insertions(+), 4 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 f7d3b22e..102e2a3e 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -65,7 +65,7 @@ else + INSTALLOSX = + endif + +-SUBDIRS=include sasldb common lib plugins utils $(PWC) $(SAM) $(SAD) ++SUBDIRS=include sasldb common lib plugins utils $(PWC) $(SAM) $(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 3610671b..dd7908a3 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1516,8 +1516,9 @@ plugins/Makefile + lib/Makefile + utils/Makefile + sample/Makefile +-pwcheck/Makefile]) ++pwcheck/Makefile ++tests/Makefile]) + AC_OUTPUT + + AC_MSG_NOTICE([ + +diff --git a/tests/Makefile.am b/tests/Makefile.am +new file mode 100644 +index 00000000..1edf010a +--- /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 00000000..f645adf4 +--- /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 00000000..7168b2f1 +--- /dev/null ++++ b/tests/t_common.c +@@ -0,0 +1,68 @@ ++/* TBD, add (C) */ ++ ++#include ++ ++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 00000000..4ee1976c +--- /dev/null ++++ b/tests/t_common.h +@@ -0,0 +1,15 @@ ++/* TBD, add (C) */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++ ++#include ++ ++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 00000000..c833c055 +--- /dev/null ++++ b/tests/t_gssapi_cli.c +@@ -0,0 +1,95 @@ ++/* TBD, add (C) */ ++ ++#include "t_common.h" ++ ++#include ++#include ++#include ++#include ++ ++#ifdef HAVE_UNISTD_H ++#include ++#endif ++ ++#include ++#include ++ ++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 00000000..29f538dd +--- /dev/null ++++ b/tests/t_gssapi_srv.c +@@ -0,0 +1,111 @@ ++/* TBD, add (C) */ ++ ++#include "t_common.h" ++ ++#include ++#include ++#include ++#include ++ ++#ifdef HAVE_UNISTD_H ++#include ++#endif ++ ++#include ++#include ++ ++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; ++} ++ + diff --git a/cyrus-sasl-2.1.27-Add-support-for-setting-max-ssf-0-to-GSS-SPNEGO.patch b/cyrus-sasl-2.1.27-Add-support-for-setting-max-ssf-0-to-GSS-SPNEGO.patch new file mode 100644 index 0000000..46f89ed --- /dev/null +++ b/cyrus-sasl-2.1.27-Add-support-for-setting-max-ssf-0-to-GSS-SPNEGO.patch @@ -0,0 +1,95 @@ +Backport of part of Upstream PR#603 minimal part needed for interop + +From 0e722dd3266d5ebd0f889462cc23856fde3d21ed Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Thu, 19 Sep 2019 15:58:04 -0400 +Subject: [PATCH] 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 +--- + m4/sasl2.m4 | 13 +++++++++++++ + plugins/gssapi.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 57 insertions(+), 1 deletion(-) + +diff --git a/m4/sasl2.m4 b/m4/sasl2.m4 +index 17f5d081..60306943 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 ++ ]) ++ 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 46de7d48..cca6cc0a 100644 +--- a/plugins/gssapi.c ++++ b/plugins/gssapi.c +@@ -2123,7 +2123,50 @@ 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/cyrus-sasl-2.1.27-coverity.patch b/cyrus-sasl-2.1.27-coverity.patch new file mode 100644 index 0000000..871ea10 --- /dev/null +++ b/cyrus-sasl-2.1.27-coverity.patch @@ -0,0 +1,56 @@ +diff -up cyrus-sasl-2.1.27/include/makemd5.c.coverity cyrus-sasl-2.1.27/include/makemd5.c +--- cyrus-sasl-2.1.27/include/makemd5.c.coverity 2021-04-12 15:10:25.421431535 +0200 ++++ cyrus-sasl-2.1.27/include/makemd5.c 2021-04-12 15:56:46.752827737 +0200 +@@ -107,7 +107,6 @@ my_strupr(char *s) + } + } + +- + #define BITSIZE(TYPE) \ + { \ + int b = 0; TYPE x = 1, zero = 0; char *pre = "U"; \ +@@ -129,6 +128,8 @@ my_strupr(char *s) + static void + try_signed(FILE *f, int len) + { ++/* Local macros for not-installed program. No coverity/compiler issues! */ ++#pragma GCC diagnostic ignored "-Wformat-overflow" + #ifdef HAVE_INT8_T + BITSIZE(int8_t); + #endif +@@ -149,6 +150,7 @@ try_signed(FILE *f, int len) + BITSIZE(long long); + #endif + fprintf(f, "/* There is no %d bit type */\n", len); ++#pragma GCC pop + } + + static void +diff -up cyrus-sasl-2.1.27/saslauthd/lak.c.coverity cyrus-sasl-2.1.27/saslauthd/lak.c +--- cyrus-sasl-2.1.27/saslauthd/lak.c.coverity 2018-11-08 18:29:57.000000000 +0100 ++++ cyrus-sasl-2.1.27/saslauthd/lak.c 2021-04-12 15:10:25.433431630 +0200 +@@ -337,9 +337,9 @@ static int lak_config_read( + EMPTY(conf->group_search_base) ) + strlcpy(conf->group_search_base, conf->search_base, LAK_DN_LEN); + +- fclose(infile); ++ fclose(infile); + +- return LAK_OK; ++ return LAK_OK; + } + + static int lak_config_int( +diff -up cyrus-sasl-2.1.27/saslauthd/saslauthd-main.c.coverity cyrus-sasl-2.1.27/saslauthd/saslauthd-main.c +--- cyrus-sasl-2.1.27/saslauthd/saslauthd-main.c.coverity 2018-01-19 15:13:40.000000000 +0100 ++++ cyrus-sasl-2.1.27/saslauthd/saslauthd-main.c 2021-04-12 15:10:25.433431630 +0200 +@@ -833,7 +833,8 @@ void detach_tty() { + } + + logger(L_INFO, L_FUNC, "master pid is: %lu", (unsigned long)master_pid); +- ++ /* null_fd expected to be more than 2, so it is closed after dups, no leaks occur */ ++ /* coverity[leaked_handle : FALSE]*/ + return; + } + diff --git a/cyrus-sasl-2.1.27-cumulative-digestmd5.patch b/cyrus-sasl-2.1.27-cumulative-digestmd5.patch new file mode 100644 index 0000000..c0df481 --- /dev/null +++ b/cyrus-sasl-2.1.27-cumulative-digestmd5.patch @@ -0,0 +1,554 @@ +diff -uPr cyrus-sasl-2.1.27/plugins/digestmd5.c cyrus-sasl-2.1.27.digestmd5/plugins/digestmd5.c +--- cyrus-sasl-2.1.27/plugins/digestmd5.c 2021-09-30 17:13:06.573093526 -0400 ++++ cyrus-sasl-2.1.27.digestmd5/plugins/digestmd5.c 2021-09-30 17:26:31.818378442 -0400 +@@ -80,6 +80,12 @@ + # endif + #endif /* WITH_DES */ + ++/* legacy provider with openssl 3.0 */ ++#if OPENSSL_VERSION_NUMBER >= 0x30000000L ++# include ++# include ++#endif ++ + #ifdef WIN32 + # include + #else /* Unix */ +@@ -170,6 +176,12 @@ + + typedef struct cipher_context cipher_context_t; + ++typedef struct crypto_context { ++ void *libctx; ++ cipher_context_t *enc_ctx; ++ cipher_context_t *dec_ctx; ++} crypto_context_t; ++ + /* cached auth info used for fast reauth */ + typedef struct reauth_entry { + char *authid; +@@ -254,12 +266,12 @@ + decode_context_t decode_context; + + /* if privacy mode is used use these functions for encode and decode */ ++ char *cipher_name; + cipher_function_t *cipher_enc; + cipher_function_t *cipher_dec; + cipher_init_t *cipher_init; + cipher_free_t *cipher_free; +- struct cipher_context *cipher_enc_context; +- struct cipher_context *cipher_dec_context; ++ crypto_context_t crypto; + } context_t; + + struct digest_cipher { +@@ -888,7 +900,7 @@ + char *output, + unsigned *outputlen) + { +- des_context_t *c = (des_context_t *) text->cipher_dec_context; ++ des_context_t *c = (des_context_t *) text->crypto.dec_ctx; + int padding, p; + + des_ede2_cbc_encrypt((void *) input, +@@ -925,7 +937,7 @@ + char *output, + unsigned *outputlen) + { +- des_context_t *c = (des_context_t *) text->cipher_enc_context; ++ des_context_t *c = (des_context_t *) text->crypto.enc_ctx; + int len; + int paddinglen; + +@@ -973,7 +985,7 @@ + return SASL_FAIL; + memcpy(c->ivec, ((char *) enckey) + 8, 8); + +- text->cipher_enc_context = (cipher_context_t *) c; ++ text->crypto.enc_ctx = (cipher_context_t *) c; + + /* setup dec context */ + c++; +@@ -987,7 +999,7 @@ + + memcpy(c->ivec, ((char *) deckey) + 8, 8); + +- text->cipher_dec_context = (cipher_context_t *) c; ++ text->crypto.dec_ctx = (cipher_context_t *) c; + + return SASL_OK; + } +@@ -1006,7 +1018,7 @@ + char *output, + unsigned *outputlen) + { +- des_context_t *c = (des_context_t *) text->cipher_dec_context; ++ des_context_t *c = (des_context_t *) text->crypto.dec_ctx; + int p, padding = 0; + + des_cbc_encrypt((void *) input, +@@ -1046,7 +1058,7 @@ + char *output, + unsigned *outputlen) + { +- des_context_t *c = (des_context_t *) text->cipher_enc_context; ++ des_context_t *c = (des_context_t *) text->crypto.enc_ctx; + int len; + int paddinglen; + +@@ -1093,7 +1105,7 @@ + + memcpy(c->ivec, ((char *) enckey) + 8, 8); + +- text->cipher_enc_context = (cipher_context_t *) c; ++ text->crypto.enc_ctx = (cipher_context_t *) c; + + /* setup dec context */ + c++; +@@ -1102,60 +1114,143 @@ + + memcpy(c->ivec, ((char *) deckey) + 8, 8); + +- text->cipher_dec_context = (cipher_context_t *) c; ++ text->crypto.dec_ctx = (cipher_context_t *) c; + + return SASL_OK; + } + + static void free_des(context_t *text) + { +- /* free des contextss. only cipher_enc_context needs to be free'd, +- since cipher_dec_context was allocated at the same time. */ +- if (text->cipher_enc_context) text->utils->free(text->cipher_enc_context); ++ /* free des contextss. only enc_ctx needs to be free'd, ++ since dec_cxt was allocated at the same time. */ ++ if (text->crypto.enc_ctx) { ++ text->utils->free(text->crypto.enc_ctx); ++ } + } + + #endif /* WITH_DES */ + + #ifdef WITH_RC4 +-#ifdef HAVE_OPENSSL + #include + ++#if OPENSSL_VERSION_NUMBER >= 0x30000000L ++typedef struct ossl3_library_context { ++ OSSL_LIB_CTX *libctx; ++ OSSL_PROVIDER *legacy_provider; ++ OSSL_PROVIDER *default_provider; ++} ossl3_context_t; ++ ++static int init_ossl3_ctx(context_t *text) ++{ ++ ossl3_context_t *ctx = text->utils->malloc(sizeof(ossl3_context_t)); ++ if (!ctx) return SASL_NOMEM; ++ ++ ctx->libctx = OSSL_LIB_CTX_new(); ++ if (!ctx->libctx) { ++ text->utils->free(ctx); ++ return SASL_FAIL; ++ } ++ ++ /* Load both legacy and default provider as both may be needed */ ++ /* if they fail keep going and an error will be raised when we try to ++ * fetch the cipher later */ ++ ctx->legacy_provider = OSSL_PROVIDER_load(ctx->libctx, "legacy"); ++ ctx->default_provider = OSSL_PROVIDER_load(ctx->libctx, "default"); ++ text->crypto.libctx = (void *)ctx; ++ ++ return SASL_OK; ++} ++ ++static void free_ossl3_ctx(context_t *text) ++{ ++ ossl3_context_t *ctx; ++ ++ if (!text->crypto.libctx) return; ++ ++ ctx = (ossl3_context_t *)text->crypto.libctx; ++ ++ if (ctx->legacy_provider) OSSL_PROVIDER_unload(ctx->legacy_provider); ++ if (ctx->default_provider) OSSL_PROVIDER_unload(ctx->default_provider); ++ if (ctx->libctx) OSSL_LIB_CTX_free(ctx->libctx); ++ ++ text->utils->free(ctx); ++ text->crypto.libctx = NULL; ++} ++#endif ++ + static void free_rc4(context_t *text) + { +- if (text->cipher_enc_context) { +- EVP_CIPHER_CTX_free((EVP_CIPHER_CTX *)text->cipher_enc_context); +- text->cipher_enc_context = NULL; +- } +- if (text->cipher_dec_context) { +- EVP_CIPHER_CTX_free((EVP_CIPHER_CTX *)text->cipher_dec_context); +- text->cipher_dec_context = NULL; ++ if (text->crypto.enc_ctx) { ++ EVP_CIPHER_CTX_free((EVP_CIPHER_CTX *)text->crypto.enc_ctx); ++ text->crypto.enc_ctx = NULL; ++ } ++ if (text->crypto.dec_ctx) { ++ EVP_CIPHER_CTX_free((EVP_CIPHER_CTX *)text->crypto.dec_ctx); ++ text->crypto.dec_ctx = NULL; + } ++#if OPENSSL_VERSION_NUMBER >= 0x30000000L ++ free_ossl3_ctx(text); ++#endif + } + + static int init_rc4(context_t *text, + unsigned char enckey[16], + unsigned char deckey[16]) + { + EVP_CIPHER_CTX *ctx; + int rc; + +- ctx = EVP_CIPHER_CTX_new(); +- if (ctx == NULL) return SASL_NOMEM; ++#if OPENSSL_VERSION_NUMBER >= 0x30000000L ++ EVP_CIPHER *cipher; ++ ossl3_context_t *ossl3_ctx; + +- rc = EVP_EncryptInit_ex(ctx, EVP_rc4(), NULL, enckey, NULL); +- if (rc != 1) return SASL_FAIL; ++ rc = init_ossl3_ctx(text); ++ if (rc != SASL_OK) return rc; ++ ++ ossl3_ctx = (ossl3_context_t *)text->crypto.libctx; ++ cipher = EVP_CIPHER_fetch(ossl3_ctx->libctx, "RC4", ""); ++#else ++ const EVP_CIPHER *cipher; ++ cipher = EVP_rc4(); ++#endif + +- text->cipher_enc_context = (void *)ctx; + + ctx = EVP_CIPHER_CTX_new(); +- if (ctx == NULL) return SASL_NOMEM; ++ if (ctx == NULL) { ++ rc = SASL_NOMEM; ++ goto done; ++ } + +- rc = EVP_DecryptInit_ex(ctx, EVP_rc4(), NULL, deckey, NULL); +- if (rc != 1) return SASL_FAIL; ++ rc = EVP_EncryptInit_ex(ctx, cipher, NULL, enckey, NULL); ++ if (rc != 1) { ++ rc = SASL_FAIL; ++ goto done; ++ } ++ text->crypto.enc_ctx = (void *)ctx; + +- text->cipher_dec_context = (void *)ctx; ++ ctx = EVP_CIPHER_CTX_new(); ++ if (ctx == NULL) { ++ rc = SASL_NOMEM; ++ goto done; ++ } + +- return SASL_OK; ++ rc = EVP_DecryptInit_ex(ctx, cipher, NULL, deckey, NULL); ++ if (rc != 1) { ++ rc = SASL_FAIL; ++ goto done; ++ } ++ text->crypto.dec_ctx = (void *)ctx; ++ ++ rc = SASL_OK; ++ ++done: ++#if OPENSSL_VERSION_NUMBER >= 0x30000000L ++ EVP_CIPHER_free(cipher); ++#endif ++ if (rc != SASL_OK) { ++ free_rc4(text); ++ } ++ return rc; + } + + static int dec_rc4(context_t *text, +@@ -1169,14 +1260,14 @@ + int rc; + + /* decrypt the text part & HMAC */ +- rc = EVP_DecryptUpdate((EVP_CIPHER_CTX *)text->cipher_dec_context, ++ rc = EVP_DecryptUpdate((EVP_CIPHER_CTX *)text->crypto.dec_ctx, + (unsigned char *)output, &len, + (const unsigned char *)input, inputlen); + if (rc != 1) return SASL_FAIL; + + *outputlen = len; + +- rc = EVP_DecryptFinal_ex((EVP_CIPHER_CTX *)text->cipher_dec_context, ++ rc = EVP_DecryptFinal_ex((EVP_CIPHER_CTX *)text->crypto.dec_ctx, + (unsigned char *)output + len, &len); + if (rc != 1) return SASL_FAIL; + +@@ -1198,7 +1289,7 @@ + int len; + int rc; + /* encrypt the text part */ +- rc = EVP_EncryptUpdate((EVP_CIPHER_CTX *)text->cipher_enc_context, ++ rc = EVP_EncryptUpdate((EVP_CIPHER_CTX *)text->crypto.enc_ctx, + (unsigned char *)output, &len, + (const unsigned char *)input, inputlen); + if (rc != 1) return SASL_FAIL; +@@ -1206,14 +1297,14 @@ + *outputlen = len; + + /* encrypt the `MAC part */ +- rc = EVP_EncryptUpdate((EVP_CIPHER_CTX *)text->cipher_enc_context, ++ rc = EVP_EncryptUpdate((EVP_CIPHER_CTX *)text->crypto.enc_ctx, + (unsigned char *)output + *outputlen, &len, + digest, 10); + if (rc != 1) return SASL_FAIL; + + *outputlen += len; + +- rc = EVP_EncryptFinal_ex((EVP_CIPHER_CTX *)text->cipher_enc_context, ++ rc = EVP_EncryptFinal_ex((EVP_CIPHER_CTX *)text->crypto.enc_ctx, + (unsigned char *)output + *outputlen, &len); + if (rc != 1) return SASL_FAIL; + +@@ -1221,194 +1312,11 @@ + + return SASL_OK; + } +-#else +-/* quick generic implementation of RC4 */ +-struct rc4_context_s { +- unsigned char sbox[256]; +- int i, j; +-}; +- +-typedef struct rc4_context_s rc4_context_t; +- +-static void rc4_init(rc4_context_t *text, +- const unsigned char *key, +- unsigned keylen) +-{ +- int i, j; +- +- /* fill in linearly s0=0 s1=1... */ +- for (i=0;i<256;i++) +- text->sbox[i]=i; +- +- j=0; +- for (i = 0; i < 256; i++) { +- unsigned char tmp; +- /* j = (j + Si + Ki) mod 256 */ +- j = (j + text->sbox[i] + key[i % keylen]) % 256; +- +- /* swap Si and Sj */ +- tmp = text->sbox[i]; +- text->sbox[i] = text->sbox[j]; +- text->sbox[j] = tmp; +- } +- +- /* counters initialized to 0 */ +- text->i = 0; +- text->j = 0; +-} +- +-static void rc4_encrypt(rc4_context_t *text, +- const char *input, +- char *output, +- unsigned len) +-{ +- int tmp; +- int i = text->i; +- int j = text->j; +- int t; +- int K; +- const char *input_end = input + len; +- +- while (input < input_end) { +- i = (i + 1) % 256; +- +- j = (j + text->sbox[i]) % 256; +- +- /* swap Si and Sj */ +- tmp = text->sbox[i]; +- text->sbox[i] = text->sbox[j]; +- text->sbox[j] = tmp; +- +- t = (text->sbox[i] + text->sbox[j]) % 256; +- +- K = text->sbox[t]; +- +- /* byte K is Xor'ed with plaintext */ +- *output++ = *input++ ^ K; +- } +- +- text->i = i; +- text->j = j; +-} +- +-static void rc4_decrypt(rc4_context_t *text, +- const char *input, +- char *output, +- unsigned len) +-{ +- int tmp; +- int i = text->i; +- int j = text->j; +- int t; +- int K; +- const char *input_end = input + len; +- +- while (input < input_end) { +- i = (i + 1) % 256; +- +- j = (j + text->sbox[i]) % 256; +- +- /* swap Si and Sj */ +- tmp = text->sbox[i]; +- text->sbox[i] = text->sbox[j]; +- text->sbox[j] = tmp; +- +- t = (text->sbox[i] + text->sbox[j]) % 256; +- +- K = text->sbox[t]; +- +- /* byte K is Xor'ed with plaintext */ +- *output++ = *input++ ^ K; +- } +- +- text->i = i; +- text->j = j; +-} +- +-static void free_rc4(context_t *text) +-{ +- /* free rc4 context structures */ +- +- if (text->cipher_enc_context) { +- text->utils->free(text->cipher_enc_context); +- text->cipher_enc_context = NULL; +- } +- if (text->cipher_dec_context) { +- text->utils->free(text->cipher_dec_context); +- text->cipher_dec_context = NULL; +- } +-} +- +-static int init_rc4(context_t *text, +- unsigned char enckey[16], +- unsigned char deckey[16]) +-{ +- /* allocate rc4 context structures */ +- text->cipher_enc_context= +- (cipher_context_t *) text->utils->malloc(sizeof(rc4_context_t)); +- if (text->cipher_enc_context == NULL) return SASL_NOMEM; +- +- text->cipher_dec_context= +- (cipher_context_t *) text->utils->malloc(sizeof(rc4_context_t)); +- if (text->cipher_dec_context == NULL) return SASL_NOMEM; +- +- /* initialize them */ +- rc4_init((rc4_context_t *) text->cipher_enc_context, +- (const unsigned char *) enckey, 16); +- rc4_init((rc4_context_t *) text->cipher_dec_context, +- (const unsigned char *) deckey, 16); +- +- return SASL_OK; +-} +- +-static int dec_rc4(context_t *text, +- const char *input, +- unsigned inputlen, +- unsigned char digest[16] __attribute__((unused)), +- char *output, +- unsigned *outputlen) +-{ +- /* decrypt the text part & HMAC */ +- rc4_decrypt((rc4_context_t *) text->cipher_dec_context, +- input, output, inputlen); +- +- /* no padding so we just subtract the HMAC to get the text length */ +- *outputlen = inputlen - 10; +- +- return SASL_OK; +-} +- +-static int enc_rc4(context_t *text, +- const char *input, +- unsigned inputlen, +- unsigned char digest[16], +- char *output, +- unsigned *outputlen) +-{ +- /* pad is zero */ +- *outputlen = inputlen+10; +- +- /* encrypt the text part */ +- rc4_encrypt((rc4_context_t *) text->cipher_enc_context, +- input, +- output, +- inputlen); +- +- /* encrypt the HMAC part */ +- rc4_encrypt((rc4_context_t *) text->cipher_enc_context, +- (const char *) digest, +- (output)+inputlen, 10); +- +- return SASL_OK; +-} +-#endif /* HAVE_OPENSSL */ + #endif /* WITH_RC4 */ + + struct digest_cipher available_ciphers[] = + { + #ifdef WITH_RC4 +- { "rc4-40", 40, 5, 0x01, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 }, +- { "rc4-56", 56, 7, 0x02, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 }, + { "rc4", 128, 16, 0x04, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 }, + #endif + #ifdef WITH_DES +@@ -2815,6 +2729,7 @@ + } + + if (cptr->name) { ++ text->cipher_name = cptr->name; + text->cipher_enc = cptr->cipher_enc; + text->cipher_dec = cptr->cipher_dec; + text->cipher_init = cptr->cipher_init; +@@ -2958,7 +2873,10 @@ + if (text->cipher_init) { + if (text->cipher_init(text, enckey, deckey) != SASL_OK) { + sparams->utils->seterror(sparams->utils->conn, 0, +- "couldn't init cipher"); ++ "couldn't init cipher '%s'", ++ text->cipher_name); ++ result = SASL_FAIL; ++ goto FreeAllMem; + } + } + } +@@ -3509,6 +3427,7 @@ + oparams->mech_ssf = ctext->cipher->ssf; + + nbits = ctext->cipher->n; ++ text->cipher_name = ctext->cipher->name; + text->cipher_enc = ctext->cipher->cipher_enc; + text->cipher_dec = ctext->cipher->cipher_dec; + text->cipher_free = ctext->cipher->cipher_free; +@@ -3733,7 +3652,13 @@ + + /* initialize cipher if need be */ + if (text->cipher_init) { +- text->cipher_init(text, enckey, deckey); ++ if (text->cipher_init(text, enckey, deckey) != SASL_OK) { ++ params->utils->seterror(params->utils->conn, 0, ++ "internal error: failed to init cipher '%s'", ++ text->cipher_name); ++ result = SASL_FAIL; ++ goto FreeAllocatedMem; ++ } + } + } + diff --git a/cyrus-sasl-2.1.27-cumulative-ossl3.patch b/cyrus-sasl-2.1.27-cumulative-ossl3.patch new file mode 100644 index 0000000..b61d0a3 --- /dev/null +++ b/cyrus-sasl-2.1.27-cumulative-ossl3.patch @@ -0,0 +1,109 @@ +diff -uPr cyrus-sasl-2.1.27/configure.ac cyrus-sasl-2.1.27.ossl3/configure.ac +--- cyrus-sasl-2.1.27/configure.ac 2021-10-06 11:29:53.274375206 -0400 ++++ cyrus-sasl-2.1.27.ossl3/configure.ac 2021-10-06 11:31:19.966726775 -0400 +@@ -1115,7 +1115,11 @@ + with_rc4=yes) + + if test "$with_rc4" != no; then +- AC_DEFINE(WITH_RC4,[],[Use RC4]) ++ if test "$with_openssl" = no; then ++ AC_WARN([OpenSSL not found -- RC4 will be disabled]) ++ else ++ AC_DEFINE(WITH_RC4,[],[Use RC4]) ++ fi + fi + + building_for_macosx=no +diff -uPr cyrus-sasl-2.1.27/plugins/scram.c cyrus-sasl-2.1.27.ossl3/plugins/scram.c +--- cyrus-sasl-2.1.27/plugins/scram.c 2018-11-08 12:29:57.000000000 -0500 ++++ cyrus-sasl-2.1.27.ossl3/plugins/scram.c 2021-10-06 11:31:04.407484201 -0400 +@@ -65,7 +65,9 @@ + + #include + #include ++#if OPENSSL_VERSION_NUMBER < 0x30000000L + #include ++#endif + + /***************************** Common Section *****************************/ + +@@ -291,6 +293,32 @@ + } + #endif + ++#if OPENSSL_VERSION_NUMBER >= 0x30000000L ++ ++/* Decalre as void given functions never use the result */ ++void *HMAC(const EVP_MD *evp_md, const void *key, int key_len, ++ const unsigned char *data, size_t data_len, ++ unsigned char *md, unsigned int *md_len) ++{ ++ const char *digest; ++ size_t digest_size; ++ size_t out_len; ++ void *ret = NULL; ++ ++ digest = EVP_MD_get0_name(evp_md); ++ if (digest == NULL) { ++ return NULL; ++ } ++ digest_size = EVP_MD_size(evp_md); ++ ++ ret = EVP_Q_mac(NULL, "hmac", NULL, digest, NULL, key, key_len, ++ data, data_len, md, digest_size, &out_len); ++ if (ret != NULL) { ++ *md_len = (unsigned int)out_len; ++ } ++ return ret; ++} ++#endif + + /* The result variable need to point to a buffer big enough for the [SHA-*] hash */ + static void +diff -uPr cyrus-sasl-2.1.27/saslauthd/lak.c cyrus-sasl-2.1.27.ossl3/saslauthd/lak.c +--- cyrus-sasl-2.1.27/saslauthd/lak.c 2022-01-09 11:30:50.000000000 -0400 ++++ cyrus-sasl-2.1.27.ossl3/saslauthd/lak.c 2022-01-09 11:30:50.000000001 -0400 +@@ -1806,18 +1806,36 @@ + return rc; + } + +- EVP_DigestInit(mdctx, md); +- EVP_DigestUpdate(mdctx, passwd, strlen(passwd)); ++ rc = EVP_DigestInit(mdctx, md); ++ if (rc != 1) { ++ rc = LAK_FAIL; ++ goto done; ++ } ++ rc = EVP_DigestUpdate(mdctx, passwd, strlen(passwd)); ++ if (rc != 1) { ++ rc = LAK_FAIL; ++ goto done; ++ } + if (hrock->salted) { +- EVP_DigestUpdate(mdctx, &cred[EVP_MD_size(md)], +- clen - EVP_MD_size(md)); ++ rc = EVP_DigestUpdate(mdctx, &cred[EVP_MD_size(md)], ++ clen - EVP_MD_size(md)); ++ if (rc != 1) { ++ rc = LAK_FAIL; ++ goto done; ++ } ++ } ++ rc = EVP_DigestFinal(mdctx, digest, NULL); ++ if (rc != 1) { ++ rc = LAK_FAIL; ++ goto done; + } +- EVP_DigestFinal(mdctx, digest, NULL); +- EVP_MD_CTX_free(mdctx); + + rc = memcmp((char *)cred, (char *)digest, EVP_MD_size(md)); ++ rc = rc ? LAK_INVALID_PASSWORD : LAK_OK; ++done: ++ EVP_MD_CTX_free(mdctx); + free(cred); +- return rc ? LAK_INVALID_PASSWORD : LAK_OK; ++ return rc; + } + + #endif /* HAVE_OPENSSL */ diff --git a/cyrus-sasl-2.1.27-more-tests.patch b/cyrus-sasl-2.1.27-more-tests.patch new file mode 100644 index 0000000..7e6435c --- /dev/null +++ b/cyrus-sasl-2.1.27-more-tests.patch @@ -0,0 +1,761 @@ +From 10ac4d4822023b24734acde3c07186937ad52813 Mon Sep 17 00:00:00 2001 +From: Dmitry Belyavskiy +Date: Wed, 6 Jan 2021 12:38:46 +0100 +Subject: [PATCH] Some basic PLAIN auth tests + +Signed-off-by: Dmitry Belyavskiy +--- + tests/runtests.py | 91 +++++++++++++++++++++++++++++++++++++++++ + tests/t_gssapi_cli.c | 97 +++++++++++++++++++++++++++++++++++++++----- + tests/t_gssapi_srv.c | 78 +++++++++++++++++++++++++++-------- + 3 files changed, 239 insertions(+), 27 deletions(-) + +diff --git a/tests/runtests.py b/tests/runtests.py +index fc9cf244..513ed3ff 100755 +--- a/tests/runtests.py ++++ b/tests/runtests.py +@@ -239,6 +239,96 @@ def gssapi_tests(testdir): + + os.killpg(kdc.pid, signal.SIGTERM) + ++def setup_plain(testdir): ++ """ Create sasldb file """ ++ sasldbfile = os.path.join(testdir, 'testsasldb.db') ++ ++ sasldbenv = {'SASL_PATH': os.path.join(testdir, '../../plugins/.libs'), ++ 'LD_LIBRARY_PATH' : os.path.join(testdir, '../../lib/.libs')} ++ ++ passwdprog = os.path.join(testdir, '../../utils/saslpasswd2') ++ ++ echo = subprocess.Popen(('echo', '1234567'), stdout=subprocess.PIPE) ++ subprocess.check_call([ ++ passwdprog, "-f", sasldbfile, "-c", "test", ++ "-u", "host.realm.test", "-p" ++ ], stdin=echo.stdout, env=sasldbenv, timeout=5) ++ ++ return (sasldbfile, sasldbenv) ++ ++def plain_test(sasldbfile, sasldbenv): ++ try: ++ srv = subprocess.Popen(["../tests/t_gssapi_srv", "-P", sasldbfile], ++ stdout=subprocess.PIPE, ++ stderr=subprocess.PIPE, env=sasldbenv) ++ srv.stdout.readline() # Wait for srv to say it is ready ++ cli = subprocess.Popen(["../tests/t_gssapi_cli", "-P", "1234567"], ++ stdout=subprocess.PIPE, ++ stderr=subprocess.PIPE, env=sasldbenv) ++ 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: PLAIN CLI({}) SRV({})".format( ++ cli.stdout.read().decode('utf-8').strip(), ++ srv.stdout.read().decode('utf-8').strip())) ++ return ++ ++def plain_mismatch_test(sasldbfile, sasldbenv): ++ result = "FAIL" ++ try: ++ srv = subprocess.Popen(["../tests/t_gssapi_srv", "-P", sasldbfile], ++ stdout=subprocess.PIPE, ++ stderr=subprocess.PIPE, env=sasldbenv) ++ 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", "-P", "12345678"], ++ stdout=subprocess.PIPE, ++ stderr=subprocess.PIPE, env=sasldbenv) ++ 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())) ++ return ++ ++def plain_tests(testdir): ++ sasldbfile, sasldbenv = setup_plain(testdir) ++ #print("DB file: {}, ENV: {}".format(sasldbfile, sasldbenv)) ++ print('SASLDB PLAIN:') ++ print(' ', end='') ++ plain_test(sasldbfile, sasldbenv) ++ ++ print('SASLDB PLAIN PASSWORD MISMATCH:') ++ print(' ', end='') ++ plain_mismatch_test(sasldbfile, sasldbenv) + + if __name__ == "__main__": + +@@ -254,3 +344,4 @@ def gssapi_tests(testdir): + os.makedirs(T) + + gssapi_tests(T) ++ plain_tests(T) +diff --git a/tests/t_gssapi_cli.c b/tests/t_gssapi_cli.c +index a44a3f58..20d22070 100644 +--- a/tests/t_gssapi_cli.c ++++ b/tests/t_gssapi_cli.c +@@ -16,6 +16,8 @@ + #include + #include + ++const char *testpass = NULL; ++ + static int setup_socket(void) + { + struct sockaddr_in addr; +@@ -34,9 +36,60 @@ static int setup_socket(void) + return sock; + } + ++static int get_user(void *context __attribute__((unused)), ++ int id, ++ const char **result, ++ unsigned *len) ++{ ++ const char *testuser = "test@host.realm.test"; ++ ++ if (! result) ++ return SASL_BADPARAM; ++ ++ switch (id) { ++ case SASL_CB_USER: ++ case SASL_CB_AUTHNAME: ++ *result = testuser; ++ break; ++ default: ++ return SASL_BADPARAM; ++ } ++ ++ if (len) *len = strlen(*result); ++ ++ return SASL_OK; ++} ++ ++static int get_pass(sasl_conn_t *conn __attribute__((unused)), ++ void *context __attribute__((unused)), ++ int id, ++ sasl_secret_t **psecret) ++{ ++ size_t len; ++ static sasl_secret_t *x; ++ ++ /* paranoia check */ ++ if (! conn || ! psecret || id != SASL_CB_PASS) ++ return SASL_BADPARAM; ++ ++ len = strlen(testpass); ++ ++ x = (sasl_secret_t *) realloc(x, sizeof(sasl_secret_t) + len); ++ ++ if (!x) { ++ return SASL_NOMEM; ++ } ++ ++ x->len = len; ++ strcpy((char *)x->data, testpass); ++ ++ *psecret = x; ++ return SASL_OK; ++} ++ + int main(int argc, char *argv[]) + { +- sasl_callback_t callbacks[2] = {}; ++ sasl_callback_t callbacks[4] = {}; + char buf[8192]; + const char *chosenmech; + sasl_conn_t *conn; +@@ -46,12 +99,18 @@ int main(int argc, char *argv[]) + char cb_buf[256]; + int sd; + int c, r; ++ const char *sasl_mech = "GSSAPI"; ++ int plain = 0; + +- while ((c = getopt(argc, argv, "c:")) != EOF) { ++ while ((c = getopt(argc, argv, "c:P:")) != EOF) { + switch (c) { + case 'c': + parse_cb(&cb, cb_buf, 256, optarg); + break; ++ case 'P': ++ plain = 1; ++ testpass = optarg; ++ break; + default: + break; + } +@@ -64,6 +123,22 @@ int main(int argc, char *argv[]) + callbacks[1].id = SASL_CB_LIST_END; + callbacks[1].proc = NULL; + callbacks[1].context = NULL; ++ callbacks[2].id = SASL_CB_LIST_END; ++ callbacks[2].proc = NULL; ++ callbacks[2].context = NULL; ++ callbacks[3].id = SASL_CB_LIST_END; ++ callbacks[3].proc = NULL; ++ callbacks[3].context = NULL; ++ ++ if (plain) { ++ sasl_mech = "PLAIN"; ++ ++ callbacks[1].id = SASL_CB_AUTHNAME; ++ callbacks[1].proc = (sasl_callback_ft)&get_user; ++ ++ callbacks[2].id = SASL_CB_PASS; ++ callbacks[2].proc = (sasl_callback_ft)&get_pass; ++ } + + r = sasl_client_init(callbacks); + if (r != SASL_OK) exit(-1); +@@ -78,11 +153,11 @@ int main(int argc, char *argv[]) + sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb); + } + +- r = sasl_client_start(conn, "GSSAPI", NULL, &data, &len, &chosenmech); ++ 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)); +- exit(-1); ++ saslerr(r, "starting SASL negotiation"); ++ printf("\n%s\n", sasl_errdetail(conn)); ++ exit(-1); + } + + sd = setup_socket(); +@@ -92,11 +167,11 @@ int main(int argc, char *argv[]) + 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); ++ 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); + } + } + +diff --git a/tests/t_gssapi_srv.c b/tests/t_gssapi_srv.c +index ef1217f6..430cad65 100644 +--- a/tests/t_gssapi_srv.c ++++ b/tests/t_gssapi_srv.c +@@ -1,4 +1,5 @@ +-/* Copyright (C) Simo Sorce ++/* Copyright (C) Simo Sorce , ++ * Dmitry Belyavskiy + * See COPYING file for License */ + + #include "t_common.h" +@@ -15,6 +16,10 @@ + #include + #include + ++const char *sasldb_path = NULL, ++ *auxprop_plugin = "sasldb", ++ *pwcheck_method = "auxprop-hashed"; ++ + static int setup_socket(void) + { + struct sockaddr_in addr; +@@ -45,9 +50,38 @@ static int setup_socket(void) + return sd; + } + ++static int test_getopt(void *context __attribute__((unused)), ++ const char *plugin_name __attribute__((unused)), ++ const char *option, ++ const char **result, ++ unsigned *len) ++{ ++ if (sasldb_path && !strcmp(option, "sasldb_path")) { ++ *result = sasldb_path; ++ if (len) ++ *len = (unsigned) strlen(sasldb_path); ++ return SASL_OK; ++ } ++ ++ if (sasldb_path && !strcmp(option, "auxprop_plugin")) { ++ *result = auxprop_plugin; ++ if (len) ++ *len = (unsigned) strlen(auxprop_plugin); ++ return SASL_OK; ++ } ++ ++ if (sasldb_path && !strcmp(option, "pwcheck_method")) { ++ *result = pwcheck_method; ++ if (len) ++ *len = (unsigned) strlen(pwcheck_method); ++ return SASL_OK; ++ } ++ return SASL_FAIL; ++} ++ + int main(int argc, char *argv[]) + { +- sasl_callback_t callbacks[2] = {}; ++ sasl_callback_t callbacks[3] = {}; + char buf[8192]; + sasl_conn_t *conn; + const char *data; +@@ -56,25 +90,33 @@ int main(int argc, char *argv[]) + unsigned char cb_buf[256]; + int sd; + int c, r; ++ const char *sasl_mech = "GSSAPI"; ++ int plain = 0; + +- while ((c = getopt(argc, argv, "c:")) != EOF) { ++ while ((c = getopt(argc, argv, "c:P:")) != EOF) { + switch (c) { + case 'c': + parse_cb(&cb, cb_buf, 256, optarg); + break; ++ case 'P': ++ plain = 1; ++ sasldb_path = optarg; ++ break; + default: + break; + } + } + +- + /* 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].id = SASL_CB_GETOPT; ++ callbacks[1].proc = (sasl_callback_ft)&test_getopt; + callbacks[1].context = NULL; ++ callbacks[2].id = SASL_CB_LIST_END; ++ callbacks[2].proc = NULL; ++ callbacks[2].context = NULL; + + r = sasl_server_init(callbacks, "t_gssapi_srv"); + if (r != SASL_OK) exit(-1); +@@ -90,16 +132,20 @@ int main(int argc, char *argv[]) + sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb); + } + ++ if (plain) { ++ sasl_mech = "PLAIN"; ++ } ++ + sd = setup_socket(); + + len = 8192; + recv_string(sd, buf, &len); + +- 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)); +- exit(-1); ++ saslerr(r, "starting SASL negotiation"); ++ printf("\n%s\n", sasl_errdetail(conn)); ++ exit(-1); + } + + while (r == SASL_CONTINUE) { +@@ -107,12 +153,12 @@ int main(int argc, char *argv[]) + 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); +- } ++ 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); ++ } + + } + +From d95b0afef1289194148090874799428e9e4f4cff Mon Sep 17 00:00:00 2001 +From: Simo Sorce +Date: Wed, 15 Apr 2020 11:57:17 -0400 +Subject: [PATCH] Test GSS-SPNEGO as well + +Signed-off-by: Simo Sorce +--- + tests/runtests.py | 91 ++++++++++++++++++++++++++++++++++++++++---- + tests/t_common.c | 13 ++++--- + tests/t_common.h | 3 +- + tests/t_gssapi_cli.c | 22 ++++++++++- + tests/t_gssapi_srv.c | 25 ++++++++++-- + 5 files changed, 134 insertions(+), 20 deletions(-) + +diff --git a/tests/runtests.py b/tests/runtests.py +index 513ed3ff..7be60745 100755 +--- a/tests/runtests.py ++++ b/tests/runtests.py +@@ -6,6 +6,7 @@ + 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 + + def setup_plain(testdir): + """ Create sasldb file """ +@@ -343,5 +416,9 @@ def plain_tests(testdir): + shutil.rmtree(T) + os.makedirs(T) + +- gssapi_tests(T) + plain_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 ++#include + #include + #include + +@@ -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 20d22070..b1dd6ce0 100644 +--- a/tests/t_gssapi_cli.c ++++ b/tests/t_gssapi_cli.c +@@ -101,8 +101,10 @@ int main(int argc, char *argv[]) + int c, r; + const char *sasl_mech = "GSSAPI"; + int plain = 0; ++ bool spnego = false; ++ bool zeromaxssf = false; + +- while ((c = getopt(argc, argv, "c:P:")) != EOF) { ++ while ((c = getopt(argc, argv, "c:P:zN")) != EOF) { + switch (c) { + case 'c': + parse_cb(&cb, cb_buf, 256, optarg); +@@ -111,6 +113,12 @@ int main(int argc, char *argv[]) + plain = 1; + testpass = optarg; + break; ++ case 'z': ++ zeromaxssf = true; ++ break; ++ case 'N': ++ spnego = true; ++ break; + default: + break; + } +@@ -153,6 +161,16 @@ 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); ++ } ++ + r = sasl_client_start(conn, sasl_mech, NULL, &data, &len, &chosenmech); + if (r != SASL_OK && r != SASL_CONTINUE) { + saslerr(r, "starting SASL negotiation"); +@@ -165,7 +183,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 430cad65..0adbd12f 100644 +--- a/tests/t_gssapi_srv.c ++++ b/tests/t_gssapi_srv.c +@@ -92,8 +92,10 @@ int main(int argc, char *argv[]) + int c, r; + const char *sasl_mech = "GSSAPI"; + int plain = 0; ++ bool spnego = false; ++ bool zeromaxssf = false; + +- while ((c = getopt(argc, argv, "c:P:")) != EOF) { ++ while ((c = getopt(argc, argv, "c:P:zN")) != EOF) { + switch (c) { + case 'c': + parse_cb(&cb, cb_buf, 256, optarg); +@@ -102,6 +104,12 @@ int main(int argc, char *argv[]) + plain = 1; + sasldb_path = optarg; + break; ++ case 'z': ++ zeromaxssf = true; ++ break; ++ case 'N': ++ spnego = true; ++ break; + default: + break; + } +@@ -136,10 +144,20 @@ int main(int argc, char *argv[]) + sasl_mech = "PLAIN"; + } + ++ 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, sasl_mech, buf, len, &data, &len); + if (r != SASL_OK && r != SASL_CONTINUE) { +@@ -151,7 +169,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) { +@@ -159,7 +177,6 @@ int main(int argc, char *argv[]) + printf("\n%s\n", sasl_errdetail(conn)); + exit(-1); + } +- + } + + if (r != SASL_OK) exit(-1); + diff --git a/cyrus-sasl-2.1.27-nostrncpy.patch b/cyrus-sasl-2.1.27-nostrncpy.patch new file mode 100644 index 0000000..d009047 --- /dev/null +++ b/cyrus-sasl-2.1.27-nostrncpy.patch @@ -0,0 +1,51 @@ +diff --git a/plugins/gssapi.c b/plugins/gssapi.c +index 5d900c5e..4688bb9a 100644 +--- a/plugins/gssapi.c ++++ b/plugins/gssapi.c +@@ -1567,7 +1567,6 @@ int gssapiv2_server_plug_init( + { + #ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY + const char *keytab = NULL; +- char keytab_path[1024]; + unsigned int rl; + #endif + +@@ -1589,15 +1588,7 @@ int gssapiv2_server_plug_init( + return SASL_FAIL; + } + +- if(strlen(keytab) > 1024) { +- utils->log(NULL, SASL_LOG_ERR, +- "path to keytab is > 1024 characters"); +- return SASL_BUFOVER; +- } +- +- strncpy(keytab_path, keytab, 1024); +- +- gsskrb5_register_acceptor_identity(keytab_path); ++ gsskrb5_register_acceptor_identity(keytab); + } + #endif + +diff --git a/plugins/ntlm.c b/plugins/ntlm.c +index aeb3ac34..8a7d9065 100644 +--- a/plugins/ntlm.c ++++ b/plugins/ntlm.c +@@ -375,10 +375,15 @@ static unsigned char *P16_lm(unsigned char *P16, sasl_secret_t *passwd, + unsigned *buflen __attribute__((unused)), + int *result) + { +- char P14[14]; ++ char P14[14] = { 0 }; ++ int Plen; + unsigned char S8[] = { 0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 }; + +- strncpy(P14, (const char *) passwd->data, sizeof(P14)); ++ Plen = sizeof(P14); ++ if (passwd->len < Plen) { ++ Plen = passwd->len; ++ } ++ memcpy(P14, (const char *) passwd->data, Plen); + ucase(P14, sizeof(P14)); + + E(P16, (unsigned char *) P14, sizeof(P14), S8, sizeof(S8)); diff --git a/cyrus-sasl.spec b/cyrus-sasl.spec index c6bb2c8..da256de 100644 --- a/cyrus-sasl.spec +++ b/cyrus-sasl.spec @@ -1,59 +1,76 @@ -%define anolis_release 4 - -%global username saslauth -%global plugindir %{_libdir}/sasl2 - -Name: cyrus-sasl -Version: 2.1.28 -Release: %{anolis_release}%{?dist} -Summary: Implementation of Cyrus SASL API - -License: BSD with advertising -URL: https://www.cyrusimap.org/sasl/ -Source0: https://github.com/cyrusimap/cyrus-sasl/releases/download/cyrus-sasl-%{version}/cyrus-sasl-%{version}.tar.gz -Source1: saslauthd.service -Source2: saslauthd.sysconfig - -BuildRequires: autoconf -BuildRequires: automake -BuildRequires: libtool -BuildRequires: pkgconfig -BuildRequires: groff -BuildRequires: gdbm-devel -BuildRequires: krb5-devel -BuildRequires: openssl-devel -BuildRequires: pam-devel -BuildRequires: mariadb-connector-c-devel -BuildRequires: libpq-devel -BuildRequires: zlib-devel -BuildRequires: openldap-devel -BuildRequires: python3 -BuildRequires: nss_wrapper -BuildRequires: socket_wrapper -BuildRequires: krb5-server -BuildRequires: make -Requires(pre): /usr/sbin/useradd /usr/sbin/groupadd +%define anolis_release 5 + +%global username saslauth + +%global bootstrap_cyrus_sasl 0 + +Summary: The Cyrus SASL library +Name: cyrus-sasl +Version: 2.1.28 +Release: %{anolis_release}%{?dist} +License: BSD with advertising +URL: https://www.cyrusimap.org/sasl/ + +Source0: https://github.com/cyrusimap/cyrus-sasl/releases/download/cyrus-sasl-%{version}/cyrus-sasl-%{version}.tar.gz +Source3: saslauth.sysusers +Source5: saslauthd.service +Source7: sasl-mechlist.c +Source9: saslauthd.sysconfig +# From upstream git, required for reconfigure after applying patches to configure.ac +# https://raw.githubusercontent.com/cyrusimap/cyrus-sasl/master/autogen.sh +Source11: autogen.sh + + +Requires: %{name}-lib = %{version}-%{release} +Patch11: cyrus-sasl-2.1.25-no_rpath.patch +Patch15: cyrus-sasl-2.1.20-saslauthd.conf-path.patch +Patch23: cyrus-sasl-2.1.23-man.patch +Patch24: cyrus-sasl-2.1.21-sizes.patch +# The 64 bit *INT8 type is not used anywhere and other types match +Patch49: cyrus-sasl-2.1.26-md5global.patch + +Patch101: cyrus-sasl-2.1.27-Add-basic-test-infrastructure.patch +Patch102: cyrus-sasl-2.1.27-Add-Channel-Binding-support-for-GSSAPI-GSS-SPNEGO.patch +#https://github.com/simo5/cyrus-sasl/commit/ebd2387f06c84c7f9aac3167ec041bb01e5c6e48 +Patch106: cyrus-sasl-2.1.27-nostrncpy.patch +# Upstream PR: https://github.com/cyrusimap/cyrus-sasl/pull/635 +Patch107: cyrus-sasl-2.1.27-more-tests.patch +Patch108: cyrus-sasl-2.1.27-Add-support-for-setting-max-ssf-0-to-GSS-SPNEGO.patch +Patch500: cyrus-sasl-2.1.27-coverity.patch +Patch501: cyrus-sasl-2.1.27-cumulative-digestmd5.patch +Patch502: cyrus-sasl-2.1.27-cumulative-ossl3.patch + + +BuildRequires: autoconf, automake, libtool, gdbm-devel, groff +BuildRequires: krb5-devel >= 1.2.2, openssl-devel, pam-devel, pkgconfig +BuildRequires: mariadb-connector-c-devel, libpq-devel, zlib-devel +%if ! %{bootstrap_cyrus_sasl} +BuildRequires: openldap-devel +%endif +BuildRequires: python3 nss_wrapper socket_wrapper krb5-server +BuildRequires: make +Requires(pre): /usr/sbin/useradd /usr/sbin/groupadd Requires(postun): /usr/sbin/userdel /usr/sbin/groupdel -Requires: /sbin/nologin -Provides: user(%{username}) -Provides: group(%{username}) +Requires: /sbin/nologin +Provides: user(%username) +Provides: group(%username) + %description -This is the Cyrus SASL API implementation. It can be used on the client -or server side to provide authentication and authorization services. -See RFC 4422 for more information. +The %{name} package contains the Cyrus implementation of SASL. +SASL is the Simple Authentication and Security Layer, a method for +adding authentication support to connection-based protocols. %package lib Summary: Shared libraries needed by applications which use Cyrus SASL %description lib -The %{name}-lib package contains shared libraries needed by applications -using the Cyrus SASL library. +The %{name}-lib package contains shared libraries which are needed by +applications which use the Cyrus SASL library. %package devel Requires: %{name}-lib = %{version}-%{release} Requires: %{name} = %{version}-%{release} -Requires: pkgconfig Summary: Files needed for developing applications with Cyrus SASL %description devel @@ -93,21 +110,7 @@ Summary: NTLM authentication support for Cyrus SASL The %{name}-ntlm package contains the Cyrus SASL plugin which supports the NTLM authentication scheme. -%package scram -Requires: %{name}-lib = %{version}-%{release} -Summary: SCRAM auxprop support for Cyrus SASL -%description scram -The %{name}-scram package contains the Cyrus SASL plugin which supports -the SCRAM authentication scheme. - -%package gs2 -Requires: %{name}-lib = %{version}-%{release} -Summary: GS2 support for Cyrus SASL - -%description gs2 -The %{name}-gs2 package contains the Cyrus SASL plugin which supports -the GS2 authentication scheme. - +# This would more appropriately be named cyrus-sasl-auxprop-sql. %package sql Requires: %{name}-lib = %{version}-%{release} Summary: SQL auxprop support for Cyrus SASL @@ -116,6 +119,8 @@ Summary: SQL auxprop support for Cyrus SASL The %{name}-sql package contains the Cyrus SASL plugin which supports using a RDBMS for storing shared secrets. +%if ! %{bootstrap_cyrus_sasl} +# This was *almost* named cyrus-sasl-auxprop-ldapdb, but that's a lot of typing. %package ldap Requires: %{name}-lib = %{version}-%{release} Summary: LDAP auxprop support for Cyrus SASL @@ -123,6 +128,23 @@ Summary: LDAP auxprop support for Cyrus SASL %description ldap The %{name}-ldap package contains the Cyrus SASL plugin which supports using a directory server, accessed using LDAP, for storing shared secrets. +%endif + +%package scram +Requires: %{name}-lib = %{version}-%{release} +Summary: SCRAM auxprop support for Cyrus SASL + +%description scram +The %{name}-scram package contains the Cyrus SASL plugin which supports +the SCRAM authentication scheme. + +%package gs2 +Requires: %{name}-lib = %{version}-%{release} +Summary: GS2 support for Cyrus SASL + +%description gs2 +The %{name}-gs2 package contains the Cyrus SASL plugin which supports +the GS2 authentication scheme. %package doc Summary: Documentation files for %{name} @@ -132,38 +154,41 @@ BuildArch: noarch %description doc The %{name}-doc package contains documentation files for %{name}. +### + %prep -%autosetup +%autosetup -p1 -n cyrus-sasl-%{version} # dlcompat may cause potential legal risks find %{_builddir}/%{name}-%{version} -maxdepth 1 -name 'dlcompat-*' -exec rm -rf {} \; %build -APPEND_CFLAGS="" -INC_DIR="$( mysql_config --include )" -if [ "x$INC_DIR" != "x-I%{_includedir}" ] ; then - APPEND_CFLAGS="$APPEND_CFLAGS $INC_DIR" -fi +# reconfigure +cp %{SOURCE11} ./ +rm configure aclocal.m4 config/ltmain.sh Makefile.in +export NOCONFIGURE=yes +sh autogen.sh -CFLAGS="$CFLAGS $APPEND_CFLAGS" -export CFLAGS +CFLAGS="$RPM_OPT_FLAGS $(mysql_config --include) $CFLAGS $CPPFLAGS -fPIC -pie -Wl,-z,relro -Wl,-z,now"; export CFLAGS + +echo "$CFLAGS" +echo "$CPPFLAGS" +echo "$LDFLAGS" %configure \ - --disable-static \ - --enable-shared \ - --disable-java \ - --with-plugindir=%{plugindir} \ - --with-configdir=%{plugindir}:%{_sysconfdir}/sasl2 \ + --enable-shared --disable-static \ + --with-plugindir=%{_libdir}/sasl2 \ + --with-configdir=%{_libdir}/sasl2:%{_sysconfdir}/sasl2 \ --disable-krb4 \ - --enable-gssapi${krb5_prefix:+=${krb5_prefix}} \ + --enable-gssapi \ --with-gss_impl=mit \ --with-rc4 \ - --with-dblib=gdbm \ - --with-dbpath=/etc/sasl2/sasldb2 \ - --with-saslauthd=/run/saslauthd --without-pwcheck \ - --without-pwcheck \ + --with-dblib=gdbm \ + --with-saslauthd=/run/saslauthd --without-pwcheck \ +%if ! %{bootstrap_cyrus_sasl} --with-ldap \ +%endif --with-devrandom=/dev/urandom \ --enable-anon \ --enable-cram \ @@ -174,42 +199,66 @@ export CFLAGS --enable-alwaystrue \ --enable-httpform \ --disable-otp \ +%if ! %{bootstrap_cyrus_sasl} --enable-ldapdb \ +%endif --enable-sql --with-mysql=yes --with-pgsql=yes \ - --without-sqlite + --without-sqlite \ + --enable-auth-sasldb \ + "$@" +%make_build sasldir=%{_libdir}/sasl2 +%make_build -C saslauthd testsaslauthd +%make_build -C sample + +# Build a small program to list the available mechanisms, because I need it. +pushd lib +../libtool --mode=link %{__cc} -o sasl2-shared-mechlist -I../include $CFLAGS %{SOURCE7} $LDFLAGS ./libsasl2.la -%{make_build} sasldir=%{plugindir} -%{make_build} -C saslauthd testsaslauthd -%{make_build} -C sample - %install -%{make_install} -%{make_install} -C plugins -mkdir -p %{buildroot}%{_bindir} -mkdir -p %{buildroot}%{_sysconfdir}/sasl2 +%make_install +%make_install -C plugins + +install -m755 -d $RPM_BUILD_ROOT%{_bindir} +./libtool --mode=install \ +install -m755 sample/client $RPM_BUILD_ROOT%{_bindir}/sasl2-sample-client ./libtool --mode=install \ -install -m 755 sample/client %{buildroot}%{_bindir}/sasl2-sample-client +install -m755 sample/server $RPM_BUILD_ROOT%{_bindir}/sasl2-sample-server ./libtool --mode=install \ -install -m 755 sample/server %{buildroot}%{_bindir}/sasl2-sample-server +install -m755 saslauthd/testsaslauthd $RPM_BUILD_ROOT%{_sbindir}/testsaslauthd + +# Install the saslauthd mdoc page in the expected location. Sure, it's not +# really a man page, but groff seems to be able to cope with it. +install -m755 -d $RPM_BUILD_ROOT%{_mandir}/man8/ +install -m644 -p saslauthd/saslauthd.mdoc $RPM_BUILD_ROOT%{_mandir}/man8/saslauthd.8 +install -m644 -p saslauthd/testsaslauthd.8 $RPM_BUILD_ROOT%{_mandir}/man8/testsaslauthd.8 + +# Install the systemd unit file for saslauthd and the config file. +install -d -m755 $RPM_BUILD_ROOT/%{_unitdir} $RPM_BUILD_ROOT/etc/sysconfig +install -m644 -p %{SOURCE5} $RPM_BUILD_ROOT/%{_unitdir}/saslauthd.service +install -m644 -p %{SOURCE9} $RPM_BUILD_ROOT/etc/sysconfig/saslauthd + +# Install the config dirs if they're not already there. +install -m755 -d $RPM_BUILD_ROOT/%{_sysconfdir}/sasl2 +install -m755 -d $RPM_BUILD_ROOT/%{_libdir}/sasl2 + +# Provide an easy way to query the list of available mechanisms. ./libtool --mode=install \ -install -m 755 saslauthd/testsaslauthd %{buildroot}%{_sbindir}/testsaslauthd +install -m755 lib/sasl2-shared-mechlist $RPM_BUILD_ROOT/%{_sbindir}/ -install -m 644 %{SOURCE1} -D %{buildroot}%{_unitdir}/saslauthd.service -install -m 644 %{SOURCE2} -D %{buildroot}%{_sysconfdir}/sysconfig/saslauthd +# Sysusers file +install -p -D -m 0644 %{SOURCE3} %{buildroot}%{_sysusersdir}/cyrus-sasl.conf -rm -f %{buildroot}%{_mandir}/cat?/* -rm -f %{buildroot}%{_libdir}/sasl2/libotp.* -find %{buildroot} -type f \( -name '*.la' -o -name '*.a' \) -delete -print +# Remove unpackaged files from the buildroot. +rm -f $RPM_BUILD_ROOT%{_libdir}/sasl2/libotp.* +rm -f $RPM_BUILD_ROOT%{_libdir}/sasl2/*.a +rm -f $RPM_BUILD_ROOT%{_mandir}/cat8/saslauthd.8 %check -make check %{?_smp_mflags} +%make_build check %pre -getent group %{username} >/dev/null || groupadd -g 76 -r %{username} -getent passwd %{username} >/dev/null || \ - useradd -r -g %{username} -d /run/saslauthd \ - -s /sbin/nologin -c "Saslauthd User" %{username} +%sysusers_create_compat %{SOURCE3} %post %systemd_post saslauthd.service @@ -228,56 +277,63 @@ getent passwd %{username} >/dev/null || \ %config(noreplace) /etc/sysconfig/saslauthd %{_unitdir}/saslauthd.service %ghost /run/saslauthd +%{_sysusersdir}/cyrus-sasl.conf %files lib %license COPYING -%doc AUTHORS doc/html/*.html %{_libdir}/libsasl*.so.* %dir %{_sysconfdir}/sasl2 -%{plugindir}/*anonymous*.so* -%{plugindir}/*sasldb*.so* +%dir %{_libdir}/sasl2 +%{_libdir}/sasl2/*anonymous*.so* +%{_libdir}/sasl2/*sasldb*.so* %{_sbindir}/saslpasswd2 %{_sbindir}/sasldblistusers2 -%files devel -%{_bindir}/sasl2-sample-client -%{_bindir}/sasl2-sample-server -%{_includedir}/* -%{_libdir}/libsasl*.*so -%{_libdir}/pkgconfig/*.pc -%{_mandir}/man3/* - %files plain -%{plugindir}/*plain*.so* -%{plugindir}/*login*.so* +%{_libdir}/sasl2/*plain*.so* +%{_libdir}/sasl2/*login*.so* +%if ! %{bootstrap_cyrus_sasl} %files ldap -%{plugindir}/*ldapdb*.so* +%{_libdir}/sasl2/*ldapdb*.so* +%endif %files md5 -%{plugindir}/*crammd5*.so* -%{plugindir}/*digestmd5*.so* +%{_libdir}/sasl2/*crammd5*.so* +%{_libdir}/sasl2/*digestmd5*.so* %files ntlm -%{plugindir}/*ntlm*.so* +%{_libdir}/sasl2/*ntlm*.so* %files sql -%{plugindir}/*sql*.so* +%{_libdir}/sasl2/*sql*.so* %files gssapi -%{plugindir}/*gssapi*.so* +%{_libdir}/sasl2/*gssapi*.so* %files scram -%{plugindir}/libscram.so* +%{_libdir}/sasl2/libscram.so* %files gs2 -%{plugindir}/libgs2.so* +%{_libdir}/sasl2/libgs2.so* + +%files devel +%{_bindir}/sasl2-sample-client +%{_bindir}/sasl2-sample-server +%{_includedir}/* +%{_libdir}/libsasl*.*so +%{_libdir}/pkgconfig/*.pc +%{_mandir}/man3/* +%{_sbindir}/sasl2-shared-mechlist %files doc %doc saslauthd/LDAP_SASLAUTHD %doc README INSTALL.TXT ChangeLog AUTHORS %changelog +* Sat Mar 4 2023 Funda Wang - 2.1.28-5 +- Add various patches + * Fri Feb 10 2023 happy_orange - 2.1.28-4 - add doc package diff --git a/sasl-mechlist.c b/sasl-mechlist.c new file mode 100644 index 0000000..680e983 --- /dev/null +++ b/sasl-mechlist.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include + +#include "sasl.h" + +static int +my_getopt(void *context, const char *plugin_name, + const char *option, const char **result, unsigned *len) +{ + if (result) { + *result = NULL; +#if 0 + fprintf(stderr, "Getopt plugin=%s%s%s/option=%s%s%s -> ", + plugin_name ? "\"" : "", + plugin_name ? plugin_name : "(null)", + plugin_name ? "\"" : "", + option ? "\"" : "", + option ? option : "(null)", + option ? "\"" : ""); + fprintf(stderr, "'%s'.\n", *result ? *result : ""); +#endif + } + if (len) { + *len = 0; + } + return 0; +} + +int +main(int argc, char **argv) +{ + int ret, i; + const char *mechs, **globals; + sasl_callback_t callbacks[] = { + {SASL_CB_GETOPT, my_getopt, NULL}, + {SASL_CB_LIST_END}, + }; + sasl_conn_t *connection; + char hostname[512]; + + if ((argc > 1) && (argv[1][0] == '-')) { + fprintf(stderr, "Usage: %s [appname [hostname] ]\n", argv[0]); + return 0; + } + + ret = sasl_server_init(callbacks, argc > 1 ? argv[1] : "sasl-mechlist"); + if (ret != SASL_OK) { + fprintf(stderr, "Error in sasl_server_init(): %s\n", + sasl_errstring(ret, NULL, NULL)); + } + + connection = NULL; + strcpy(hostname, "localhost"); + gethostname(hostname, sizeof(hostname)); + ret = sasl_server_new(argc > 2 ? argv[2] : "host", + hostname, + NULL, + NULL, + NULL, + callbacks, + 0, + &connection); + if (ret != SASL_OK) { + fprintf(stderr, "Error in sasl_server_new(): %s\n", + sasl_errstring(ret, NULL, NULL)); + } + + ret = sasl_listmech(connection, + getenv("USER") ? getenv("USER") : "root", + "Available mechanisms: ", + ",", + "\n", + &mechs, + NULL, + NULL); + if (ret != SASL_OK) { + fprintf(stderr, "Error in sasl_listmechs(): %s\n", + sasl_errstring(ret, NULL, NULL)); + } else { + fprintf(stdout, "%s", mechs); + } + + globals = sasl_global_listmech(); + for (i = 0; (globals != NULL) && (globals[i] != NULL); i++) { + if (i == 0) { + fprintf(stdout, "Library supports: "); + } + fprintf(stdout, "%s", globals[i]); + if (globals[i + 1] != NULL) { + fprintf(stdout, ","); + } else { + fprintf(stdout, "\n"); + } + } + + return 0; +} diff --git a/saslauth.sysusers b/saslauth.sysusers new file mode 100644 index 0000000..eb6e42c --- /dev/null +++ b/saslauth.sysusers @@ -0,0 +1,4 @@ +#Type Name ID GECOS Home directory Shell +g saslauth 76 +u saslauth - "Saslauthd user" /run/saslauthd /sbin/nologin +m saslauth saslauth -- Gitee