2 * Copyright (c) 2000-2002 by Solar Designer. See LICENSE.
12 #define REASON_ERROR \
16 "is the same as the old one"
17 #define REASON_SIMILAR \
18 "is based on the old one"
20 #define REASON_SHORT \
25 #define REASON_SIMPLESHORT \
26 "not enough different characters or classes for this length"
27 #define REASON_SIMPLE \
28 "not enough different characters or classes"
30 #define REASON_PERSONAL \
31 "based on personal login information"
34 "based on a dictionary word and not a passphrase"
38 typedef unsigned long fixed
;
41 * Calculates the expected number of different characters for a random
42 * password of a given length. The result is rounded down. We use this
43 * with the _requested_ minimum length (so longer passwords don't have
44 * to meet this strict requirement for their length).
46 static int expected_different(int charset
, int length
)
50 x
= ((fixed
)(charset
- 1) << FIXED_BITS
) / charset
;
52 while (--length
> 0) y
= (y
* x
) >> FIXED_BITS
;
53 z
= (fixed
)charset
* (((fixed
)1 << FIXED_BITS
) - y
);
55 return (int)(z
>> FIXED_BITS
);
59 * A password is too simple if it is too short for its class, or doesn't
60 * contain enough different characters for its class, or doesn't contain
61 * enough words for a passphrase.
63 static int is_simple(passwdqc_params_t
*params
, const char *newpass
)
65 int length
, classes
, words
, chars
;
66 int digits
, lowers
, uppers
, others
, unknowns
;
69 length
= classes
= words
= chars
= 0;
70 digits
= lowers
= uppers
= others
= unknowns
= 0;
72 while ((c
= (unsigned char)newpass
[length
])) {
75 if (!isascii(c
)) unknowns
++; else
76 if (isdigit(c
)) digits
++; else
77 if (islower(c
)) lowers
++; else
78 if (isupper(c
)) uppers
++; else
81 if (isascii(c
) && isalpha(c
) && isascii(p
) && !isalpha(p
))
85 if (!strchr(&newpass
[length
], c
))
89 if (!length
) return 1;
91 /* Upper case characters and digits used in common ways don't increase the
92 * strength of a password */
93 c
= (unsigned char)newpass
[0];
94 if (uppers
&& isascii(c
) && isupper(c
)) uppers
--;
95 c
= (unsigned char)newpass
[length
- 1];
96 if (digits
&& isascii(c
) && isdigit(c
)) digits
--;
98 /* Count the number of different character classes we've seen. We assume
99 * that there are no non-ASCII characters for digits. */
101 if (digits
) classes
++;
102 if (lowers
) classes
++;
103 if (uppers
) classes
++;
104 if (others
) classes
++;
105 if (unknowns
&& (!classes
|| (digits
&& classes
== 1))) classes
++;
107 for (; classes
> 0; classes
--)
110 if (length
>= params
->min
[0] &&
111 chars
>= expected_different(10, params
->min
[0]) - 1)
116 if (length
>= params
->min
[1] &&
117 chars
>= expected_different(36, params
->min
[1]) - 1)
119 if (!params
->passphrase_words
||
120 words
< params
->passphrase_words
)
122 if (length
>= params
->min
[2] &&
123 chars
>= expected_different(27, params
->min
[2]) - 1)
128 if (length
>= params
->min
[3] &&
129 chars
>= expected_different(62, params
->min
[3]) - 1)
134 if (length
>= params
->min
[4] &&
135 chars
>= expected_different(95, params
->min
[4]) - 1)
143 static char *unify(const char *src
)
149 if (!(dst
= malloc(strlen(src
) + 1)))
155 c
= (unsigned char)*sptr
;
156 if (isascii(c
) && isupper(c
))
157 *dptr
++ = tolower(c
);
165 static char *reverse(const char *src
)
170 if (!(dst
= malloc(strlen(src
) + 1)))
173 sptr
= &src
[strlen(src
)];
182 static void clean(char *dst
)
185 memset(dst
, 0, strlen(dst
));
191 * Needle is based on haystack if both contain a long enough common
192 * substring and needle would be too simple for a password with the
195 static int is_based(passwdqc_params_t
*params
,
196 const char *haystack
, const char *needle
, const char *original
)
204 if (!params
->match_length
) /* disabled */
207 if (params
->match_length
< 0) /* misconfigured */
210 if (strstr(haystack
, needle
)) /* based on haystack entirely */
215 length
= strlen(needle
);
216 for (i
= 0; i
<= length
- params
->match_length
; i
++)
217 for (j
= params
->match_length
; i
+ j
<= length
; j
++) {
219 for (p
= haystack
; *p
; p
++)
220 if (*p
== needle
[i
] && !strncmp(p
, &needle
[i
], j
)) {
223 if (!(scratch
= malloc(length
+ 1)))
226 memcpy(scratch
, original
, i
);
227 memcpy(&scratch
[i
], &original
[i
+ j
],
228 length
+ 1 - (i
+ j
));
229 if (is_simple(params
, scratch
)) {
243 * This wordlist check is now the least important given the checks above
244 * and the support for passphrases (which are based on dictionary words,
245 * and checked by other means). It is still useful to trap simple short
246 * passwords (if short passwords are allowed) that are word-based, but
247 * passed the other checks due to uncommon capitalization, digits, and
248 * special characters. We (mis)use the same set of words that are used
249 * to generate random passwords. This list is much smaller than those
250 * used for password crackers, and it doesn't contain common passwords
251 * that aren't short English words. Perhaps support for large wordlists
252 * should still be added, even though this is now of little importance.
254 static int is_word_based(passwdqc_params_t
*params
,
255 const char *needle
, const char *original
)
262 for (i
= 0; i
< 0x1000; i
++) {
263 memcpy(word
, _passwdqc_wordset_4k
[i
], 6);
264 if ((int)strlen(word
) < params
->match_length
) continue;
265 unified
= unify(word
);
266 if (is_based(params
, unified
, needle
, original
)) {
276 const char *_passwdqc_check(passwdqc_params_t
*params
,
277 const char *newpass
, const char *oldpass
, struct passwd
*pw
)
279 char truncated
[9], *reversed
;
280 char *u_newpass
, *u_reversed
;
282 char *u_name
, *u_gecos
;
287 u_newpass
= u_reversed
= NULL
;
289 u_name
= u_gecos
= NULL
;
293 if (oldpass
&& !strcmp(oldpass
, newpass
))
294 reason
= REASON_SAME
;
296 length
= strlen(newpass
);
298 if (!reason
&& length
< params
->min
[4])
299 reason
= REASON_SHORT
;
301 if (!reason
&& length
> params
->max
) {
302 if (params
->max
== 8) {
304 strncat(truncated
, newpass
, 8);
306 if (oldpass
&& !strncmp(oldpass
, newpass
, 8))
307 reason
= REASON_SAME
;
309 reason
= REASON_LONG
;
312 if (!reason
&& is_simple(params
, newpass
)) {
313 if (length
< params
->min
[1] && params
->min
[1] <= params
->max
)
314 reason
= REASON_SIMPLESHORT
;
316 reason
= REASON_SIMPLE
;
320 if ((reversed
= reverse(newpass
))) {
321 u_newpass
= unify(newpass
);
322 u_reversed
= unify(reversed
);
324 u_oldpass
= unify(oldpass
);
326 u_name
= unify(pw
->pw_name
);
327 u_gecos
= unify(pw
->pw_gecos
);
331 !u_newpass
|| !u_reversed
||
332 (oldpass
&& !u_oldpass
) ||
333 (pw
&& (!u_name
|| !u_gecos
)))
334 reason
= REASON_ERROR
;
337 if (!reason
&& oldpass
&& params
->similar_deny
&&
338 (is_based(params
, u_oldpass
, u_newpass
, newpass
) ||
339 is_based(params
, u_oldpass
, u_reversed
, reversed
)))
340 reason
= REASON_SIMILAR
;
343 (is_based(params
, u_name
, u_newpass
, newpass
) ||
344 is_based(params
, u_name
, u_reversed
, reversed
) ||
345 is_based(params
, u_gecos
, u_newpass
, newpass
) ||
346 is_based(params
, u_gecos
, u_reversed
, reversed
)))
347 reason
= REASON_PERSONAL
;
349 if (!reason
&& (int)strlen(newpass
) < params
->min
[2] &&
350 (is_word_based(params
, u_newpass
, newpass
) ||
351 is_word_based(params
, u_reversed
, reversed
)))
352 reason
= REASON_WORD
;
354 memset(truncated
, 0, sizeof(truncated
));
356 clean(u_newpass
); clean(u_reversed
);
358 clean(u_name
); clean(u_gecos
);