SHA256
3
0
forked from pool/pam
pam/pam-git.diff

1673 lines
57 KiB
Diff
Raw Normal View History

diff --git a/README b/README
index 21af8c4c..aa99927e 100644
--- a/README
+++ b/README
@@ -6,7 +6,7 @@ NOTES:
How to use it is as follows:
-Please look at the ci/install_dependencies.sh for the necessary
+Please look at the ci/install-dependencies.sh for the necessary
prerequisite packages to be able to build the Linux-PAM. The script
is targeted at Debian based Linux distributions so the package
names and availability might differ on other distributions.
diff --git a/configure.ac b/configure.ac
index c06bc7dd..639fc1ad 100644
--- a/configure.ac
+++ b/configure.ac
@@ -259,6 +259,8 @@ AC_MSG_RESULT([Defining \$ISA to "$ISA"])
AC_ARG_ENABLE(sconfigdir,
AS_HELP_STRING([--enable-sconfigdir=DIR],[path to module conf files @<:@default=$sysconfdir/security@:>@]),
SCONFIGDIR=$enableval, SCONFIGDIR=$sysconfdir/security)
+AC_DEFINE_UNQUOTED([SCONFIGDIR], ["$SCONFIGDIR"],
+ [Directory for PAM modules system configuration files])
AC_SUBST(SCONFIGDIR)
AC_ARG_ENABLE(pamlocking,
@@ -507,9 +509,11 @@ AC_ARG_ENABLE([vendordir],
if test -n "$enable_vendordir"; then
AC_DEFINE_UNQUOTED([VENDORDIR], ["$enable_vendordir"],
[Directory for distribution provided configuration files])
- STRINGPARAM_VENDORDIR="--stringparam vendordir '$enable_vendordir'"
+ AC_DEFINE_UNQUOTED([VENDOR_SCONFIGDIR], ["$enable_vendordir/security"],
+ [Directory for PAM modules distribution provided configuration files])
+ STRINGPARAM_VENDORDIR="--stringparam vendordir '$enable_vendordir' --stringparam profile.condition 'with_vendordir'"
else
- STRINGPARAM_VENDORDIR="--stringparam vendordir '<vendordir>'"
+ STRINGPARAM_VENDORDIR="--stringparam profile.condition 'without_vendordir'"
fi
AC_SUBST([STRINGPARAM_VENDORDIR])
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
index 78c891df..c6fd73db 100644
--- a/doc/man/Makefile.am
+++ b/doc/man/Makefile.am
@@ -43,7 +43,7 @@ XMLS = pam.3.xml pam.8.xml \
pam_item_types_std.inc.xml pam_item_types_ext.inc.xml \
pam.conf-desc.xml pam.conf-dir.xml pam.conf-syntax.xml \
misc_conv.3.xml pam_misc_paste_env.3.xml pam_misc_drop_env.3.xml \
- pam_misc_setenv.3.xml
+ pam_misc_setenv.3.xml pam_xauth_data.3.xml
if ENABLE_REGENERATE_MAN
PAM.8: pam.8
diff --git a/doc/man/pam.8.xml b/doc/man/pam.8.xml
index 464af0e5..8eef665a 100644
--- a/doc/man/pam.8.xml
+++ b/doc/man/pam.8.xml
@@ -158,15 +158,14 @@ closing hook for modules to affect the services available to a user.</para>
</para>
</listitem>
</varlistentry>
- <varlistentry>
+ <varlistentry condition="with_vendordir">
<term><filename>%vendordir%/pam.d</filename></term>
<listitem>
<para>
the <emphasis remap='B'>Linux-PAM</emphasis> vendor configuration
directory. Files in <filename>/etc/pam.d</filename> and
<filename>/usr/lib/pam.d</filename> override files with the same
- name in this directory. Only available if Linux-PAM was compiled
- with vendordir enabled.
+ name in this directory.
</para>
</listitem>
</varlistentry>
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 722ec686..c4c3c261 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -11,4 +11,4 @@ AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
LDADD = $(top_builddir)/libpam/libpam.la \
$(top_builddir)/libpam_misc/libpam_misc.la
-noinst_PROGRAMS = xsh vpass blank check_user
+noinst_PROGRAMS = xsh vpass blank check_user tty_conv
diff --git a/examples/tty_conv.c b/examples/tty_conv.c
new file mode 100644
index 00000000..23f0684c
--- /dev/null
+++ b/examples/tty_conv.c
@@ -0,0 +1,177 @@
+/* PlanC (hubenchang0515@outlook.com) -- an example application
+ * that implements a custom conversation */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <termio.h>
+#include <security/pam_appl.h>
+
+/***************************************
+ * @brief echo off/on
+ * @param[in] fd file descriptor
+ * @param[in] off 1 - echo off0 - echo on
+ ***************************************/
+static void echoOff(int fd, int off)
+{
+ struct termio tty;
+ if (ioctl(fd, TCGETA, &tty) < 0)
+ {
+ fprintf(stderr, "TCGETA failed: %s\n", strerror(errno));
+ return;
+ }
+
+ if (off)
+ {
+ tty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+ if (ioctl(fd, TCSETAF, &tty) < 0)
+ {
+ fprintf(stderr, "TCSETAF failed: %s\n", strerror(errno));
+ }
+ }
+ else
+ {
+ tty.c_lflag |= (ECHO | ECHOE | ECHOK | ECHONL);
+ if (ioctl(fd, TCSETAW, &tty) < 0)
+ {
+ fprintf(stderr, "TCSETAW failed: %s\n", strerror(errno));
+ }
+ }
+}
+
+/***************************************
+ * @brief echo off stdin
+ ***************************************/
+static void echoOffStdin(void)
+{
+ echoOff(fileno(stdin), 1);
+}
+
+/***************************************
+ * @brief echo on stdin
+ ***************************************/
+static void echoOnStdin(void)
+{
+ echoOff(fileno(stdin), 0);
+}
+
+/***************************************
+ * @brief read a line input
+ * @return the input string
+ ***************************************/
+static char *readline(void)
+{
+ char input[PAM_MAX_RESP_SIZE];
+ int i;
+
+ flockfile(stdin);
+ for (i = 0; i < PAM_MAX_RESP_SIZE; i++)
+ {
+ int ch = getchar_unlocked();
+ if (ch == '\n' || ch == '\r' ||ch == EOF)
+ break;
+ input[i] = ch;
+ }
+ funlockfile(stdin);
+ input[i] = '\0';
+
+ return (strdup(input));
+}
+
+/**************************************************
+ * @brief callback of PAM conversation
+ * @param[in] num_msg the count of message
+ * @param[in] msg PAM message
+ * @param[out] resp our response
+ * @param[in] appdata_ptr custom data passed by struct pam_conv.appdata_ptr
+ * @return state
+ **************************************************/
+static int conversation(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
+{
+ (void)(appdata_ptr);
+ int i;
+
+ /* check the count of message */
+ if (num_msg <= 0 || num_msg >= PAM_MAX_MSG_SIZE)
+ {
+ fprintf(stderr, "invalid num_msg(%d)\n", num_msg);
+ return PAM_CONV_ERR;
+ }
+
+ /* alloc memory for response */
+ if ((resp[0] = malloc(num_msg * sizeof(struct pam_response))) == NULL)
+ {
+ fprintf(stderr, "bad alloc\n");
+ return PAM_BUF_ERR;
+ }
+
+ /* response for message */
+ for (i = 0; i < num_msg; i++)
+ {
+ const struct pam_message *m = *msg + i;
+ struct pam_response *r = *resp + i;
+ r->resp_retcode = 0; /* currently un-used, zero expected */
+ switch (m->msg_style)
+ {
+ case PAM_PROMPT_ECHO_OFF: /* get the input with echo off, like the password */
+ printf("%s", m->msg);
+ echoOffStdin();
+ r->resp = readline();
+ echoOnStdin();
+ printf("\n");
+ break;
+
+ case PAM_PROMPT_ECHO_ON: /* get the input with echo on, like the username */
+ printf("%s", m->msg);
+ r->resp = readline();
+ break;
+
+ case PAM_TEXT_INFO: /* normal info */
+ printf("%s\n", m->msg);
+ break;
+
+ case PAM_ERROR_MSG: /* error info */
+ fprintf(stderr, "%s\n", m->msg);
+ break;
+
+ default:
+ fprintf(stderr, "unexpected msg_style: %d\n", m->msg_style);
+ break;
+ }
+ }
+ return PAM_SUCCESS;
+}
+
+int main(void)
+{
+ struct pam_conv pam_conv = {conversation, NULL};
+ pam_handle_t *pamh;
+
+ /* echo on while exist, like Ctrl+C on input password */
+ atexit(echoOnStdin);
+
+ if (PAM_SUCCESS != pam_start("login", NULL, &pam_conv, &pamh))
+ {
+ fprintf(stderr, "pam_start failed\n");
+ return EXIT_FAILURE;
+ }
+
+ if (PAM_SUCCESS != pam_authenticate(pamh, 0))
+ {
+ fprintf(stderr, "pam_authenticate failed\n");
+ pam_end(pamh, 0);
+ return EXIT_FAILURE;
+ }
+
+ if (PAM_SUCCESS != pam_acct_mgmt(pamh, 0))
+ {
+ fprintf(stderr, "pam_acct_mgmt failed\n");
+ pam_end(pamh, 0);
+ return EXIT_FAILURE;
+ }
+
+ pam_end(pamh, 0);
+ return EXIT_SUCCESS;
+}
diff --git a/examples/xsh.c b/examples/xsh.c
index ef4dca0c..5b34fc17 100644
--- a/examples/xsh.c
+++ b/examples/xsh.c
@@ -80,7 +80,7 @@ int main(int argc, char **argv)
tty = ttyname(fileno(stdin));
if (tty) {
retcode = pam_set_item(pamh, PAM_TTY, tty);
- bail_out(pamh,1,retcode,"pam_set_item(PAM_RHOST)");
+ bail_out(pamh,1,retcode,"pam_set_item(PAM_TTY)");
}
}
diff --git a/libpam/Makefile.am b/libpam/Makefile.am
index 55222afc..389d5d02 100644
--- a/libpam/Makefile.am
+++ b/libpam/Makefile.am
@@ -21,7 +21,7 @@ noinst_HEADERS = pam_prelude.h pam_private.h pam_tokens.h \
include/pam_inline.h include/test_assert.h
libpam_la_LDFLAGS = -no-undefined -version-info 85:1:85
-libpam_la_LIBADD = @LIBAUDIT@ $(LIBPRELUDE_LIBS) $(ECONF_LIBS) @LIBDL@
+libpam_la_LIBADD = @LIBAUDIT@ $(LIBPRELUDE_LIBS) $(ECONF_LIBS) @LIBDL@ @LTLIBINTL@
if HAVE_VERSIONING
libpam_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libpam.map
diff --git a/modules/pam_access/Makefile.am b/modules/pam_access/Makefile.am
index 5723dd59..b9fbefdb 100644
--- a/modules/pam_access/Makefile.am
+++ b/modules/pam_access/Makefile.am
@@ -18,8 +18,7 @@ securelibdir = $(SECUREDIR)
secureconfdir = $(SCONFIGDIR)
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
- -DPAM_ACCESS_CONFIG=\"$(SCONFIGDIR)/access.conf\" \
- -DACCESS_CONF_GLOB=\"$(SCONFIGDIR)/access.d/*.conf\" $(WARN_CFLAGS)
+ $(WARN_CFLAGS)
AM_LDFLAGS = -no-undefined -avoid-version -module
if HAVE_VERSIONING
AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
diff --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c
index 277192b9..0d033aa2 100644
--- a/modules/pam_access/pam_access.c
+++ b/modules/pam_access/pam_access.c
@@ -56,6 +56,9 @@
#include "pam_cc_compat.h"
#include "pam_inline.h"
+#define PAM_ACCESS_CONFIG (SCONFIGDIR "/access.conf")
+#define ACCESS_CONF_GLOB (SCONFIGDIR "/access.d/*.conf")
+
/* login_access.c from logdaemon-5.6 with several changes by A.Nogin: */
/*
diff --git a/modules/pam_env/Makefile.am b/modules/pam_env/Makefile.am
index c66112d6..beca8e1a 100644
--- a/modules/pam_env/Makefile.am
+++ b/modules/pam_env/Makefile.am
@@ -18,7 +18,7 @@ securelibdir = $(SECUREDIR)
secureconfdir = $(SCONFIGDIR)
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
- -DDEFAULT_CONF_FILE=\"$(SCONFIGDIR)/pam_env.conf\" $(WARN_CFLAGS)
+ $(WARN_CFLAGS)
AM_LDFLAGS = -no-undefined -avoid-version -module
if HAVE_VERSIONING
AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
diff --git a/modules/pam_env/pam_env.c b/modules/pam_env/pam_env.c
index f5f8cead..c03ec3a3 100644
--- a/modules/pam_env/pam_env.c
+++ b/modules/pam_env/pam_env.c
@@ -41,6 +41,8 @@ typedef struct var {
char *override;
} VAR;
+#define DEFAULT_CONF_FILE (SCONFIGDIR "/pam_env.conf")
+
#define BUF_SIZE 8192
#define MAX_ENV 8192
diff --git a/modules/pam_faillock/Makefile.am b/modules/pam_faillock/Makefile.am
index 44a49660..16d9f8bc 100644
--- a/modules/pam_faillock/Makefile.am
+++ b/modules/pam_faillock/Makefile.am
@@ -15,7 +15,7 @@ endif
XMLS = README.xml pam_faillock.8.xml faillock.8.xml faillock.conf.5.xml
dist_check_SCRIPTS = tst-pam_faillock
-TESTS = $(dist_check_SCRIPTS)
+TESTS = $(dist_check_SCRIPTS) $(check_PROGRAMS)
securelibdir = $(SECUREDIR)
secureconfdir = $(SCONFIGDIR)
@@ -33,6 +33,9 @@ if HAVE_VERSIONING
pam_faillock_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
endif
+check_PROGRAMS = tst-pam_faillock-retval
+tst_pam_faillock_retval_LDADD = $(top_builddir)/libpam/libpam.la
+
faillock_LDFLAGS = @EXE_LDFLAGS@
faillock_LDADD = $(top_builddir)/libpam/libpam.la $(LIBAUDIT)
diff --git a/modules/pam_faillock/faillock.h b/modules/pam_faillock/faillock.h
index b22a9dfb..c3f157ef 100644
--- a/modules/pam_faillock/faillock.h
+++ b/modules/pam_faillock/faillock.h
@@ -67,7 +67,10 @@ struct tally_data {
};
#define FAILLOCK_DEFAULT_TALLYDIR "/var/run/faillock"
-#define FAILLOCK_DEFAULT_CONF "/etc/security/faillock.conf"
+#define FAILLOCK_DEFAULT_CONF SCONFIGDIR "/faillock.conf"
+#ifdef VENDOR_SCONFIGDIR
+#define VENDOR_FAILLOCK_DEFAULT_CONF VENDOR_SCONFIGDIR "/faillock.conf"
+#endif
int open_tally(const char *dir, const char *user, uid_t uid, int create);
int read_tally(int fd, struct tally_data *tallies);
diff --git a/modules/pam_faillock/main.c b/modules/pam_faillock/main.c
index f62e1bb2..ea6329ca 100644
--- a/modules/pam_faillock/main.c
+++ b/modules/pam_faillock/main.c
@@ -174,6 +174,11 @@ do_user(struct options *opts, const char *user)
time_t when = tallies.records[i].time;
tm = localtime(&when);
+ if(tm == NULL) {
+ fprintf(stderr, "%s: Invalid timestamp in the tally record\n",
+ opts->progname);
+ continue;
+ }
strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm);
printf("%-19s %-5s %-52.52s %s\n", timebuf,
status & TALLY_STATUS_RHOST ? "RHOST" : (status & TALLY_STATUS_TTY ? "TTY" : "SVC"),
diff --git a/modules/pam_faillock/pam_faillock.8.xml b/modules/pam_faillock/pam_faillock.8.xml
index 58c16442..79bcbbd0 100644
--- a/modules/pam_faillock/pam_faillock.8.xml
+++ b/modules/pam_faillock/pam_faillock.8.xml
@@ -134,10 +134,17 @@
<option>conf=/path/to/config-file</option>
</term>
<listitem>
- <para>
+ <para condition="without_vendordir">
Use another configuration file instead of the default
<filename>/etc/security/faillock.conf</filename>.
</para>
+ <para condition="with_vendordir">
+ Use another configuration file instead of the default
+ which is to use the file
+ <filename>/etc/security/faillock.conf</filename> or,
+ if that one is not present, the file
+ <filename>%vendordir%/security/faillock.conf</filename>.
+ </para>
</listitem>
</varlistentry>
</variablelist>
@@ -328,6 +335,15 @@ session required pam_selinux.so open
<para>the config file for pam_faillock options</para>
</listitem>
</varlistentry>
+ <varlistentry condition="with_vendordir">
+ <term><filename>%vendordir%/security/faillock.conf</filename></term>
+ <listitem>
+ <para>
+ the config file for pam_faillock options. It will be used if
+ <filename>/etc/security/faillock.conf</filename> does not exist.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/modules/pam_faillock/pam_faillock.c b/modules/pam_faillock/pam_faillock.c
index 8328fbae..932d4281 100644
--- a/modules/pam_faillock/pam_faillock.c
+++ b/modules/pam_faillock/pam_faillock.c
@@ -192,6 +192,15 @@ read_config_file(pam_handle_t *pamh, struct options *opts, const char *cfgfile)
char linebuf[FAILLOCK_CONF_MAX_LINELEN+1];
f = fopen(cfgfile, "r");
+#ifdef VENDOR_FAILLOCK_DEFAULT_CONF
+ if (f == NULL && errno == ENOENT && cfgfile == default_faillock_conf) {
+ /*
+ * If the default configuration file in /etc does not exist,
+ * try the vendor configuration file as fallback.
+ */
+ f = fopen(VENDOR_FAILLOCK_DEFAULT_CONF, "r");
+ }
+#endif
if (f == NULL) {
/* ignore non-existent default config file */
if (errno == ENOENT && cfgfile == default_faillock_conf)
diff --git a/modules/pam_faillock/tst-pam_faillock-retval.c b/modules/pam_faillock/tst-pam_faillock-retval.c
new file mode 100644
index 00000000..133026cb
--- /dev/null
+++ b/modules/pam_faillock/tst-pam_faillock-retval.c
@@ -0,0 +1,119 @@
+/*
+ * Check pam_faillock return values.
+ */
+
+#include "test_assert.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <security/pam_appl.h>
+
+#define MODULE_NAME "pam_faillock"
+#define TEST_NAME "tst-" MODULE_NAME "-retval"
+
+static const char service_file[] = TEST_NAME ".service";
+static const char config_filename[] = TEST_NAME ".conf";
+static const char user_name[] = "root";
+static struct pam_conv conv;
+
+int
+main(void)
+{
+ pam_handle_t *pamh = NULL;
+ FILE *fp;
+ char cwd[PATH_MAX];
+
+ ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd)));
+
+ ASSERT_NE(NULL, fp = fopen(config_filename, "w"));
+ ASSERT_LT(0, fprintf(fp,
+ "deny = 2\n"
+ "unlock_time = 5\n"
+ "root_unlock_time = 5\n"));
+ ASSERT_EQ(0, fclose(fp));
+
+ /* root has access */
+ ASSERT_NE(NULL, fp = fopen(service_file, "w"));
+ ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n"
+ "auth required %s/../pam_permit/.libs/pam_permit.so\n"
+ "auth required %s/.libs/%s.so authsucc even_deny_root dir=%s conf=%s\n"
+ "account required %s/.libs/%s.so dir=%s\n"
+ "password required %s/.libs/%s.so dir=%s\n"
+ "session required %s/.libs/%s.so dir=%s\n",
+ cwd,
+ cwd, MODULE_NAME, cwd, config_filename,
+ cwd, MODULE_NAME, cwd,
+ cwd, MODULE_NAME, cwd,
+ cwd, MODULE_NAME, cwd));
+
+ ASSERT_EQ(0, fclose(fp));
+
+ ASSERT_EQ(PAM_SUCCESS,
+ pam_start_confdir(service_file, user_name, &conv, ".", &pamh));
+ ASSERT_NE(NULL, pamh);
+ ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0));
+ ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0));
+ ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0));
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0));
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0));
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0));
+ ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
+ ASSERT_EQ(0, unlink(service_file));
+ pamh = NULL;
+
+ /* root tries to login 2 times without success*/
+ ASSERT_NE(NULL, fp = fopen(service_file, "w"));
+ ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n"
+ "auth requisite %s/.libs/%s.so dir=%s preauth even_deny_root conf=%s\n"
+ "auth [success=1 default=bad] %s/../pam_debug/.libs/pam_debug.so auth=perm_denied cred=success\n"
+ "auth [default=die] %s/.libs/%s.so dir=%s authfail even_deny_root conf=%s\n"
+ "auth sufficient %s/.libs/%s.so dir=%s authsucc even_deny_root conf=%s\n",
+ cwd, MODULE_NAME, cwd, config_filename,
+ cwd,
+ cwd, MODULE_NAME, cwd, config_filename,
+ cwd, MODULE_NAME, cwd, config_filename));
+
+ ASSERT_EQ(0, fclose(fp));
+
+ ASSERT_EQ(PAM_SUCCESS,
+ pam_start_confdir(service_file, user_name, &conv, ".", &pamh));
+ ASSERT_NE(NULL, pamh);
+ ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0));
+ ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0));
+ pamh = NULL;
+ ASSERT_EQ(0, unlink(service_file));
+
+ /* root is locked for 5 sec*/
+ ASSERT_NE(NULL, fp = fopen(service_file, "w"));
+ ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n"
+ "auth requisite %s/.libs/%s.so dir=%s preauth even_deny_root conf=%s\n"
+ "auth [success=1 default=bad] %s/../pam_debug/.libs/pam_debug.so auth=success cred=success\n"
+ "auth [default=die] %s/.libs/%s.so dir=%s authfail even_deny_root conf=%s\n"
+ "auth sufficient %s/.libs/%s.so dir=%s authsucc even_deny_root conf=%s\n",
+ cwd, MODULE_NAME, cwd, config_filename,
+ cwd,
+ cwd, MODULE_NAME, cwd, config_filename,
+ cwd, MODULE_NAME, cwd, config_filename));
+
+ ASSERT_EQ(0, fclose(fp));
+
+ ASSERT_EQ(PAM_SUCCESS,
+ pam_start_confdir(service_file, user_name, &conv, ".", &pamh));
+ ASSERT_NE(NULL, pamh);
+ ASSERT_EQ(PAM_AUTH_ERR, pam_authenticate(pamh, 0));
+
+ /* waiting at least 5 sec --> login is working again*/
+ sleep(6);
+ ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0));
+
+ ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
+ ASSERT_EQ(0, unlink(service_file));
+ pamh = NULL;
+
+ ASSERT_EQ(0,unlink(user_name));
+ ASSERT_EQ(0,unlink(config_filename));
+
+ return 0;
+}
diff --git a/modules/pam_group/Makefile.am b/modules/pam_group/Makefile.am
index a9a0a1ef..fd88b952 100644
--- a/modules/pam_group/Makefile.am
+++ b/modules/pam_group/Makefile.am
@@ -18,7 +18,7 @@ securelibdir = $(SECUREDIR)
secureconfdir = $(SCONFIGDIR)
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
- -DPAM_GROUP_CONF=\"$(SCONFIGDIR)/group.conf\" $(WARN_CFLAGS)
+ $(WARN_CFLAGS)
AM_LDFLAGS = -no-undefined -avoid-version -module
if HAVE_VERSIONING
AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
diff --git a/modules/pam_group/pam_group.8.xml b/modules/pam_group/pam_group.8.xml
index 2c1c9058..e4a59dfd 100644
--- a/modules/pam_group/pam_group.8.xml
+++ b/modules/pam_group/pam_group.8.xml
@@ -38,6 +38,10 @@
By default rules for group memberships are taken from config file
<filename>/etc/security/group.conf</filename>.
</para>
+ <para condition="with_vendordir">
+ If <filename>/etc/security/group.conf</filename> does not exist,
+ <filename>%vendordir%/security/group.conf</filename> is used.
+ </para>
<para>
This module's usefulness relies on the file-systems
accessible to the user. The point being that once granted the
diff --git a/modules/pam_group/pam_group.c b/modules/pam_group/pam_group.c
index d9a35ea6..c49358a1 100644
--- a/modules/pam_group/pam_group.c
+++ b/modules/pam_group/pam_group.c
@@ -16,6 +16,7 @@
#include <time.h>
#include <syslog.h>
#include <string.h>
+#include <errno.h>
#include <grp.h>
#include <sys/types.h>
@@ -23,6 +24,10 @@
#include <fcntl.h>
#include <netdb.h>
+#define PAM_GROUP_CONF SCONFIGDIR "/group.conf"
+#ifdef VENDOR_SCONFIGDIR
+# define VENDOR_PAM_GROUP_CONF VENDOR_SCONFIGDIR "/group.conf"
+#endif
#define PAM_GROUP_BUFLEN 1000
#define FIELD_SEPARATOR ';' /* this is new as of .02 */
@@ -70,7 +75,8 @@ trim_spaces(char *buf, char *from)
#define STATE_EOF 3 /* end of file or error */
static int
-read_field(const pam_handle_t *pamh, int fd, char **buf, int *from, int *state)
+read_field(const pam_handle_t *pamh, int fd, char **buf, int *from, int *state,
+ const char *conf_filename)
{
char *to;
char *src;
@@ -89,9 +95,9 @@ read_field(const pam_handle_t *pamh, int fd, char **buf, int *from, int *state)
}
*from = 0;
*state = STATE_NL;
- fd = open(PAM_GROUP_CONF, O_RDONLY);
+ fd = open(conf_filename, O_RDONLY);
if (fd < 0) {
- pam_syslog(pamh, LOG_ERR, "error opening %s: %m", PAM_GROUP_CONF);
+ pam_syslog(pamh, LOG_ERR, "error opening %s: %m", conf_filename);
_pam_drop(*buf);
*state = STATE_EOF;
return -1;
@@ -106,7 +112,7 @@ read_field(const pam_handle_t *pamh, int fd, char **buf, int *from, int *state)
while (fd != -1 && to - *buf < PAM_GROUP_BUFLEN) {
i = pam_modutil_read(fd, to, PAM_GROUP_BUFLEN - (to - *buf));
if (i < 0) {
- pam_syslog(pamh, LOG_ERR, "error reading %s: %m", PAM_GROUP_CONF);
+ pam_syslog(pamh, LOG_ERR, "error reading %s: %m", conf_filename);
close(fd);
memset(*buf, 0, PAM_GROUP_BUFLEN);
_pam_drop(*buf);
@@ -573,6 +579,18 @@ static int check_account(pam_handle_t *pamh, const char *service,
int retval=PAM_SUCCESS;
gid_t *grps;
int no_grps;
+ const char *conf_filename = PAM_GROUP_CONF;
+
+#ifdef VENDOR_PAM_GROUP_CONF
+ /*
+ * Check whether PAM_GROUP_CONF file is available.
+ * If it does not exist, fall back to VENDOR_PAM_GROUP_CONF file.
+ */
+ struct stat stat_buffer;
+ if (stat(conf_filename, &stat_buffer) != 0 && errno == ENOENT) {
+ conf_filename = VENDOR_PAM_GROUP_CONF;
+ }
+#endif
/*
* first we get the current list of groups - the application
@@ -611,7 +629,7 @@ static int check_account(pam_handle_t *pamh, const char *service,
/* here we get the service name field */
- fd = read_field(pamh, fd, &buffer, &from, &state);
+ fd = read_field(pamh, fd, &buffer, &from, &state, conf_filename);
if (!buffer || !buffer[0]) {
/* empty line .. ? */
continue;
@@ -621,7 +639,7 @@ static int check_account(pam_handle_t *pamh, const char *service,
if (state != STATE_FIELD) {
pam_syslog(pamh, LOG_ERR,
- "%s: malformed rule #%d", PAM_GROUP_CONF, count);
+ "%s: malformed rule #%d", conf_filename, count);
continue;
}
@@ -630,10 +648,10 @@ static int check_account(pam_handle_t *pamh, const char *service,
/* here we get the terminal name field */
- fd = read_field(pamh, fd, &buffer, &from, &state);
+ fd = read_field(pamh, fd, &buffer, &from, &state, conf_filename);
if (state != STATE_FIELD) {
pam_syslog(pamh, LOG_ERR,
- "%s: malformed rule #%d", PAM_GROUP_CONF, count);
+ "%s: malformed rule #%d", conf_filename, count);
continue;
}
good &= logic_field(pamh,tty, buffer, count, is_same);
@@ -641,10 +659,10 @@ static int check_account(pam_handle_t *pamh, const char *service,
/* here we get the username field */
- fd = read_field(pamh, fd, &buffer, &from, &state);
+ fd = read_field(pamh, fd, &buffer, &from, &state, conf_filename);
if (state != STATE_FIELD) {
pam_syslog(pamh, LOG_ERR,
- "%s: malformed rule #%d", PAM_GROUP_CONF, count);
+ "%s: malformed rule #%d", conf_filename, count);
continue;
}
/* If buffer starts with @, we are using netgroups */
@@ -663,20 +681,20 @@ static int check_account(pam_handle_t *pamh, const char *service,
/* here we get the time field */
- fd = read_field(pamh, fd, &buffer, &from, &state);
+ fd = read_field(pamh, fd, &buffer, &from, &state, conf_filename);
if (state != STATE_FIELD) {
pam_syslog(pamh, LOG_ERR,
- "%s: malformed rule #%d", PAM_GROUP_CONF, count);
+ "%s: malformed rule #%d", conf_filename, count);
continue;
}
good &= logic_field(pamh,&here_and_now, buffer, count, check_time);
D(("with time: %s", good ? "passes":"fails" ));
- fd = read_field(pamh, fd, &buffer, &from, &state);
+ fd = read_field(pamh, fd, &buffer, &from, &state, conf_filename);
if (state == STATE_FIELD) {
pam_syslog(pamh, LOG_ERR,
- "%s: poorly terminated rule #%d", PAM_GROUP_CONF, count);
+ "%s: poorly terminated rule #%d", conf_filename, count);
continue;
}
diff --git a/modules/pam_limits/Makefile.am b/modules/pam_limits/Makefile.am
index 911b07b3..9ae1794d 100644
--- a/modules/pam_limits/Makefile.am
+++ b/modules/pam_limits/Makefile.am
@@ -19,8 +19,8 @@ secureconfdir = $(SCONFIGDIR)
limits_conf_dir = $(SCONFIGDIR)/limits.d
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
- -DLIMITS_FILE_DIR=\"$(limits_conf_dir)/*.conf\" \
- -DLIMITS_FILE=\"$(SCONFIGDIR)/limits.conf\" $(WARN_CFLAGS)
+ -DLIMITS_FILE_DIR=\"$(limits_conf_dir)\" \
+ $(WARN_CFLAGS)
AM_LDFLAGS = -no-undefined -avoid-version -module
if HAVE_VERSIONING
AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
diff --git a/modules/pam_limits/pam_limits.8.xml b/modules/pam_limits/pam_limits.8.xml
index bc46cbf4..08c6fc4d 100644
--- a/modules/pam_limits/pam_limits.8.xml
+++ b/modules/pam_limits/pam_limits.8.xml
@@ -57,6 +57,11 @@
If a config file is explicitly specified with a module option then the
files in the above directory are not parsed.
</para>
+ <para condition="with_vendordir">
+ If there is no explicitly specified configuration file and
+ <filename>/etc/security/limits.conf</filename> does not exist,
+ <filename>%vendordir%/security/limits.conf</filename> is used.
+ </para>
<para>
The module must not be called by a multithreaded application.
</para>
diff --git a/modules/pam_limits/pam_limits.c b/modules/pam_limits/pam_limits.c
index 7cc45d77..6fbe95fc 100644
--- a/modules/pam_limits/pam_limits.c
+++ b/modules/pam_limits/pam_limits.c
@@ -47,6 +47,10 @@
#include <libaudit.h>
#endif
+#ifndef PR_SET_NO_NEW_PRIVS
+# define PR_SET_NO_NEW_PRIVS 38 /* from <linux/prctl.h> */
+#endif
+
/* Module defines */
#define LINE_LENGTH 1024
@@ -119,9 +123,10 @@ struct pam_limit_s {
#define PAM_SET_ALL 0x0010
/* Limits from globbed files. */
-#define LIMITS_CONF_GLOB LIMITS_FILE_DIR
+#define LIMITS_CONF_GLOB (LIMITS_FILE_DIR "/*.conf")
-#define CONF_FILE (pl->conf_file != NULL)?pl->conf_file:LIMITS_FILE
+#define LIMITS_FILE (SCONFIGDIR "/limits.conf")
+#define CONF_FILE ((pl->conf_file != NULL) ? pl->conf_file : LIMITS_FILE)
static int
_pam_parse (const pam_handle_t *pamh, int argc, const char **argv,
@@ -811,14 +816,30 @@ parse_config_file(pam_handle_t *pamh, const char *uname, uid_t uid, gid_t gid,
FILE *fil;
char buf[LINE_LENGTH];
- /* check for the LIMITS_FILE */
+ /* check for the CONF_FILE */
if (ctrl & PAM_DEBUG_ARG)
pam_syslog(pamh, LOG_DEBUG, "reading settings from '%s'", CONF_FILE);
fil = fopen(CONF_FILE, "r");
if (fil == NULL) {
- pam_syslog (pamh, LOG_WARNING,
- "cannot read settings from %s: %m", CONF_FILE);
- return PAM_SERVICE_ERR;
+ int err = errno;
+
+#ifdef VENDOR_SCONFIGDIR
+ /* if the specified file does not exist, and it is not provided by
+ the user, try the vendor file as fallback. */
+ if (pl->conf_file == NULL && err == ENOENT)
+ fil = fopen(VENDOR_SCONFIGDIR "/limits.conf", "r");
+
+ if (fil == NULL)
+#endif
+ {
+ if (err == ENOENT)
+ return PAM_SUCCESS;
+
+ pam_syslog (pamh, LOG_WARNING,
+ "cannot read settings from %s: %s", CONF_FILE,
+ strerror(err));
+ return PAM_SERVICE_ERR;
+ }
}
/* start the show */
diff --git a/modules/pam_namespace/Makefile.am b/modules/pam_namespace/Makefile.am
index 47cc38e1..33375857 100644
--- a/modules/pam_namespace/Makefile.am
+++ b/modules/pam_namespace/Makefile.am
@@ -21,7 +21,7 @@ namespaceddir = $(SCONFIGDIR)/namespace.d
servicedir = $(systemdunitdir)
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
- -DSECURECONF_DIR=\"$(SCONFIGDIR)/\" $(WARN_CFLAGS)
+ $(WARN_CFLAGS)
AM_LDFLAGS = -no-undefined -avoid-version -module
if HAVE_VERSIONING
AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
diff --git a/modules/pam_namespace/pam_namespace.h b/modules/pam_namespace/pam_namespace.h
index b51f2841..169bd59f 100644
--- a/modules/pam_namespace/pam_namespace.h
+++ b/modules/pam_namespace/pam_namespace.h
@@ -90,14 +90,10 @@
/*
* Module defines
*/
-#ifndef SECURECONF_DIR
-#define SECURECONF_DIR "/etc/security/"
-#endif
-
-#define PAM_NAMESPACE_CONFIG (SECURECONF_DIR "namespace.conf")
-#define NAMESPACE_INIT_SCRIPT (SECURECONF_DIR "namespace.init")
-#define NAMESPACE_D_DIR (SECURECONF_DIR "namespace.d/")
-#define NAMESPACE_D_GLOB (SECURECONF_DIR "namespace.d/*.conf")
+#define PAM_NAMESPACE_CONFIG (SCONFIGDIR "/namespace.conf")
+#define NAMESPACE_INIT_SCRIPT (SCONFIGDIR "/namespace.init")
+#define NAMESPACE_D_DIR (SCONFIGDIR "/namespace.d/")
+#define NAMESPACE_D_GLOB (SCONFIGDIR "/namespace.d/*.conf")
/* module flags */
#define PAMNS_DEBUG 0x00000100 /* Running in debug mode */
diff --git a/modules/pam_pwhistory/opasswd.c b/modules/pam_pwhistory/opasswd.c
index a6cd3d2a..1d3242ca 100644
--- a/modules/pam_pwhistory/opasswd.c
+++ b/modules/pam_pwhistory/opasswd.c
@@ -44,6 +44,7 @@
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
@@ -74,8 +75,7 @@
#define RANDOM_DEVICE "/dev/urandom"
#endif
-#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
-#define TMP_PASSWORDS_FILE OLD_PASSWORDS_FILE".tmpXXXXXX"
+#define DEFAULT_OLD_PASSWORDS_FILE SCONFIGDIR "/opasswd"
#define DEFAULT_BUFLEN 4096
@@ -142,7 +142,7 @@ compare_password(const char *newpass, const char *oldpass)
/* Check, if the new password is already in the opasswd file. */
PAMH_ARG_DECL(int
-check_old_pass, const char *user, const char *newpass, int debug)
+check_old_pass, const char *user, const char *newpass, const char *filename, int debug)
{
int retval = PAM_SUCCESS;
FILE *oldpf;
@@ -156,10 +156,13 @@ check_old_pass, const char *user, const char *newpass, int debug)
return PAM_PWHISTORY_RUN_HELPER;
#endif
- if ((oldpf = fopen (OLD_PASSWORDS_FILE, "r")) == NULL)
+ const char *opasswd_file =
+ (filename != NULL ? filename : DEFAULT_OLD_PASSWORDS_FILE);
+
+ if ((oldpf = fopen (opasswd_file, "r")) == NULL)
{
if (errno != ENOENT)
- pam_syslog (pamh, LOG_ERR, "Cannot open %s: %m", OLD_PASSWORDS_FILE);
+ pam_syslog (pamh, LOG_ERR, "Cannot open %s: %m", opasswd_file);
return PAM_SUCCESS;
}
@@ -242,9 +245,8 @@ check_old_pass, const char *user, const char *newpass, int debug)
}
PAMH_ARG_DECL(int
-save_old_pass, const char *user, int howmany, int debug UNUSED)
+save_old_pass, const char *user, int howmany, const char *filename, int debug UNUSED)
{
- char opasswd_tmp[] = TMP_PASSWORDS_FILE;
struct stat opasswd_stat;
FILE *oldpf, *newpf;
int newpf_fd;
@@ -256,6 +258,15 @@ save_old_pass, const char *user, int howmany, int debug UNUSED)
struct passwd *pwd;
const char *oldpass;
+ /* Define opasswd file and temp file for opasswd */
+ const char *opasswd_file =
+ (filename != NULL ? filename : DEFAULT_OLD_PASSWORDS_FILE);
+ char opasswd_tmp[PATH_MAX];
+
+ if ((size_t) snprintf (opasswd_tmp, sizeof (opasswd_tmp), "%s.tmpXXXXXX",
+ opasswd_file) >= sizeof (opasswd_tmp))
+ return PAM_BUF_ERR;
+
pwd = pam_modutil_getpwnam (pamh, user);
if (pwd == NULL)
return PAM_USER_UNKNOWN;
@@ -285,24 +296,22 @@ save_old_pass, const char *user, int howmany, int debug UNUSED)
if (oldpass == NULL || *oldpass == '\0')
return PAM_SUCCESS;
- if ((oldpf = fopen (OLD_PASSWORDS_FILE, "r")) == NULL)
+ if ((oldpf = fopen (opasswd_file, "r")) == NULL)
{
if (errno == ENOENT)
{
- pam_syslog (pamh, LOG_NOTICE, "Creating %s",
- OLD_PASSWORDS_FILE);
+ pam_syslog (pamh, LOG_NOTICE, "Creating %s", opasswd_file);
do_create = 1;
}
else
{
- pam_syslog (pamh, LOG_ERR, "Cannot open %s: %m",
- OLD_PASSWORDS_FILE);
+ pam_syslog (pamh, LOG_ERR, "Cannot open %s: %m", opasswd_file);
return PAM_AUTHTOK_ERR;
}
}
else if (fstat (fileno (oldpf), &opasswd_stat) < 0)
{
- pam_syslog (pamh, LOG_ERR, "Cannot stat %s: %m", OLD_PASSWORDS_FILE);
+ pam_syslog (pamh, LOG_ERR, "Cannot stat %s: %m", opasswd_file);
fclose (oldpf);
return PAM_AUTHTOK_ERR;
}
@@ -312,7 +321,7 @@ save_old_pass, const char *user, int howmany, int debug UNUSED)
if (newpf_fd == -1)
{
pam_syslog (pamh, LOG_ERR, "Cannot create %s temp file: %m",
- OLD_PASSWORDS_FILE);
+ opasswd_file);
if (oldpf)
fclose (oldpf);
return PAM_AUTHTOK_ERR;
@@ -321,23 +330,19 @@ save_old_pass, const char *user, int howmany, int debug UNUSED)
{
if (fchmod (newpf_fd, S_IRUSR|S_IWUSR) != 0)
pam_syslog (pamh, LOG_ERR,
- "Cannot set permissions of %s temp file: %m",
- OLD_PASSWORDS_FILE);
+ "Cannot set permissions of %s temp file: %m", opasswd_file);
if (fchown (newpf_fd, 0, 0) != 0)
pam_syslog (pamh, LOG_ERR,
- "Cannot set owner/group of %s temp file: %m",
- OLD_PASSWORDS_FILE);
+ "Cannot set owner/group of %s temp file: %m", opasswd_file);
}
else
{
if (fchmod (newpf_fd, opasswd_stat.st_mode) != 0)
pam_syslog (pamh, LOG_ERR,
- "Cannot set permissions of %s temp file: %m",
- OLD_PASSWORDS_FILE);
+ "Cannot set permissions of %s temp file: %m", opasswd_file);
if (fchown (newpf_fd, opasswd_stat.st_uid, opasswd_stat.st_gid) != 0)
pam_syslog (pamh, LOG_ERR,
- "Cannot set owner/group of %s temp file: %m",
- OLD_PASSWORDS_FILE);
+ "Cannot set owner/group of %s temp file: %m", opasswd_file);
}
newpf = fdopen (newpf_fd, "w+");
if (newpf == NULL)
@@ -550,12 +555,20 @@ save_old_pass, const char *user, int howmany, int debug UNUSED)
goto error_opasswd;
}
- unlink (OLD_PASSWORDS_FILE".old");
- if (link (OLD_PASSWORDS_FILE, OLD_PASSWORDS_FILE".old") != 0 &&
+ char opasswd_backup[PATH_MAX];
+ if ((size_t) snprintf (opasswd_backup, sizeof (opasswd_backup), "%s.old",
+ opasswd_file) >= sizeof (opasswd_backup))
+ {
+ retval = PAM_BUF_ERR;
+ goto error_opasswd;
+ }
+
+ unlink (opasswd_backup);
+ if (link (opasswd_file, opasswd_backup) != 0 &&
errno != ENOENT)
pam_syslog (pamh, LOG_ERR, "Cannot create backup file of %s: %m",
- OLD_PASSWORDS_FILE);
- rename (opasswd_tmp, OLD_PASSWORDS_FILE);
+ opasswd_file);
+ rename (opasswd_tmp, opasswd_file);
error_opasswd:
unlink (opasswd_tmp);
free (buf);
diff --git a/modules/pam_pwhistory/opasswd.h b/modules/pam_pwhistory/opasswd.h
index 3f257288..19a4062c 100644
--- a/modules/pam_pwhistory/opasswd.h
+++ b/modules/pam_pwhistory/opasswd.h
@@ -57,10 +57,10 @@ void
helper_log_err(int err, const char *format, ...);
#endif
-PAMH_ARG_DECL(int
-check_old_pass, const char *user, const char *newpass, int debug);
+PAMH_ARG_DECL(int check_old_pass, const char *user, const char *newpass,
+ const char *filename, int debug);
-PAMH_ARG_DECL(int
-save_old_pass, const char *user, int howmany, int debug);
+PAMH_ARG_DECL(int save_old_pass, const char *user, int howmany,
+ const char *filename, int debug);
#endif /* __OPASSWD_H__ */
diff --git a/modules/pam_pwhistory/pam_pwhistory.8.xml b/modules/pam_pwhistory/pam_pwhistory.8.xml
index d88115c2..df16a776 100644
--- a/modules/pam_pwhistory/pam_pwhistory.8.xml
+++ b/modules/pam_pwhistory/pam_pwhistory.8.xml
@@ -36,6 +36,9 @@
<arg choice="opt">
authtok_type=<replaceable>STRING</replaceable>
</arg>
+ <arg choice="opt">
+ file=<replaceable>/path/filename</replaceable>
+ </arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -137,6 +140,19 @@
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <option>file=<replaceable>/path/filename</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ Store password history in file <filename>/path/filename</filename>
+ rather than the default location. The default location is
+ <filename>/etc/security/opasswd</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
@@ -213,7 +229,7 @@ password required pam_unix.so use_authtok
<varlistentry>
<term><filename>/etc/security/opasswd</filename></term>
<listitem>
- <para>File with password history</para>
+ <para>Default file with password history</para>
</listitem>
</varlistentry>
</variablelist>
diff --git a/modules/pam_pwhistory/pam_pwhistory.c b/modules/pam_pwhistory/pam_pwhistory.c
index ce2c21f5..9c1bdd87 100644
--- a/modules/pam_pwhistory/pam_pwhistory.c
+++ b/modules/pam_pwhistory/pam_pwhistory.c
@@ -69,6 +69,7 @@ struct options_t {
int enforce_for_root;
int remember;
int tries;
+ const char *filename;
};
typedef struct options_t options_t;
@@ -104,13 +105,23 @@ parse_option (pam_handle_t *pamh, const char *argv, options_t *options)
options->enforce_for_root = 1;
else if (pam_str_skip_icase_prefix(argv, "authtok_type=") != NULL)
{ /* ignore, for pam_get_authtok */; }
+ else if ((str = pam_str_skip_icase_prefix(argv, "file=")) != NULL)
+ {
+ if (*str != '/')
+ {
+ pam_syslog (pamh, LOG_ERR,
+ "pam_pwhistory: file path should be absolute: %s", argv);
+ }
+ else
+ options->filename = str;
+ }
else
pam_syslog (pamh, LOG_ERR, "pam_pwhistory: unknown option: %s", argv);
}
static int
run_save_helper(pam_handle_t *pamh, const char *user,
- int howmany, int debug)
+ int howmany, const char *filename, int debug)
{
int retval, child;
struct sigaction newsa, oldsa;
@@ -123,7 +134,7 @@ run_save_helper(pam_handle_t *pamh, const char *user,
if (child == 0)
{
static char *envp[] = { NULL };
- char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
+ char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_PIPE_FD,
PAM_MODUTIL_PIPE_FD,
@@ -137,9 +148,10 @@ run_save_helper(pam_handle_t *pamh, const char *user,
args[0] = (char *)PWHISTORY_HELPER;
args[1] = (char *)"save";
args[2] = (char *)user;
+ args[3] = (char *)filename;
DIAG_POP_IGNORE_CAST_QUAL;
- if (asprintf(&args[3], "%d", howmany) < 0 ||
- asprintf(&args[4], "%d", debug) < 0)
+ if (asprintf(&args[4], "%d", howmany) < 0 ||
+ asprintf(&args[5], "%d", debug) < 0)
{
pam_syslog(pamh, LOG_ERR, "asprintf: %m");
_exit(PAM_SYSTEM_ERR);
@@ -185,7 +197,7 @@ run_save_helper(pam_handle_t *pamh, const char *user,
static int
run_check_helper(pam_handle_t *pamh, const char *user,
- const char *newpass, int debug)
+ const char *newpass, const char *filename, int debug)
{
int retval, child, fds[2];
struct sigaction newsa, oldsa;
@@ -202,7 +214,7 @@ run_check_helper(pam_handle_t *pamh, const char *user,
if (child == 0)
{
static char *envp[] = { NULL };
- char *args[] = { NULL, NULL, NULL, NULL, NULL };
+ char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
/* reopen stdin as pipe */
if (dup2(fds[0], STDIN_FILENO) != STDIN_FILENO)
@@ -223,8 +235,9 @@ run_check_helper(pam_handle_t *pamh, const char *user,
args[0] = (char *)PWHISTORY_HELPER;
args[1] = (char *)"check";
args[2] = (char *)user;
+ args[3] = (char *)filename;
DIAG_POP_IGNORE_CAST_QUAL;
- if (asprintf(&args[3], "%d", debug) < 0)
+ if (asprintf(&args[4], "%d", debug) < 0)
{
pam_syslog(pamh, LOG_ERR, "asprintf: %m");
_exit(PAM_SYSTEM_ERR);
@@ -323,10 +336,10 @@ pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
return PAM_SUCCESS;
}
- retval = save_old_pass (pamh, user, options.remember, options.debug);
+ retval = save_old_pass (pamh, user, options.remember, options.filename, options.debug);
if (retval == PAM_PWHISTORY_RUN_HELPER)
- retval = run_save_helper(pamh, user, options.remember, options.debug);
+ retval = run_save_helper(pamh, user, options.remember, options.filename, options.debug);
if (retval != PAM_SUCCESS)
return retval;
@@ -358,9 +371,9 @@ pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
if (options.debug)
pam_syslog (pamh, LOG_DEBUG, "check against old password file");
- retval = check_old_pass (pamh, user, newpass, options.debug);
+ retval = check_old_pass (pamh, user, newpass, options.filename, options.debug);
if (retval == PAM_PWHISTORY_RUN_HELPER)
- retval = run_check_helper(pamh, user, newpass, options.debug);
+ retval = run_check_helper(pamh, user, newpass, options.filename, options.debug);
if (retval != PAM_SUCCESS)
{
diff --git a/modules/pam_pwhistory/pwhistory_helper.c b/modules/pam_pwhistory/pwhistory_helper.c
index b08a14a7..7a61ae53 100644
--- a/modules/pam_pwhistory/pwhistory_helper.c
+++ b/modules/pam_pwhistory/pwhistory_helper.c
@@ -51,7 +51,7 @@
static int
-check_history(const char *user, const char *debug)
+check_history(const char *user, const char *filename, const char *debug)
{
char pass[PAM_MAX_RESP_SIZE + 1];
char *passwords[] = { pass };
@@ -68,7 +68,7 @@ check_history(const char *user, const char *debug)
return PAM_AUTHTOK_ERR;
}
- retval = check_old_pass(user, pass, dbg);
+ retval = check_old_pass(user, pass, filename, dbg);
memset(pass, '\0', PAM_MAX_RESP_SIZE); /* clear memory of the password */
@@ -76,13 +76,13 @@ check_history(const char *user, const char *debug)
}
static int
-save_history(const char *user, const char *howmany, const char *debug)
+save_history(const char *user, const char *filename, const char *howmany, const char *debug)
{
int num = atoi(howmany);
int dbg = atoi(debug); /* no need to be too fancy here */
int retval;
- retval = save_old_pass(user, num, dbg);
+ retval = save_old_pass(user, num, filename, dbg);
return retval;
}
@@ -92,13 +92,14 @@ main(int argc, char *argv[])
{
const char *option;
const char *user;
+ const char *filename;
/*
* we establish that this program is running with non-tty stdin.
* this is to discourage casual use.
*/
- if (isatty(STDIN_FILENO) || argc < 4)
+ if (isatty(STDIN_FILENO) || argc < 5)
{
fprintf(stderr,
"This binary is not designed for running in this way.\n");
@@ -107,11 +108,12 @@ main(int argc, char *argv[])
option = argv[1];
user = argv[2];
+ filename = argv[3];
- if (strcmp(option, "check") == 0 && argc == 4)
- return check_history(user, argv[3]);
- else if (strcmp(option, "save") == 0 && argc == 5)
- return save_history(user, argv[3], argv[4]);
+ if (strcmp(option, "check") == 0 && argc == 5)
+ return check_history(user, filename, argv[4]);
+ else if (strcmp(option, "save") == 0 && argc == 6)
+ return save_history(user, filename, argv[4], argv[5]);
fprintf(stderr, "This binary is not designed for running in this way.\n");
diff --git a/modules/pam_rootok/pam_rootok.c b/modules/pam_rootok/pam_rootok.c
index dd374c53..9bc15abf 100644
--- a/modules/pam_rootok/pam_rootok.c
+++ b/modules/pam_rootok/pam_rootok.c
@@ -53,11 +53,10 @@ static int
PAM_FORMAT((printf, 2, 3))
log_callback (int type UNUSED, const char *fmt, ...)
{
- int audit_fd;
va_list ap;
#ifdef HAVE_LIBAUDIT
- audit_fd = audit_open();
+ int audit_fd = audit_open();
if (audit_fd >= 0) {
char *buf;
diff --git a/modules/pam_sepermit/Makefile.am b/modules/pam_sepermit/Makefile.am
index 18a89b60..bed3b149 100644
--- a/modules/pam_sepermit/Makefile.am
+++ b/modules/pam_sepermit/Makefile.am
@@ -13,7 +13,7 @@ dist_man_MANS = pam_sepermit.8 sepermit.conf.5
endif
XMLS = README.xml pam_sepermit.8.xml sepermit.conf.5.xml
dist_check_SCRIPTS = tst-pam_sepermit
-TESTS = $(dist_check_SCRIPTS)
+TESTS = $(dist_check_SCRIPTS) $(check_PROGRAMS)
securelibdir = $(SECUREDIR)
secureconfdir = $(SCONFIGDIR)
@@ -21,7 +21,6 @@ sepermitlockdir = ${localstatedir}/run/sepermit
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
-I$(top_srcdir)/libpam_misc/include \
- -D SEPERMIT_CONF_FILE=\"$(SCONFIGDIR)/sepermit.conf\" \
-D SEPERMIT_LOCKDIR=\"$(sepermitlockdir)\" $(WARN_CFLAGS)
pam_sepermit_la_LIBADD = $(top_builddir)/libpam/libpam.la @LIBSELINUX@
@@ -33,6 +32,9 @@ endif
dist_secureconf_DATA = sepermit.conf
securelib_LTLIBRARIES = pam_sepermit.la
+check_PROGRAMS = tst-pam_sepermit-retval
+tst_pam_sepermit_retval_LDADD = $(top_builddir)/libpam/libpam.la
+
install-data-local:
mkdir -p $(DESTDIR)$(sepermitlockdir)
diff --git a/modules/pam_sepermit/pam_sepermit.8.xml b/modules/pam_sepermit/pam_sepermit.8.xml
index 30d9cc54..5763c346 100644
--- a/modules/pam_sepermit/pam_sepermit.8.xml
+++ b/modules/pam_sepermit/pam_sepermit.8.xml
@@ -54,7 +54,11 @@
<refentrytitle>sepermit.conf</refentrytitle><manvolnum>5</manvolnum>
</citerefentry> for details.
</para>
-
+ <para condition="with_vendordir">
+ If there is no explicitly specified configuration file and
+ <filename>/etc/security/sepermit.conf</filename> does not exist,
+ <filename>%vendordir%/security/sepermit.conf</filename> is used.
+ </para>
</refsect1>
<refsect1 id="pam_sepermit-options">
diff --git a/modules/pam_sepermit/pam_sepermit.c b/modules/pam_sepermit/pam_sepermit.c
index f7d98d5b..5fbc8fdd 100644
--- a/modules/pam_sepermit/pam_sepermit.c
+++ b/modules/pam_sepermit/pam_sepermit.c
@@ -61,6 +61,12 @@
#include <selinux/selinux.h>
+#include "pam_inline.h"
+
+#define SEPERMIT_CONF_FILE (SCONFIGDIR "/sepermit.conf")
+#ifdef VENDOR_SCONFIGDIR
+# define SEPERMIT_VENDOR_CONF_FILE (VENDOR_SCONFIGDIR "/sepermit.conf");
+#endif
#define MODULE "pam_sepermit"
#define OPT_DELIM ":"
@@ -370,16 +376,31 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
const char *user = NULL;
char *seuser = NULL;
char *level = NULL;
- const char *cfgfile = SEPERMIT_CONF_FILE;
+ const char *cfgfile = NULL;
/* Parse arguments. */
for (i = 0; i < argc; i++) {
+ const char *str;
+
if (strcmp(argv[i], "debug") == 0) {
debug = 1;
+ } else if ((str = pam_str_skip_prefix(argv[i], "conf=")) != NULL) {
+ cfgfile = str;
+ } else {
+ pam_syslog(pamh, LOG_ERR, "unknown option: %s", argv[i]);
}
- if (strcmp(argv[i], "conf=") == 0) {
- cfgfile = argv[i] + 5;
- }
+ }
+
+ if (cfgfile == NULL) {
+#ifdef SEPERMIT_VENDOR_CONF_FILE
+ struct stat buffer;
+
+ cfgfile = SEPERMIT_CONF_FILE;
+ if (stat(cfgfile, &buffer) != 0 && errno == ENOENT)
+ cfgfile = SEPERMIT_VENDOR_CONF_FILE;
+#else
+ cfgfile = SEPERMIT_CONF_FILE;
+#endif
}
if (debug)
diff --git a/modules/pam_sepermit/tst-pam_sepermit-retval.c b/modules/pam_sepermit/tst-pam_sepermit-retval.c
new file mode 100644
index 00000000..321bd6d1
--- /dev/null
+++ b/modules/pam_sepermit/tst-pam_sepermit-retval.c
@@ -0,0 +1,158 @@
+/*
+ * Check pam_sepermit return values and conf= option.
+ *
+ * Copyright (c) 2020-2022 Dmitry V. Levin <ldv@altlinux.org>
+ */
+
+#include "test_assert.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <security/pam_appl.h>
+
+#define MODULE_NAME "pam_sepermit"
+#define TEST_NAME "tst-" MODULE_NAME "-retval"
+
+static const char service_file[] = TEST_NAME ".service";
+static const char missing_file[] = TEST_NAME ".missing";
+static const char config_file[] = TEST_NAME ".conf";
+static struct pam_conv conv;
+
+int
+main(void)
+{
+ pam_handle_t *pamh = NULL;
+ FILE *fp;
+ char cwd[PATH_MAX];
+
+ ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd)));
+
+ /* PAM_USER_UNKNOWN */
+ ASSERT_NE(NULL, fp = fopen(service_file, "w"));
+ ASSERT_LT(0,
+ fprintf(fp, "#%%PAM-1.0\n"
+ "auth required %s/.libs/%s.so\n"
+ "account required %s/.libs/%s.so\n"
+ "password required %s/.libs/%s.so\n"
+ "session required %s/.libs/%s.so\n",
+ cwd, MODULE_NAME,
+ cwd, MODULE_NAME,
+ cwd, MODULE_NAME,
+ cwd, MODULE_NAME));
+ ASSERT_EQ(0, fclose(fp));
+
+ ASSERT_EQ(PAM_SUCCESS,
+ pam_start_confdir(service_file, "", &conv, ".", &pamh));
+ ASSERT_NE(NULL, pamh);
+ ASSERT_EQ(PAM_USER_UNKNOWN, pam_authenticate(pamh, 0));
+ ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0));
+ ASSERT_EQ(PAM_USER_UNKNOWN, pam_acct_mgmt(pamh, 0));
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0));
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0));
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0));
+ ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
+ pamh = NULL;
+
+ ASSERT_NE(NULL, fp = fopen(config_file, "w"));
+ ASSERT_LT(0, fprintf(fp, "nosuchuser:ignore\n"));
+ ASSERT_EQ(0, fclose(fp));
+
+ /*
+ * conf= specifies an existing file,
+ * PAM_IGNORE -> PAM_PERM_DENIED
+ */
+ ASSERT_NE(NULL, fp = fopen(service_file, "w"));
+ ASSERT_LT(0,
+ fprintf(fp, "#%%PAM-1.0\n"
+ "auth required %s/.libs/%s.so conf=%s\n"
+ "account required %s/.libs/%s.so conf=%s\n"
+ "password required %s/.libs/%s.so conf=%s\n"
+ "session required %s/.libs/%s.so conf=%s\n",
+ cwd, MODULE_NAME, config_file,
+ cwd, MODULE_NAME, config_file,
+ cwd, MODULE_NAME, config_file,
+ cwd, MODULE_NAME, config_file));
+ ASSERT_EQ(0, fclose(fp));
+
+ ASSERT_EQ(PAM_SUCCESS,
+ pam_start_confdir(service_file, "root", &conv, ".", &pamh));
+ ASSERT_NE(NULL, pamh);
+ ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0));
+ ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0));
+ ASSERT_EQ(PAM_PERM_DENIED, pam_acct_mgmt(pamh, 0));
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0));
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0));
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0));
+ ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
+ pamh = NULL;
+
+ /*
+ * conf= specifies an existing file,
+ * PAM_IGNORE -> PAM_SUCCESS
+ */
+ ASSERT_NE(NULL, fp = fopen(service_file, "w"));
+ ASSERT_LT(0,
+ fprintf(fp, "#%%PAM-1.0\n"
+ "auth required %s/.libs/%s.so conf=%s\n"
+ "auth required %s/../pam_permit/.libs/pam_permit.so\n"
+ "account required %s/.libs/%s.so conf=%s\n"
+ "account required %s/../pam_permit/.libs/pam_permit.so\n"
+ "password required %s/.libs/%s.so conf=%s\n"
+ "password required %s/../pam_permit/.libs/pam_permit.so\n"
+ "session required %s/.libs/%s.so conf=%s\n"
+ "session required %s/../pam_permit/.libs/pam_permit.so\n",
+ cwd, MODULE_NAME, config_file, cwd,
+ cwd, MODULE_NAME, config_file, cwd,
+ cwd, MODULE_NAME, config_file, cwd,
+ cwd, MODULE_NAME, config_file, cwd));
+ ASSERT_EQ(0, fclose(fp));
+
+ ASSERT_EQ(PAM_SUCCESS,
+ pam_start_confdir(service_file, "root", &conv, ".", &pamh));
+ ASSERT_NE(NULL, pamh);
+ ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0));
+ ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0));
+ ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0));
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0));
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0));
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0));
+ ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
+ pamh = NULL;
+
+ /*
+ * conf= specifies a missing file,
+ * PAM_IGNORE -> PAM_PERM_DENIED
+ */
+ ASSERT_NE(NULL, fp = fopen(service_file, "w"));
+ ASSERT_LT(0,
+ fprintf(fp, "#%%PAM-1.0\n"
+ "auth required %s/.libs/%s.so conf=%s\n"
+ "account required %s/.libs/%s.so conf=%s\n"
+ "password required %s/.libs/%s.so conf=%s\n"
+ "session required %s/.libs/%s.so conf=%s\n",
+ cwd, MODULE_NAME, missing_file,
+ cwd, MODULE_NAME, missing_file,
+ cwd, MODULE_NAME, missing_file,
+ cwd, MODULE_NAME, missing_file));
+ ASSERT_EQ(0, fclose(fp));
+
+ ASSERT_EQ(PAM_SUCCESS,
+ pam_start_confdir(service_file, "root", &conv, ".", &pamh));
+ ASSERT_NE(NULL, pamh);
+ ASSERT_EQ(PAM_SERVICE_ERR, pam_authenticate(pamh, 0));
+ ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0));
+ ASSERT_EQ(PAM_SERVICE_ERR, pam_acct_mgmt(pamh, 0));
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0));
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0));
+ ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0));
+ ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
+ pamh = NULL;
+
+ /* cleanup */
+ ASSERT_EQ(0, unlink(config_file));
+ ASSERT_EQ(0, unlink(service_file));
+
+ return 0;
+}
diff --git a/modules/pam_time/Makefile.am b/modules/pam_time/Makefile.am
index 833d51a6..f34f8dce 100644
--- a/modules/pam_time/Makefile.am
+++ b/modules/pam_time/Makefile.am
@@ -18,7 +18,7 @@ securelibdir = $(SECUREDIR)
secureconfdir = $(SCONFIGDIR)
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
- -DPAM_TIME_CONF=\"$(SCONFIGDIR)/time.conf\" $(WARN_CFLAGS)
+ $(WARN_CFLAGS)
AM_LDFLAGS = -no-undefined -avoid-version -module
if HAVE_VERSIONING
AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
diff --git a/modules/pam_time/pam_time.c b/modules/pam_time/pam_time.c
index 089ae22d..8eebc914 100644
--- a/modules/pam_time/pam_time.c
+++ b/modules/pam_time/pam_time.c
@@ -33,6 +33,8 @@
#include <libaudit.h>
#endif
+#define PAM_TIME_CONF (SCONFIGDIR "/time.conf")
+
#define PAM_TIME_BUFLEN 1000
#define FIELD_SEPARATOR ';' /* this is new as of .02 */
diff --git a/modules/pam_unix/passverify.c b/modules/pam_unix/passverify.c
index f2474a5b..c8ab49f3 100644
--- a/modules/pam_unix/passverify.c
+++ b/modules/pam_unix/passverify.c
@@ -334,7 +334,7 @@ PAMH_ARG_DECL(int check_shadow_expiry,
#define PW_TMPFILE "/etc/npasswd"
#define SH_TMPFILE "/etc/nshadow"
-#define OPW_TMPFILE "/etc/security/nopasswd"
+#define OPW_TMPFILE SCONFIGDIR "/nopasswd"
/*
* i64c - convert an integer to a radix 64 character
diff --git a/modules/pam_unix/passverify.h b/modules/pam_unix/passverify.h
index c07037d2..463ef185 100644
--- a/modules/pam_unix/passverify.h
+++ b/modules/pam_unix/passverify.h
@@ -8,7 +8,7 @@
#define PAM_UNIX_RUN_HELPER PAM_CRED_INSUFFICIENT
-#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
+#define OLD_PASSWORDS_FILE SCONFIGDIR "/opasswd"
int
is_pwd_shadowed(const struct passwd *pwd);
diff --git a/xtests/run-xtests.sh b/xtests/run-xtests.sh
index 14f585d9..ff9a4dc1 100755
--- a/xtests/run-xtests.sh
+++ b/xtests/run-xtests.sh
@@ -18,10 +18,12 @@ all=0
mkdir -p /etc/security
for config in access.conf group.conf time.conf limits.conf ; do
- cp /etc/security/$config /etc/security/$config-pam-xtests
+ [ -f "/etc/security/$config" ] &&
+ mv /etc/security/$config /etc/security/$config-pam-xtests
install -m 644 "${SRCDIR}"/$config /etc/security/$config
done
-mv /etc/security/opasswd /etc/security/opasswd-pam-xtests
+[ -f /etc/security/opasswd ] &&
+ mv /etc/security/opasswd /etc/security/opasswd-pam-xtests
for testname in $XTESTS ; do
for cfg in "${SRCDIR}"/$testname*.pamd ; do
@@ -47,11 +49,15 @@ for testname in $XTESTS ; do
all=`expr $all + 1`
rm -f /etc/pam.d/$testname*
done
-mv /etc/security/access.conf-pam-xtests /etc/security/access.conf
-mv /etc/security/group.conf-pam-xtests /etc/security/group.conf
-mv /etc/security/time.conf-pam-xtests /etc/security/time.conf
-mv /etc/security/limits.conf-pam-xtests /etc/security/limits.conf
-mv /etc/security/opasswd-pam-xtests /etc/security/opasswd
+
+for config in access.conf group.conf time.conf limits.conf opasswd ; do
+ if [ -f "/etc/security/$config-pam-xtests" ]; then
+ mv /etc/security/$config-pam-xtests /etc/security/$config
+ else
+ rm -f /etc/security/$config
+ fi
+done
+
if test "$failed" -ne 0; then
echo "==================="
echo "$failed of $all tests failed"