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/pam_shells.c | 73 +++++++++++++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 10 deletions(-) (limited to 'modules/pam_shells/pam_shells.c') 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