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"