From 9cfee1810d7756fdae56b5757b6bf8e8bec92495 Mon Sep 17 00:00:00 2001 From: Thorsten Kukuk Date: Fri, 8 Nov 2013 15:35:41 +0100 Subject: Allow DES as compatibility option for /etc/login.defs * modules/pam_unix/support.h: Add UNIX_DES --- modules/pam_unix/support.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'modules/pam_unix/support.h') diff --git a/modules/pam_unix/support.h b/modules/pam_unix/support.h index 65759384..6f5b2eb6 100644 --- a/modules/pam_unix/support.h +++ b/modules/pam_unix/support.h @@ -97,8 +97,9 @@ typedef struct { password hash algorithms */ #define UNIX_BLOWFISH_PASS 26 /* new password hashes will use blowfish */ #define UNIX_MIN_PASS_LEN 27 /* min length for password */ +#define UNIX_DES 28 /* DES, default */ /* -------------- */ -#define UNIX_CTRLS_ 28 /* number of ctrl arguments defined */ +#define UNIX_CTRLS_ 29 /* number of ctrl arguments defined */ #define UNIX_DES_CRYPT(ctrl) (off(UNIX_MD5_PASS,ctrl)&&off(UNIX_BIGCRYPT,ctrl)&&off(UNIX_SHA256_PASS,ctrl)&&off(UNIX_SHA512_PASS,ctrl)&&off(UNIX_BLOWFISH_PASS,ctrl)) @@ -135,6 +136,7 @@ static const UNIX_Ctrls unix_args[UNIX_CTRLS_] = /* UNIX_ALGO_ROUNDS */ {"rounds=", _ALL_ON_, 0100000000, 0}, /* UNIX_BLOWFISH_PASS */ {"blowfish", _ALL_ON_^(0260420000), 0200000000, 1}, /* UNIX_MIN_PASS_LEN */ {"minlen=", _ALL_ON_, 0400000000, 0}, +/* UNIX_DES */ {"des", _ALL_ON_^(0260420000), 0, 1}, }; #define UNIX_DEFAULTS (unix_args[UNIX__NONULL].flag) -- cgit v1.2.3 From b0ec5d1e472a0cd74972bfe9575dcf6a3d0cad1c Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Fri, 24 Jan 2014 15:32:08 +0000 Subject: Introduce pam_modutil_sanitize_helper_fds This change introduces pam_modutil_sanitize_helper_fds - a new function that redirects standard descriptors and closes all other descriptors. pam_modutil_sanitize_helper_fds supports three types of input and output redirection: - PAM_MODUTIL_IGNORE_FD: do not redirect at all. - PAM_MODUTIL_PIPE_FD: redirect to a pipe. For stdin, it is implemented by creating a pipe, closing its write end, and redirecting stdin to its read end. Likewise, for stdout/stderr it is implemented by creating a pipe, closing its read end, and redirecting to its write end. Unlike stdin redirection, stdout/stderr redirection to a pipe has a side effect that a process writing to such descriptor should be prepared to handle SIGPIPE appropriately. - PAM_MODUTIL_NULL_FD: redirect to /dev/null. For stdin, it is implemented via PAM_MODUTIL_PIPE_FD because there is no functional difference. For stdout/stderr, it is classic redirection to /dev/null. PAM_MODUTIL_PIPE_FD is usually more suitable due to linux kernel security restrictions, but when the helper process might be writing to the corresponding descriptor and termination of the helper process by SIGPIPE is not desirable, one should choose PAM_MODUTIL_NULL_FD. * libpam/pam_modutil_sanitize.c: New file. * libpam/Makefile.am (libpam_la_SOURCES): Add it. * libpam/include/security/pam_modutil.h (pam_modutil_redirect_fd, pam_modutil_sanitize_helper_fds): New declarations. * libpam/libpam.map (LIBPAM_MODUTIL_1.1.9): New interface. * modules/pam_exec/pam_exec.c (call_exec): Use pam_modutil_sanitize_helper_fds. * modules/pam_mkhomedir/pam_mkhomedir.c (create_homedir): Likewise. * modules/pam_unix/pam_unix_acct.c (_unix_run_verify_binary): Likewise. * modules/pam_unix/pam_unix_passwd.c (_unix_run_update_binary): Likewise. * modules/pam_unix/support.c (_unix_run_helper_binary): Likewise. * modules/pam_xauth/pam_xauth.c (run_coprocess): Likewise. * modules/pam_unix/support.h (MAX_FD_NO): Remove. --- libpam/Makefile.am | 2 +- libpam/include/security/pam_modutil.h | 13 +++ libpam/libpam.map | 5 + libpam/pam_modutil_sanitize.c | 175 ++++++++++++++++++++++++++++++++++ modules/pam_exec/pam_exec.c | 34 ++----- modules/pam_mkhomedir/pam_mkhomedir.c | 15 +-- modules/pam_unix/pam_unix_acct.c | 23 ++--- modules/pam_unix/pam_unix_passwd.c | 20 ++-- modules/pam_unix/support.c | 20 ++-- modules/pam_unix/support.h | 2 - modules/pam_xauth/pam_xauth.c | 26 +++-- 11 files changed, 251 insertions(+), 84 deletions(-) create mode 100644 libpam/pam_modutil_sanitize.c (limited to 'modules/pam_unix/support.h') diff --git a/libpam/Makefile.am b/libpam/Makefile.am index 417ca779..685a797d 100644 --- a/libpam/Makefile.am +++ b/libpam/Makefile.am @@ -43,4 +43,4 @@ libpam_la_SOURCES = pam_account.c pam_auth.c pam_data.c pam_delay.c \ pam_modutil_cleanup.c pam_modutil_getpwnam.c pam_modutil_ioloop.c \ pam_modutil_getgrgid.c pam_modutil_getpwuid.c pam_modutil_getgrnam.c \ pam_modutil_getspnam.c pam_modutil_getlogin.c pam_modutil_ingroup.c \ - pam_modutil_priv.c + pam_modutil_priv.c pam_modutil_sanitize.c diff --git a/libpam/include/security/pam_modutil.h b/libpam/include/security/pam_modutil.h index 8087ba15..4ce8c552 100644 --- a/libpam/include/security/pam_modutil.h +++ b/libpam/include/security/pam_modutil.h @@ -129,6 +129,19 @@ extern int PAM_NONNULL((1,2)) pam_modutil_regain_priv(pam_handle_t *pamh, struct pam_modutil_privs *p); +enum pam_modutil_redirect_fd { + PAM_MODUTIL_IGNORE_FD, /* do not redirect */ + PAM_MODUTIL_PIPE_FD, /* redirect to a pipe */ + PAM_MODUTIL_NULL_FD, /* redirect to /dev/null */ +}; + +/* redirect standard descriptors, close all other descriptors. */ +extern int PAM_NONNULL((1)) +pam_modutil_sanitize_helper_fds(pam_handle_t *pamh, + enum pam_modutil_redirect_fd redirect_stdin, + enum pam_modutil_redirect_fd redirect_stdout, + enum pam_modutil_redirect_fd redirect_stderr); + #ifdef __cplusplus } #endif diff --git a/libpam/libpam.map b/libpam/libpam.map index b0885d65..d6835b47 100644 --- a/libpam/libpam.map +++ b/libpam/libpam.map @@ -67,3 +67,8 @@ LIBPAM_MODUTIL_1.1.3 { pam_modutil_drop_priv; pam_modutil_regain_priv; } LIBPAM_MODUTIL_1.1; + +LIBPAM_MODUTIL_1.1.9 { + global: + pam_modutil_sanitize_helper_fds; +} LIBPAM_MODUTIL_1.1.3; diff --git a/libpam/pam_modutil_sanitize.c b/libpam/pam_modutil_sanitize.c new file mode 100644 index 00000000..65f85d01 --- /dev/null +++ b/libpam/pam_modutil_sanitize.c @@ -0,0 +1,175 @@ +/* + * This file implements the following functions: + * pam_modutil_sanitize_helper_fds: + * redirects standard descriptors, closes all other descriptors. + */ + +#include "pam_modutil_private.h" +#include +#include +#include +#include +#include + +/* + * Creates a pipe, closes its write end, redirects fd to its read end. + * Returns fd on success, -1 otherwise. + */ +static int +redirect_in_pipe(pam_handle_t *pamh, int fd, const char *name) +{ + int in[2]; + + if (pipe(in) < 0) { + pam_syslog(pamh, LOG_ERR, "Could not create pipe: %m"); + return -1; + } + + close(in[1]); + + if (in[0] == fd) + return fd; + + if (dup2(in[0], fd) != fd) { + pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", name); + fd = -1; + } + + close(in[0]); + return fd; +} + +/* + * Creates a pipe, closes its read end, redirects fd to its write end. + * Returns fd on success, -1 otherwise. + */ +static int +redirect_out_pipe(pam_handle_t *pamh, int fd, const char *name) +{ + int out[2]; + + if (pipe(out) < 0) { + pam_syslog(pamh, LOG_ERR, "Could not create pipe: %m"); + return -1; + } + + close(out[0]); + + if (out[1] == fd) + return fd; + + if (dup2(out[1], fd) != fd) { + pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", name); + fd = -1; + } + + close(out[1]); + return fd; +} + +/* + * Opens /dev/null for writing, redirects fd there. + * Returns fd on success, -1 otherwise. + */ +static int +redirect_out_null(pam_handle_t *pamh, int fd, const char *name) +{ + int null = open("/dev/null", O_WRONLY); + + if (null < 0) { + pam_syslog(pamh, LOG_ERR, "open of %s failed: %m", "/dev/null"); + return -1; + } + + if (null == fd) + return fd; + + if (dup2(null, fd) != fd) { + pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", name); + fd = -1; + } + + close(null); + return fd; +} + +static int +redirect_out(pam_handle_t *pamh, enum pam_modutil_redirect_fd mode, + int fd, const char *name) +{ + switch (mode) { + case PAM_MODUTIL_PIPE_FD: + if (redirect_out_pipe(pamh, fd, name) < 0) + return -1; + break; + case PAM_MODUTIL_NULL_FD: + if (redirect_out_null(pamh, fd, name) < 0) + return -1; + break; + case PAM_MODUTIL_IGNORE_FD: + break; + } + return fd; +} + +/* Closes all descriptors after stderr. */ +static void +close_fds(void) +{ + /* + * An arbitrary upper limit for the maximum file descriptor number + * returned by RLIMIT_NOFILE. + */ + const int MAX_FD_NO = 65535; + + /* The lower limit is the same as for _POSIX_OPEN_MAX. */ + const int MIN_FD_NO = 20; + + int fd; + struct rlimit rlim; + + if (getrlimit(RLIMIT_NOFILE, &rlim) || rlim.rlim_max > MAX_FD_NO) + fd = MAX_FD_NO; + else if (rlim.rlim_max < MIN_FD_NO) + fd = MIN_FD_NO; + else + fd = rlim.rlim_max - 1; + + for (; fd > STDERR_FILENO; --fd) + close(fd); +} + +int +pam_modutil_sanitize_helper_fds(pam_handle_t *pamh, + enum pam_modutil_redirect_fd stdin_mode, + enum pam_modutil_redirect_fd stdout_mode, + enum pam_modutil_redirect_fd stderr_mode) +{ + if (stdin_mode != PAM_MODUTIL_IGNORE_FD && + redirect_in_pipe(pamh, STDIN_FILENO, "stdin") < 0) { + return -1; + } + + if (redirect_out(pamh, stdout_mode, STDOUT_FILENO, "stdout") < 0) + return -1; + + /* + * If stderr should not be ignored and + * redirect mode for stdout and stderr are the same, + * optimize by redirecting stderr to stdout. + */ + if (stderr_mode != PAM_MODUTIL_IGNORE_FD && + stdout_mode == stderr_mode) { + if (dup2(STDOUT_FILENO, STDERR_FILENO) != STDERR_FILENO) { + pam_syslog(pamh, LOG_ERR, + "dup2 of %s failed: %m", "stderr"); + return -1; + } + } else { + if (redirect_out(pamh, stderr_mode, STDERR_FILENO, "stderr") < 0) + return -1; + } + + close_fds(); + return 0; +} diff --git a/modules/pam_exec/pam_exec.c b/modules/pam_exec/pam_exec.c index b56e4b26..12c44444 100644 --- a/modules/pam_exec/pam_exec.c +++ b/modules/pam_exec/pam_exec.c @@ -302,6 +302,10 @@ call_exec (const char *pam_type, pam_handle_t *pamh, char **envlist, **tmp; int envlen, nitems; char *envstr; + enum pam_modutil_redirect_fd redirect_stdin = + expose_authtok ? PAM_MODUTIL_IGNORE_FD : PAM_MODUTIL_PIPE_FD; + enum pam_modutil_redirect_fd redirect_stdout = + (use_stdout || logfile) ? PAM_MODUTIL_IGNORE_FD : PAM_MODUTIL_NULL_FD; /* First, move all the pipes off of stdin, stdout, and stderr, to ensure * that calls to dup2 won't close them. */ @@ -330,18 +334,6 @@ call_exec (const char *pam_type, pam_handle_t *pamh, _exit (err); } } - else - { - close (STDIN_FILENO); - - /* New stdin. */ - if ((i = open ("/dev/null", O_RDWR)) < 0) - { - int err = errno; - pam_syslog (pamh, LOG_ERR, "open of /dev/null failed: %m"); - _exit (err); - } - } /* Set up stdout. */ @@ -374,26 +366,18 @@ call_exec (const char *pam_type, pam_handle_t *pamh, free (buffer); } } - else - { - close (STDOUT_FILENO); - if ((i = open ("/dev/null", O_RDWR)) < 0) - { - int err = errno; - pam_syslog (pamh, LOG_ERR, "open of /dev/null failed: %m"); - _exit (err); - } - } - if (dup2 (STDOUT_FILENO, STDERR_FILENO) == -1) + if ((use_stdout || logfile) && + dup2 (STDOUT_FILENO, STDERR_FILENO) == -1) { int err = errno; pam_syslog (pamh, LOG_ERR, "dup2 failed: %m"); _exit (err); } - for (i = 3; i < sysconf (_SC_OPEN_MAX); i++) - close (i); + if (pam_modutil_sanitize_helper_fds(pamh, redirect_stdin, + redirect_stdout, redirect_stdout) < 0) + _exit(1); if (call_setuid) if (setuid (geteuid ()) == -1) diff --git a/modules/pam_mkhomedir/pam_mkhomedir.c b/modules/pam_mkhomedir/pam_mkhomedir.c index a867a738..c9220897 100644 --- a/modules/pam_mkhomedir/pam_mkhomedir.c +++ b/modules/pam_mkhomedir/pam_mkhomedir.c @@ -58,8 +58,6 @@ #include #include -#define MAX_FD_NO 10000 - /* argument parsing */ #define MKHOMEDIR_DEBUG 020 /* be verbose about things */ #define MKHOMEDIR_QUIET 040 /* keep quiet about things */ @@ -131,18 +129,13 @@ create_homedir (pam_handle_t *pamh, options_t *opt, /* fork */ child = fork(); if (child == 0) { - int i; - struct rlimit rlim; static char *envp[] = { NULL }; const char *args[] = { NULL, NULL, NULL, NULL, NULL }; - if (getrlimit(RLIMIT_NOFILE, &rlim)==0) { - if (rlim.rlim_max >= MAX_FD_NO) - rlim.rlim_max = MAX_FD_NO; - for (i=0; i < (int)rlim.rlim_max; i++) { - close(i); - } - } + if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_PIPE_FD, + PAM_MODUTIL_PIPE_FD, + PAM_MODUTIL_PIPE_FD) < 0) + _exit(PAM_SYSTEM_ERR); /* exec the mkhomedir helper */ args[0] = MKHOMEDIR_HELPER; diff --git a/modules/pam_unix/pam_unix_acct.c b/modules/pam_unix/pam_unix_acct.c index dc505e73..27998451 100644 --- a/modules/pam_unix/pam_unix_acct.c +++ b/modules/pam_unix/pam_unix_acct.c @@ -98,24 +98,21 @@ int _unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, /* fork */ child = fork(); if (child == 0) { - int i=0; - struct rlimit rlim; static char *envp[] = { NULL }; const char *args[] = { NULL, NULL, NULL, NULL }; - /* reopen stdout as pipe */ - dup2(fds[1], STDOUT_FILENO); - /* XXX - should really tidy up PAM here too */ - if (getrlimit(RLIMIT_NOFILE,&rlim)==0) { - if (rlim.rlim_max >= MAX_FD_NO) - rlim.rlim_max = MAX_FD_NO; - for (i=0; i < (int)rlim.rlim_max; i++) { - if (i != STDOUT_FILENO) { - close(i); - } - } + /* reopen stdout as pipe */ + if (dup2(fds[1], STDOUT_FILENO) != STDOUT_FILENO) { + pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdout"); + _exit(PAM_AUTHINFO_UNAVAIL); + } + + if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_PIPE_FD, + PAM_MODUTIL_IGNORE_FD, + PAM_MODUTIL_PIPE_FD) < 0) { + _exit(PAM_AUTHINFO_UNAVAIL); } if (geteuid() == 0) { diff --git a/modules/pam_unix/pam_unix_passwd.c b/modules/pam_unix/pam_unix_passwd.c index 5f3a3db3..606071ea 100644 --- a/modules/pam_unix/pam_unix_passwd.c +++ b/modules/pam_unix/pam_unix_passwd.c @@ -201,8 +201,6 @@ static int _unix_run_update_binary(pam_handle_t *pamh, unsigned int ctrl, const /* fork */ child = fork(); if (child == 0) { - int i=0; - struct rlimit rlim; static char *envp[] = { NULL }; const char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL }; char buffer[16]; @@ -210,15 +208,15 @@ static int _unix_run_update_binary(pam_handle_t *pamh, unsigned int ctrl, const /* XXX - should really tidy up PAM here too */ /* reopen stdin as pipe */ - dup2(fds[0], STDIN_FILENO); - - if (getrlimit(RLIMIT_NOFILE,&rlim)==0) { - if (rlim.rlim_max >= MAX_FD_NO) - rlim.rlim_max = MAX_FD_NO; - for (i=0; i < (int)rlim.rlim_max; i++) { - if (i != STDIN_FILENO) - close(i); - } + if (dup2(fds[0], STDIN_FILENO) != STDIN_FILENO) { + pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin"); + _exit(PAM_AUTHINFO_UNAVAIL); + } + + if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD, + PAM_MODUTIL_PIPE_FD, + PAM_MODUTIL_PIPE_FD) < 0) { + _exit(PAM_AUTHINFO_UNAVAIL); } /* exec binary helper */ diff --git a/modules/pam_unix/support.c b/modules/pam_unix/support.c index 3a849c81..fdb45c20 100644 --- a/modules/pam_unix/support.c +++ b/modules/pam_unix/support.c @@ -564,23 +564,21 @@ static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd, /* fork */ child = fork(); if (child == 0) { - int i=0; - struct rlimit rlim; static char *envp[] = { NULL }; const char *args[] = { NULL, NULL, NULL, NULL }; /* XXX - should really tidy up PAM here too */ /* reopen stdin as pipe */ - dup2(fds[0], STDIN_FILENO); - - if (getrlimit(RLIMIT_NOFILE,&rlim)==0) { - if (rlim.rlim_max >= MAX_FD_NO) - rlim.rlim_max = MAX_FD_NO; - for (i=0; i < (int)rlim.rlim_max; i++) { - if (i != STDIN_FILENO) - close(i); - } + if (dup2(fds[0], STDIN_FILENO) != STDIN_FILENO) { + pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin"); + _exit(PAM_AUTHINFO_UNAVAIL); + } + + if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD, + PAM_MODUTIL_PIPE_FD, + PAM_MODUTIL_PIPE_FD) < 0) { + _exit(PAM_AUTHINFO_UNAVAIL); } if (geteuid() == 0) { diff --git a/modules/pam_unix/support.h b/modules/pam_unix/support.h index 6f5b2eb6..cd6ddb76 100644 --- a/modules/pam_unix/support.h +++ b/modules/pam_unix/support.h @@ -141,8 +141,6 @@ static const UNIX_Ctrls unix_args[UNIX_CTRLS_] = #define UNIX_DEFAULTS (unix_args[UNIX__NONULL].flag) -#define MAX_FD_NO 2000000 - /* use this to free strings. ESPECIALLY password strings */ #define _pam_delete(xx) \ diff --git a/modules/pam_xauth/pam_xauth.c b/modules/pam_xauth/pam_xauth.c index c7ce55ab..2be43513 100644 --- a/modules/pam_xauth/pam_xauth.c +++ b/modules/pam_xauth/pam_xauth.c @@ -128,7 +128,6 @@ run_coprocess(pam_handle_t *pamh, const char *input, char **output, /* We're the child. */ size_t j; const char *args[10]; - int maxopened; /* Drop privileges. */ if (setgid(gid) == -1) { @@ -150,19 +149,26 @@ run_coprocess(pam_handle_t *pamh, const char *input, char **output, (unsigned long) geteuid ()); _exit (err); } - /* Initialize the argument list. */ - memset(args, 0, sizeof(args)); /* Set the pipe descriptors up as stdin and stdout, and close * everything else, including the original values for the * descriptors. */ - dup2(ipipe[0], STDIN_FILENO); - dup2(opipe[1], STDOUT_FILENO); - maxopened = (int)sysconf(_SC_OPEN_MAX); - for (i = 0; i < maxopened; i++) { - if ((i != STDIN_FILENO) && (i != STDOUT_FILENO)) { - close(i); - } + if (dup2(ipipe[0], STDIN_FILENO) != STDIN_FILENO) { + int err = errno; + pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin"); + _exit(err); } + if (dup2(opipe[1], STDOUT_FILENO) != STDOUT_FILENO) { + int err = errno; + pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdout"); + _exit(err); + } + if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD, + PAM_MODUTIL_IGNORE_FD, + PAM_MODUTIL_NULL_FD) < 0) { + _exit(1); + } + /* Initialize the argument list. */ + memset(args, 0, sizeof(args)); /* Convert the varargs list into a regular array of strings. */ va_start(ap, command); args[0] = command; -- cgit v1.2.3 From aa7acd0d8ab1cba8e079ee2b801c944896d2638b Mon Sep 17 00:00:00 2001 From: Thorsten Kukuk Date: Thu, 23 Apr 2015 16:04:32 +0200 Subject: Add "quiet" option to pam_unix to suppress informential info messages from session. * modules/pam_unix/pam_unix.8.xml: Document new option. * modules/pam_unix/support.h: Add quiet option. * modules/pam_unix/pam_unix_sess.c: Don't print LOG_INFO messages if 'quiet' option is set. --- modules/pam_unix/pam_unix.8.xml | 15 +++++++++++++++ modules/pam_unix/pam_unix_sess.c | 10 ++++++---- modules/pam_unix/support.h | 6 ++++-- 3 files changed, 25 insertions(+), 6 deletions(-) (limited to 'modules/pam_unix/support.h') diff --git a/modules/pam_unix/pam_unix.8.xml b/modules/pam_unix/pam_unix.8.xml index 9ce084e3..e1702420 100644 --- a/modules/pam_unix/pam_unix.8.xml +++ b/modules/pam_unix/pam_unix.8.xml @@ -129,6 +129,21 @@ + + + + + + + Turns off informational messages namely messages about + session open and close via + + syslog3 + . + + + + diff --git a/modules/pam_unix/pam_unix_sess.c b/modules/pam_unix/pam_unix_sess.c index d1376732..5d001816 100644 --- a/modules/pam_unix/pam_unix_sess.c +++ b/modules/pam_unix/pam_unix_sess.c @@ -96,8 +96,9 @@ pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) if (login_name == NULL) { login_name = ""; } - pam_syslog(pamh, LOG_INFO, "session opened for user %s by %s(uid=%lu)", - user_name, login_name, (unsigned long)getuid()); + if (off (UNIX_QUIET, ctrl)) + pam_syslog(pamh, LOG_INFO, "session opened for user %s by %s(uid=%lu)", + user_name, login_name, (unsigned long)getuid()); return PAM_SUCCESS; } @@ -126,8 +127,9 @@ pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) "close_session - error recovering service"); return PAM_SESSION_ERR; } - pam_syslog(pamh, LOG_INFO, "session closed for user %s", - user_name); + if (off (UNIX_QUIET, ctrl)) + pam_syslog(pamh, LOG_INFO, "session closed for user %s", + user_name); return PAM_SUCCESS; } diff --git a/modules/pam_unix/support.h b/modules/pam_unix/support.h index cd6ddb76..3729ce0c 100644 --- a/modules/pam_unix/support.h +++ b/modules/pam_unix/support.h @@ -97,9 +97,10 @@ typedef struct { password hash algorithms */ #define UNIX_BLOWFISH_PASS 26 /* new password hashes will use blowfish */ #define UNIX_MIN_PASS_LEN 27 /* min length for password */ -#define UNIX_DES 28 /* DES, default */ +#define UNIX_QUIET 28 /* Don't print informational messages */ +#define UNIX_DES 29 /* DES, default */ /* -------------- */ -#define UNIX_CTRLS_ 29 /* number of ctrl arguments defined */ +#define UNIX_CTRLS_ 30 /* number of ctrl arguments defined */ #define UNIX_DES_CRYPT(ctrl) (off(UNIX_MD5_PASS,ctrl)&&off(UNIX_BIGCRYPT,ctrl)&&off(UNIX_SHA256_PASS,ctrl)&&off(UNIX_SHA512_PASS,ctrl)&&off(UNIX_BLOWFISH_PASS,ctrl)) @@ -136,6 +137,7 @@ static const UNIX_Ctrls unix_args[UNIX_CTRLS_] = /* UNIX_ALGO_ROUNDS */ {"rounds=", _ALL_ON_, 0100000000, 0}, /* UNIX_BLOWFISH_PASS */ {"blowfish", _ALL_ON_^(0260420000), 0200000000, 1}, /* UNIX_MIN_PASS_LEN */ {"minlen=", _ALL_ON_, 0400000000, 0}, +/* UNIX_QUIET */ {"quiet", _ALL_ON_, 01000000000, 0}, /* UNIX_DES */ {"des", _ALL_ON_^(0260420000), 0, 1}, }; -- cgit v1.2.3 From 8bb171506fc2579669fd86bd29885f256e26ccb0 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Wed, 17 Feb 2016 14:21:41 +0100 Subject: pam_unix: Add no_pass_expiry option to ignore password expiration. * modules/pam_unix/pam_unix.8.xml: Document the no_pass_expiry option. * modules/pam_unix/pam_unix_acct.c (pam_sm_acct_mgmt): If no_pass_expiry is on and return value data is not set to PAM_SUCCESS then ignore PAM_NEW_AUTHTOK_REQD and PAM_AUTHTOK_EXPIRED returns. * modules/pam_unix/pam_unix_auth.c (pam_sm_authenticate): Always set the return value data. (pam_sm_setcred): Test for likeauth option and use the return value data only if set. * modules/pam_unix/support.h: Add the no_pass_expiry option. --- modules/pam_unix/pam_unix.8.xml | 19 +++++++++++++++++++ modules/pam_unix/pam_unix_acct.c | 13 +++++++++++++ modules/pam_unix/pam_unix_auth.c | 20 +++++++++++--------- modules/pam_unix/support.h | 6 ++++-- 4 files changed, 47 insertions(+), 11 deletions(-) (limited to 'modules/pam_unix/support.h') diff --git a/modules/pam_unix/pam_unix.8.xml b/modules/pam_unix/pam_unix.8.xml index a8b64bb5..6d8e4ba0 100644 --- a/modules/pam_unix/pam_unix.8.xml +++ b/modules/pam_unix/pam_unix.8.xml @@ -364,6 +364,25 @@ + + + + + + + When set ignore password expiration as defined by the + shadow entry of the user. The option has an + effect only in case pam_unix was not used + for the authentication or it returned authentication failure + meaning that other authentication source or method succeeded. + The example can be public key authentication in + sshd. The module will return + PAM_SUCCESS instead of eventual + PAM_NEW_AUTHTOK_REQD or + PAM_AUTHTOK_EXPIRED. + + + Invalid arguments are logged with diff --git a/modules/pam_unix/pam_unix_acct.c b/modules/pam_unix/pam_unix_acct.c index 27998451..f8b39c91 100644 --- a/modules/pam_unix/pam_unix_acct.c +++ b/modules/pam_unix/pam_unix_acct.c @@ -235,6 +235,19 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) } else retval = check_shadow_expiry(pamh, spent, &daysleft); + if (on(UNIX_NO_PASS_EXPIRY, ctrl)) { + const void *pretval = NULL; + int authrv = PAM_AUTHINFO_UNAVAIL; /* authentication not called */ + + if (pam_get_data(pamh, "unix_setcred_return", &pretval) == PAM_SUCCESS + && pretval) + authrv = *(const int *)pretval; + + if (authrv != PAM_SUCCESS + && (retval == PAM_NEW_AUTHTOK_REQD || retval == PAM_AUTHTOK_EXPIRED)) + retval = PAM_SUCCESS; + } + switch (retval) { case PAM_ACCT_EXPIRED: pam_syslog(pamh, LOG_NOTICE, diff --git a/modules/pam_unix/pam_unix_auth.c b/modules/pam_unix/pam_unix_auth.c index 44573e6c..9a547b3a 100644 --- a/modules/pam_unix/pam_unix_auth.c +++ b/modules/pam_unix/pam_unix_auth.c @@ -82,14 +82,13 @@ #define AUTH_RETURN \ do { \ - if (on(UNIX_LIKE_AUTH, ctrl) && ret_data) { \ + if (ret_data) { \ D(("recording return code for next time [%d]", \ retval)); \ *ret_data = retval; \ pam_set_data(pamh, "unix_setcred_return", \ (void *) ret_data, setcred_free); \ - } else if (ret_data) \ - free (ret_data); \ + } \ D(("done. [%s]", pam_strerror(pamh, retval))); \ return retval; \ } while (0) @@ -115,9 +114,8 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv); /* Get a few bytes so we can pass our return value to - pam_sm_setcred(). */ - if (on(UNIX_LIKE_AUTH, ctrl)) - ret_data = malloc(sizeof(int)); + pam_sm_setcred() and pam_sm_acct_mgmt(). */ + ret_data = malloc(sizeof(int)); /* get the user'name' */ @@ -194,20 +192,24 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) */ int -pam_sm_setcred (pam_handle_t *pamh, int flags UNUSED, - int argc UNUSED, const char **argv UNUSED) +pam_sm_setcred (pam_handle_t *pamh, int flags, + int argc, const char **argv) { int retval; const void *pretval = NULL; + unsigned int ctrl; D(("called.")); + ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv); + retval = PAM_SUCCESS; D(("recovering return code from auth call")); /* We will only find something here if UNIX_LIKE_AUTH is set -- don't worry about an explicit check of argv. */ - if (pam_get_data(pamh, "unix_setcred_return", &pretval) == PAM_SUCCESS + if (on(UNIX_LIKE_AUTH, ctrl) + && pam_get_data(pamh, "unix_setcred_return", &pretval) == PAM_SUCCESS && pretval) { retval = *(const int *)pretval; pam_set_data(pamh, "unix_setcred_return", NULL, NULL); diff --git a/modules/pam_unix/support.h b/modules/pam_unix/support.h index 3729ce0c..b767c265 100644 --- a/modules/pam_unix/support.h +++ b/modules/pam_unix/support.h @@ -98,9 +98,10 @@ typedef struct { #define UNIX_BLOWFISH_PASS 26 /* new password hashes will use blowfish */ #define UNIX_MIN_PASS_LEN 27 /* min length for password */ #define UNIX_QUIET 28 /* Don't print informational messages */ -#define UNIX_DES 29 /* DES, default */ +#define UNIX_NO_PASS_EXPIRY 29 /* Don't check for password expiration if not used for authentication */ +#define UNIX_DES 30 /* DES, default */ /* -------------- */ -#define UNIX_CTRLS_ 30 /* number of ctrl arguments defined */ +#define UNIX_CTRLS_ 31 /* number of ctrl arguments defined */ #define UNIX_DES_CRYPT(ctrl) (off(UNIX_MD5_PASS,ctrl)&&off(UNIX_BIGCRYPT,ctrl)&&off(UNIX_SHA256_PASS,ctrl)&&off(UNIX_SHA512_PASS,ctrl)&&off(UNIX_BLOWFISH_PASS,ctrl)) @@ -138,6 +139,7 @@ static const UNIX_Ctrls unix_args[UNIX_CTRLS_] = /* UNIX_BLOWFISH_PASS */ {"blowfish", _ALL_ON_^(0260420000), 0200000000, 1}, /* UNIX_MIN_PASS_LEN */ {"minlen=", _ALL_ON_, 0400000000, 0}, /* UNIX_QUIET */ {"quiet", _ALL_ON_, 01000000000, 0}, +/* UNIX_NO_PASS_EXPIRY */ {"no_pass_expiry", _ALL_ON_, 02000000000, 0}, /* UNIX_DES */ {"des", _ALL_ON_^(0260420000), 0, 1}, }; -- cgit v1.2.3 From 7e09188c5dc4d0372ac7016f682cf63c686afe4a Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Wed, 6 Apr 2016 14:27:07 +0200 Subject: pam_unix: Use pam_get_authtok() instead of direct pam_prompt() calls. We have to drop support for not_set_pass option which is not much useful anyway. Instead we get proper support for authtok_type option. * modules/pam_unix/pam_unix.8.xml: Removed not_set_pass option, added authtok_ty pe option. * modules/pam_unix/pam_unix_auth.c (pam_sm_authenticate): Replace _unix_read_pas sword() call with equivalent pam_get_authtok() call. * modules/pam_unix/pam_unix_passwd.c (pam_sm_chauthtok): Likewise and also drop support for not_set_pass. * modules/pam_unix/support.c (_unix_read_password): Remove. * modules/pam_unix/support.h: Remove UNIX_NOT_SET_PASS add UNIX_AUTHTOK_TYPE. --- modules/pam_unix/pam_unix.8.xml | 8 +- modules/pam_unix/pam_unix_auth.c | 5 +- modules/pam_unix/pam_unix_passwd.c | 58 ++++---------- modules/pam_unix/support.c | 154 ------------------------------------- modules/pam_unix/support.h | 6 +- 5 files changed, 23 insertions(+), 208 deletions(-) (limited to 'modules/pam_unix/support.h') diff --git a/modules/pam_unix/pam_unix.8.xml b/modules/pam_unix/pam_unix.8.xml index 6d8e4ba0..60d90979 100644 --- a/modules/pam_unix/pam_unix.8.xml +++ b/modules/pam_unix/pam_unix.8.xml @@ -217,13 +217,13 @@ - + - This argument is used to inform the module that it is not to - pay attention to/make available the old or new passwords from/to - other (stacked) password modules. + This argument can be used to modify the password prompt + when changing passwords to include the type of the password. + Empty by default. diff --git a/modules/pam_unix/pam_unix_auth.c b/modules/pam_unix/pam_unix_auth.c index 9f66c5d6..673861e4 100644 --- a/modules/pam_unix/pam_unix_auth.c +++ b/modules/pam_unix/pam_unix_auth.c @@ -103,7 +103,7 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) unsigned int ctrl; int retval, *ret_data = NULL; const char *name; - const void *p; + const char *p; D(("called.")); @@ -151,8 +151,7 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) } /* get this user's authentication token */ - retval = _unix_read_password(pamh, ctrl, NULL, _("Password: "), NULL - ,_UNIX_AUTHTOK, &p); + retval = pam_get_authtok(pamh, PAM_AUTHTOK, &p , NULL); if (retval != PAM_SUCCESS) { if (retval != PAM_CONV_AGAIN) { pam_syslog(pamh, LOG_CRIT, diff --git a/modules/pam_unix/pam_unix_passwd.c b/modules/pam_unix/pam_unix_passwd.c index fa293278..c2e43423 100644 --- a/modules/pam_unix/pam_unix_passwd.c +++ b/modules/pam_unix/pam_unix_passwd.c @@ -612,7 +612,8 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) /* */ const char *user; - const void *pass_old, *pass_new; + const void *item; + const char *pass_old, *pass_new; /* */ D(("called.")); @@ -680,8 +681,6 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) * obtain and verify the current password (OLDAUTHTOK) for * the user. */ - char *Announce; - D(("prelim check")); if (_unix_blankpasswd(pamh, ctrl, user)) { @@ -689,22 +688,12 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) } else if (off(UNIX__IAMROOT, ctrl) || (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, user, 0, 1))) { /* instruct user what is happening */ - if (asprintf(&Announce, _("Changing password for %s."), - user) < 0) { - pam_syslog(pamh, LOG_CRIT, - "password - out of memory"); - return PAM_BUF_ERR; + if (off(UNIX__QUIET, ctrl)) { + retval = pam_info(pamh, _("Changing password for %s."), user); + if (retval != PAM_SUCCESS) + return retval; } - - lctrl = ctrl; - set(UNIX__OLD_PASSWD, lctrl); - retval = _unix_read_password(pamh, lctrl - ,Announce - ,_("(current) UNIX password: ") - ,NULL - ,_UNIX_OLD_AUTHTOK - ,&pass_old); - free(Announce); + retval = pam_get_authtok(pamh, PAM_OLDAUTHTOK, &pass_old, NULL); if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_NOTICE, @@ -725,12 +714,7 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) pass_old = NULL; return retval; } - retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old); pass_old = NULL; - if (retval != PAM_SUCCESS) { - pam_syslog(pamh, LOG_CRIT, - "failed to set PAM_OLDAUTHTOK"); - } retval = _unix_verify_shadow(pamh,user, ctrl); if (retval == PAM_AUTHTOK_ERR) { if (off(UNIX__IAMROOT, ctrl)) @@ -760,23 +744,14 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) * previous call to this function]. */ - if (off(UNIX_NOT_SET_PASS, ctrl)) { - retval = pam_get_item(pamh, PAM_OLDAUTHTOK - ,&pass_old); - } else { - retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK - ,&pass_old); - if (retval == PAM_NO_MODULE_DATA) { - retval = PAM_SUCCESS; - pass_old = NULL; - } - } - D(("pass_old [%s]", pass_old)); + retval = pam_get_item(pamh, PAM_OLDAUTHTOK, &item); if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_NOTICE, "user not authenticated"); return retval; } + pass_old = item; + D(("pass_old [%s]", pass_old)); D(("get new password now")); @@ -785,7 +760,9 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) if (on(UNIX_USE_AUTHTOK, lctrl)) { set(UNIX_USE_FIRST_PASS, lctrl); } - retry = 0; + if (on(UNIX_USE_FIRST_PASS, lctrl)) { + retry = MAX_PASSWD_TRIES-1; + } retval = PAM_AUTHTOK_ERR; while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) { /* @@ -793,12 +770,7 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) * password -- needed for pluggable password strength checking */ - retval = _unix_read_password(pamh, lctrl - ,NULL - ,_("Enter new UNIX password: ") - ,_("Retype new UNIX password: ") - ,_UNIX_NEW_AUTHTOK - ,&pass_new); + retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass_new, NULL); if (retval != PAM_SUCCESS) { if (on(UNIX_DEBUG, ctrl)) { @@ -822,7 +794,7 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new, pass_min_len); - if (retval != PAM_SUCCESS && off(UNIX_NOT_SET_PASS, ctrl)) { + if (retval != PAM_SUCCESS) { pam_set_item(pamh, PAM_AUTHTOK, NULL); } } diff --git a/modules/pam_unix/support.c b/modules/pam_unix/support.c index 0fd1dba4..fc8595e9 100644 --- a/modules/pam_unix/support.c +++ b/modules/pam_unix/support.c @@ -853,160 +853,6 @@ cleanup: return retval; } -/* - * obtain a password from the user - */ - -int _unix_read_password(pam_handle_t * pamh - ,unsigned int ctrl - ,const char *comment - ,const char *prompt1 - ,const char *prompt2 - ,const char *data_name - ,const void **pass) -{ - int authtok_flag; - int retval = PAM_SUCCESS; - char *token; - - D(("called")); - - /* - * make sure nothing inappropriate gets returned - */ - - *pass = token = NULL; - - /* - * which authentication token are we getting? - */ - - authtok_flag = on(UNIX__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK; - - /* - * should we obtain the password from a PAM item ? - */ - - if (on(UNIX_TRY_FIRST_PASS, ctrl) || on(UNIX_USE_FIRST_PASS, ctrl)) { - retval = pam_get_item(pamh, authtok_flag, pass); - if (retval != PAM_SUCCESS) { - /* very strange. */ - pam_syslog(pamh, LOG_ALERT, - "pam_get_item returned error to unix-read-password" - ); - return retval; - } else if (*pass != NULL) { /* we have a password! */ - return PAM_SUCCESS; - } else if (on(UNIX_USE_AUTHTOK, ctrl) - && off(UNIX__OLD_PASSWD, ctrl)) { - return PAM_AUTHTOK_ERR; - } else if (on(UNIX_USE_FIRST_PASS, ctrl)) { - return PAM_AUTHTOK_RECOVERY_ERR; /* didn't work */ - } - } - /* - * getting here implies we will have to get the password from the - * user directly. - */ - - { - int replies=1; - char *resp[2] = { NULL, NULL }; - - if (comment != NULL && off(UNIX__QUIET, ctrl)) { - retval = pam_info(pamh, "%s", comment); - } - - if (retval == PAM_SUCCESS) { - retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, - &resp[0], "%s", prompt1); - - if (retval == PAM_SUCCESS && prompt2 != NULL) { - retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, - &resp[1], "%s", prompt2); - ++replies; - } - } - - if (resp[0] != NULL && resp[replies-1] != NULL) { - /* interpret the response */ - - if (retval == PAM_SUCCESS) { /* a good conversation */ - - token = resp[0]; - if (token != NULL) { - if (replies == 2) { - /* verify that password entered correctly */ - if (strcmp(token, resp[replies - 1])) { - /* mistyped */ - retval = PAM_AUTHTOK_RECOVERY_ERR; - _make_remark(pamh, ctrl, - PAM_ERROR_MSG, MISTYPED_PASS); - } - } - } else { - pam_syslog(pamh, LOG_NOTICE, - "could not recover authentication token"); - } - - } - - } else { - retval = (retval == PAM_SUCCESS) - ? PAM_AUTHTOK_RECOVERY_ERR : retval; - } - - resp[0] = NULL; - if (replies > 1) - _pam_delete(resp[1]); - } - - if (retval != PAM_SUCCESS) { - _pam_delete(token); - - if (on(UNIX_DEBUG, ctrl)) - pam_syslog(pamh, LOG_DEBUG, - "unable to obtain a password"); - return retval; - } - /* 'token' is the entered password */ - - if (off(UNIX_NOT_SET_PASS, ctrl)) { - - /* we store this password as an item */ - - retval = pam_set_item(pamh, authtok_flag, token); - _pam_delete(token); /* clean it up */ - if (retval != PAM_SUCCESS - || (retval = pam_get_item(pamh, authtok_flag, pass)) - != PAM_SUCCESS) { - - *pass = NULL; - pam_syslog(pamh, LOG_CRIT, "error manipulating password"); - return retval; - - } - } else { - /* - * then store it as data specific to this module. pam_end() - * will arrange to clean it up. - */ - - retval = pam_set_data(pamh, data_name, (void *) token, _cleanup); - if (retval != PAM_SUCCESS) { - pam_syslog(pamh, LOG_CRIT, - "error manipulating password data [%s]", - pam_strerror(pamh, retval)); - _pam_delete(token); - return retval; - } - *pass = token; - token = NULL; /* break link to password */ - } - - return PAM_SUCCESS; -} - /* ****************************************************************** * * Copyright (c) Jan Rêkorajski 1999. * Copyright (c) Andrew G. Morgan 1996-8. diff --git a/modules/pam_unix/support.h b/modules/pam_unix/support.h index b767c265..b4c279c3 100644 --- a/modules/pam_unix/support.h +++ b/modules/pam_unix/support.h @@ -18,8 +18,6 @@ * typed were not the same. */ -#define MISTYPED_PASS "Sorry, passwords do not match" - /* type definition for the control options */ typedef struct { @@ -72,7 +70,7 @@ typedef struct { some information may be sensitive */ #define UNIX_USE_FIRST_PASS 4 #define UNIX_TRY_FIRST_PASS 5 -#define UNIX_NOT_SET_PASS 6 /* don't set the AUTHTOK items */ +#define UNIX_AUTHTOK_TYPE 6 /* TYPE for pam_get_authtok() */ #define UNIX__PRELIM 7 /* internal */ #define UNIX__UPDATE 8 /* internal */ @@ -116,7 +114,7 @@ static const UNIX_Ctrls unix_args[UNIX_CTRLS_] = /* UNIX_AUDIT */ {"audit", _ALL_ON_, 010, 0}, /* UNIX_USE_FIRST_PASS */ {"use_first_pass", _ALL_ON_^(060), 020, 0}, /* UNIX_TRY_FIRST_PASS */ {"try_first_pass", _ALL_ON_^(060), 040, 0}, -/* UNIX_NOT_SET_PASS */ {"not_set_pass", _ALL_ON_, 0100, 0}, +/* UNIX_AUTHTOK_TYPE */ {"authtok_type=", _ALL_ON_, 0100, 0}, /* UNIX__PRELIM */ {NULL, _ALL_ON_^(0600), 0200, 0}, /* UNIX__UPDATE */ {NULL, _ALL_ON_^(0600), 0400, 0}, /* UNIX__NONULL */ {NULL, _ALL_ON_, 01000, 0}, -- cgit v1.2.3