sbonazzo / rpms / cyrus-sasl

Forked from rpms/cyrus-sasl 2 years ago
Clone

Blame SOURCES/cyrus-sasl-2.1.27-Add-basic-test-infrastructure.patch

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