2 * This program is copyright Alec Muffett 1993. The author disclaims all
3 * responsibility or liability with respect to it's usage or its effect
4 * upon hardware or computer systems, and maintains copyright as set out
5 * in the "LICENCE" document which accompanies distributions of Crack v4.0
10 * Modified as part of the krb5-strength project as follows:
12 * 2007-03-22 Russ Allbery <eagle@eyrie.org>
13 * - Add four-, five-, and six-character prefix and suffix rules.
14 * - Longer passwords must contain more different characters, up to 8.
15 * - Disable GECOS checking (useless for a server).
16 * - Replace exit(-1) with return when dictionary doesn't exist.
17 * - Additional system includes for other functions.
18 * 2009-10-14 Russ Allbery <eagle@eyrie.org>
19 * - Add ANSI C protototypes for all functions.
20 * - Tweaks for const cleanliness.
21 * - Add parentheses around assignment used for its truth value.
22 * - Change a variable to unsigned int to avoid gcc warnings.
23 * - Remove the unused FascistGecos function.
24 * 2012-05-11 Russ Allbery <eagle@eyrie.org>
25 * - Change MINLENGTH to 8.
26 * - Use a separate buffer to hold the reversed password.
27 * - Also check whether a password is a duplicated dictionary word.
28 * 2013-09-24 Russ Allbery <eagle@eyrie.org>
29 * - Replaced MAXSTEP with allowing one increment per four characters.
30 * - Changed error for very short passwords to match current CrackLib.
31 * - Close the dictionary after each password lookup.
34 static const char vers_id[] = "fascist.c : v2.3p3 Alec Muffett 14 dec 1997";
37 #include <sys/types.h>
43 #define ISSKIP(x) (isspace(x) || ispunct(x))
52 static const char *r_destructors[] = {
53 ":", /* noop - must do this to test raw word. */
59 "[", /* trimming leading/trailing junk */
64 "]]]", /* 5 for 8 char passwords */
65 /* This is for longer passwords, it should be based on length */
66 "]]]]", /* 6 for 10 char passwords */
68 "]]]]]", /* 7 for 12 char passwords */
70 "]]]]]]", /* 8 for 14 char passwords */
73 "/?p@?p", /* purging out punctuation/symbols/junk */
77 /* attempt reverse engineering of password strings */
82 "/$s$s/0s0o/2s2a/3s3e",
83 "/$s$s/0s0o/2s2a/3s3e/5s5s",
84 "/$s$s/0s0o/2s2a/3s3e/5s5s/1s1i",
85 "/$s$s/0s0o/2s2a/3s3e/5s5s/1s1l",
86 "/$s$s/0s0o/2s2a/3s3e/5s5s/1s1i/4s4a",
87 "/$s$s/0s0o/2s2a/3s3e/5s5s/1s1i/4s4h",
88 "/$s$s/0s0o/2s2a/3s3e/5s5s/1s1l/4s4a",
89 "/$s$s/0s0o/2s2a/3s3e/5s5s/1s1l/4s4h",
90 "/$s$s/0s0o/2s2a/3s3e/5s5s/4s4a",
91 "/$s$s/0s0o/2s2a/3s3e/5s5s/4s4h",
92 "/$s$s/0s0o/2s2a/3s3e/5s5s/4s4a",
93 "/$s$s/0s0o/2s2a/3s3e/5s5s/4s4h",
94 "/$s$s/0s0o/2s2a/3s3e/1s1i",
95 "/$s$s/0s0o/2s2a/3s3e/1s1l",
96 "/$s$s/0s0o/2s2a/3s3e/1s1i/4s4a",
97 "/$s$s/0s0o/2s2a/3s3e/1s1i/4s4h",
98 "/$s$s/0s0o/2s2a/3s3e/1s1l/4s4a",
99 "/$s$s/0s0o/2s2a/3s3e/1s1l/4s4h",
100 "/$s$s/0s0o/2s2a/3s3e/4s4a",
101 "/$s$s/0s0o/2s2a/3s3e/4s4h",
102 "/$s$s/0s0o/2s2a/3s3e/4s4a",
103 "/$s$s/0s0o/2s2a/3s3e/4s4h",
104 "/$s$s/0s0o/2s2a/5s5s",
105 "/$s$s/0s0o/2s2a/5s5s/1s1i",
106 "/$s$s/0s0o/2s2a/5s5s/1s1l",
107 "/$s$s/0s0o/2s2a/5s5s/1s1i/4s4a",
108 "/$s$s/0s0o/2s2a/5s5s/1s1i/4s4h",
109 "/$s$s/0s0o/2s2a/5s5s/1s1l/4s4a",
110 "/$s$s/0s0o/2s2a/5s5s/1s1l/4s4h",
111 "/$s$s/0s0o/2s2a/5s5s/4s4a",
112 "/$s$s/0s0o/2s2a/5s5s/4s4h",
113 "/$s$s/0s0o/2s2a/5s5s/4s4a",
114 "/$s$s/0s0o/2s2a/5s5s/4s4h",
115 "/$s$s/0s0o/2s2a/1s1i",
116 "/$s$s/0s0o/2s2a/1s1l",
117 "/$s$s/0s0o/2s2a/1s1i/4s4a",
118 "/$s$s/0s0o/2s2a/1s1i/4s4h",
119 "/$s$s/0s0o/2s2a/1s1l/4s4a",
120 "/$s$s/0s0o/2s2a/1s1l/4s4h",
121 "/$s$s/0s0o/2s2a/4s4a",
122 "/$s$s/0s0o/2s2a/4s4h",
123 "/$s$s/0s0o/2s2a/4s4a",
124 "/$s$s/0s0o/2s2a/4s4h",
126 "/$s$s/0s0o/3s3e/5s5s",
127 "/$s$s/0s0o/3s3e/5s5s/1s1i",
128 "/$s$s/0s0o/3s3e/5s5s/1s1l",
129 "/$s$s/0s0o/3s3e/5s5s/1s1i/4s4a",
130 "/$s$s/0s0o/3s3e/5s5s/1s1i/4s4h",
131 "/$s$s/0s0o/3s3e/5s5s/1s1l/4s4a",
132 "/$s$s/0s0o/3s3e/5s5s/1s1l/4s4h",
133 "/$s$s/0s0o/3s3e/5s5s/4s4a",
134 "/$s$s/0s0o/3s3e/5s5s/4s4h",
135 "/$s$s/0s0o/3s3e/5s5s/4s4a",
136 "/$s$s/0s0o/3s3e/5s5s/4s4h",
137 "/$s$s/0s0o/3s3e/1s1i",
138 "/$s$s/0s0o/3s3e/1s1l",
139 "/$s$s/0s0o/3s3e/1s1i/4s4a",
140 "/$s$s/0s0o/3s3e/1s1i/4s4h",
141 "/$s$s/0s0o/3s3e/1s1l/4s4a",
142 "/$s$s/0s0o/3s3e/1s1l/4s4h",
143 "/$s$s/0s0o/3s3e/4s4a",
144 "/$s$s/0s0o/3s3e/4s4h",
145 "/$s$s/0s0o/3s3e/4s4a",
146 "/$s$s/0s0o/3s3e/4s4h",
148 "/$s$s/0s0o/5s5s/1s1i",
149 "/$s$s/0s0o/5s5s/1s1l",
150 "/$s$s/0s0o/5s5s/1s1i/4s4a",
151 "/$s$s/0s0o/5s5s/1s1i/4s4h",
152 "/$s$s/0s0o/5s5s/1s1l/4s4a",
153 "/$s$s/0s0o/5s5s/1s1l/4s4h",
154 "/$s$s/0s0o/5s5s/4s4a",
155 "/$s$s/0s0o/5s5s/4s4h",
156 "/$s$s/0s0o/5s5s/4s4a",
157 "/$s$s/0s0o/5s5s/4s4h",
160 "/$s$s/0s0o/1s1i/4s4a",
161 "/$s$s/0s0o/1s1i/4s4h",
162 "/$s$s/0s0o/1s1l/4s4a",
163 "/$s$s/0s0o/1s1l/4s4h",
170 "/$s$s/2s2a/3s3e/5s5s",
171 "/$s$s/2s2a/3s3e/5s5s/1s1i",
172 "/$s$s/2s2a/3s3e/5s5s/1s1l",
173 "/$s$s/2s2a/3s3e/5s5s/1s1i/4s4a",
174 "/$s$s/2s2a/3s3e/5s5s/1s1i/4s4h",
175 "/$s$s/2s2a/3s3e/5s5s/1s1l/4s4a",
176 "/$s$s/2s2a/3s3e/5s5s/1s1l/4s4h",
177 "/$s$s/2s2a/3s3e/5s5s/4s4a",
178 "/$s$s/2s2a/3s3e/5s5s/4s4h",
179 "/$s$s/2s2a/3s3e/5s5s/4s4a",
180 "/$s$s/2s2a/3s3e/5s5s/4s4h",
181 "/$s$s/2s2a/3s3e/1s1i",
182 "/$s$s/2s2a/3s3e/1s1l",
183 "/$s$s/2s2a/3s3e/1s1i/4s4a",
184 "/$s$s/2s2a/3s3e/1s1i/4s4h",
185 "/$s$s/2s2a/3s3e/1s1l/4s4a",
186 "/$s$s/2s2a/3s3e/1s1l/4s4h",
187 "/$s$s/2s2a/3s3e/4s4a",
188 "/$s$s/2s2a/3s3e/4s4h",
189 "/$s$s/2s2a/3s3e/4s4a",
190 "/$s$s/2s2a/3s3e/4s4h",
192 "/$s$s/2s2a/5s5s/1s1i",
193 "/$s$s/2s2a/5s5s/1s1l",
194 "/$s$s/2s2a/5s5s/1s1i/4s4a",
195 "/$s$s/2s2a/5s5s/1s1i/4s4h",
196 "/$s$s/2s2a/5s5s/1s1l/4s4a",
197 "/$s$s/2s2a/5s5s/1s1l/4s4h",
198 "/$s$s/2s2a/5s5s/4s4a",
199 "/$s$s/2s2a/5s5s/4s4h",
200 "/$s$s/2s2a/5s5s/4s4a",
201 "/$s$s/2s2a/5s5s/4s4h",
204 "/$s$s/2s2a/1s1i/4s4a",
205 "/$s$s/2s2a/1s1i/4s4h",
206 "/$s$s/2s2a/1s1l/4s4a",
207 "/$s$s/2s2a/1s1l/4s4h",
214 "/$s$s/3s3e/5s5s/1s1i",
215 "/$s$s/3s3e/5s5s/1s1l",
216 "/$s$s/3s3e/5s5s/1s1i/4s4a",
217 "/$s$s/3s3e/5s5s/1s1i/4s4h",
218 "/$s$s/3s3e/5s5s/1s1l/4s4a",
219 "/$s$s/3s3e/5s5s/1s1l/4s4h",
220 "/$s$s/3s3e/5s5s/4s4a",
221 "/$s$s/3s3e/5s5s/4s4h",
222 "/$s$s/3s3e/5s5s/4s4a",
223 "/$s$s/3s3e/5s5s/4s4h",
226 "/$s$s/3s3e/1s1i/4s4a",
227 "/$s$s/3s3e/1s1i/4s4h",
228 "/$s$s/3s3e/1s1l/4s4a",
229 "/$s$s/3s3e/1s1l/4s4h",
237 "/$s$s/5s5s/1s1i/4s4a",
238 "/$s$s/5s5s/1s1i/4s4h",
239 "/$s$s/5s5s/1s1l/4s4a",
240 "/$s$s/5s5s/1s1l/4s4h",
258 "/0s0o/2s2a/3s3e/5s5s",
259 "/0s0o/2s2a/3s3e/5s5s/1s1i",
260 "/0s0o/2s2a/3s3e/5s5s/1s1l",
261 "/0s0o/2s2a/3s3e/5s5s/1s1i/4s4a",
262 "/0s0o/2s2a/3s3e/5s5s/1s1i/4s4h",
263 "/0s0o/2s2a/3s3e/5s5s/1s1l/4s4a",
264 "/0s0o/2s2a/3s3e/5s5s/1s1l/4s4h",
265 "/0s0o/2s2a/3s3e/5s5s/4s4a",
266 "/0s0o/2s2a/3s3e/5s5s/4s4h",
267 "/0s0o/2s2a/3s3e/5s5s/4s4a",
268 "/0s0o/2s2a/3s3e/5s5s/4s4h",
269 "/0s0o/2s2a/3s3e/1s1i",
270 "/0s0o/2s2a/3s3e/1s1l",
271 "/0s0o/2s2a/3s3e/1s1i/4s4a",
272 "/0s0o/2s2a/3s3e/1s1i/4s4h",
273 "/0s0o/2s2a/3s3e/1s1l/4s4a",
274 "/0s0o/2s2a/3s3e/1s1l/4s4h",
275 "/0s0o/2s2a/3s3e/4s4a",
276 "/0s0o/2s2a/3s3e/4s4h",
277 "/0s0o/2s2a/3s3e/4s4a",
278 "/0s0o/2s2a/3s3e/4s4h",
280 "/0s0o/2s2a/5s5s/1s1i",
281 "/0s0o/2s2a/5s5s/1s1l",
282 "/0s0o/2s2a/5s5s/1s1i/4s4a",
283 "/0s0o/2s2a/5s5s/1s1i/4s4h",
284 "/0s0o/2s2a/5s5s/1s1l/4s4a",
285 "/0s0o/2s2a/5s5s/1s1l/4s4h",
286 "/0s0o/2s2a/5s5s/4s4a",
287 "/0s0o/2s2a/5s5s/4s4h",
288 "/0s0o/2s2a/5s5s/4s4a",
289 "/0s0o/2s2a/5s5s/4s4h",
292 "/0s0o/2s2a/1s1i/4s4a",
293 "/0s0o/2s2a/1s1i/4s4h",
294 "/0s0o/2s2a/1s1l/4s4a",
295 "/0s0o/2s2a/1s1l/4s4h",
302 "/0s0o/3s3e/5s5s/1s1i",
303 "/0s0o/3s3e/5s5s/1s1l",
304 "/0s0o/3s3e/5s5s/1s1i/4s4a",
305 "/0s0o/3s3e/5s5s/1s1i/4s4h",
306 "/0s0o/3s3e/5s5s/1s1l/4s4a",
307 "/0s0o/3s3e/5s5s/1s1l/4s4h",
308 "/0s0o/3s3e/5s5s/4s4a",
309 "/0s0o/3s3e/5s5s/4s4h",
310 "/0s0o/3s3e/5s5s/4s4a",
311 "/0s0o/3s3e/5s5s/4s4h",
314 "/0s0o/3s3e/1s1i/4s4a",
315 "/0s0o/3s3e/1s1i/4s4h",
316 "/0s0o/3s3e/1s1l/4s4a",
317 "/0s0o/3s3e/1s1l/4s4h",
325 "/0s0o/5s5s/1s1i/4s4a",
326 "/0s0o/5s5s/1s1i/4s4h",
327 "/0s0o/5s5s/1s1l/4s4a",
328 "/0s0o/5s5s/1s1l/4s4h",
346 "/2s2a/3s3e/5s5s/1s1i",
347 "/2s2a/3s3e/5s5s/1s1l",
348 "/2s2a/3s3e/5s5s/1s1i/4s4a",
349 "/2s2a/3s3e/5s5s/1s1i/4s4h",
350 "/2s2a/3s3e/5s5s/1s1l/4s4a",
351 "/2s2a/3s3e/5s5s/1s1l/4s4h",
352 "/2s2a/3s3e/5s5s/4s4a",
353 "/2s2a/3s3e/5s5s/4s4h",
354 "/2s2a/3s3e/5s5s/4s4a",
355 "/2s2a/3s3e/5s5s/4s4h",
358 "/2s2a/3s3e/1s1i/4s4a",
359 "/2s2a/3s3e/1s1i/4s4h",
360 "/2s2a/3s3e/1s1l/4s4a",
361 "/2s2a/3s3e/1s1l/4s4h",
369 "/2s2a/5s5s/1s1i/4s4a",
370 "/2s2a/5s5s/1s1i/4s4h",
371 "/2s2a/5s5s/1s1l/4s4a",
372 "/2s2a/5s5s/1s1l/4s4h",
391 "/3s3e/5s5s/1s1i/4s4a",
392 "/3s3e/5s5s/1s1i/4s4h",
393 "/3s3e/5s5s/1s1l/4s4a",
394 "/3s3e/5s5s/1s1l/4s4h",
436 FascistLook(PWDICT *pwp, const char *instring)
439 unsigned int mindiff;
442 char junk[STRINGSIZE];
444 char rpassword[STRINGSIZE];
445 char reverse[STRINGSIZE];
448 notfound = PW_WORDS(pwp);
449 /* already truncated if from FascistCheck() */
450 /* but pretend it wasn't ... */
451 strncpy(rpassword, instring, TRUNCSTRINGSIZE);
452 rpassword[TRUNCSTRINGSIZE - 1] = '\0';
453 password = rpassword;
455 pw_len = strlen(password);
458 return ("it is WAY too short");
463 return ("it is too short");
469 for (i = 0; i < STRINGSIZE && password[i]; i++)
471 if (!strchr(junk, password[i]))
473 *(jptr++) = password[i];
479 * mindiff is the number of different characters the password has to
480 * contain. The original CrackLib always requires five different
481 * characters. This increases that number up to 8 for passwords longer
482 * than nine characters.
484 if (pw_len < 2 * MINDIFF)
489 if ((pw_len % 2) == 0)
491 mindiff = (pw_len + 1) / 2;
494 mindiff = pw_len / 2;
497 if (mindiff > MAXMINDIFF)
499 mindiff = MAXMINDIFF;
503 if (strlen(junk) < mindiff)
505 return ("it does not contain enough DIFFERENT characters");
508 strcpy(password, Lowercase(password));
512 while (*password && isspace(*password))
519 return ("it is all whitespace");
524 while (ptr[0] && ptr[1])
526 if ((ptr[1] == (ptr[0] + 1)) || (ptr[1] == (ptr[0] - 1)))
533 /* Allow one simple letter increment per three characters. */
534 if (i > (int) strlen(password) / 3)
536 return ("it is too simplistic/systematic");
539 if (PMatch("aadddddda", password)) /* smirk */
541 return ("it looks like a National Insurance number.");
544 /* This is pretty useless for a server. */
545 #ifdef HAVE_GECOS_AVAILABLE
546 if (ptr = FascistGecos(password, getuid()))
552 /* it should be safe to use Mangle with its reliance on STRINGSIZE
553 since password cannot be longer than TRUNCSTRINGSIZE;
554 nonetheless this is not an elegant solution */
556 for (i = 0; r_destructors[i]; i++)
560 if (!(a = Mangle(password, r_destructors[i])))
566 printf("%-16s (dict)\n", a);
569 if (FindPW(pwp, a) != notfound)
571 return ("it is based on a dictionary word");
575 strcpy(reverse, Reverse(password));
577 for (i = 0; r_destructors[i]; i++)
581 if (!(a = Mangle(reverse, r_destructors[i])))
586 printf("%-16s (reversed dict)\n", a);
588 if (FindPW(pwp, a) != notfound)
590 return ("it is based on a (reversed) dictionary word");
594 /* Check for a duplicated word. */
596 if ((pw_len % 2) == 0)
598 if (strncmp(password, password + (pw_len / 2), pw_len / 2) == 0)
600 password[pw_len / 2] = '\0';
601 for (i = 0; r_destructors[i]; i++)
605 if (!(a = Mangle(password, r_destructors[i])))
610 printf("%-16s (duplicated dict)\n", a);
612 if (FindPW(pwp, a) != notfound)
614 return ("it is based on a (duplicated) dictionary word");
624 FascistCheck(const char *password, const char *path)
627 char pwtrunced[STRINGSIZE];
630 /* security problem: assume we may have been given a really long
631 password (buffer attack) and so truncate it to a workable size;
632 try to define workable size as something from which we cannot
633 extend a buffer beyond its limits in the rest of the code */
635 strncpy(pwtrunced, password, TRUNCSTRINGSIZE);
636 pwtrunced[TRUNCSTRINGSIZE - 1] = '\0'; /* enforce */
638 /* perhaps someone should put something here to check if password
639 is really long and syslog() a message denoting buffer attacks? */
641 if (!(pwp = PWOpen(path, "r")))
644 return "Cannot check password: dictionary unavailable";
647 result = FascistLook(pwp, pwtrunced);