2 * Copyright (c) 2008 Thorsten Kukuk
3 * Author: Thorsten Kukuk <kukuk@suse.de>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, and the entire permission notice in its entirety,
10 * including the disclaimer of warranties.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote
15 * products derived from this software without specific prior
18 * ALTERNATIVELY, this product may be distributed under the terms of
19 * the GNU Public License, in which case the provisions of the GPL are
20 * required INSTEAD OF the above restrictions. (This clause is
21 * necessary due to a potential bad interaction between the GPL and
22 * the restrictions contained in a BSD-style copyright.)
24 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
25 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
28 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
34 * OF THE POSSIBILITY OF SUCH DAMAGE.
37 #if defined(HAVE_CONFIG_H)
41 #define PAM_SM_PASSWORD
51 #include <sys/types.h>
54 #include <security/pam_modules.h>
55 #include <security/pam_modutil.h>
56 #include <security/pam_ext.h>
57 #include <security/_pam_macros.h>
61 #define DEFAULT_BUFLEN 2048
69 typedef struct options_t options_t;
73 parse_option (pam_handle_t *pamh, const char *argv, options_t *options)
75 if (strcasecmp (argv, "try_first_pass") == 0)
77 else if (strcasecmp (argv, "use_first_pass") == 0)
79 else if (strcasecmp (argv, "use_authtok") == 0)
80 /* ignore, handled by pam_get_authtok */;
81 else if (strcasecmp (argv, "debug") == 0)
83 else if (strncasecmp (argv, "remember=", 9) == 0)
85 options->remember = strtol(&argv[9], NULL, 10);
86 if (options->remember < 0)
87 options->remember = 0;
88 if (options->remember > 400)
89 options->remember = 400;
91 else if (strncasecmp (argv, "retry=", 6) == 0)
93 options->tries = strtol(&argv[6], NULL, 10);
94 if (options->tries < 0)
97 else if (strcasecmp (argv, "enforce_for_root") == 0)
98 options->enforce_for_root = 1;
99 else if (strncasecmp (argv, "authtok_type=", 13) == 0)
100 { /* ignore, for pam_get_authtok */; }
102 pam_syslog (pamh, LOG_ERR, "pam_pwhistory: unknown option: %s", argv);
107 pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
115 memset (&options, 0, sizeof (options));
117 /* Set some default values, which could be overwritten later. */
118 options.remember = 10;
121 /* Parse parameters for module */
122 for ( ; argc-- > 0; argv++)
123 parse_option (pamh, *argv, &options);
126 pam_syslog (pamh, LOG_DEBUG, "pam_sm_chauthtok entered");
129 if (options.remember == 0)
132 retval = pam_get_user (pamh, &user, NULL);
133 if (retval != PAM_SUCCESS)
136 if (user == NULL || strlen (user) == 0)
139 pam_syslog (pamh, LOG_DEBUG,
140 "User is not known to system");
142 return PAM_USER_UNKNOWN;
145 if (flags & PAM_PRELIM_CHECK)
148 pam_syslog (pamh, LOG_DEBUG,
149 "pam_sm_chauthtok(PAM_PRELIM_CHECK)");
154 pwd = pam_modutil_getpwnam (pamh, user);
156 return PAM_USER_UNKNOWN;
158 /* Ignore root if not enforced */
159 if (pwd->pw_uid == 0 && !options.enforce_for_root)
162 if ((strcmp(pwd->pw_passwd, "x") == 0) ||
163 ((pwd->pw_passwd[0] == '#') &&
164 (pwd->pw_passwd[1] == '#') &&
165 (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)))
167 struct spwd *spw = pam_modutil_getspnam (pamh, user);
169 return PAM_USER_UNKNOWN;
171 retval = save_old_password (pamh, user, pwd->pw_uid, spw->sp_pwdp,
172 options.remember, options.debug);
173 if (retval != PAM_SUCCESS)
178 retval = save_old_password (pamh, user, pwd->pw_uid, pwd->pw_passwd,
179 options.remember, options.debug);
180 if (retval != PAM_SUCCESS)
186 while ((newpass == NULL) && (tries < options.tries))
188 retval = pam_get_authtok (pamh, PAM_AUTHTOK, &newpass, NULL);
189 if (retval != PAM_SUCCESS && retval != PAM_TRY_AGAIN)
191 if (retval == PAM_CONV_AGAIN)
192 retval = PAM_INCOMPLETE;
200 pam_syslog (pamh, LOG_DEBUG, "got new auth token");
202 pam_syslog (pamh, LOG_DEBUG, "got no auth token");
205 if (newpass == NULL || retval == PAM_TRY_AGAIN)
209 pam_syslog (pamh, LOG_DEBUG, "check against old password file");
211 if (check_old_password (pamh, user, newpass,
212 options.debug) != PAM_SUCCESS)
215 _("Password has been already used. Choose another."));
217 /* Remove password item, else following module will use it */
218 pam_set_item (pamh, PAM_AUTHTOK, (void *) NULL);
222 if (newpass == NULL && tries >= options.tries)
225 pam_syslog (pamh, LOG_DEBUG, "Aborted, too many tries");
234 /* static module data */
235 struct pam_module _pam_pwhistory_modstruct = {