From 5043ff201514cd30ce32017518ae869bd1ee35b8 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Wed, 20 Jun 2018 11:56:34 +0200
Subject: [PATCH 1/2] utils: make create_ipa_preauth_indicator() public as
 create_preauth_indicator()

---
 Makefile.am                  |  1 +
 src/providers/ipa/ipa_init.c | 59 +-----------------------------
 src/util/util.h              |  3 ++
 src/util/util_preauth.c      | 86 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 91 insertions(+), 58 deletions(-)
 create mode 100644 src/util/util_preauth.c

diff --git a/Makefile.am b/Makefile.am
index d62d135be..73e40def8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1027,6 +1027,7 @@ libsss_util_la_SOURCES = \
     src/sbus/sssd_dbus_utils.c \
     src/util/util.c \
     src/util/util_ext.c \
+    src/util/util_preauth.c \
     src/util/memory.c \
     src/util/safe-format-string.c \
     src/util/server.c \
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index 931145985..6818e2171 100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -438,63 +438,6 @@ static errno_t ipa_init_sdap_auth_ctx(TALLOC_CTX *mem_ctx,
     return EOK;
 }
 
-static void cleanup_ipa_preauth_indicator(void)
-{
-    int ret;
-
-    ret = unlink(PAM_PREAUTH_INDICATOR);
-    if (ret != EOK) {
-        ret = errno;
-        DEBUG(SSSDBG_OP_FAILURE,
-              "Failed to remove preauth indicator file [%s] %d [%s].\n",
-              PAM_PREAUTH_INDICATOR, ret, sss_strerror(ret));
-    }
-}
-
-static errno_t create_ipa_preauth_indicator(void)
-{
-    TALLOC_CTX *tmp_ctx;
-    errno_t ret;
-    int fd;
-
-    tmp_ctx = talloc_new(NULL);
-    if (tmp_ctx == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
-        return ENOMEM;
-    }
-
-    fd = open(PAM_PREAUTH_INDICATOR, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW,
-              0644);
-    if (fd < 0) {
-        if (errno != EEXIST) {
-            DEBUG(SSSDBG_OP_FAILURE,
-                  "Failed to create preauth indicator file [%s].\n",
-                  PAM_PREAUTH_INDICATOR);
-            ret = EOK;
-            goto done;
-        }
-
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "Preauth indicator file [%s] already exists. "
-              "Maybe it is left after an unplanned exit. Continuing.\n",
-              PAM_PREAUTH_INDICATOR);
-    } else {
-        close(fd);
-    }
-
-    ret = atexit(cleanup_ipa_preauth_indicator);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "atexit failed. Continuing.\n");
-    }
-
-    ret = EOK;
-
-done:
-    talloc_free(tmp_ctx);
-
-    return ret;
-}
-
 static struct sdap_ext_member_ctx *
 ipa_create_ext_members_ctx(TALLOC_CTX *mem_ctx,
                            struct ipa_id_ctx *id_ctx)
@@ -563,7 +506,7 @@ static errno_t ipa_init_auth_ctx(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-    ret = create_ipa_preauth_indicator();
+    ret = create_preauth_indicator();
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, PREAUTH_INDICATOR_ERROR);
         sss_log(SSSDBG_CRIT_FAILURE, PREAUTH_INDICATOR_ERROR);
diff --git a/src/util/util.h b/src/util/util.h
index 2785ac2e2..11220c83c 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -709,6 +709,9 @@ int sss_create_dir(const char *parent_dir_path,
 int selinux_file_context(const char *dst_name);
 int reset_selinux_file_context(void);
 
+/* from util_preauth.c */
+errno_t create_preauth_indicator(void);
+
 #ifdef SSSD_LIBEXEC_PATH
 #define P11_CHILD_LOG_FILE "p11_child"
 #define P11_CHILD_PATH SSSD_LIBEXEC_PATH"/p11_child"
diff --git a/src/util/util_preauth.c b/src/util/util_preauth.c
new file mode 100644
index 000000000..a2b0ac622
--- /dev/null
+++ b/src/util/util_preauth.c
@@ -0,0 +1,86 @@
+/*
+    SSSD
+
+    Calls to manage the preauth indicator file
+
+    Authors:
+        Sumit Bose <sbose@redhat.com>
+
+    Copyright (C) 2018 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "util/util.h"
+#include "sss_client/sss_cli.h"
+
+static void cleanup_preauth_indicator(void)
+{
+    int ret;
+
+    ret = unlink(PAM_PREAUTH_INDICATOR);
+    if (ret != EOK && errno != ENOENT) {
+        ret = errno;
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Failed to remove preauth indicator file [%s] %d [%s].\n",
+              PAM_PREAUTH_INDICATOR, ret, sss_strerror(ret));
+    }
+}
+
+errno_t create_preauth_indicator(void)
+{
+    TALLOC_CTX *tmp_ctx;
+    errno_t ret;
+    int fd;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+        return ENOMEM;
+    }
+
+    fd = open(PAM_PREAUTH_INDICATOR, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW,
+              0644);
+    if (fd < 0) {
+        if (errno != EEXIST) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "Failed to create preauth indicator file [%s].\n",
+                  PAM_PREAUTH_INDICATOR);
+            ret = EOK;
+            goto done;
+        }
+
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Preauth indicator file [%s] already exists. Continuing.\n",
+              PAM_PREAUTH_INDICATOR);
+    } else {
+        close(fd);
+    }
+
+    ret = atexit(cleanup_preauth_indicator);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "atexit failed. Continuing.\n");
+    }
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+
+    return ret;
+}

From 74de22ba4f5abe8b6b7443faad1290d056037e0c Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Fri, 22 Jun 2018 16:42:55 +0200
Subject: [PATCH 2/2] PAM: create pre-auth indicator file

If pam_cert_auth is enabled the PAM responder will automatically create
the pre-authentication indicator file to tell pam_sss to do the pre-auth
step to find out about the available authentication methods.

Related to https://pagure.io/SSSD/sssd/issue/3500
---
 src/responder/pam/pamsrv.c           |   7 ++
 src/tests/intg/Makefile.am           |   2 +
 src/tests/intg/config.py.m4          |   1 +
 src/tests/intg/test_pam_responder.py | 133 +++++++++++++++++++++++++++++++++++
 4 files changed, 143 insertions(+)
 create mode 100644 src/tests/intg/test_pam_responder.py

diff --git a/src/responder/pam/pamsrv.c b/src/responder/pam/pamsrv.c
index 2072c13fc..4ddd1d0b3 100644
--- a/src/responder/pam/pamsrv.c
+++ b/src/responder/pam/pamsrv.c
@@ -345,6 +345,13 @@ static int pam_process_init(TALLOC_CTX *mem_ctx,
                   "enabled or not.\n");
             goto done;
         }
+
+        ret = create_preauth_indicator();
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "Failed to create pre-authentication indicator file, "
+                  "Smartcard authentication might not work as expected.\n");
+        }
     }
 
     ret = EOK;
diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am
index 126ca6cda..84f6932e6 100644
--- a/src/tests/intg/Makefile.am
+++ b/src/tests/intg/Makefile.am
@@ -37,6 +37,7 @@ dist_noinst_DATA = \
     test_pysss_nss_idmap.py \
     test_infopipe.py \
     test_ssh_pubkey.py \
+    test_pam_responder.py \
     $(NULL)
 
 EXTRA_DIST = data/cwrap-dbus-system.conf.in
@@ -76,6 +77,7 @@ config.py: config.py.m4
 	   -D "sysconfdir=\`$(sysconfdir)'" \
 	   -D "nsslibdir=\`$(nsslibdir)'" \
 	   -D "dbpath=\`$(dbpath)'" \
+	   -D "pubconfpath=\`$(pubconfpath)'" \
 	   -D "pidpath=\`$(pidpath)'" \
 	   -D "logpath=\`$(logpath)'" \
 	   -D "mcpath=\`$(mcpath)'" \
diff --git a/src/tests/intg/config.py.m4 b/src/tests/intg/config.py.m4
index 04f78d869..aed81282e 100644
--- a/src/tests/intg/config.py.m4
+++ b/src/tests/intg/config.py.m4
@@ -9,6 +9,7 @@ SSSDCONFDIR = SYSCONFDIR + "/sssd"
 CONF_PATH = SSSDCONFDIR + "/sssd.conf"
 DB_PATH = "dbpath"
 PID_PATH = "pidpath"
+PUBCONF_PATH = "pubconfpath"
 PIDFILE_PATH = PID_PATH + "/sssd.pid"
 LOG_PATH = "logpath"
 MCACHE_PATH = "mcpath"
diff --git a/src/tests/intg/test_pam_responder.py b/src/tests/intg/test_pam_responder.py
new file mode 100644
index 000000000..0d524b9db
--- /dev/null
+++ b/src/tests/intg/test_pam_responder.py
@@ -0,0 +1,133 @@
+#
+# Test for the PAM responder
+#
+# Copyright (c) 2018 Red Hat, Inc.
+# Author: Sumit Bose <sbose@redhat.com>
+#
+# This is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 only
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+
+"""
+Tests for the PAM responder
+"""
+import os
+import stat
+import signal
+import errno
+import subprocess
+import time
+import pytest
+
+import config
+
+from .util import unindent
+
+
+
+def format_pam_cert_auth_conf():
+    """Format a basic SSSD configuration"""
+    return unindent("""\
+        [sssd]
+        domains = auth_only
+        services = pam
+
+        [nss]
+
+        [pam]
+        pam_cert_auth = True
+        debug_level = 10
+
+        [domain/auth_only]
+        id_provider = ldap
+        auth_provider = ldap
+        chpass_provider = ldap
+        access_provider = ldap
+    """).format(**locals())
+
+
+def create_conf_file(contents):
+    """Create sssd.conf with specified contents"""
+    conf = open(config.CONF_PATH, "w")
+    conf.write(contents)
+    conf.close()
+    os.chmod(config.CONF_PATH, stat.S_IRUSR | stat.S_IWUSR)
+
+
+def create_conf_fixture(request, contents):
+    """
+    Create sssd.conf with specified contents and add teardown for removing it
+    """
+    create_conf_file(contents)
+
+    def cleanup_conf_file():
+        """Remove sssd.conf, if it exists"""
+        if os.path.lexists(config.CONF_PATH):
+            os.unlink(config.CONF_PATH)
+
+    request.addfinalizer(cleanup_conf_file)
+
+
+def create_sssd_process():
+    """Start the SSSD process"""
+    if subprocess.call(["sssd", "-D", "-f"]) != 0:
+        raise Exception("sssd start failed")
+
+
+def cleanup_sssd_process():
+    """Stop the SSSD process and remove its state"""
+    try:
+        with open(config.PIDFILE_PATH, "r") as pid_file:
+            pid = int(pid_file.read())
+        os.kill(pid, signal.SIGTERM)
+        while True:
+            try:
+                os.kill(pid, signal.SIGCONT)
+            except OSError as ex:
+                break
+            time.sleep(1)
+    except OSError as ex:
+        pass
+    for path in os.listdir(config.DB_PATH):
+        os.unlink(config.DB_PATH + "/" + path)
+    for path in os.listdir(config.MCACHE_PATH):
+        os.unlink(config.MCACHE_PATH + "/" + path)
+
+    # make sure that the indicator file is removed during shutdown
+    try:
+        assert not os.stat(config.PUBCONF_PATH + "/pam_preauth_available")
+    except OSError as ex:
+        if ex.errno != errno.ENOENT:
+            raise ex
+
+
+
+
+def create_sssd_fixture(request):
+    """Start SSSD and add teardown for stopping it and removing its state"""
+    create_sssd_process()
+    request.addfinalizer(cleanup_sssd_process)
+
+
+@pytest.fixture
+def simple_pam_cert_auth(request):
+    """Setup SSSD with pam_cert_auth=True"""
+    conf = format_pam_cert_auth_conf()
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+    return None
+
+
+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)
