diff options
author | Stefan Schubert <schubi@suse.de> | 2022-11-16 18:37:14 +0100 |
---|---|---|
committer | Thorsten Kukuk <5908016+thkukuk@users.noreply.github.com> | 2022-12-12 10:15:12 +0100 |
commit | d71de05146d03c271bd5507724d24d1ad17c2be5 (patch) | |
tree | e536e38bf8809dbe89c6d4a4532a1e0b41032cd1 /modules | |
parent | e4917734c63ef6e887b3c4760771a14f75b9eb1f (diff) | |
download | pam-d71de05146d03c271bd5507724d24d1ad17c2be5.tar.gz pam-d71de05146d03c271bd5507724d24d1ad17c2be5.tar.bz2 pam-d71de05146d03c271bd5507724d24d1ad17c2be5.zip |
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=<vendor_dir> and
NOT defined --disable-econf, the files which define valid login shells will
be parsed in following order:
- <vendor_dir>/shells
- <vendor_dir>/shells.d/*
- /etc/shells.d/shells
But all files in <vendor_dir> 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
Diffstat (limited to 'modules')
-rw-r--r-- | modules/pam_shells/Makefile.am | 4 | ||||
-rw-r--r-- | modules/pam_shells/pam_shells.8.xml | 12 | ||||
-rw-r--r-- | modules/pam_shells/pam_shells.c | 73 |
3 files changed, 75 insertions, 14 deletions
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 <filename>/etc/shells</filename>. </para> + + <para condition="with_vendordir_and_with_econf"> + If this file does not exist, entries are taken from files + <filename>%vendordir%/shells</filename>, + <filename>%vendordir%/shells.d/*</filename> and + <filename>/etc/shells.d/*</filename> in that order. + </para> + <para> - It also checks if <filename>/etc/shells</filename> is a plain - file and not world writable. + It also checks if needed files (e.g. <filename>/etc/shells</filename>) are plain + files and not world writable. </para> </refsect1> 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 <string.h> #include <stdio.h> #include <stdlib.h> +#include <stdbool.h> #include <sys/stat.h> #include <syslog.h> #include <unistd.h> +#if defined (USE_ECONF) && defined (VENDORDIR) +#include <libeconf.h> +#endif #include <security/pam_modules.h> #include <security/pam_modutil.h> #include <security/pam_ext.h> #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; |