diff options
author | Martyn Welch <martyn@welchs.me.uk> | 2022-02-07 12:20:27 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-07 13:20:27 +0100 |
commit | 11c35109a67fa82b75f8d427fcb5ab41f61550f7 (patch) | |
tree | bb995a6b8cd3ad8a5906fbcac753159f3222894e /modules | |
parent | 5602198320902d02197876daf399cd5ae27a316f (diff) | |
download | pam-11c35109a67fa82b75f8d427fcb5ab41f61550f7.tar.gz pam-11c35109a67fa82b75f8d427fcb5ab41f61550f7.tar.bz2 pam-11c35109a67fa82b75f8d427fcb5ab41f61550f7.zip |
pam_pwhistory: Enable alternate location for password history file (#396)
Sometimes, especially in embedded devices, the /etc directory can be
read-only and/or not saved over upgrades. In order to ensure password
policies are maintained across upgrades and the module functions on
read-only file systems, allow the location of the password history file
to be set in the PAM configuration.
Signed-off-by: Edward <jinzhou.zhu1@ge.com>
[Martyn Welch: Updated commit message and ported to latest version]
Signed-off-by: Martyn Welch <martyn.welch@collabora.com>
Signed-off-by: Dmitry V. Levin <ldv@altlinux.org>
Diffstat (limited to 'modules')
-rw-r--r-- | modules/pam_pwhistory/opasswd.c | 65 | ||||
-rw-r--r-- | modules/pam_pwhistory/opasswd.h | 8 | ||||
-rw-r--r-- | modules/pam_pwhistory/pam_pwhistory.8.xml | 18 | ||||
-rw-r--r-- | modules/pam_pwhistory/pam_pwhistory.c | 35 | ||||
-rw-r--r-- | modules/pam_pwhistory/pwhistory_helper.c | 20 |
5 files changed, 95 insertions, 51 deletions
diff --git a/modules/pam_pwhistory/opasswd.c b/modules/pam_pwhistory/opasswd.c index 2af9ef9b..1d3242ca 100644 --- a/modules/pam_pwhistory/opasswd.c +++ b/modules/pam_pwhistory/opasswd.c @@ -44,6 +44,7 @@ #include <ctype.h> #include <errno.h> #include <fcntl.h> +#include <limits.h> #include <stdio.h> #include <unistd.h> #include <string.h> @@ -74,8 +75,7 @@ #define RANDOM_DEVICE "/dev/urandom" #endif -#define OLD_PASSWORDS_FILE SCONFIGDIR "/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 @@ <arg choice="opt"> authtok_type=<replaceable>STRING</replaceable> </arg> + <arg choice="opt"> + file=<replaceable>/path/filename</replaceable> + </arg> </cmdsynopsis> </refsynopsisdiv> @@ -137,6 +140,19 @@ </listitem> </varlistentry> + <varlistentry> + <term> + <option>file=<replaceable>/path/filename</replaceable></option> + </term> + <listitem> + <para> + Store password history in file <filename>/path/filename</filename> + rather than the default location. The default location is + <filename>/etc/security/opasswd</filename>. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect1> @@ -213,7 +229,7 @@ password required pam_unix.so use_authtok <varlistentry> <term><filename>/etc/security/opasswd</filename></term> <listitem> - <para>File with password history</para> + <para>Default file with password history</para> </listitem> </varlistentry> </variablelist> 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"); |