From d71de05146d03c271bd5507724d24d1ad17c2be5 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Wed, 16 Nov 2022 18:37:14 +0100 Subject: pam_shells: Use the vendor directory as fallback for a distribution provided default config if there is no one in /etc. If pam will be compiled with the option --enable-vendordir= and NOT defined --disable-econf, the files which define valid login shells will be parsed in following order: - /shells - /shells.d/* - /etc/shells.d/shells But all files in will be ingnored if the user has defined his own file /etc/shells. This commit solves issue: https://github.com/linux-pam/linux-pam/issues/498 --- modules/pam_shells/Makefile.am | 4 +- modules/pam_shells/pam_shells.8.xml | 12 +++++- modules/pam_shells/pam_shells.c | 73 ++++++++++++++++++++++++++++++++----- 3 files changed, 75 insertions(+), 14 deletions(-) (limited to 'modules/pam_shells') diff --git a/modules/pam_shells/Makefile.am b/modules/pam_shells/Makefile.am index b91bada5..3ce3e1d0 100644 --- a/modules/pam_shells/Makefile.am +++ b/modules/pam_shells/Makefile.am @@ -18,14 +18,14 @@ securelibdir = $(SECUREDIR) secureconfdir = $(SCONFIGDIR) AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \ - $(WARN_CFLAGS) + $(WARN_CFLAGS) $(ECONF_CFLAGS) AM_LDFLAGS = -no-undefined -avoid-version -module if HAVE_VERSIONING AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map endif securelib_LTLIBRARIES = pam_shells.la -pam_shells_la_LIBADD = $(top_builddir)/libpam/libpam.la +pam_shells_la_LIBADD = $(top_builddir)/libpam/libpam.la $(ECONF_LIBS) if ENABLE_REGENERATE_MAN dist_noinst_DATA = README diff --git a/modules/pam_shells/pam_shells.8.xml b/modules/pam_shells/pam_shells.8.xml index 15f47671..73b4855a 100644 --- a/modules/pam_shells/pam_shells.8.xml +++ b/modules/pam_shells/pam_shells.8.xml @@ -29,9 +29,17 @@ pam_shells is a PAM module that only allows access to the system if the user's shell is listed in /etc/shells. + + + If this file does not exist, entries are taken from files + %vendordir%/shells, + %vendordir%/shells.d/* and + /etc/shells.d/* in that order. + + - It also checks if /etc/shells is a plain - file and not world writable. + It also checks if needed files (e.g. /etc/shells) are plain + files and not world writable. diff --git a/modules/pam_shells/pam_shells.c b/modules/pam_shells/pam_shells.c index dc8f4878..abebdd0c 100644 --- a/modules/pam_shells/pam_shells.c +++ b/modules/pam_shells/pam_shells.c @@ -13,27 +13,47 @@ #include #include #include +#include #include #include #include +#if defined (USE_ECONF) && defined (VENDORDIR) +#include +#endif #include #include #include #define SHELL_FILE "/etc/shells" - +#define SHELLS "shells" +#define ETCDIR "/etc" #define DEFAULT_SHELL "/bin/sh" +static bool check_file(const char *filename, const void *pamh) +{ + struct stat sb; + + if (stat(filename, &sb)) { + pam_syslog(pamh, LOG_ERR, "Cannot stat %s: %m", filename); + return false; /* must have /etc/shells */ + } + + if ((sb.st_mode & S_IWOTH) || !S_ISREG(sb.st_mode)) { + pam_syslog(pamh, LOG_ERR, + "%s is either world writable or not a normal file", + filename); + return false; + } + return true; +} + static int perform_check(pam_handle_t *pamh) { int retval = PAM_AUTH_ERR; const char *userName; const char *userShell; - char shellFileLine[256]; - struct stat sb; struct passwd * pw; - FILE * shellFile; retval = pam_get_user(pamh, &userName, NULL); if (retval != PAM_SUCCESS) { @@ -48,18 +68,50 @@ static int perform_check(pam_handle_t *pamh) if (userShell[0] == '\0') userShell = DEFAULT_SHELL; - if (stat(SHELL_FILE,&sb)) { - pam_syslog(pamh, LOG_ERR, "Cannot stat %s: %m", SHELL_FILE); - return PAM_AUTH_ERR; /* must have /etc/shells */ +#if defined (USE_ECONF) && defined (VENDORDIR) + size_t size = 0; + econf_err error; + char **keys; + econf_file *key_file; + + error = econf_readDirsWithCallback(&key_file, + VENDORDIR, + ETCDIR, + SHELLS, + NULL, + "", /* key only */ + "#", /* comment */ + check_file, pamh); + if (error) { + pam_syslog(pamh, LOG_ERR, + "Cannot parse shell files: %s", + econf_errString(error)); + return PAM_AUTH_ERR; } - if ((sb.st_mode & S_IWOTH) || !S_ISREG(sb.st_mode)) { + error = econf_getKeys(key_file, NULL, &size, &keys); + if (error) { pam_syslog(pamh, LOG_ERR, - "%s is either world writable or not a normal file", - SHELL_FILE); + "Cannot evaluate entries in shell files: %s", + econf_errString(error)); + econf_free (key_file); return PAM_AUTH_ERR; } + retval = 1; + for (size_t i = 0; i < size; i++) { + retval = strcmp(keys[i], userShell); + if (!retval) + break; + } + econf_free (key_file); +#else + char shellFileLine[256]; + FILE * shellFile; + + if (!check_file(SHELL_FILE, pamh)) + return PAM_AUTH_ERR; + shellFile = fopen(SHELL_FILE,"r"); if (shellFile == NULL) { /* Check that we opened it successfully */ pam_syslog(pamh, LOG_ERR, "Error opening %s: %m", SHELL_FILE); @@ -75,6 +127,7 @@ static int perform_check(pam_handle_t *pamh) } fclose(shellFile); + #endif if (retval) { return PAM_AUTH_ERR; -- cgit v1.2.3