From 6155a267d399d111706c4496c3877e216936c3b2 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Fri, 7 Sep 2018 22:26:21 +0200 Subject: [PATCH 19/19] intg: add Smartcard authentication tests Two test for Smartcard authentication of a local user, i.e. a user managed by the files provider, are added. One for a successful authentication, the other for a failed authentication with a wrong PIN. Related to https://pagure.io/SSSD/sssd/issue/3500 Reviewed-by: Jakub Hrozek (cherry picked from commit 657f3b89bca9adfb13f0867c91f1d76845d2d6dd) --- configure.ac | 1 + contrib/ci/deps.sh | 2 + contrib/sssd.spec.in | 1 + src/external/cwrap.m4 | 5 ++ src/external/intgcheck.m4 | 1 + src/tests/intg/Makefile.am | 24 ++++++- src/tests/intg/test_pam_responder.py | 131 ++++++++++++++++++++++++++++++++--- 7 files changed, 155 insertions(+), 10 deletions(-) diff --git a/configure.ac b/configure.ac index 1aac65f4d85b9974adc5ba3e5196b00be5d279f1..891610e14490a4e78e1e95e63c18d9c6a9a8afb4 100644 --- a/configure.ac +++ b/configure.ac @@ -488,6 +488,7 @@ AM_CONDITIONAL([HAVE_CHECK], [test x$have_check != x]) AM_CHECK_CMOCKA AM_CHECK_UID_WRAPPER AM_CHECK_NSS_WRAPPER +AM_CHECK_PAM_WRAPPER AM_CHECK_TEST_CA # Check if the user wants SSSD to be compiled with systemtap probes diff --git a/contrib/ci/deps.sh b/contrib/ci/deps.sh index 5906e5332ba99ce137174f3630e269b8c561f996..c04c7aab03f78185d96000142a71001ab52a66a7 100644 --- a/contrib/ci/deps.sh +++ b/contrib/ci/deps.sh @@ -46,6 +46,7 @@ if [[ "$DISTRO_BRANCH" == -redhat-* ]]; then pyldb rpm-build uid_wrapper + pam_wrapper python-requests curl-devel krb5-server @@ -117,6 +118,7 @@ if [[ "$DISTRO_BRANCH" == -debian-* ]]; then fakeroot libnss-wrapper libuid-wrapper + libpam-wrapper python-pytest python-ldap python-ldb diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in index 5ebd51f41f60cfdcd1d65c1c58dea4b296fd1ed1..26fae6d68dbe4d14f85ddf34bb4871bc34db3b3d 100644 --- a/contrib/sssd.spec.in +++ b/contrib/sssd.spec.in @@ -237,6 +237,7 @@ BuildRequires: selinux-policy-targeted BuildRequires: libcmocka-devel >= 1.0.0 BuildRequires: uid_wrapper BuildRequires: nss_wrapper +BuildRequires: pam_wrapper # Test CA requires openssl independent if SSSD is build with NSS or openssl, # openssh is needed for ssh-keygen and NSS builds need nss-tools for certutil. diff --git a/src/external/cwrap.m4 b/src/external/cwrap.m4 index b8489cc765f34f3bc6ad4e4b7e69626f6ea8060e..6e3487c13f734e311a2262b0f71495167489c710 100644 --- a/src/external/cwrap.m4 +++ b/src/external/cwrap.m4 @@ -28,3 +28,8 @@ AC_DEFUN([AM_CHECK_NSS_WRAPPER], [ AM_CHECK_WRAPPER(nss_wrapper, HAVE_NSS_WRAPPER) ]) + +AC_DEFUN([AM_CHECK_PAM_WRAPPER], +[ + AM_CHECK_WRAPPER(pam_wrapper, HAVE_PAM_WRAPPER) +]) diff --git a/src/external/intgcheck.m4 b/src/external/intgcheck.m4 index 60a7bf306ddefd748cf9bac62d3767e7512b6d64..c14f66978b8087586cf1c5ac2d60a07e4f90d45d 100644 --- a/src/external/intgcheck.m4 +++ b/src/external/intgcheck.m4 @@ -22,6 +22,7 @@ AC_DEFUN([SSS_ENABLE_INTGCHECK_REQS], [ if test x"$enable_intgcheck_reqs" = xyes; then SSS_INTGCHECK_REQ([HAVE_UID_WRAPPER], [uid_wrapper]) SSS_INTGCHECK_REQ([HAVE_NSS_WRAPPER], [nss_wrapper]) + SSS_INTGCHECK_REQ([HAVE_PAM_WRAPPER], [pam_wrapper]) SSS_INTGCHECK_REQ([HAVE_SLAPD], [slapd]) SSS_INTGCHECK_REQ([HAVE_LDAPMODIFY], [ldapmodify]) SSS_INTGCHECK_REQ([HAVE_FAKEROOT], [fakeroot]) diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am index 6f7605bd4edbf26ef3ce97acea74fc92216eede9..bb3a7f01ae4f79fa05cd661993e8f9872ecd0450 100644 --- a/src/tests/intg/Makefile.am +++ b/src/tests/intg/Makefile.am @@ -105,13 +105,29 @@ passwd: root group: echo "root:x:0:" > $@ +PAM_SERVICE_DIR=pam_service_dir +pam_sss_service: + $(MKDIR_P) $(PAM_SERVICE_DIR) + echo "auth required $(DESTDIR)$(pammoddir)/pam_sss.so" > $(PAM_SERVICE_DIR)/$@ + echo "account required $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@ + echo "password required $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@ + echo "session required $(DESTDIR)$(pammoddir)/pam_sss.so" >> $(PAM_SERVICE_DIR)/$@ + CLEANFILES=config.py config.pyc passwd group clean-local: rm -Rf root rm -f $(builddir)/cwrap-dbus-system.conf -intgcheck-installed: config.py passwd group +if HAVE_NSS +PAM_CERT_DB_PATH="sql:$(DESTDIR)$(sysconfdir)/pki/nssdb" +SOFTHSM2_CONF="" +else +PAM_CERT_DB_PATH="$(abs_builddir)/../test_CA/SSSD_test_CA.pem" +SOFTHSM2_CONF="$(abs_builddir)/../test_CA/softhsm2_one.conf" +endif + +intgcheck-installed: config.py passwd group pam_sss_service pipepath="$(DESTDIR)$(pipepath)"; \ if test $${#pipepath} -gt 80; then \ echo "error: Pipe directory path too long," \ @@ -131,12 +147,18 @@ intgcheck-installed: config.py passwd group LDB_MODULES_PATH="$(DESTDIR)$(ldblibdir)" \ NON_WRAPPED_UID=$$(id -u) \ LD_PRELOAD="$(libdir)/getsockopt_wrapper.so:$$nss_wrapper:$$uid_wrapper" \ + LD_LIBRARY_PATH="$$LD_LIBRARY_PATH:$(DESTDIR)$(nsslibdir)" \ NSS_WRAPPER_PASSWD="$(abs_builddir)/passwd" \ NSS_WRAPPER_GROUP="$(abs_builddir)/group" \ NSS_WRAPPER_MODULE_SO_PATH="$(DESTDIR)$(nsslibdir)/libnss_sss.so.2" \ NSS_WRAPPER_MODULE_FN_PREFIX="sss" \ UID_WRAPPER=1 \ UID_WRAPPER_ROOT=1 \ + PAM_WRAPPER=0 \ + PAM_WRAPPER_SERVICE_DIR="$(abs_builddir)/$(PAM_SERVICE_DIR)" \ + PAM_WRAPPER_PATH=$$(pkg-config --libs pam_wrapper) \ + PAM_CERT_DB_PATH=$(PAM_CERT_DB_PATH) \ + SOFTHSM2_CONF=$(SOFTHSM2_CONF) \ DBUS_SOCK_DIR="$(DESTDIR)$(runstatedir)/dbus/" \ DBUS_SESSION_BUS_ADDRESS="unix:path=$$DBUS_SOCK_DIR/fake_socket" \ DBUS_SYSTEM_BUS_ADDRESS="unix:path=$$DBUS_SOCK_DIR/system_bus_socket" \ diff --git a/src/tests/intg/test_pam_responder.py b/src/tests/intg/test_pam_responder.py index cf6fff2db7ba9c9c69e1dd9abe5663a02cedd72e..c6d048cd342838fe312287eaffff734e30ba9e1c 100644 --- a/src/tests/intg/test_pam_responder.py +++ b/src/tests/intg/test_pam_responder.py @@ -27,31 +27,44 @@ import signal import errno import subprocess import time -import pytest +import shutil import config -from util import unindent +import pytest +from intg.util import unindent +from intg.files_ops import passwd_ops_setup -def format_pam_cert_auth_conf(): +USER1 = dict(name='user1', passwd='x', uid=10001, gid=20001, + gecos='User for tests', + dir='/home/user1', + shell='/bin/bash') + + +def format_pam_cert_auth_conf(config): """Format a basic SSSD configuration""" return unindent("""\ [sssd] + debug_level = 10 domains = auth_only - services = pam + services = pam, nss [nss] + debug_level = 10 [pam] pam_cert_auth = True + pam_p11_allowed_services = +pam_sss_service + pam_cert_db_path = {config.PAM_CERT_DB_PATH} debug_level = 10 [domain/auth_only] - id_provider = ldap - auth_provider = ldap - chpass_provider = ldap - access_provider = ldap + debug_level = 10 + id_provider = files + + [certmap/auth_only/user1] + matchrule = .*CN=SSSD test cert 0001.* """).format(**locals()) @@ -79,6 +92,8 @@ def create_conf_fixture(request, contents): def create_sssd_process(): """Start the SSSD process""" + os.environ["SSS_FILES_PASSWD"] = os.environ["NSS_WRAPPER_PASSWD"] + os.environ["SSS_FILES_GROUP"] = os.environ["NSS_WRAPPER_GROUP"] if subprocess.call(["sssd", "-D", "-f"]) != 0: raise Exception("sssd start failed") @@ -116,12 +131,41 @@ def create_sssd_fixture(request): request.addfinalizer(cleanup_sssd_process) +def create_nssdb(): + os.mkdir(config.SYSCONFDIR + "/pki") + os.mkdir(config.SYSCONFDIR + "/pki/nssdb") + if subprocess.call(["certutil", "-N", "-d", + "sql:" + config.SYSCONFDIR + "/pki/nssdb/", + "--empty-password"]) != 0: + raise Exception("certutil failed") + + pkcs11_txt = open(config.SYSCONFDIR + "/pki/nssdb/pkcs11.txt", "w") + pkcs11_txt.write("library=libsoftokn3.so\nname=soft\n" + + "parameters=configdir='sql:" + config.ABS_BUILDDIR + + "/../test_CA/p11_nssdb' " + + "dbSlotDescription='SSSD Test Slot' " + + "dbTokenDescription='SSSD Test Token' " + + "secmod='secmod.db' flags=readOnly)\n\n") + pkcs11_txt.close() + + +def cleanup_nssdb(): + shutil.rmtree(config.SYSCONFDIR + "/pki") + + +def create_nssdb_fixture(request): + create_nssdb() + request.addfinalizer(cleanup_nssdb) + + @pytest.fixture def simple_pam_cert_auth(request): """Setup SSSD with pam_cert_auth=True""" - conf = format_pam_cert_auth_conf() + config.PAM_CERT_DB_PATH = os.environ['PAM_CERT_DB_PATH'] + conf = format_pam_cert_auth_conf(config) create_conf_fixture(request, conf) create_sssd_fixture(request) + create_nssdb_fixture(request) return None @@ -129,3 +173,72 @@ def test_preauth_indicator(simple_pam_cert_auth): """Check if preauth indicator file is created""" statinfo = os.stat(config.PUBCONF_PATH + "/pam_preauth_available") assert stat.S_ISREG(statinfo.st_mode) + + +@pytest.fixture +def pam_wrapper_setup(request): + pwrap_runtimedir = os.getenv("PAM_WRAPPER_SERVICE_DIR") + if pwrap_runtimedir is None: + raise ValueError("The PAM_WRAPPER_SERVICE_DIR variable is unset\n") + + +def test_sc_auth_wrong_pin(simple_pam_cert_auth, pam_wrapper_setup, + passwd_ops_setup): + + passwd_ops_setup.useradd(**USER1) + current_env = os.environ.copy() + current_env['PAM_WRAPPER'] = "1" + current_env['SSSD_INTG_PEER_UID'] = "0" + current_env['SSSD_INTG_PEER_GID'] = "0" + current_env['LD_PRELOAD'] += ':' + os.environ['PAM_WRAPPER_PATH'] + + sssctl = subprocess.Popen(["sssctl", "user-checks", "user1", + "--action=auth", "--service=pam_sss_service"], + universal_newlines=True, + env=current_env, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + try: + out, err = sssctl.communicate(input="111") + except: + sssctl.kill() + out, err = sssctl.communicate() + + sssctl.stdin.close() + sssctl.stdout.close() + + if sssctl.wait() != 0: + raise Exception("sssctl failed") + + assert err.find("pam_authenticate for user [user1]: " + + "Authentication failure") != -1 + + +def test_sc_auth(simple_pam_cert_auth, pam_wrapper_setup, passwd_ops_setup): + + passwd_ops_setup.useradd(**USER1) + current_env = os.environ.copy() + current_env['PAM_WRAPPER'] = "1" + current_env['SSSD_INTG_PEER_UID'] = "0" + current_env['SSSD_INTG_PEER_GID'] = "0" + current_env['LD_PRELOAD'] += ':' + os.environ['PAM_WRAPPER_PATH'] + + sssctl = subprocess.Popen(["sssctl", "user-checks", "user1", + "--action=auth", "--service=pam_sss_service"], + universal_newlines=True, + env=current_env, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + try: + out, err = sssctl.communicate(input="123456") + except: + sssctl.kill() + out, err = sssctl.communicate() + + sssctl.stdin.close() + sssctl.stdout.close() + + if sssctl.wait() != 0: + raise Exception("sssctl failed") + + assert err.find("pam_authenticate for user [user1]: Success") != -1 -- 2.14.4