]> granicus.if.org Git - shadow/blob - libmisc/obscure.c
* libmisc/chowntty.c: Improve the logs for fchown and fchmod
[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 bool 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 false;
62                 }
63         }
64
65         return true;
66 }
67
68 /*
69  * more than half of the characters are different ones.
70  */
71
72 static bool similar (const char *old, const char *new)
73 {
74         int i, j;
75
76         /*
77          * XXX - sometimes this fails when changing from a simple password
78          * to a really long one (MD5).  For now, I just return success if
79          * the new password is long enough.  Please feel free to suggest
80          * something better...  --marekm
81          */
82         if (strlen (new) >= 8) {
83                 return false;
84         }
85
86         for (i = j = 0; ('\0' != new[i]) && ('\0' != old[i]); i++) {
87                 if (strchr (new, old[i]) != NULL) {
88                         j++;
89                 }
90         }
91
92         if (i >= j * 2) {
93                 return false;
94         }
95
96         return true;
97 }
98
99 /*
100  * a nice mix of characters.
101  */
102
103 static int simple (unused const char *old, const char *new)
104 {
105         bool digits = false;
106         bool uppers = false;
107         bool lowers = false;
108         bool others = false;
109         int size;
110         int i;
111
112         for (i = 0; '\0' != new[i]; i++) {
113                 if (isdigit (new[i])) {
114                         digits = true;
115                 } else if (isupper (new[i])) {
116                         uppers = true;
117                 } else if (islower (new[i])) {
118                         lowers = true;
119                 } else {
120                         others = true;
121                 }
122         }
123
124         /*
125          * The scam is this - a password of only one character type
126          * must be 8 letters long.  Two types, 7, and so on.
127          */
128
129         size = 9;
130         if (digits) {
131                 size--;
132         }
133         if (uppers) {
134                 size--;
135         }
136         if (lowers) {
137                 size--;
138         }
139         if (others) {
140                 size--;
141         }
142
143         if (size <= i) {
144                 return false;
145         }
146
147         return true;
148 }
149
150 static char *str_lower (char *string)
151 {
152         char *cp;
153
154         for (cp = string; '\0' != *cp; cp++) {
155                 *cp = tolower (*cp);
156         }
157         return string;
158 }
159
160 static const char *password_check (const char *old, const char *new,
161                                    const struct passwd *pwdp)
162 {
163         const char *msg = NULL;
164         char *oldmono, *newmono, *wrapped;
165
166 #ifdef HAVE_LIBCRACK
167         char *dictpath;
168
169 #ifdef HAVE_LIBCRACK_PW
170         char *FascistCheckPw ();
171 #else
172         char *FascistCheck ();
173 #endif
174 #endif
175
176         if (strcmp (new, old) == 0) {
177                 return _("no change");
178         }
179
180         newmono = str_lower (xstrdup (new));
181         oldmono = str_lower (xstrdup (old));
182         wrapped = xmalloc (strlen (oldmono) * 2 + 1);
183         strcpy (wrapped, oldmono);
184         strcat (wrapped, oldmono);
185
186         if (palindrome (oldmono, newmono)) {
187                 msg = _("a palindrome");
188         } else if (strcmp (oldmono, newmono) == 0) {
189                 msg = _("case changes only");
190         } else if (similar (oldmono, newmono)) {
191                 msg = _("too similar");
192         } else if (simple (old, new)) {
193                 msg = _("too simple");
194         } else if (strstr (wrapped, newmono) != NULL) {
195                 msg = _("rotated");
196         } else {
197 #ifdef HAVE_LIBCRACK
198                 /*
199                  * Invoke Alec Muffett's cracklib routines.
200                  */
201
202                 dictpath = getdef_str ("CRACKLIB_DICTPATH");
203                 if (NULL != dictpath) {
204 #ifdef HAVE_LIBCRACK_PW
205                         msg = FascistCheckPw (new, dictpath, pwdp);
206 #else
207                         msg = FascistCheck (new, dictpath);
208 #endif
209                 }
210 #endif
211         }
212         strzero (newmono);
213         strzero (oldmono);
214         strzero (wrapped);
215         free (newmono);
216         free (oldmono);
217         free (wrapped);
218
219         return msg;
220 }
221
222 /*ARGSUSED*/
223 static const char *obscure_msg (const char *old, const char *new,
224                                     const struct passwd *pwdp)
225 {
226         int maxlen, oldlen, newlen;
227         char *new1, *old1;
228         const char *msg;
229         char *result;
230
231         oldlen = strlen (old);
232         newlen = strlen (new);
233
234         if (newlen < getdef_num ("PASS_MIN_LEN", 0)) {
235                 return _("too short");
236         }
237
238         /*
239          * Remaining checks are optional.
240          */
241         if (!getdef_bool ("OBSCURE_CHECKS_ENAB")) {
242                 return NULL;
243         }
244
245         msg = password_check (old, new, pwdp);
246         if (NULL != msg) {
247                 return msg;
248         }
249
250         result = getdef_str ("ENCRYPT_METHOD");
251         if (NULL == result) {
252         /* The traditional crypt() truncates passwords to 8 chars.  It is
253            possible to circumvent the above checks by choosing an easy
254            8-char password and adding some random characters to it...
255            Example: "password$%^&*123".  So check it again, this time
256            truncated to the maximum length.  Idea from npasswd.  --marekm */
257
258                 if (getdef_bool ("MD5_CRYPT_ENAB")) {
259                         return NULL;
260                 }
261
262         } else {
263
264                 if (   (strcmp (result, "MD5")    == 0)
265 #ifdef USE_SHA_CRYPT
266                     || (strcmp (result, "SHA256") == 0)
267                     || (strcmp (result, "SHA512") == 0)
268 #endif
269                     ) {
270                         return NULL;
271                 }
272
273         }
274         maxlen = getdef_num ("PASS_MAX_LEN", 8);
275         if (   (oldlen <= maxlen)
276             && (newlen <= maxlen)) {
277                 return NULL;
278         }
279
280         new1 = xstrdup (new);
281         old1 = xstrdup (old);
282         if (newlen > maxlen) {
283                 new1[maxlen] = '\0';
284         }
285         if (oldlen > maxlen) {
286                 old1[maxlen] = '\0';
287         }
288
289         msg = password_check (old1, new1, pwdp);
290
291         memzero (new1, newlen);
292         memzero (old1, oldlen);
293         free (new1);
294         free (old1);
295
296         return msg;
297 }
298
299 /*
300  * Obscure - see if password is obscure enough.
301  *
302  *      The programmer is encouraged to add as much complexity to this
303  *      routine as desired.  Included are some of my favorite ways to
304  *      check passwords.
305  */
306
307 int obscure (const char *old, const char *new, const struct passwd *pwdp)
308 {
309         const char *msg = obscure_msg (old, new, pwdp);
310
311         if (NULL != msg) {
312                 printf (_("Bad password: %s.  "), msg);
313                 return 0;
314         }
315         return 1;
316 }
317
318 #else                           /* !USE_PAM */
319 extern int errno;               /* warning: ANSI C forbids an empty source file */
320 #endif                          /* !USE_PAM */