RHEL 9.0.0 Alpha bootstrap

The content of this branch was automatically imported from Fedora ELN
with the following as its source:
https://src.fedoraproject.org/rpms/cyrus-sasl#e6b724063170dc972e127f012171504e9c52263a
This commit is contained in:
Petr Šabata 2020-10-14 23:27:14 +02:00
parent c448b5007a
commit 5299d93aea
26 changed files with 3737 additions and 0 deletions

5
.gitignore vendored
View File

@ -0,0 +1,5 @@
cyrus-sasl-2.1.23-nodlcompatorsrp.tar.gz
/cyrus-sasl-2.1.25-nodlcompatorsrp.tar.gz
/cyrus-sasl-2.1.26-nodlcompatorsrp.tar.gz
/cyrus-sasl-2.1.27-rc7-nodlcompatorsrp.tar.gz
/cyrus-sasl-2.1.27-nodlcompatorsrp.tar.gz

37
autogen.sh Normal file
View File

@ -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

View File

@ -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

View File

@ -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 <inttypes.h>
+#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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#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 <inttypes.h>\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);

View File

@ -0,0 +1,22 @@
--- a/sasldb/db_berkeley.c
+++ b/sasldb/db_berkeley.c
@@ -100,7 +100,7 @@ static int berkeleydb_open(const sasl_ut
ret = db_create(mbdb, NULL, 0);
if (ret == 0 && *mbdb != NULL)
{
-#if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1
+#if (DB_VERSION_MAJOR > 4) || ((DB_VERSION_MAJOR == 4) && (DB_VERSION_MINOR >= 1))
ret = (*mbdb)->open(*mbdb, NULL, path, NULL, DB_HASH, flags, 0660);
#else
ret = (*mbdb)->open(*mbdb, path, NULL, DB_HASH, flags, 0660);
--- a/utils/dbconverter-2.c
+++ b/utils/dbconverter-2.c
@@ -214,7 +214,7 @@ static int berkeleydb_open(const char *p
ret = db_create(mbdb, NULL, 0);
if (ret == 0 && *mbdb != NULL)
{
-#if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1
+#if (DB_VERSION_MAJOR > 4) || ((DB_VERSION_MAJOR == 4) && (DB_VERSION_MINOR >= 1))
ret = (*mbdb)->open(*mbdb, NULL, path, NULL, DB_HASH, DB_CREATE, 0664);
#else
ret = (*mbdb)->open(*mbdb, path, NULL, DB_HASH, DB_CREATE, 0664);

View File

@ -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 <roberto@connexer.com>,
+for the Debian project (but may be used by others).

View File

@ -0,0 +1,16 @@
diff -up cyrus-sasl-2.1.23/lib/common.c.race cyrus-sasl-2.1.23/lib/common.c
--- cyrus-sasl-2.1.23/lib/common.c.race 2010-02-22 09:57:28.000000000 +0100
+++ cyrus-sasl-2.1.23/lib/common.c 2010-02-22 09:59:30.000000000 +0100
@@ -794,7 +794,11 @@ void sasl_dispose(sasl_conn_t **pconn)
if (result!=SASL_OK) return;
/* *pconn might have become NULL by now */
- if (! (*pconn)) return;
+ if (! (*pconn))
+ {
+ sasl_MUTEX_UNLOCK(free_mutex);
+ return;
+ }
(*pconn)->destroy_conn(*pconn);
sasl_FREE(*pconn);

View File

@ -0,0 +1,70 @@
diff -up cyrus-sasl-2.1.23/saslauthd/auth_rimap.c.rimap2 cyrus-sasl-2.1.23/saslauthd/auth_rimap.c
--- cyrus-sasl-2.1.23/saslauthd/auth_rimap.c.rimap2 2011-06-01 05:54:13.339252378 +0200
+++ cyrus-sasl-2.1.23/saslauthd/auth_rimap.c 2011-06-01 05:54:39.626252337 +0200
@@ -1,3 +1,4 @@
+
/* MODULE: auth_rimap */
/* COPYRIGHT
@@ -368,6 +369,30 @@ auth_rimap (
alarm(NETWORK_IO_TIMEOUT);
rc = read(s, rbuf, sizeof(rbuf));
alarm(0);
+ if ( rc>0 ) {
+ /* check if there is more to read */
+ fd_set perm;
+ int fds, ret;
+ struct timeval timeout;
+
+ FD_ZERO(&perm);
+ FD_SET(s, &perm);
+ fds = s +1;
+
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ while( select (fds, &perm, NULL, NULL, &timeout ) >0 ) {
+ if ( FD_ISSET(s, &perm) ) {
+ ret = read(s, rbuf+rc, sizeof(rbuf)-rc);
+ if ( ret<0 ) {
+ rc = ret;
+ break;
+ } else {
+ rc += ret;
+ }
+ }
+ }
+ }
if (rc == -1) {
syslog(LOG_WARNING, "auth_rimap: read (banner): %m");
(void) close(s);
@@ -457,6 +482,30 @@ auth_rimap (
alarm(NETWORK_IO_TIMEOUT);
rc = read(s, rbuf, sizeof(rbuf));
alarm(0);
+ if ( rc>0 ) {
+ /* check if there is more to read */
+ fd_set perm;
+ int fds, ret;
+ struct timeval timeout;
+
+ FD_ZERO(&perm);
+ FD_SET(s, &perm);
+ fds = s +1;
+
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ while( select (fds, &perm, NULL, NULL, &timeout ) >0 ) {
+ if ( FD_ISSET(s, &perm) ) {
+ ret = read(s, rbuf+rc, sizeof(rbuf)-rc);
+ if ( ret<0 ) {
+ rc = ret;
+ break;
+ } else {
+ rc += ret;
+ }
+ }
+ }
+ }
(void) close(s); /* we're done with the remote */
if (rc == -1) {
syslog(LOG_WARNING, "auth_rimap: read (response): %m");

View File

@ -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_runpath_switch="none"
+ ])

View File

@ -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

View File

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

View File

@ -0,0 +1,641 @@
From 82e299e970461c153a036bb1fbc84e808f926e12 Mon Sep 17 00:00:00 2001
From: Simo Sorce <simo@redhat.com>
Date: Tue, 5 May 2020 14:06:57 -0400
Subject: [PATCH] Add basic test infrastructure
First test is for SASL/GSSAPI
Backport of upstream commit id:
18ff41d5d18f61c2ded7235dad1d9618aa84784b
Signed-off-by: Simo Sorce <simo@redhat.com>
---
Makefile.am | 2 +-
configure.ac | 3 +-
tests/Makefile.am | 79 +++++++++++++++++++
tests/runtests.py | 179 +++++++++++++++++++++++++++++++++++++++++++
tests/t_common.c | 68 ++++++++++++++++
tests/t_common.h | 15 ++++
tests/t_gssapi_cli.c | 95 +++++++++++++++++++++++
tests/t_gssapi_srv.c | 111 +++++++++++++++++++++++++++
8 files changed, 550 insertions(+), 2 deletions(-)
create mode 100644 tests/Makefile.am
create mode 100755 tests/runtests.py
create mode 100644 tests/t_common.c
create mode 100644 tests/t_common.h
create mode 100644 tests/t_gssapi_cli.c
create mode 100644 tests/t_gssapi_srv.c
diff --git a/Makefile.am b/Makefile.am
index 83dae6f..fc24509 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -70,7 +70,7 @@ else
INSTALLOSX =
endif
-SUBDIRS=include sasldb common lib plugins utils $(PWC) $(SAM) $(JAV) $(SAD)
+SUBDIRS=include sasldb common lib plugins utils $(PWC) $(SAM) $(JAV) $(SAD) tests
EXTRA_DIST=config doc docsrc win32 mac dlcompat-20010505 NTMakefile \
INSTALL.TXT libsasl2.pc.in
diff --git a/configure.ac b/configure.ac
index ca5936a..c1d2182 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1575,7 +1575,8 @@ java/javax/Makefile
java/javax/security/Makefile
java/javax/security/auth/Makefile
java/javax/security/auth/callback/Makefile
-pwcheck/Makefile)
+pwcheck/Makefile
+tests/Makefile)
AC_MSG_NOTICE([
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..1edf010
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,79 @@
+# Makefile.am -- automake input for cyrus-sasl tests
+# Simo Sorce
+#
+################################################################
+# Copyright (c) 2000 Carnegie Mellon University. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. The name "Carnegie Mellon University" must not be used to
+# endorse or promote products derived from this software without
+# prior written permission. For permission or any other legal
+# details, please contact
+# Office of Technology Transfer
+# Carnegie Mellon University
+# 5000 Forbes Avenue
+# Pittsburgh, PA 15213-3890
+# (412) 268-4387, fax: (412) 268-7395
+# tech-transfer@andrew.cmu.edu
+#
+# 4. Redistributions of any form whatsoever must retain the following
+# acknowledgment:
+# "This product includes software developed by Computing Services
+# at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+#
+# CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+# FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+# AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+# OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+################################################################
+
+AM_CPPFLAGS=-I$(top_srcdir)/include -DPLUGINDIR='"${top_srcdir}/plugins/.libs"'
+
+COMMON_LDADD = ../lib/libsasl2.la $(GSSAPIBASE_LIBS) $(GSSAPI_LIBS) $(LIB_SOCKET)
+
+t_gssapi_cli_SOURCES = \
+ t_common.c \
+ t_gssapi_cli.c
+
+t_gssapi_cli_LDADD = $(COMMON_LDADD)
+
+t_gssapi_srv_SOURCES = \
+ t_common.c \
+ t_gssapi_srv.c
+
+t_gssapi_srv_LDADD = $(COMMON_LDADD)
+
+check_PROGRAMS = \
+ t_gssapi_cli \
+ t_gssapi_srv \
+ $(NULL)
+
+noinst_PROGRAMS = $(check_PROGRAMS)
+
+EXTRA_DIST = \
+ runtests.py \
+ $(NULL)
+
+all: $(check_PROGRAMS)
+
+check:
+if MACOSX
+# skip Mac OSX for now
+else
+ $(srcdir)/runtests.py $(CHECKARGS)
+endif
diff --git a/tests/runtests.py b/tests/runtests.py
new file mode 100755
index 0000000..f645adf
--- /dev/null
+++ b/tests/runtests.py
@@ -0,0 +1,179 @@
+#!/usr/bin/python3
+
+import argparse
+import os
+import shutil
+import signal
+import subprocess
+import time
+from string import Template
+
+
+def setup_socket_wrappers(testdir):
+ """ Try to set up socket wrappers """
+ wrapdir = os.path.join(testdir, 'w')
+ os.makedirs(wrapdir)
+
+ wrappers = subprocess.Popen(['pkg-config', '--exists', 'socket_wrapper'])
+ wrappers.wait()
+ if wrappers.returncode != 0:
+ raise Exception('Socket Wrappers not available')
+
+ wrappers = subprocess.Popen(['pkg-config', '--exists', 'nss_wrapper'])
+ wrappers.wait()
+ if wrappers.returncode != 0:
+ raise Exception('NSS Wrappers not available')
+
+ hosts = os.path.join(wrapdir, 'hosts')
+ with open(hosts, 'w+') as conffile:
+ conffile.write('127.0.0.9 host.realm.test')
+
+ return {'LD_PRELOAD': 'libsocket_wrapper.so libnss_wrapper.so',
+ 'SOCKET_WRAPPER_DIR': wrapdir,
+ 'SOCKET_WRAPPER_DEFAULT_IFACE': '9',
+ 'NSS_WRAPPER_HOSTNAME': 'host.realm.test',
+ 'NSS_WRAPPER_HOSTS': hosts}
+
+
+KERBEROS_CONF = '''
+[libdefaults]
+ default_realm = REALM.TEST
+ dns_lookup_realm = false
+ dns_lookup_kdc = false
+ rdns = false
+ ticket_lifetime = 24h
+ forwardable = yes
+ default_ccache_name = FILE://${TESTDIR}/ccache
+ udp_preference_limit = 1
+
+[domain_realm]
+ .realm.test = REALM.TEST
+ realm.test = REALM.TEST
+
+[realms]
+ REALM.TEST = {
+ kdc = 127.0.0.9
+ admin_server = 127.0.0.9
+ acl_file = ${TESTDIR}/kadm.acl
+ dict_file = /usr/share/dict/words
+ admin_keytab = ${TESTDIR}/kadm.keytab
+ database_name = ${TESTDIR}/kdc.db
+ key_stash_file = ${TESTDIR}/kdc.stash
+ }
+
+[kdcdefaults]
+ kdc_ports = 88
+ kdc_tcp_ports = 88
+
+[logging]
+ kdc = FILE:${TESTDIR}/kdc.log
+ admin_server = FILE:${TESTDIR}/kadm.log
+ default = FILE:${TESTDIR}/krb5.log
+'''
+
+
+def setup_kdc(testdir, env):
+ """ Setup KDC and start process """
+ krbconf = os.path.join(testdir, 'krb.conf')
+ env['KRB5_CONFIG'] = krbconf
+
+ kenv = {'KRB5_KDC_PROFILE': krbconf,
+ 'PATH': '/sbin:/bin:/usr/sbin:/usr/bin'}
+ kenv.update(env)
+
+ # KDC/KRB5 CONFIG
+ templ = Template(KERBEROS_CONF)
+ text = templ.substitute({'TESTDIR': testdir})
+ with open(krbconf, 'w+') as conffile:
+ conffile.write(text)
+
+ testlog = os.path.join(testdir, 'kdc.log')
+ log = open(testlog, 'a')
+
+ subprocess.check_call([
+ "kdb5_util", "create",
+ "-r", "REALM.TEST", "-s", "-P", "password"
+ ], stdout=log, stderr=log, env=kenv, timeout=5)
+
+ kdc = subprocess.Popen(['krb5kdc', '-n'], env=kenv, preexec_fn=os.setsid)
+ time.sleep(5)
+
+ # Add a user and genrate a keytab
+ keytab = os.path.join(testdir, "user.keytab")
+ subprocess.check_call([
+ "kadmin.local", "-q",
+ "addprinc -randkey user"
+ ], stdout=log, stderr=log, env=kenv, timeout=5)
+
+ subprocess.check_call([
+ "kadmin.local", "-q",
+ "ktadd -k {} user".format(keytab)
+ ], stdout=log, stderr=log, env=kenv, timeout=5)
+ env['KRB5_CLIENT_KTNAME'] = keytab
+
+ # Add a service and genrate a keytab
+ keytab = os.path.join(testdir, "test.keytab")
+ subprocess.check_call([
+ "kadmin.local", "-q",
+ "addprinc -randkey test/host.realm.test"
+ ], stdout=log, stderr=log, env=kenv, timeout=5)
+
+ subprocess.check_call([
+ "kadmin.local", "-q",
+ "ktadd -k {} test/host.realm.test".format(keytab)
+ ], stdout=log, stderr=log, env=kenv, timeout=5)
+ env['KRB5_KTNAME'] = keytab
+
+ return kdc, env
+
+
+def gssapi_tests(testdir):
+ """ SASL/GSSAPI Tests """
+ env = setup_socket_wrappers(testdir)
+ kdc, kenv = setup_kdc(testdir, env)
+ #print("KDC: {}, ENV: {}".format(kdc, kenv))
+ kenv['KRB5_TRACE'] = os.path.join(testdir, 'trace.log')
+
+ try:
+ srv = subprocess.Popen(["../tests/t_gssapi_srv"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, env=kenv)
+ srv.stdout.readline() # Wait for srv to say it is ready
+ cli = subprocess.Popen(["../tests/t_gssapi_cli"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, env=kenv)
+ try:
+ cli.wait(timeout=5)
+ srv.wait(timeout=5)
+ except Exception as e:
+ print("Failed on {}".format(e));
+ cli.kill()
+ srv.kill()
+ if cli.returncode != 0 or srv.returncode != 0:
+ raise Exception("CLI ({}): {} --> SRV ({}): {}".format(
+ cli.returncode, cli.stderr.read().decode('utf-8'),
+ srv.returncode, srv.stderr.read().decode('utf-8')))
+ except Exception as e:
+ print("FAIL: {}".format(e))
+
+ print("PASS: CLI({}) SRV({})".format(
+ cli.stdout.read().decode('utf-8').strip(),
+ srv.stdout.read().decode('utf-8').strip()))
+
+ os.killpg(kdc.pid, signal.SIGTERM)
+
+
+if __name__ == "__main__":
+
+ P = argparse.ArgumentParser(description='Cyrus SASL Tests')
+ P.add_argument('--testdir', default=os.path.join(os.getcwd(), '.tests'),
+ help="Directory for running tests")
+ A = vars(P.parse_args())
+
+ T = A['testdir']
+
+ if os.path.exists(T):
+ shutil.rmtree(T)
+ os.makedirs(T)
+
+ gssapi_tests(T)
diff --git a/tests/t_common.c b/tests/t_common.c
new file mode 100644
index 0000000..7168b2f
--- /dev/null
+++ b/tests/t_common.c
@@ -0,0 +1,68 @@
+/* TBD, add (C) */
+
+#include <t_common.h>
+
+void s_error(const char *hdr, ssize_t ret, ssize_t len, int err)
+{
+ fprintf(stderr, "%s l:%ld/%ld [%d] %s",
+ hdr, ret, len, err, strerror(err));
+ exit(-1);
+}
+
+void send_string(int sd, const char *s, unsigned int l)
+{
+ ssize_t ret;
+
+fprintf(stderr, "s:%u ", l);
+fflush(stderr);
+
+ ret = send(sd, &l, sizeof(l), 0);
+ if (ret != sizeof(l)) s_error("send size", ret, sizeof(l), errno);
+
+ if (l == 0) return;
+
+ ret = send(sd, s, l, 0);
+ if (ret != l) s_error("send data", ret, l, errno);
+}
+
+void recv_string(int sd, char *buf, unsigned int *buflen)
+{
+ unsigned int l;
+ ssize_t ret;
+
+ ret = recv(sd, &l, sizeof(l), MSG_WAITALL);
+ if (ret != sizeof(l)) s_error("recv size", ret, sizeof(l), errno);
+
+ if (l == 0) {
+fprintf(stderr, "r:0 ");
+fflush(stderr);
+ *buflen = 0;
+ return;
+ }
+
+ if (*buflen < l) s_error("recv len", l, *buflen, E2BIG);
+
+ ret = recv(sd, buf, l, 0);
+ if (ret != l) s_error("recv data", ret, l, errno);
+
+fprintf(stderr, "r:%ld ", ret);
+fflush(stderr);
+ *buflen = ret;
+}
+
+void saslerr(int why, const char *what)
+{
+ fprintf(stderr, "%s: %s", what, sasl_errstring(why, NULL, NULL));
+}
+
+int getpath(void *context __attribute__((unused)), const char **path)
+{
+ if (! path) {
+ return SASL_BADPARAM;
+ }
+
+ *path = PLUGINDIR;
+ return SASL_OK;
+}
+
+
diff --git a/tests/t_common.h b/tests/t_common.h
new file mode 100644
index 0000000..4ee1976
--- /dev/null
+++ b/tests/t_common.h
@@ -0,0 +1,15 @@
+/* TBD, add (C) */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/socket.h>
+
+#include <sasl.h>
+
+void s_error(const char *hdr, ssize_t ret, ssize_t len, int err);
+void send_string(int sd, const char *s, unsigned int l);
+void recv_string(int sd, char *buf, unsigned int *buflen);
+void saslerr(int why, const char *what);
+int getpath(void *context __attribute__((unused)), const char **path);
diff --git a/tests/t_gssapi_cli.c b/tests/t_gssapi_cli.c
new file mode 100644
index 0000000..c833c05
--- /dev/null
+++ b/tests/t_gssapi_cli.c
@@ -0,0 +1,95 @@
+/* TBD, add (C) */
+
+#include "t_common.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <arpa/inet.h>
+#include <saslplug.h>
+
+static int setup_socket(void)
+{
+ struct sockaddr_in addr;
+ int sock, ret;
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0) s_error("socket", 0, 0, errno);
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr("127.0.0.9");
+ addr.sin_port = htons(9000);
+
+ ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret != 0) s_error("connect", 0, 0, errno);
+
+ return sock;
+}
+
+int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
+{
+ sasl_callback_t callbacks[2] = {};
+ char buf[8192];
+ const char *chosenmech;
+ sasl_conn_t *conn;
+ const char *data;
+ unsigned int len;
+ int sd;
+ int r;
+
+ /* initialize the sasl library */
+ callbacks[0].id = SASL_CB_GETPATH;
+ callbacks[0].proc = (sasl_callback_ft)&getpath;
+ callbacks[0].context = NULL;
+ callbacks[1].id = SASL_CB_LIST_END;
+ callbacks[1].proc = NULL;
+ callbacks[1].context = NULL;
+
+ r = sasl_client_init(callbacks);
+ if (r != SASL_OK) exit(-1);
+
+ r = sasl_client_new("test", "host.realm.test", NULL, NULL, NULL, 0, &conn);
+ if (r != SASL_OK) {
+ saslerr(r, "allocating connection state");
+ exit(-1);
+ }
+
+ r = sasl_client_start(conn, "GSSAPI", NULL, &data, &len, &chosenmech);
+ if (r != SASL_OK && r != SASL_CONTINUE) {
+ saslerr(r, "starting SASL negotiation");
+ printf("\n%s\n", sasl_errdetail(conn));
+ exit(-1);
+ }
+
+ sd = setup_socket();
+
+ while (r == SASL_CONTINUE) {
+ send_string(sd, data, len);
+ len = 8192;
+ recv_string(sd, buf, &len);
+
+ r = sasl_client_step(conn, buf, len, NULL, &data, &len);
+ if (r != SASL_OK && r != SASL_CONTINUE) {
+ saslerr(r, "performing SASL negotiation");
+ printf("\n%s\n", sasl_errdetail(conn));
+ exit(-1);
+ }
+ }
+
+ if (r != SASL_OK) exit(-1);
+
+ if (len > 0) {
+ send_string(sd, data, len);
+ }
+
+ fprintf(stdout, "DONE\n");
+ fflush(stdout);
+ return 0;
+}
+
diff --git a/tests/t_gssapi_srv.c b/tests/t_gssapi_srv.c
new file mode 100644
index 0000000..29f538d
--- /dev/null
+++ b/tests/t_gssapi_srv.c
@@ -0,0 +1,111 @@
+/* TBD, add (C) */
+
+#include "t_common.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <arpa/inet.h>
+#include <saslplug.h>
+
+static int setup_socket(void)
+{
+ struct sockaddr_in addr;
+ int sock, ret, sd;
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0) s_error("socket", 0, 0, errno);
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr("127.0.0.9");
+ addr.sin_port = htons(9000);
+
+ ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret != 0) s_error("bind", 0, 0, errno);
+
+ ret = listen(sock, 1);
+ if (ret != 0) s_error("listen", 0, 0, errno);
+
+ /* signal we are ready */
+ fprintf(stdout, "READY\n");
+ fflush(stdout);
+
+ /* block until the client connects */
+ sd = accept(sock, NULL, NULL);
+ if (sd < 0) s_error("accept", 0, 0, errno);
+
+ close(sock);
+ return sd;
+}
+
+int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
+{
+ sasl_callback_t callbacks[2] = {};
+ char buf[8192];
+ sasl_conn_t *conn;
+ const char *data;
+ unsigned int len;
+ int sd;
+ int r;
+
+ /* initialize the sasl library */
+ callbacks[0].id = SASL_CB_GETPATH;
+ callbacks[0].proc = (sasl_callback_ft)&getpath;
+ callbacks[0].context = NULL;
+ callbacks[1].id = SASL_CB_LIST_END;
+ callbacks[1].proc = NULL;
+ callbacks[1].context = NULL;
+
+ r = sasl_server_init(callbacks, "t_gssapi_srv");
+ if (r != SASL_OK) exit(-1);
+
+ r = sasl_server_new("test", "host.realm.test", NULL, NULL, NULL,
+ callbacks, 0, &conn);
+ if (r != SASL_OK) {
+ saslerr(r, "allocating connection state");
+ exit(-1);
+ }
+
+ sd = setup_socket();
+
+ len = 8192;
+ recv_string(sd, buf, &len);
+
+ r = sasl_server_start(conn, "GSSAPI", buf, len, &data, &len);
+ if (r != SASL_OK && r != SASL_CONTINUE) {
+ saslerr(r, "starting SASL negotiation");
+ printf("\n%s\n", sasl_errdetail(conn));
+ exit(-1);
+ }
+
+ while (r == SASL_CONTINUE) {
+ send_string(sd, data, len);
+ len = 8192;
+ recv_string(sd, buf, &len);
+
+ r = sasl_server_step(conn, buf, len, &data, &len);
+ if (r != SASL_OK && r != SASL_CONTINUE) {
+ saslerr(r, "performing SASL negotiation");
+ printf("\n%s\n", sasl_errdetail(conn));
+ exit(-1);
+ }
+
+ }
+
+ if (r != SASL_OK) exit(-1);
+
+ if (len > 0) {
+ send_string(sd, data, len);
+ }
+
+ fprintf(stdout, "DONE\n");
+ fflush(stdout);
+ return 0;
+}
+
--
2.18.2

View File

@ -0,0 +1,435 @@
From 49e965f41257a0ed299c58a7cf1c120ddf944aaa Mon Sep 17 00:00:00 2001
From: Simo Sorce <simo@redhat.com>
Date: Tue, 5 May 2020 14:51:36 -0400
Subject: [PATCH] Add support for setting max ssf 0 to GSS-SPNEGO
Bacport form this proposed PR (still open at bacport time):
https://github.com/cyrusimap/cyrus-sasl/pull/603
Signed-off-by: Simo Sorce <simo@redhat.com>
---
m4/sasl2.m4 | 13 +++++++
plugins/gssapi.c | 44 ++++++++++++++++++++-
tests/runtests.py | 91 ++++++++++++++++++++++++++++++++++++++++----
tests/t_common.c | 13 ++++---
tests/t_common.h | 3 +-
tests/t_gssapi_cli.c | 25 ++++++++++--
tests/t_gssapi_srv.c | 28 +++++++++++---
7 files changed, 194 insertions(+), 23 deletions(-)
diff --git a/m4/sasl2.m4 b/m4/sasl2.m4
index 56e0504..6effe99 100644
--- a/m4/sasl2.m4
+++ b/m4/sasl2.m4
@@ -287,6 +287,19 @@ if test "$gssapi" != no; then
AC_CHECK_FUNCS(gss_oid_equal)
LIBS="$cmu_save_LIBS"
+ cmu_save_LIBS="$LIBS"
+ LIBS="$LIBS $GSSAPIBASE_LIBS"
+ if test "$ac_cv_header_gssapi_gssapi_krb5_h" = "yes"; then
+ AC_CHECK_DECL(GSS_KRB5_CRED_NO_CI_FLAGS_X,
+ [AC_DEFINE(HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X,1,
+ [Define if your GSSAPI implementation supports GSS_KRB5_CRED_NO_CI_FLAGS_X])],,
+ [
+ AC_INCLUDES_DEFAULT
+ #include <gssapi/gssapi_krb5.h>
+ ])
+ fi
+ LIBS="$cmu_save_LIBS"
+
cmu_save_LIBS="$LIBS"
LIBS="$LIBS $GSSAPIBASE_LIBS"
AC_CHECK_FUNCS(gss_get_name_attribute)
diff --git a/plugins/gssapi.c b/plugins/gssapi.c
index 5d900c5..7480316 100644
--- a/plugins/gssapi.c
+++ b/plugins/gssapi.c
@@ -1783,7 +1783,49 @@ static int gssapi_client_mech_step(void *conn_context,
/* We want to try for privacy */
req_flags |= GSS_C_CONF_FLAG;
}
- }
+#ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
+ /* The krb5 mechanism automatically adds INTEG and CONF flags even when
+ * not specified, this has the effect of rendering explicit requests
+ * of no confidentiality and integrity via setting maxssf 0 moot.
+ * However to interoperate with Windows machines it needs to be
+ * possible to unset these flags as Windows machines refuse to allow
+ * two layers (say TLS and GSSAPI) to both provide these services.
+ * So if we do not suppress these flags a SASL/GSS-SPNEGO negotiation
+ * over, say, LDAPS will fail against Windows Servers */
+ } else if (params->props.max_ssf == 0) {
+ gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
+ if (client_creds == GSS_C_NO_CREDENTIAL) {
+ gss_OID_set_desc mechs = { 0 };
+ gss_OID_set desired_mechs = GSS_C_NO_OID_SET;
+ if (text->mech_type != GSS_C_NO_OID) {
+ mechs.count = 1;
+ mechs.elements = text->mech_type;
+ desired_mechs = &mechs;
+ }
+
+ maj_stat = gss_acquire_cred(&min_stat, GSS_C_NO_NAME,
+ GSS_C_INDEFINITE, desired_mechs,
+ GSS_C_INITIATE,
+ &text->client_creds, NULL, NULL);
+ if (GSS_ERROR(maj_stat)) {
+ sasl_gss_seterror(text->utils, maj_stat, min_stat);
+ sasl_gss_free_context_contents(text);
+ return SASL_FAIL;
+ }
+ client_creds = text->client_creds;
+ }
+
+ maj_stat = gss_set_cred_option(&min_stat, &client_creds,
+ (gss_OID)GSS_KRB5_CRED_NO_CI_FLAGS_X,
+ &empty_buffer);
+ if (GSS_ERROR(maj_stat)) {
+ sasl_gss_seterror(text->utils, maj_stat, min_stat);
+ sasl_gss_free_context_contents(text);
+ return SASL_FAIL;
+ }
+#endif
+ }
+
if (params->props.security_flags & SASL_SEC_PASS_CREDENTIALS) {
req_flags = req_flags | GSS_C_DELEG_FLAG;
diff --git a/tests/runtests.py b/tests/runtests.py
index fc9cf24..4106401 100755
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -6,6 +6,7 @@ import os
import shutil
import signal
import subprocess
+import sys
import time
from string import Template
@@ -149,11 +150,12 @@ def gssapi_basic_test(kenv):
srv.returncode, srv.stderr.read().decode('utf-8')))
except Exception as e:
print("FAIL: {}".format(e))
- return
+ return 1
print("PASS: CLI({}) SRV({})".format(
cli.stdout.read().decode('utf-8').strip(),
srv.stdout.read().decode('utf-8').strip()))
+ return 0
def gssapi_channel_binding_test(kenv):
try:
@@ -178,11 +180,12 @@ def gssapi_channel_binding_test(kenv):
srv.returncode, srv.stderr.read().decode('utf-8')))
except Exception as e:
print("FAIL: {}".format(e))
- return
+ return 1
print("PASS: CLI({}) SRV({})".format(
cli.stdout.read().decode('utf-8').strip(),
srv.stdout.read().decode('utf-8').strip()))
+ return 0
def gssapi_channel_binding_mismatch_test(kenv):
result = "FAIL"
@@ -212,11 +215,70 @@ def gssapi_channel_binding_mismatch_test(kenv):
cli.returncode, cli_err, srv.returncode, srv_err))
except Exception as e:
print("{}: {}".format(result, e))
- return
+ return 0
print("FAIL: This test should fail [CLI({}) SRV({})]".format(
cli.stdout.read().decode('utf-8').strip(),
srv.stdout.read().decode('utf-8').strip()))
+ return 1
+
+def gss_spnego_basic_test(kenv):
+ try:
+ srv = subprocess.Popen(["../tests/t_gssapi_srv", "-N"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, env=kenv)
+ srv.stdout.readline() # Wait for srv to say it is ready
+ cli = subprocess.Popen(["../tests/t_gssapi_cli", "-N"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, env=kenv)
+ try:
+ cli.wait(timeout=5)
+ srv.wait(timeout=5)
+ except Exception as e:
+ print("Failed on {}".format(e));
+ cli.kill()
+ srv.kill()
+ if cli.returncode != 0 or srv.returncode != 0:
+ raise Exception("CLI ({}): {} --> SRV ({}): {}".format(
+ cli.returncode, cli.stderr.read().decode('utf-8'),
+ srv.returncode, srv.stderr.read().decode('utf-8')))
+ except Exception as e:
+ print("FAIL: {}".format(e))
+ return 1
+
+ print("PASS: CLI({}) SRV({})".format(
+ cli.stdout.read().decode('utf-8').strip(),
+ srv.stdout.read().decode('utf-8').strip()))
+ return 0
+
+def gss_spnego_zeromaxssf_test(kenv):
+ try:
+ srv = subprocess.Popen(["../tests/t_gssapi_srv", "-N", "-z"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, env=kenv)
+ srv.stdout.readline() # Wait for srv to say it is ready
+ cli = subprocess.Popen(["../tests/t_gssapi_cli", "-N", "-z"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, env=kenv)
+ try:
+ cli.wait(timeout=5)
+ srv.wait(timeout=5)
+ except Exception as e:
+ print("Failed on {}".format(e));
+ cli.kill()
+ srv.kill()
+ if cli.returncode != 0 or srv.returncode != 0:
+ raise Exception("CLI ({}): {} --> SRV ({}): {}".format(
+ cli.returncode, cli.stderr.read().decode('utf-8'),
+ srv.returncode, srv.stderr.read().decode('utf-8')))
+ except Exception as e:
+ print("FAIL: {}".format(e))
+ return 1
+
+ print("PASS: CLI({}) SRV({})".format(
+ cli.stdout.read().decode('utf-8').strip(),
+ srv.stdout.read().decode('utf-8').strip()))
+ return 0
def gssapi_tests(testdir):
""" SASL/GSSAPI Tests """
@@ -225,20 +287,32 @@ def gssapi_tests(testdir):
#print("KDC: {}, ENV: {}".format(kdc, kenv))
kenv['KRB5_TRACE'] = os.path.join(testdir, 'trace.log')
+ err = 0
+
print('GSSAPI BASIC:')
print(' ', end='')
- gssapi_basic_test(kenv)
+ err += gssapi_basic_test(kenv)
print('GSSAPI CHANNEL BINDING:')
print(' ', end='')
- gssapi_channel_binding_test(kenv)
+ err += gssapi_channel_binding_test(kenv)
print('GSSAPI CHANNEL BINDING MISMTACH:')
print(' ', end='')
- gssapi_channel_binding_mismatch_test(kenv)
+ err += gssapi_channel_binding_mismatch_test(kenv)
+
+ print('GSS-SPNEGO BASIC:')
+ print(' ', end='')
+ err += gss_spnego_basic_test(kenv)
+
+ print('GSS-SPNEGO 0 MAXSSF:')
+ print(' ', end='')
+ err += gss_spnego_zeromaxssf_test(kenv)
os.killpg(kdc.pid, signal.SIGTERM)
+ return err
+
if __name__ == "__main__":
@@ -253,4 +327,7 @@ if __name__ == "__main__":
shutil.rmtree(T)
os.makedirs(T)
- gssapi_tests(T)
+ err = gssapi_tests(T)
+ if err != 0:
+ print('{} test(s) FAILED'.format(err))
+ sys.exit(-1)
diff --git a/tests/t_common.c b/tests/t_common.c
index 478e6a1..f56098e 100644
--- a/tests/t_common.c
+++ b/tests/t_common.c
@@ -23,20 +23,21 @@ void send_string(int sd, const char *s, unsigned int l)
if (ret != l) s_error("send data", ret, l, errno);
}
-void recv_string(int sd, char *buf, unsigned int *buflen)
+void recv_string(int sd, char *buf, unsigned int *buflen, bool allow_eof)
{
+ unsigned int bufsize = *buflen;
unsigned int l;
ssize_t ret;
+ *buflen = 0;
+
ret = recv(sd, &l, sizeof(l), MSG_WAITALL);
+ if (allow_eof && ret == 0) return;
if (ret != sizeof(l)) s_error("recv size", ret, sizeof(l), errno);
- if (l == 0) {
- *buflen = 0;
- return;
- }
+ if (l == 0) return;
- if (*buflen < l) s_error("recv len", l, *buflen, E2BIG);
+ if (bufsize < l) s_error("recv len", l, bufsize, E2BIG);
ret = recv(sd, buf, l, 0);
if (ret != l) s_error("recv data", ret, l, errno);
diff --git a/tests/t_common.h b/tests/t_common.h
index a10def1..be24a53 100644
--- a/tests/t_common.h
+++ b/tests/t_common.h
@@ -4,6 +4,7 @@
#include "config.h"
#include <errno.h>
+#include <stdbool.h>
#include <stdio.h>
#include <sys/socket.h>
@@ -12,7 +13,7 @@
void s_error(const char *hdr, ssize_t ret, ssize_t len, int err);
void send_string(int sd, const char *s, unsigned int l);
-void recv_string(int sd, char *buf, unsigned int *buflen);
+void recv_string(int sd, char *buf, unsigned int *buflen, bool allow_eof);
void saslerr(int why, const char *what);
int getpath(void *context __attribute__((unused)), const char **path);
void parse_cb(sasl_channel_binding_t *cb, char *buf, unsigned max, char *in);
diff --git a/tests/t_gssapi_cli.c b/tests/t_gssapi_cli.c
index a44a3f5..d9eafe1 100644
--- a/tests/t_gssapi_cli.c
+++ b/tests/t_gssapi_cli.c
@@ -46,12 +46,21 @@ int main(int argc, char *argv[])
char cb_buf[256];
int sd;
int c, r;
+ const char *sasl_mech = "GSSAPI";
+ bool spnego = false;
+ bool zeromaxssf = false;
- while ((c = getopt(argc, argv, "c:")) != EOF) {
+ while ((c = getopt(argc, argv, "c:zN")) != EOF) {
switch (c) {
case 'c':
parse_cb(&cb, cb_buf, 256, optarg);
break;
+ case 'z':
+ zeromaxssf = true;
+ break;
+ case 'N':
+ spnego = true;
+ break;
default:
break;
}
@@ -78,7 +87,17 @@ int main(int argc, char *argv[])
sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb);
}
- r = sasl_client_start(conn, "GSSAPI", NULL, &data, &len, &chosenmech);
+ if (spnego) {
+ sasl_mech = "GSS-SPNEGO";
+ }
+
+ if (zeromaxssf) {
+ /* set all security properties to 0 including maxssf */
+ sasl_security_properties_t secprops = { 0 };
+ sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
+ }
+
+ r = sasl_client_start(conn, sasl_mech, NULL, &data, &len, &chosenmech);
if (r != SASL_OK && r != SASL_CONTINUE) {
saslerr(r, "starting SASL negotiation");
printf("\n%s\n", sasl_errdetail(conn));
@@ -90,7 +109,7 @@ int main(int argc, char *argv[])
while (r == SASL_CONTINUE) {
send_string(sd, data, len);
len = 8192;
- recv_string(sd, buf, &len);
+ recv_string(sd, buf, &len, false);
r = sasl_client_step(conn, buf, len, NULL, &data, &len);
if (r != SASL_OK && r != SASL_CONTINUE) {
diff --git a/tests/t_gssapi_srv.c b/tests/t_gssapi_srv.c
index ef1217f..448a218 100644
--- a/tests/t_gssapi_srv.c
+++ b/tests/t_gssapi_srv.c
@@ -56,12 +56,21 @@ int main(int argc, char *argv[])
unsigned char cb_buf[256];
int sd;
int c, r;
+ const char *sasl_mech = "GSSAPI";
+ bool spnego = false;
+ bool zeromaxssf = false;
- while ((c = getopt(argc, argv, "c:")) != EOF) {
+ while ((c = getopt(argc, argv, "c:zN")) != EOF) {
switch (c) {
case 'c':
parse_cb(&cb, cb_buf, 256, optarg);
break;
+ case 'z':
+ zeromaxssf = true;
+ break;
+ case 'N':
+ spnego = true;
+ break;
default:
break;
}
@@ -90,12 +99,22 @@ int main(int argc, char *argv[])
sasl_setprop(conn, SASL_CHANNEL_BINDING, &cb);
}
+ if (spnego) {
+ sasl_mech = "GSS-SPNEGO";
+ }
+
+ if (zeromaxssf) {
+ /* set all security properties to 0 including maxssf */
+ sasl_security_properties_t secprops = { 0 };
+ sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
+ }
+
sd = setup_socket();
len = 8192;
- recv_string(sd, buf, &len);
+ recv_string(sd, buf, &len, false);
- r = sasl_server_start(conn, "GSSAPI", buf, len, &data, &len);
+ r = sasl_server_start(conn, sasl_mech, buf, len, &data, &len);
if (r != SASL_OK && r != SASL_CONTINUE) {
saslerr(r, "starting SASL negotiation");
printf("\n%s\n", sasl_errdetail(conn));
@@ -105,7 +124,7 @@ int main(int argc, char *argv[])
while (r == SASL_CONTINUE) {
send_string(sd, data, len);
len = 8192;
- recv_string(sd, buf, &len);
+ recv_string(sd, buf, &len, true);
r = sasl_server_step(conn, buf, len, &data, &len);
if (r != SASL_OK && r != SASL_CONTINUE) {
@@ -113,7 +132,6 @@ int main(int argc, char *argv[])
printf("\n%s\n", sasl_errdetail(conn));
exit(-1);
}
-
}
if (r != SASL_OK) exit(-1);
--
2.18.2

View File

@ -0,0 +1,42 @@
From ec070b2e83a4ee698c08d6d68c205aea4d90b0bb Mon Sep 17 00:00:00 2001
From: Simo Sorce <simo@redhat.com>
Date: Tue, 5 May 2020 14:31:10 -0400
Subject: [PATCH] Emit debug log only in case of errors
Backport of commit id:
ccc5e547d4b40ee2b182a9945f8f6cc10b4fdf48
Signed-off-by: Simo Sorce <simo@redhat.com>
---
plugins/gssapi.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/plugins/gssapi.c b/plugins/gssapi.c
index 7480316..6bcd78e 100644
--- a/plugins/gssapi.c
+++ b/plugins/gssapi.c
@@ -1444,9 +1444,6 @@ gssapi_server_mech_step(void *conn_context,
if (text == NULL) return SASL_BADPROT;
- params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
- "GSSAPI server step %d\n", text->state);
-
switch (text->state) {
case SASL_GSSAPI_STATE_AUTHNEG:
@@ -1496,8 +1493,10 @@ gssapi_server_mech_step(void *conn_context,
}
oparams->doneflag = 1;
+ } else {
+ params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
+ "GSSAPI server step failed: %d\n", text->state);
}
-
return ret;
}
--
2.18.2

View File

@ -0,0 +1,25 @@
From dcc9f51cbd4ed622cfb0f9b1c141eb2ffe3b12f1 Mon Sep 17 00:00:00 2001
From: Quanah Gibson-Mount <quanah@symas.com>
Date: Tue, 18 Feb 2020 19:05:12 +0000
Subject: [PATCH] Fix #587
Off by one error in common.c, CVE-2019-19906.
Thanks to Stephan Zeisberg for reporting
---
lib/common.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/common.c b/lib/common.c
index bc3bf1df..9969d6aa 100644
--- a/lib/common.c
+++ b/lib/common.c
@@ -190,7 +190,7 @@ int _sasl_add_string(char **out, size_t *alloclen,
if (add==NULL) add = "(null)";
- addlen=strlen(add); /* only compute once */
+ addlen=strlen(add)+1; /* only compute once */
if (_buf_alloc(out, alloclen, (*outlen)+addlen)!=SASL_OK)
return SASL_NOMEM;

View File

@ -0,0 +1,156 @@
From 8aa9ae816ddf66921b4a8a0f422517e6f2e55ac6 Mon Sep 17 00:00:00 2001
From: Simo Sorce <simo@redhat.com>
Date: Wed, 27 Mar 2019 14:29:08 -0400
Subject: [PATCH] Use Openssl RC4 when available
Signed-off-by: Simo Sorce <simo@redhat.com>
---
configure.ac | 5 +--
plugins/digestmd5.c | 107 +++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 108 insertions(+), 4 deletions(-)
diff --git a/configure.ac b/configure.ac
index 388f5d02..cfdee4a2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1103,13 +1103,12 @@ AC_ARG_WITH(configdir, [ --with-configdir=DIR set the directory where confi
configdir='${plugindir}:${sysconfdir}/sasl2')
AC_SUBST(configdir)
-dnl look for rc4 libraries. we accept the CMU one or one from openSSL
-AC_ARG_WITH(rc4, [ --with-rc4 use internal rc4 routines [[yes]] ],
+AC_ARG_WITH(rc4, [ --with-rc4 use rc4 routines [[yes]] ],
with_rc4=$withval,
with_rc4=yes)
if test "$with_rc4" != no; then
- AC_DEFINE(WITH_RC4,[],[Use internal RC4 implementation?])
+ AC_DEFINE(WITH_RC4,[],[Use RC4])
fi
building_for_macosx=no
diff --git a/plugins/digestmd5.c b/plugins/digestmd5.c
index df35093d..c6b54317 100644
--- a/plugins/digestmd5.c
+++ b/plugins/digestmd5.c
@@ -1117,6 +1117,111 @@ static void free_des(context_t *text)
#endif /* WITH_DES */
#ifdef WITH_RC4
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+
+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;
+ }
+}
+
+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;
+
+ rc = EVP_EncryptInit_ex(ctx, EVP_rc4(), NULL, enckey, NULL);
+ if (rc != 1) return SASL_FAIL;
+
+ text->cipher_enc_context = (void *)ctx;
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL) return SASL_NOMEM;
+
+ rc = EVP_DecryptInit_ex(ctx, EVP_rc4(), NULL, deckey, NULL);
+ if (rc != 1) return SASL_FAIL;
+
+ text->cipher_dec_context = (void *)ctx;
+
+ 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)
+{
+ int len;
+ int rc;
+
+ /* decrypt the text part & HMAC */
+ rc = EVP_DecryptUpdate((EVP_CIPHER_CTX *)text->cipher_dec_context,
+ (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,
+ (unsigned char *)output + len, &len);
+ if (rc != 1) return SASL_FAIL;
+
+ *outputlen += len;
+
+ /* subtract the HMAC to get the text length */
+ *outputlen -= 10;
+
+ return SASL_OK;
+}
+
+static int enc_rc4(context_t *text,
+ const char *input,
+ unsigned inputlen,
+ unsigned char digest[16],
+ char *output,
+ unsigned *outputlen)
+{
+ int len;
+ int rc;
+ /* encrypt the text part */
+ rc = EVP_EncryptUpdate((EVP_CIPHER_CTX *)text->cipher_enc_context,
+ (unsigned char *)output, &len,
+ (const unsigned char *)input, inputlen);
+ if (rc != 1) return SASL_FAIL;
+
+ *outputlen = len;
+
+ /* encrypt the `MAC part */
+ rc = EVP_EncryptUpdate((EVP_CIPHER_CTX *)text->cipher_enc_context,
+ (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,
+ (unsigned char *)output + *outputlen, &len);
+ if (rc != 1) return SASL_FAIL;
+
+ *outputlen += len;
+
+ return SASL_OK;
+}
+#else
/* quick generic implementation of RC4 */
struct rc4_context_s {
unsigned char sbox[256];
@@ -1296,7 +1401,7 @@ static int enc_rc4(context_t *text,
return SASL_OK;
}
-
+#endif /* HAVE_OPENSSL */
#endif /* WITH_RC4 */
struct digest_cipher available_ciphers[] =

1128
cyrus-sasl.spec Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,41 @@
#!/bin/bash -e
#
# See https://github.com/cyrusimap/cyrus-sasl/releases for unmodified sources.
#
tmppath=`mktemp -d ${TMPDIR:-/tmp}/make-no-dlcompat-tarball-XXXXXX`
if test -z "$tmppath" ; then
echo Error creating temporary directory.
exit 1
fi
trap "rm -fr $tmppath" EXIT
initialdir=`pwd`
for tarball in ${initialdir}/cyrus-sasl-*.tar.{gz,bz2} ; do
if ! test -s "$tarball" ; then
continue
fi
rm -fr $tmppath/*
pushd $tmppath > /dev/null
case "$tarball" in
*nodlcompat*)
: Do nothing.
;;
*.gz)
gzip -dc "$tarball" | tar xf -
rm -fr cyrus-sasl-*/dlcompat*
rm -fr cyrus-sasl-*/plugins/srp*
tar cf - * | gzip -9c > \
$initialdir/`basename $tarball .tar.gz`-nodlcompatorsrp.tar.gz
;;
*.bz2)
bzip2 -dc "$tarball" | tar xf -
rm -fr cyrus-sasl-*/dlcompat*
rm -fr cyrus-sasl-*/plugins/srp*
tar cf - * | bzip2 -9c > \
$initialdir/`basename $tarball .tar.bz2`-nodlcompatorsrp.tar.bz2
;;
esac
popd > /dev/null
done

99
sasl-mechlist.c Normal file
View File

@ -0,0 +1,99 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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;
}

12
saslauthd.service Normal file
View File

@ -0,0 +1,12 @@
[Unit]
Description=SASL authentication daemon.
[Service]
Type=forking
PIDFile=/run/saslauthd/saslauthd.pid
EnvironmentFile=/etc/sysconfig/saslauthd
ExecStart=/usr/sbin/saslauthd -m $SOCKETDIR -a $MECH $FLAGS
RuntimeDirectory=saslauthd
[Install]
WantedBy=multi-user.target

11
saslauthd.sysconfig Normal file
View File

@ -0,0 +1,11 @@
# Directory in which to place saslauthd's listening socket, pid file, and so
# on. This directory must already exist.
SOCKETDIR=/run/saslauthd
# Mechanism to use when checking passwords. Run "saslauthd -v" to get a list
# of which mechanism your installation was compiled with the ablity to use.
MECH=pam
# Additional flags to pass to saslauthd on the command line. See saslauthd(8)
# for the list of accepted flags.
FLAGS=

1
sources Normal file
View File

@ -0,0 +1 @@
SHA512 (cyrus-sasl-2.1.27-nodlcompatorsrp.tar.gz) = 86c45f729d5cf0aacc20c880d1cea5d927536c602d5d5866838036f00babfd5b89cb94d14a2c6ca4d78073f8f5c08da0f1f64a9a32b26c5f1e28b0d9246fd38e

View File

@ -0,0 +1,69 @@
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# Makefile of /CoreOS/cyrus-sasl/Sanity/sanity-ldapdb-plugin
# Description: The ldapdb auxprop plugin provides access to credentials stored in an LDAP server.
# Author: David Spurek <dspurek@redhat.com>
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# Copyright (c) 2012 Red Hat, Inc. All rights reserved.
#
# This copyrighted material is made available to anyone wishing
# to use, modify, copy, or redistribute it subject to the terms
# and conditions of the GNU General Public License version 2.
#
# This program is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free
# Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
export TEST=/CoreOS/cyrus-sasl/Sanity/sanity-ldapdb-plugin
export TESTVERSION=1.0
BUILT_FILES=
FILES=$(METADATA) runtest.sh Makefile PURPOSE
.PHONY: all install download clean
run: $(FILES) build
./runtest.sh
build: $(BUILT_FILES)
test -x runtest.sh || chmod a+x runtest.sh
clean:
rm -f *~ $(BUILT_FILES)
include /usr/share/rhts/lib/rhts-make.include
$(METADATA): Makefile
@echo "Owner: David Spurek <dspurek@redhat.com>" > $(METADATA)
@echo "Name: $(TEST)" >> $(METADATA)
@echo "TestVersion: $(TESTVERSION)" >> $(METADATA)
@echo "Path: $(TEST_DIR)" >> $(METADATA)
@echo "Description: The ldapdb auxprop plugin provides access to credentials stored in an LDAP server." >> $(METADATA)
@echo "Type: Sanity" >> $(METADATA)
@echo "TestTime: 15m" >> $(METADATA)
@echo "RunFor: cyrus-sasl" >> $(METADATA)
@echo "Requires: cyrus-sasl" >> $(METADATA)
@echo "Requires: cyrus-sasl-md5" >> $(METADATA)
@echo "Requires: cyrus-sasl-ldap" >> $(METADATA)
@echo "Requires: cyrus-sasl-plain cyrus-sasl-devel" >> $(METADATA)
@echo "Requires: expect" >> $(METADATA)
@echo "Requires: openldap-servers" >> $(METADATA)
@echo "Requires: openldap-clients" >> $(METADATA)
@echo "Priority: Normal" >> $(METADATA)
@echo "License: GPLv2" >> $(METADATA)
@echo "Confidential: no" >> $(METADATA)
@echo "Destructive: no" >> $(METADATA)
rhts-lint $(METADATA)

View File

@ -0,0 +1,3 @@
PURPOSE of /CoreOS/cyrus-sasl/Sanity/sanity-ldapdb-plugin
Description: The ldapdb auxprop plugin provides access to credentials stored in an LDAP server.
Author: David Spurek <dspurek@redhat.com>

View File

@ -0,0 +1,249 @@
#!/bin/bash
# vim: dict=/usr/share/beakerlib/dictionary.vim cpt=.,w,b,u,t,i,k
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# runtest.sh of /CoreOS/cyrus-sasl/Sanity/sanity-ldapdb-plugin
# Description: The ldapdb auxprop plugin provides access to credentials stored in an LDAP server.
# Author: David Spurek <dspurek@redhat.com>
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# Copyright (c) 2012 Red Hat, Inc. All rights reserved.
#
# This copyrighted material is made available to anyone wishing
# to use, modify, copy, or redistribute it subject to the terms
# and conditions of the GNU General Public License version 2.
#
# This program is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free
# Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Include Beaker environment
. /usr/bin/rhts-environment.sh || exit 1
. /usr/share/beakerlib/beakerlib.sh || exit 1
PACKAGE="cyrus-sasl"
PACKAGES=( "cyrus-sasl" \
"cyrus-sasl-devel" \
"cyrus-sasl-ldap" \
"cyrus-sasl-plain" \
"expect" \
"pam" \
"openldap" \
"openldap-clients" \
"openldap-servers" \
"cyrus-sasl-md5" )
# else branch is also relevant for Fedora
if rlIsRHEL '<6'; then
SERVICE_LDAP=ldap
else
SERVICE_LDAP=slapd
fi
ldapdb_id="sasluser"
ldapdb_pw="x"
SASL_PASSWORD="x"
SASL_USER="test"
if [ "`uname -i`" = "i386" ]; then
LIBDIR=/usr/lib
else
LIBDIR=/usr/lib64
fi
rlIsRHEL 5 && [ "`uname -i`" = "ia64" ] && LIBDIR=/usr/lib
function slapd_conf {
cat >/etc/openldap/slapd.conf<<'EOF'
include /etc/openldap/schema/core.schema
include /etc/openldap/schema/cosine.schema
include /etc/openldap/schema/inetorgperson.schema
include /etc/openldap/schema/nis.schema
allow bind_v2
pidfile /var/run/openldap/slapd.pid
argsfile /var/run/openldap/slapd.args
database bdb
suffix "dc=my-domain,dc=com"
rootdn "uid=admin,dc=my-domain,dc=com"
rootpw x
directory /var/lib/ldap
password-hash {CLEARTEXT}
authz-policy to
authz-regexp
uid=(.*),cn=.*,cn=auth
"ldap:///dc=my-domain,dc=com??sub?(uid=$1)"
index objectClass eq,pres
index ou,cn,mail,surname,givenname eq,pres,sub
index uidNumber,gidNumber,loginShell eq,pres
index uid,memberUid eq,pres,sub
index nisMapName,nisMapEntry eq,pres,sub
access to * by * write
access to * by * read
access to * by * auth
EOF
return $?
}
function data_ldif {
cat >data.ldif<<EOF
dn: dc=my-domain,dc=com
objectclass: top
objectclass: domain
dc: my-domain
dn: ou=Admins,dc=my-domain,dc=com
objectclass: top
objectclass: organizationalUnit
ou: Admins
dn: uid=$ldapdb_id,ou=People,dc=my-domain,dc=com
objectClass: person
objectClass: inetOrgPerson
userPassword: $ldapdb_pw
uid: $ldapdb_id
cn: $ldapdb_id
sn: $ldapdb_id
authzTo: ldap:///ou=People,dc=my-domain,dc=com??sub?(&(objectclass=inetOrgPerson)(uid=*))
dn: ou=People,dc=my-domain,dc=com
objectclass: top
objectclass: organizationalUnit
ou: People
dn: uid=$SASL_USER,ou=People,dc=my-domain,dc=com
objectClass: person
objectClass: inetOrgPerson
userPassword: x
uid: $SASL_USER
cn: $SASL_USER
sn: $SASL_USER
EOF
return $?
}
function sasl_client {
expect <<EOF
set timeout 30
spawn sasl2-sample-client -p 8000 -s rcmd -m PLAIN localhost
expect {
timeout {exit 1}
eof {exit 2}
-nocase "please enter an authentication id:" { puts $1 ; send "$1\r"}
}
expect {
timeout {exit 3}
eof {exit 4}
-nocase "please enter an authorization id:" { puts $1 ; send "$1\r"}
}
expect {
timeout {exit 5}
eof {exit 6}
-nocase "Password:" { puts $2 ; send "$2\r"}
}
expect {
timeout {exit 8}
-nocase "successful authentication" { expect eof ; exit 0}
-nocase "authentication failed" {exit 9}
}
expect eof
exit 0
EOF
}
# ldapdb configuration for services, in this test for sasl2-sample-server
# configuration may be for smtpd.conf,imapd.conf instead of sample.conf
function smtpd_ldapdb {
cat >$LIBDIR/sasl2/sample.conf<<EOF
pwcheck_method: auxprop
auxprop_plugin: ldapdb
mech_list: PLAIN LOGIN CRAM-MD5 DIGEST-MD5
ldapdb_uri: ldap://localhost
ldapdb_id: $ldapdb_id
ldapdb_pw: $ldapdb_pw
ldapdb_mech: DIGEST-MD5
EOF
return $?
}
rlJournalStart
rlPhaseStartSetup
for P in ${PACKAGES[@]}; do rlCheckRpm $P || rlDie "Package $P is missing"; done
rlRun "TmpDir=\$(mktemp -d)" 0 "Creating tmp directory"
rlRun "pushd $TmpDir"
rlFileBackup --clean "$LIBDIR/sasl2/sample.conf"
rlFileBackup --clean "/etc/sasldb2"
rlRun "smtpd_ldapdb" 0
rlServiceStop $SERVICE_LDAP
# Back-up.
rlFileBackup --clean /var/run/openldap
rlFileBackup --clean /var/lib/ldap && rm -rf /var/lib/ldap/*
rlFileBackup --clean /etc/openldap/
rlRun "slapd_conf" 0
rlRun "cat /etc/openldap/slapd.conf" 0
if rlIsRHEL '>=6' || rlIsFedora '>=14'; then
rm -rf /etc/openldap/slapd.d/*
slaptest -f /etc/openldap/slapd.conf -F /etc/openldap/slapd.d/
fi
rlRun "data_ldif" 0
rlRun "slapadd -l data.ldif" 0
chown -R ldap:ldap /var/lib/ldap/* && chmod -R a+rx /etc/openldap/
rlRun "restorecon -vvRF /etc/openldap/"
rlRun "service $SERVICE_LDAP start && sleep 10" 0
rlPhaseEnd
rlPhaseStartTest
rlRun "ldapsearch -LLL -H ldap://localhost -s base -b '' -x supportedSASLMechanisms" 0
rlRun "ldapsearch -H ldap://localhost -x -b 'dc=my-domain,dc=com' '(objectclass=*)'" 0 "Check ldap entries without SASL"
# this two ldapwhoami commands may be used for testing purposes
# rlRun "ldapwhoami -U $ldapdb_id -Y digest-md5" 0
# rlRun "ldapwhoami -U $ldapdb_id -X u:test@localhost -Y digest-md5" 0
# sasl sample server uses ldap sasluser as sasl bind id
# then try search user passed to sample client in ldap database
rlRun "sasl2-sample-server -p 8000 -s rcmd -m PLAIN &>sample_server.log &" 0
SASL_PID=`pgrep -f "sasl2-sample-server -p 8000 -s rcmd -m PLAIN"`
rlRun "sasl_client $SASL_USER ${SASL_PASSWORD}" 0
rlRun "sasl_client baduser ${SASL_PASSWORD}" 9
rlRun "kill $SASL_PID" 0 ; sleep 5
rlRun "cat sample_server.log" 0
rlPhaseEnd
rlPhaseStartCleanup
rlRun "service $SERVICE_LDAP stop && sleep 10" 0
rlFileRestore
rlServiceRestore $SERVICE_LDAP
rlRun "popd"
rlRun "rm -r $TmpDir" 0 "Removing tmp directory"
rlPhaseEnd
rlJournalPrintText
rlJournalEnd

18
tests/tests.yml Normal file
View File

@ -0,0 +1,18 @@
---
# This first play always runs on the local staging system
- hosts: localhost
roles:
- role: standard-test-beakerlib
tags:
- classic
tests:
- sanity-ldapdb-plugin
required_packages:
- cyrus-sasl
- cyrus-sasl-md5
- cyrus-sasl-ldap
- cyrus-sasl-plain
- cyrus-sasl-devel
- expect
- openldap-servers
- openldap-clients