]> granicus.if.org Git - shadow/blob - libmisc/obscure.c
* libmisc/getlong.c: Reset errno before calling strtol().
[shadow] / libmisc / obscure.c
1 /*
2  * Copyright (c) 1989 - 1994, Julianne Frances Haugh
3  * Copyright (c) 1996 - 1999, Marek Michałkiewicz
4  * Copyright (c) 2003 - 2005, Tomasz Kłoczko
5  * Copyright (c) 2007 - 2008, Nicolas François
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the copyright holders or contributors may not be used to
17  *    endorse or promote products derived from this software without
18  *    specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <config.h>
34
35 #ifndef USE_PAM
36
37 #ident "$Id$"
38
39
40 /*
41  * This version of obscure.c contains modifications to support "cracklib"
42  * by Alec Muffet (alec.muffett@uk.sun.com).  You must obtain the Cracklib
43  * library source code for this function to operate.
44  */
45 #include <ctype.h>
46 #include <stdio.h>
47 #include "prototypes.h"
48 #include "defines.h"
49 #include "getdef.h"
50 /*
51  * can't be a palindrome - like `R A D A R' or `M A D A M'
52  */
53 static int palindrome (unused const char *old, const char *new)
54 {
55         int i, j;
56
57         i = strlen (new);
58
59         for (j = 0; j < i; j++)
60                 if (new[i - j - 1] != new[j])
61                         return 0;
62
63         return 1;
64 }
65
66 /*
67  * more than half of the characters are different ones.
68  */
69
70 static int similar (const char *old, const char *new)
71 {
72         int i, j;
73
74         /*
75          * XXX - sometimes this fails when changing from a simple password
76          * to a really long one (MD5).  For now, I just return success if
77          * the new password is long enough.  Please feel free to suggest
78          * something better...  --marekm
79          */
80         if (strlen (new) >= 8)
81                 return 0;
82
83         for (i = j = 0; new[i] && old[i]; i++)
84                 if (strchr (new, old[i]))
85                         j++;
86
87         if (i >= j * 2)
88                 return 0;
89
90         return 1;
91 }
92
93 /*
94  * a nice mix of characters.
95  */
96
97 static int simple (unused const char *old, const char *new)
98 {
99         int digits = 0;
100         int uppers = 0;
101         int lowers = 0;
102         int others = 0;
103         int size;
104         int i;
105
106         for (i = 0; new[i]; i++) {
107                 if (isdigit (new[i]))
108                         digits++;
109                 else if (isupper (new[i]))
110                         uppers++;
111                 else if (islower (new[i]))
112                         lowers++;
113                 else
114                         others++;
115         }
116
117         /*
118          * The scam is this - a password of only one character type
119          * must be 8 letters long.  Two types, 7, and so on.
120          */
121
122         size = 9;
123         if (digits)
124                 size--;
125         if (uppers)
126                 size--;
127         if (lowers)
128                 size--;
129         if (others)
130                 size--;
131
132         if (size <= i)
133                 return 0;
134
135         return 1;
136 }
137
138 static char *str_lower (char *string)
139 {
140         char *cp;
141
142         for (cp = string; *cp; cp++)
143                 *cp = tolower (*cp);
144         return string;
145 }
146
147 static const char *password_check (const char *old, const char *new,
148                                    const struct passwd *pwdp)
149 {
150         const char *msg = NULL;
151         char *oldmono, *newmono, *wrapped;
152
153 #ifdef HAVE_LIBCRACK
154         char *dictpath;
155
156 #ifdef HAVE_LIBCRACK_PW
157         char *FascistCheckPw ();
158 #else
159         char *FascistCheck ();
160 #endif
161 #endif
162
163         if (strcmp (new, old) == 0)
164                 return _("no change");
165
166         newmono = str_lower (xstrdup (new));
167         oldmono = str_lower (xstrdup (old));
168         wrapped = xmalloc (strlen (oldmono) * 2 + 1);
169         strcpy (wrapped, oldmono);
170         strcat (wrapped, oldmono);
171
172         if (palindrome (oldmono, newmono))
173                 msg = _("a palindrome");
174
175         if (!msg && strcmp (oldmono, newmono) == 0)
176                 msg = _("case changes only");
177
178         if (!msg && similar (oldmono, newmono))
179                 msg = _("too similar");
180
181         if (!msg && simple (old, new))
182                 msg = _("too simple");
183
184         if (!msg && strstr (wrapped, newmono))
185                 msg = _("rotated");
186
187 #ifdef HAVE_LIBCRACK
188         /*
189          * Invoke Alec Muffett's cracklib routines.
190          */
191
192         if (!msg && (dictpath = getdef_str ("CRACKLIB_DICTPATH")))
193 #ifdef HAVE_LIBCRACK_PW
194                 msg = FascistCheckPw (new, dictpath, pwdp);
195 #else
196                 msg = FascistCheck (new, dictpath);
197 #endif
198 #endif
199         strzero (newmono);
200         strzero (oldmono);
201         strzero (wrapped);
202         free (newmono);
203         free (oldmono);
204         free (wrapped);
205
206         return msg;
207 }
208
209  /*ARGSUSED*/
210     static const char *obscure_msg (const char *old, const char *new,
211                                     const struct passwd *pwdp)
212 {
213         int maxlen, oldlen, newlen;
214         char *new1, *old1;
215         const char *msg;
216         char *result;
217
218         oldlen = strlen (old);
219         newlen = strlen (new);
220
221         if (newlen < getdef_num ("PASS_MIN_LEN", 0))
222                 return _("too short");
223
224         /*
225          * Remaining checks are optional.
226          */
227         if (!getdef_bool ("OBSCURE_CHECKS_ENAB"))
228                 return NULL;
229
230         msg = password_check (old, new, pwdp);
231         if (msg)
232                 return msg;
233
234         if ((result = getdef_str ("ENCRYPT_METHOD")) == NULL) {
235         /* The traditional crypt() truncates passwords to 8 chars.  It is
236            possible to circumvent the above checks by choosing an easy
237            8-char password and adding some random characters to it...
238            Example: "password$%^&*123".  So check it again, this time
239            truncated to the maximum length.  Idea from npasswd.  --marekm */
240
241                 if (getdef_bool ("MD5_CRYPT_ENAB"))
242                         return NULL;
243
244         } else {
245
246                 if (   !strcmp (result, "MD5")
247 #ifdef USE_SHA_CRYPT
248                     || !strcmp (result, "SHA256")
249                     || !strcmp (result, "SHA512")
250 #endif
251                     )
252                         return NULL;
253
254         }
255         maxlen = getdef_num ("PASS_MAX_LEN", 8);
256         if (oldlen <= maxlen && newlen <= maxlen)
257                 return NULL;
258
259         new1 = xstrdup (new);
260         old1 = xstrdup (old);
261         if (newlen > maxlen)
262                 new1[maxlen] = '\0';
263         if (oldlen > maxlen)
264                 old1[maxlen] = '\0';
265
266         msg = password_check (old1, new1, pwdp);
267
268         memzero (new1, newlen);
269         memzero (old1, oldlen);
270         free (new1);
271         free (old1);
272
273         return msg;
274 }
275
276 /*
277  * Obscure - see if password is obscure enough.
278  *
279  *      The programmer is encouraged to add as much complexity to this
280  *      routine as desired.  Included are some of my favorite ways to
281  *      check passwords.
282  */
283
284 int obscure (const char *old, const char *new, const struct passwd *pwdp)
285 {
286         const char *msg = obscure_msg (old, new, pwdp);
287
288         if (msg) {
289                 printf (_("Bad password: %s.  "), msg);
290                 return 0;
291         }
292         return 1;
293 }
294
295 #else                           /* !USE_PAM */
296 extern int errno;               /* warning: ANSI C forbids an empty source file */
297 #endif                          /* !USE_PAM */