123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- /*
- * pam_pwhistory module
- *
- * Copyright (c) 2008, 2012 Thorsten Kukuk
- * Author: Thorsten Kukuk <kukuk@thkukuk.de>
- * Copyright (c) 2013 Red Hat, Inc.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * ALTERNATIVELY, this product may be distributed under the terms of
- * the GNU Public License, in which case the provisions of the GPL are
- * required INSTEAD OF the above restrictions. (This clause is
- * necessary due to a potential bad interaction between the GPL and
- * the restrictions contained in a BSD-style copyright.)
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #if defined(HAVE_CONFIG_H)
- #include <config.h>
- #endif
- #include <pwd.h>
- #include <errno.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <syslog.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/time.h>
- #include <sys/resource.h>
- #include <sys/wait.h>
- #include <signal.h>
- #include <fcntl.h>
- #include <security/pam_modules.h>
- #include <security/pam_modutil.h>
- #include <security/pam_ext.h>
- #include <security/_pam_macros.h>
- #include "opasswd.h"
- #include "pam_inline.h"
- struct options_t {
- int debug;
- int enforce_for_root;
- int remember;
- int tries;
- };
- typedef struct options_t options_t;
- static void
- parse_option (pam_handle_t *pamh, const char *argv, options_t *options)
- {
- const char *str;
- if (strcasecmp (argv, "try_first_pass") == 0)
- /* ignore */;
- else if (strcasecmp (argv, "use_first_pass") == 0)
- /* ignore */;
- else if (strcasecmp (argv, "use_authtok") == 0)
- /* ignore, handled by pam_get_authtok */;
- else if (strcasecmp (argv, "debug") == 0)
- options->debug = 1;
- else if ((str = pam_str_skip_icase_prefix(argv, "remember=")) != NULL)
- {
- options->remember = strtol(str, NULL, 10);
- if (options->remember < 0)
- options->remember = 0;
- if (options->remember > 400)
- options->remember = 400;
- }
- else if ((str = pam_str_skip_icase_prefix(argv, "retry=")) != NULL)
- {
- options->tries = strtol(str, NULL, 10);
- if (options->tries < 0)
- options->tries = 1;
- }
- else if (strcasecmp (argv, "enforce_for_root") == 0)
- options->enforce_for_root = 1;
- else if (pam_str_skip_icase_prefix(argv, "authtok_type=") != NULL)
- { /* ignore, for pam_get_authtok */; }
- 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 retval, child;
- struct sigaction newsa, oldsa;
- memset(&newsa, '\0', sizeof(newsa));
- newsa.sa_handler = SIG_DFL;
- sigaction(SIGCHLD, &newsa, &oldsa);
- child = fork();
- if (child == 0)
- {
- static char *envp[] = { NULL };
- char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
- 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 binary helper */
- DIAG_PUSH_IGNORE_CAST_QUAL;
- args[0] = (char *)PWHISTORY_HELPER;
- args[1] = (char *)"save";
- args[2] = (char *)user;
- DIAG_POP_IGNORE_CAST_QUAL;
- if (asprintf(&args[3], "%d", howmany) < 0 ||
- asprintf(&args[4], "%d", debug) < 0)
- {
- pam_syslog(pamh, LOG_ERR, "asprintf: %m");
- _exit(PAM_SYSTEM_ERR);
- }
- execve(args[0], args, envp);
- pam_syslog(pamh, LOG_ERR, "helper binary execve failed: %s: %m", args[0]);
- _exit(PAM_SYSTEM_ERR);
- }
- else if (child > 0)
- {
- /* wait for child */
- int rc = 0;
- while ((rc = waitpid (child, &retval, 0)) == -1 &&
- errno == EINTR);
- if (rc < 0)
- {
- pam_syslog(pamh, LOG_ERR, "pwhistory_helper save: waitpid: %m");
- retval = PAM_SYSTEM_ERR;
- }
- else if (!WIFEXITED(retval))
- {
- pam_syslog(pamh, LOG_ERR, "pwhistory_helper save abnormal exit: %d", retval);
- retval = PAM_SYSTEM_ERR;
- }
- else
- {
- retval = WEXITSTATUS(retval);
- }
- }
- else
- {
- pam_syslog(pamh, LOG_ERR, "fork failed: %m");
- retval = PAM_SYSTEM_ERR;
- }
- sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */
- return retval;
- }
- static int
- run_check_helper(pam_handle_t *pamh, const char *user,
- const char *newpass, int debug)
- {
- int retval, child, fds[2];
- struct sigaction newsa, oldsa;
- /* create a pipe for the password */
- if (pipe(fds) != 0)
- return PAM_SYSTEM_ERR;
- memset(&newsa, '\0', sizeof(newsa));
- newsa.sa_handler = SIG_DFL;
- sigaction(SIGCHLD, &newsa, &oldsa);
- child = fork();
- if (child == 0)
- {
- static char *envp[] = { NULL };
- char *args[] = { NULL, NULL, NULL, NULL, NULL };
- /* reopen stdin as pipe */
- if (dup2(fds[0], STDIN_FILENO) != STDIN_FILENO)
- {
- pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin");
- _exit(PAM_SYSTEM_ERR);
- }
- if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD,
- PAM_MODUTIL_PIPE_FD,
- PAM_MODUTIL_PIPE_FD) < 0)
- {
- _exit(PAM_SYSTEM_ERR);
- }
- /* exec binary helper */
- DIAG_PUSH_IGNORE_CAST_QUAL;
- args[0] = (char *)PWHISTORY_HELPER;
- args[1] = (char *)"check";
- args[2] = (char *)user;
- DIAG_POP_IGNORE_CAST_QUAL;
- if (asprintf(&args[3], "%d", debug) < 0)
- {
- pam_syslog(pamh, LOG_ERR, "asprintf: %m");
- _exit(PAM_SYSTEM_ERR);
- }
- execve(args[0], args, envp);
- pam_syslog(pamh, LOG_ERR, "helper binary execve failed: %s: %m", args[0]);
- _exit(PAM_SYSTEM_ERR);
- }
- else if (child > 0)
- {
- /* wait for child */
- int rc = 0;
- if (newpass == NULL)
- newpass = "";
- /* send the password to the child */
- if (write(fds[1], newpass, strlen(newpass)+1) == -1)
- {
- pam_syslog(pamh, LOG_ERR, "Cannot send password to helper: %m");
- retval = PAM_SYSTEM_ERR;
- }
- newpass = NULL;
- close(fds[0]); /* close here to avoid possible SIGPIPE above */
- close(fds[1]);
- while ((rc = waitpid (child, &retval, 0)) == -1 &&
- errno == EINTR);
- if (rc < 0)
- {
- pam_syslog(pamh, LOG_ERR, "pwhistory_helper check: waitpid: %m");
- retval = PAM_SYSTEM_ERR;
- }
- else if (!WIFEXITED(retval))
- {
- pam_syslog(pamh, LOG_ERR, "pwhistory_helper check abnormal exit: %d", retval);
- retval = PAM_SYSTEM_ERR;
- }
- else
- {
- retval = WEXITSTATUS(retval);
- }
- }
- else
- {
- pam_syslog(pamh, LOG_ERR, "fork failed: %m");
- close(fds[0]);
- close(fds[1]);
- retval = PAM_SYSTEM_ERR;
- }
- sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */
- return retval;
- }
- /* This module saves the current hashed password in /etc/security/opasswd
- and then compares the new password with all entries in this file. */
- int
- pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
- {
- const char *newpass;
- const char *user;
- int retval, tries;
- options_t options;
- memset (&options, 0, sizeof (options));
- /* Set some default values, which could be overwritten later. */
- options.remember = 10;
- options.tries = 1;
- /* Parse parameters for module */
- for ( ; argc-- > 0; argv++)
- parse_option (pamh, *argv, &options);
- if (options.debug)
- pam_syslog (pamh, LOG_DEBUG, "pam_sm_chauthtok entered");
- if (options.remember == 0)
- return PAM_IGNORE;
- retval = pam_get_user (pamh, &user, NULL);
- if (retval != PAM_SUCCESS)
- return retval;
- if (flags & PAM_PRELIM_CHECK)
- {
- if (options.debug)
- pam_syslog (pamh, LOG_DEBUG,
- "pam_sm_chauthtok(PAM_PRELIM_CHECK)");
- return PAM_SUCCESS;
- }
- retval = save_old_pass (pamh, user, options.remember, options.debug);
- if (retval == PAM_PWHISTORY_RUN_HELPER)
- retval = run_save_helper(pamh, user, options.remember, options.debug);
- if (retval != PAM_SUCCESS)
- return retval;
- newpass = NULL;
- tries = 0;
- while ((newpass == NULL) && (tries < options.tries))
- {
- retval = pam_get_authtok (pamh, PAM_AUTHTOK, &newpass, NULL);
- if (retval != PAM_SUCCESS && retval != PAM_TRY_AGAIN)
- {
- if (retval == PAM_CONV_AGAIN)
- retval = PAM_INCOMPLETE;
- return retval;
- }
- tries++;
- if (options.debug)
- {
- if (newpass)
- pam_syslog (pamh, LOG_DEBUG, "got new auth token");
- else
- pam_syslog (pamh, LOG_DEBUG, "got no auth token");
- }
- if (newpass == NULL || retval == PAM_TRY_AGAIN)
- continue;
- if (options.debug)
- pam_syslog (pamh, LOG_DEBUG, "check against old password file");
- retval = check_old_pass (pamh, user, newpass, options.debug);
- if (retval == PAM_PWHISTORY_RUN_HELPER)
- retval = run_check_helper(pamh, user, newpass, options.debug);
- if (retval != PAM_SUCCESS)
- {
- if (getuid() || options.enforce_for_root ||
- (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
- {
- pam_error (pamh,
- _("Password has been already used. Choose another."));
- newpass = NULL;
- /* Remove password item, else following module will use it */
- pam_set_item (pamh, PAM_AUTHTOK, (void *) NULL);
- }
- else
- pam_info (pamh,
- _("Password has been already used."));
- }
- }
- if (newpass == NULL && tries >= options.tries)
- {
- if (options.debug)
- pam_syslog (pamh, LOG_DEBUG, "Aborted, too many tries");
- return PAM_MAXTRIES;
- }
- return PAM_SUCCESS;
- }
|