Import Linux-PAM.
[profile/ivi/pam.git] / modules / pam_pwhistory / pam_pwhistory.c
1 /*
2  * Copyright (c) 2008 Thorsten Kukuk
3  * Author: Thorsten Kukuk <kukuk@suse.de>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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
16  *    written permission.
17  *
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.)
23  *
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.
35  */
36
37 #if defined(HAVE_CONFIG_H)
38 #include <config.h>
39 #endif
40
41 #define PAM_SM_PASSWORD
42
43 #include <pwd.h>
44 #include <errno.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <shadow.h>
50 #include <syslog.h>
51 #include <sys/types.h>
52 #include <sys/stat.h>
53
54 #include <security/pam_modules.h>
55 #include <security/pam_modutil.h>
56 #include <security/pam_ext.h>
57 #include <security/_pam_macros.h>
58
59 #include "opasswd.h"
60
61 #define DEFAULT_BUFLEN 2048
62
63 struct options_t {
64   int debug;
65   int enforce_for_root;
66   int remember;
67   int tries;
68 };
69 typedef struct options_t options_t;
70
71
72 static void
73 parse_option (pam_handle_t *pamh, const char *argv, options_t *options)
74 {
75   if (strcasecmp (argv, "try_first_pass") == 0)
76     /* ignore */;
77   else if (strcasecmp (argv, "use_first_pass") == 0)
78     /* ignore */;
79   else if (strcasecmp (argv, "use_authtok") == 0)
80     /* ignore, handled by pam_get_authtok */;
81   else if (strcasecmp (argv, "debug") == 0)
82     options->debug = 1;
83   else if (strncasecmp (argv, "remember=", 9) == 0)
84     {
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;
90     }
91   else if (strncasecmp (argv, "retry=", 6) == 0)
92     {
93       options->tries = strtol(&argv[6], NULL, 10);
94       if (options->tries < 0)
95         options->tries = 1;
96     }
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 */; }
101   else
102     pam_syslog (pamh, LOG_ERR, "pam_pwhistory: unknown option: %s", argv);
103 }
104
105
106 PAM_EXTERN int
107 pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
108 {
109   struct passwd *pwd;
110   const char *newpass;
111   const char *user;
112     int retval, tries;
113   options_t options;
114
115   memset (&options, 0, sizeof (options));
116
117   /* Set some default values, which could be overwritten later.  */
118   options.remember = 10;
119   options.tries = 1;
120
121   /* Parse parameters for module */
122   for ( ; argc-- > 0; argv++)
123     parse_option (pamh, *argv, &options);
124
125   if (options.debug)
126     pam_syslog (pamh, LOG_DEBUG, "pam_sm_chauthtok entered");
127
128
129   if (options.remember == 0)
130     return PAM_IGNORE;
131
132   retval = pam_get_user (pamh, &user, NULL);
133   if (retval != PAM_SUCCESS)
134     return retval;
135
136   if (user == NULL || strlen (user) == 0)
137     {
138       if (options.debug)
139         pam_syslog (pamh, LOG_DEBUG,
140                     "User is not known to system");
141
142       return PAM_USER_UNKNOWN;
143     }
144
145   if (flags & PAM_PRELIM_CHECK)
146     {
147       if (options.debug)
148         pam_syslog (pamh, LOG_DEBUG,
149                     "pam_sm_chauthtok(PAM_PRELIM_CHECK)");
150
151       return PAM_SUCCESS;
152     }
153
154   pwd = pam_modutil_getpwnam (pamh, user);
155   if (pwd == NULL)
156     return PAM_USER_UNKNOWN;
157
158   /* Ignore root if not enforced */
159   if (pwd->pw_uid == 0 && !options.enforce_for_root)
160     return PAM_SUCCESS;
161
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)))
166     {
167       struct spwd *spw = pam_modutil_getspnam (pamh, user);
168       if (spw == NULL)
169         return PAM_USER_UNKNOWN;
170
171       retval = save_old_password (pamh, user, pwd->pw_uid, spw->sp_pwdp,
172                                   options.remember, options.debug);
173       if (retval != PAM_SUCCESS)
174         return retval;
175     }
176   else
177     {
178       retval = save_old_password (pamh, user, pwd->pw_uid, pwd->pw_passwd,
179                                   options.remember, options.debug);
180       if (retval != PAM_SUCCESS)
181         return retval;
182     }
183
184   newpass = NULL;
185   tries = 0;
186   while ((newpass == NULL) && (tries < options.tries))
187     {
188       retval = pam_get_authtok (pamh, PAM_AUTHTOK, &newpass, NULL);
189       if (retval != PAM_SUCCESS && retval != PAM_TRY_AGAIN)
190         {
191           if (retval == PAM_CONV_AGAIN)
192             retval = PAM_INCOMPLETE;
193           return retval;
194         }
195       tries++;
196
197       if (options.debug)
198         {
199           if (newpass)
200             pam_syslog (pamh, LOG_DEBUG, "got new auth token");
201           else
202             pam_syslog (pamh, LOG_DEBUG, "got no auth token");
203         }
204
205       if (newpass == NULL || retval == PAM_TRY_AGAIN)
206         continue;
207
208       if (options.debug)
209         pam_syslog (pamh, LOG_DEBUG, "check against old password file");
210
211       if (check_old_password (pamh, user, newpass,
212                               options.debug) != PAM_SUCCESS)
213         {
214           pam_error (pamh,
215                      _("Password has been already used. Choose another."));
216           newpass = NULL;
217           /* Remove password item, else following module will use it */
218           pam_set_item (pamh, PAM_AUTHTOK, (void *) NULL);
219         }
220     }
221
222   if (newpass == NULL && tries >= options.tries)
223     {
224       if (options.debug)
225         pam_syslog (pamh, LOG_DEBUG, "Aborted, too many tries");
226       return PAM_MAXTRIES;
227     }
228
229   return PAM_SUCCESS;
230 }
231
232
233 #ifdef PAM_STATIC
234 /* static module data */
235 struct pam_module _pam_pwhistory_modstruct = {
236   "pam_pwhistory",
237   NULL,
238   NULL,
239   NULL,
240   NULL,
241   NULL,
242   pam_sm_chauthtok
243 };
244 #endif