From 656f9b5474bf0d533f8d03b944e76018b1746c1d10179006f81867f1bfd05169 Mon Sep 17 00:00:00 2001 From: Thorsten Kukuk Date: Fri, 11 Mar 2022 11:29:42 +0000 Subject: [PATCH] Accepting request 961064 from home:kukuk:tiu - pam-hostnames-in-access_conf.patch: update with upstream submission. Fixes several bugs including memory leaks. - Move group.conf and faillock.conf to /usr/etc/security - Update to current git for enhanced vendordir support (pam-git.diff) Obsoletes: - 0001-Include-pam_xauth_data.3.xml-in-source-archive-400.patch - 0002-Only-include-vendordir-in-manual-page-if-set-401.patch - 0003-Use-vendor-specific-limits.conf-as-fallback-402.patch OBS-URL: https://build.opensuse.org/request/show/961064 OBS-URL: https://build.opensuse.org/package/show/Linux-PAM/pam?expand=0&rev=259 --- ...uth_data.3.xml-in-source-archive-400.patch | 25 - ...-vendordir-in-manual-page-if-set-401.patch | 51 - ...specific-limits.conf-as-fallback-402.patch | 61 - pam-git.diff | 1672 +++++++++++++++++ pam-hostnames-in-access_conf.patch | 134 +- pam.changes | 20 + pam.spec | 12 +- 7 files changed, 1781 insertions(+), 194 deletions(-) delete mode 100644 0001-Include-pam_xauth_data.3.xml-in-source-archive-400.patch delete mode 100644 0002-Only-include-vendordir-in-manual-page-if-set-401.patch delete mode 100644 0003-Use-vendor-specific-limits.conf-as-fallback-402.patch create mode 100644 pam-git.diff diff --git a/0001-Include-pam_xauth_data.3.xml-in-source-archive-400.patch b/0001-Include-pam_xauth_data.3.xml-in-source-archive-400.patch deleted file mode 100644 index 885b699..0000000 --- a/0001-Include-pam_xauth_data.3.xml-in-source-archive-400.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 00a46bcead2857002ed720f22b558b6f6d349fc8 Mon Sep 17 00:00:00 2001 -From: Thorsten Kukuk <5908016+thkukuk@users.noreply.github.com> -Date: Tue, 2 Nov 2021 11:45:59 +0100 -Subject: [PATCH 1/3] Include pam_xauth_data.3.xml in source archive (#400) - ---- - doc/man/Makefile.am | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -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 --- -2.31.1 - diff --git a/0002-Only-include-vendordir-in-manual-page-if-set-401.patch b/0002-Only-include-vendordir-in-manual-page-if-set-401.patch deleted file mode 100644 index f5d345e..0000000 --- a/0002-Only-include-vendordir-in-manual-page-if-set-401.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 04109c25a7dbd11404f7f23a9a405b9b9d6b7246 Mon Sep 17 00:00:00 2001 -From: Thorsten Kukuk <5908016+thkukuk@users.noreply.github.com> -Date: Tue, 2 Nov 2021 11:46:24 +0100 -Subject: [PATCH 2/3] Only include vendordir in manual page if set (#401) - ---- - configure.ac | 4 ++-- - doc/man/pam.8.xml | 5 ++--- - 2 files changed, 4 insertions(+), 5 deletions(-) - -diff --git a/configure.ac b/configure.ac -index c06bc7dd..eb98d69a 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -507,9 +507,9 @@ 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'" -+ STRINGPARAM_VENDORDIR="--stringparam vendordir '$enable_vendordir' --stringparam profile.condition 'with_vendordir'" - else -- STRINGPARAM_VENDORDIR="--stringparam vendordir ''" -+ STRINGPARAM_VENDORDIR="--stringparam profile.condition 'without_vendordir'" - fi - AC_SUBST([STRINGPARAM_VENDORDIR]) - -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. - - - -- -+ - %vendordir%/pam.d - - - the Linux-PAM vendor configuration - directory. Files in /etc/pam.d and - /usr/lib/pam.d override files with the same -- name in this directory. Only available if Linux-PAM was compiled -- with vendordir enabled. -+ name in this directory. - - - --- -2.31.1 - diff --git a/0003-Use-vendor-specific-limits.conf-as-fallback-402.patch b/0003-Use-vendor-specific-limits.conf-as-fallback-402.patch deleted file mode 100644 index fab98fe..0000000 --- a/0003-Use-vendor-specific-limits.conf-as-fallback-402.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 5deaac423159103d02b146afa753a8ebb7fddf09 Mon Sep 17 00:00:00 2001 -From: Thorsten Kukuk <5908016+thkukuk@users.noreply.github.com> -Date: Wed, 3 Nov 2021 09:02:40 +0100 -Subject: [PATCH 3/3] Use vendor specific limits.conf as fallback (#402) - -* Use vendor specific limits.conf as fallback ---- - modules/pam_limits/pam_limits.8.xml | 6 ++++++ - modules/pam_limits/pam_limits.c | 19 ++++++++++++++++--- - 2 files changed, 22 insertions(+), 3 deletions(-) - -diff --git a/modules/pam_limits/pam_limits.8.xml b/modules/pam_limits/pam_limits.8.xml -index bc46cbf4..c1c10eca 100644 ---- a/modules/pam_limits/pam_limits.8.xml -+++ b/modules/pam_limits/pam_limits.8.xml -@@ -57,6 +57,12 @@ - If a config file is explicitly specified with a module option then the - files in the above directory are not parsed. - -+ -+ If there is no explicitly specified configuration file and -+ /etc/security/limits.conf does not exist, -+ %vendordir%/security/limits.conf is used. -+ If this file does not exist, too, an error is thrown. -+ - - The module must not be called by a multithreaded application. - -diff --git a/modules/pam_limits/pam_limits.c b/modules/pam_limits/pam_limits.c -index 7cc45d77..53188965 100644 ---- a/modules/pam_limits/pam_limits.c -+++ b/modules/pam_limits/pam_limits.c -@@ -816,9 +816,22 @@ parse_config_file(pam_handle_t *pamh, const char *uname, uid_t uid, gid_t gid, - 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 VENDORDIR -+ /* 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(VENDORDIR"/security/limits.conf", "r"); -+ -+ if (fil == NULL) -+#endif -+ { -+ pam_syslog (pamh, LOG_WARNING, -+ "cannot read settings from %s: %s", CONF_FILE, -+ strerror(err)); -+ return PAM_SERVICE_ERR; -+ } - } - - /* start the show */ --- -2.31.1 - diff --git a/pam-git.diff b/pam-git.diff new file mode 100644 index 0000000..3f05e3c --- /dev/null +++ b/pam-git.diff @@ -0,0 +1,1672 @@ +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 ''" ++ 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. + + + +- ++ + %vendordir%/pam.d + + + the Linux-PAM vendor configuration + directory. Files in /etc/pam.d and + /usr/lib/pam.d override files with the same +- name in this directory. Only available if Linux-PAM was compiled +- with vendordir enabled. ++ name in this directory. + + + +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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/*************************************** ++ * @brief echo off/on ++ * @param[in] fd file descriptor ++ * @param[in] off 1 - echo off,0 - 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 @@ + + + +- ++ + Use another configuration file instead of the default + /etc/security/faillock.conf. + ++ ++ Use another configuration file instead of the default ++ which is to use the file ++ /etc/security/faillock.conf or, ++ if that one is not present, the file ++ %vendordir%/security/faillock.conf. ++ + + + +@@ -328,6 +335,15 @@ session required pam_selinux.so open + the config file for pam_faillock options + + ++ ++ %vendordir%/security/faillock.conf ++ ++ ++ the config file for pam_faillock options. It will be used if ++ /etc/security/faillock.conf does not exist. ++ ++ ++ + + + +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 ++#include ++#include ++#include ++#include ++ ++#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 + /etc/security/group.conf. + ++ ++ If /etc/security/group.conf does not exist, ++ %vendordir%/security/group.conf is used. ++ + + 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 + #include + #include ++#include + + #include + #include +@@ -23,6 +24,10 @@ + #include + #include + ++#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. + ++ ++ If there is no explicitly specified configuration file and ++ /etc/security/limits.conf does not exist, ++ %vendordir%/security/limits.conf is used. ++ + + The module must not be called by a multithreaded application. + +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 + #endif + ++#ifndef PR_SET_NO_NEW_PRIVS ++# define PR_SET_NO_NEW_PRIVS 38 /* from */ ++#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 + #include + #include ++#include + #include + #include + #include +@@ -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 @@ + + authtok_type=STRING + ++ ++ file=/path/filename ++ + + + +@@ -137,6 +140,19 @@ + + + ++ ++ ++ ++ ++ ++ ++ Store password history in file /path/filename ++ rather than the default location. The default location is ++ /etc/security/opasswd. ++ ++ ++ ++ + + + +@@ -213,7 +229,7 @@ password required pam_unix.so use_authtok + + /etc/security/opasswd + +- File with password history ++ Default file with password history + + + +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 @@ + sepermit.conf5 + for details. + +- ++ ++ If there is no explicitly specified configuration file and ++ /etc/security/sepermit.conf does not exist, ++ %vendordir%/security/sepermit.conf is used. ++ + + + +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 + ++#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 ++ */ ++ ++#include "test_assert.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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 + #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" diff --git a/pam-hostnames-in-access_conf.patch b/pam-hostnames-in-access_conf.patch index 3f2c1f9..076ff17 100644 --- a/pam-hostnames-in-access_conf.patch +++ b/pam-hostnames-in-access_conf.patch @@ -1,12 +1,52 @@ -Index: Linux-PAM-1.3.91/modules/pam_access/pam_access.c -=================================================================== ---- Linux-PAM-1.3.91.orig/modules/pam_access/pam_access.c -+++ Linux-PAM-1.3.91/modules/pam_access/pam_access.c -@@ -699,10 +699,10 @@ string_match (pam_handle_t *pamh, const - return (NO); +From d275f22cf28da287e93b5e5a1fdb8a68b2815982 Mon Sep 17 00:00:00 2001 +From: Thorsten Kukuk +Date: Thu, 24 Feb 2022 10:37:32 +0100 +Subject: [PATCH] pam_access: handle hostnames in access.conf + +According to the manual page, the following entry is valid but does not +work: +-:root:ALL EXCEPT localhost + +See https://bugzilla.suse.com/show_bug.cgi?id=1019866 + +Patched is based on PR#226 from Josef Moellers +--- + modules/pam_access/pam_access.c | 95 ++++++++++++++++++++++++++------- + 1 file changed, 76 insertions(+), 19 deletions(-) + +diff --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c +index 0d033aa20..3cec542be 100644 +--- a/modules/pam_access/pam_access.c ++++ b/modules/pam_access/pam_access.c +@@ -640,7 +640,7 @@ remote_match (pam_handle_t *pamh, char *tok, struct login_info *item) + if ((str_len = strlen(string)) > tok_len + && strcasecmp(tok, string + str_len - tok_len) == 0) + return YES; +- } else if (tok[tok_len - 1] == '.') { ++ } else if (tok[tok_len - 1] == '.') { /* internet network numbers (end with ".") */ + struct addrinfo hint; + + memset (&hint, '\0', sizeof (hint)); +@@ -681,7 +681,7 @@ remote_match (pam_handle_t *pamh, char *tok, struct login_info *item) + return NO; + } + +- /* Assume network/netmask with an IP of a host. */ ++ /* Assume network/netmask, IP address or hostname. */ + return network_netmask_match(pamh, tok, string, item); } -- +@@ -699,7 +699,7 @@ string_match (pam_handle_t *pamh, const char *tok, const char *string, + /* + * If the token has the magic value "ALL" the match always succeeds. + * Otherwise, return YES if the token fully matches the string. +- * "NONE" token matches NULL string. ++ * "NONE" token matches NULL string. + */ + + if (strcasecmp(tok, "ALL") == 0) { /* all: always matches */ +@@ -717,7 +717,8 @@ string_match (pam_handle_t *pamh, const char *tok, const char *string, + /* network_netmask_match - match a string against one token * where string is a hostname or ip (v4,v6) address and tok - * represents either a single ip (v4,v6) address or a network/netmask @@ -15,13 +55,11 @@ Index: Linux-PAM-1.3.91/modules/pam_access/pam_access.c */ static int network_netmask_match (pam_handle_t *pamh, -@@ -711,10 +711,14 @@ network_netmask_match (pam_handle_t *pam +@@ -726,10 +727,12 @@ network_netmask_match (pam_handle_t *pamh, char *netmask_ptr; char netmask_string[MAXHOSTNAMELEN + 1]; int addr_type; -+ struct addrinfo *ai; -+ struct sockaddr_storage tok_addr; -+ struct addrinfo hint; ++ struct addrinfo *ai = NULL; if (item->debug) - pam_syslog (pamh, LOG_DEBUG, @@ -31,33 +69,17 @@ Index: Linux-PAM-1.3.91/modules/pam_access/pam_access.c /* OK, check if tok is of type addr/mask */ if ((netmask_ptr = strchr(tok, '/')) != NULL) { -@@ -724,7 +728,7 @@ network_netmask_match (pam_handle_t *pam - *netmask_ptr = 0; - netmask_ptr++; - -- if (isipaddr(tok, &addr_type, NULL) == NO) -+ if (isipaddr(tok, &addr_type, &tok_addr) == NO) - { /* no netaddr */ - return NO; - } -@@ -748,19 +752,47 @@ network_netmask_match (pam_handle_t *pam +@@ -763,54 +766,108 @@ network_netmask_match (pam_handle_t *pamh, netmask_ptr = number_to_netmask(netmask, addr_type, netmask_string, MAXHOSTNAMELEN); } - } + -+ /* -+ * Although isipaddr() has already converted the IP address, -+ * we call getaddrinfo here to properly construct an addrinfo list -+ */ -+ memset (&hint, '\0', sizeof (hint)); -+ hint.ai_flags = 0; -+ hint.ai_family = AF_UNSPEC; -+ -+ ai = NULL; /* just to be on the safe side */ -+ -+ /* The following should not fail ... */ -+ if (getaddrinfo (tok, NULL, &hint, &ai) != 0) ++ /* ++ * Construct an addrinfo list from the IP address. ++ * This should not fail as the input is a correct IP address... ++ */ ++ if (getaddrinfo (tok, NULL, NULL, &ai) != 0) + { + return NO; + } @@ -70,15 +92,9 @@ Index: Linux-PAM-1.3.91/modules/pam_access/pam_access.c + * It is either an IP address or a hostname. + * Let getaddrinfo sort everything out + */ -+ memset (&hint, '\0', sizeof (hint)); -+ hint.ai_flags = 0; -+ hint.ai_family = AF_UNSPEC; -+ -+ ai = NULL; /* just to be on the safe side */ -+ -+ if (getaddrinfo (string, NULL, &hint, &ai) != 0) ++ if (getaddrinfo (tok, NULL, NULL, &ai) != 0) { -+ pam_syslog(pamh, LOG_ERR, "cannot resolve hostname \"%s\"", string); ++ pam_syslog(pamh, LOG_ERR, "cannot resolve hostname \"%s\"", tok); + return NO; } @@ -87,13 +103,25 @@ Index: Linux-PAM-1.3.91/modules/pam_access/pam_access.c if (isipaddr(string, NULL, NULL) != YES) { - /* Assume network/netmask with a name of a host. */ -- struct addrinfo hint; -- +- /* Assume network/netmask with a name of a host. */ + struct addrinfo hint; + ++ /* Assume network/netmask with a name of a host. */ memset (&hint, '\0', sizeof (hint)); hint.ai_flags = AI_CANONNAME; hint.ai_family = AF_UNSPEC; -@@ -773,29 +805,54 @@ network_netmask_match (pam_handle_t *pam + + if (item->gai_rv != 0) ++ { ++ freeaddrinfo(ai); + return NO; ++ } + else if (!item->res && + (item->gai_rv = getaddrinfo (string, NULL, &hint, &item->res)) != 0) ++ { ++ freeaddrinfo(ai); + return NO; ++ } else { struct addrinfo *runp = item->res; @@ -103,14 +131,18 @@ Index: Linux-PAM-1.3.91/modules/pam_access/pam_access.c { char buf[INET6_ADDRSTRLEN]; - DIAG_PUSH_IGNORE_CAST_ALIGN; +- DIAG_PUSH_IGNORE_CAST_ALIGN; - inet_ntop (runp->ai_family, - runp->ai_family == AF_INET - ? (void *) &((struct sockaddr_in *) runp->ai_addr)->sin_addr - : (void *) &((struct sockaddr_in6 *) runp->ai_addr)->sin6_addr, - buf, sizeof (buf)); -+ (void) getnameinfo (runp->ai_addr, runp->ai_addrlen, buf, sizeof (buf), NULL, 0, NI_NUMERICHOST); - DIAG_POP_IGNORE_CAST_ALIGN; +- DIAG_POP_IGNORE_CAST_ALIGN; ++ if (getnameinfo (runp->ai_addr, runp->ai_addrlen, buf, sizeof (buf), NULL, 0, NI_NUMERICHOST) != 0) ++ { ++ freeaddrinfo(ai); ++ return NO; ++ } - if (are_addresses_equal(buf, tok, netmask_ptr)) + for (runp1 = ai; runp1 != NULL; runp1 = runp1->ai_next) @@ -121,7 +153,11 @@ Index: Linux-PAM-1.3.91/modules/pam_access/pam_access.c + if (runp->ai_family != runp1->ai_family) + continue; + -+ (void) getnameinfo (runp1->ai_addr, runp1->ai_addrlen, buf1, sizeof (buf1), NULL, 0, NI_NUMERICHOST); ++ if (getnameinfo (runp1->ai_addr, runp1->ai_addrlen, buf1, sizeof (buf1), NULL, 0, NI_NUMERICHOST) != 0) ++ { ++ freeaddrinfo(ai); ++ return NO; ++ } + + if (are_addresses_equal (buf, buf1, netmask_ptr)) + { diff --git a/pam.changes b/pam.changes index ef1c80b..2a2c654 100644 --- a/pam.changes +++ b/pam.changes @@ -1,3 +1,23 @@ +------------------------------------------------------------------- +Fri Mar 11 11:25:35 UTC 2022 - Thorsten Kukuk + +- pam-hostnames-in-access_conf.patch: update with upstream + submission. Fixes several bugs including memory leaks. + +------------------------------------------------------------------- +Wed Feb 9 14:05:01 UTC 2022 - Thorsten Kukuk + +- Move group.conf and faillock.conf to /usr/etc/security + +------------------------------------------------------------------- +Mon Feb 7 09:46:16 UTC 2022 - Thorsten Kukuk + +- Update to current git for enhanced vendordir support (pam-git.diff) + Obsoletes: + - 0001-Include-pam_xauth_data.3.xml-in-source-archive-400.patch + - 0002-Only-include-vendordir-in-manual-page-if-set-401.patch + - 0003-Use-vendor-specific-limits.conf-as-fallback-402.patch + ------------------------------------------------------------------- Mon Dec 13 13:06:47 UTC 2021 - Thorsten Kukuk diff --git a/pam.spec b/pam.spec index 5c3afd1..6f5d2a4 100644 --- a/pam.spec +++ b/pam.spec @@ -69,9 +69,7 @@ Patch2: pam-hostnames-in-access_conf.patch Patch3: pam-xauth_ownership.patch Patch4: pam-bsc1177858-dont-free-environment-string.patch Patch10: pam_xauth_data.3.xml.patch -Patch11: 0001-Include-pam_xauth_data.3.xml-in-source-archive-400.patch -Patch12: 0002-Only-include-vendordir-in-manual-page-if-set-401.patch -Patch13: 0003-Use-vendor-specific-limits.conf-as-fallback-402.patch +Patch11: pam-git.diff BuildRequires: audit-devel BuildRequires: bison BuildRequires: flex @@ -183,8 +181,6 @@ cp -a %{SOURCE12} . %patch4 -p1 %patch10 -p1 %patch11 -p1 -%patch12 -p1 -%patch13 -p1 %build bash ./pam-login_defs-check.sh @@ -258,7 +254,7 @@ install -D -m 644 %{SOURCE2} %{buildroot}%{_rpmmacrodir}/macros.pam install -Dm0644 %{SOURCE13} %{buildroot}%{_tmpfilesdir}/pam.conf mkdir %{buildroot}%{_distconfdir}/security -mv %{buildroot}%{_sysconfdir}/security/limits.conf %{buildroot}%{_distconfdir}/security/limits.conf +mv %{buildroot}%{_sysconfdir}/security/{limits.conf,faillock.conf,group.conf} %{buildroot}%{_distconfdir}/security/ # Remove manual pages for main package %if !%{build_doc} @@ -328,8 +324,8 @@ done %endif %config(noreplace) %{_sysconfdir}/environment %config(noreplace) %{_pam_secconfdir}/access.conf -%config(noreplace) %{_pam_secconfdir}/group.conf -%config(noreplace) %{_pam_secconfdir}/faillock.conf +%{_distconfdir}/security/group.conf +%{_distconfdir}/security/faillock.conf %{_distconfdir}/security/limits.conf %config(noreplace) %{_pam_secconfdir}/pam_env.conf %if %{enable_selinux}