1 /* vi: set sw=4 ts=4: */
3 * Mini weak password checker implementation for busybox
5 * Copyright (C) 2006 Tito Ragusa <farmatito@tiscali.it>
7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
11 1) should contain at least six characters (man passwd);
12 2) empty passwords are not permitted;
13 3) should contain a mix of four different types of characters
17 special characters such as !@#$%^&*,;".
18 This password types should not be permitted:
19 a) pure numbers: birthdates, social security number, license plate, phone numbers;
20 b) words and all letters only passwords (uppercase, lowercase or mixed)
21 as palindromes, consecutive or repetitive letters
22 or adjacent letters on your keyboard;
23 c) username, real name, company name or (e-mail?) address
24 in any form (as-is, reversed, capitalized, doubled, etc.).
25 (we can check only against username, gecos and hostname)
26 d) common and obvious letter-number replacements
27 (e.g. replace the letter O with number 0)
28 such as "M1cr0$0ft" or "P@ssw0rd" (CAVEAT: we cannot check for them
29 without the use of a dictionary).
31 For each missing type of characters an increase of password length is
34 If user is root we warn only.
36 CAVEAT: some older versions of crypt() truncates passwords to 8 chars,
37 so that aaaaaaaa1Q$ is equal to aaaaaaaa making it possible to fool
38 some of our checks. We don't test for this special case as newer versions
39 of crypt do not truncate passwords.
49 /* passwords should consist of 6 (to 8 characters) */
53 static int string_checker_helper(const char *p1, const char *p2) __attribute__ ((__pure__));
55 static int string_checker_helper(const char *p1, const char *p2)
57 /* as-is or capitalized */
58 if (strcasecmp(p1, p2) == 0
60 || strcasestr(p2, p1) != NULL
61 /* invert in case haystack is shorter than needle */
62 || strcasestr(p1, p2) != NULL)
67 static int string_checker(const char *p1, const char *p2)
71 int ret = string_checker_helper(p1, p2);
72 /* Make our own copy */
73 char *p = bb_xstrdup(p1);
83 /* check reversed string */
84 ret |= string_checker_helper(p, p2);
86 memset(p, 0, strlen(p1));
96 static const char *obscure_msg(const char *old_p, const char *new_p, const struct passwd *pw)
102 /* Add 1 for each type of characters to the minlen of password */
103 int size = MINLEN + 8;
108 if (!new_p || (length = strlen(new_p)) < MINLEN)
111 /* no username as-is, as sub-string, reversed, capitalized, doubled */
112 if (string_checker(new_p, pw->pw_name)) {
113 return "similar to username";
115 /* no gecos as-is, as sub-string, reversed, capitalized, doubled */
116 if (string_checker(new_p, pw->pw_gecos)) {
117 return "similar to gecos";
119 /* hostname as-is, as sub-string, reversed, capitalized, doubled */
120 if (gethostname(hostname, 255) == 0) {
121 hostname[254] = '\0';
122 if (string_checker(new_p, hostname)) {
123 return "similar to hostname";
127 /* Should / Must contain a mix of: */
128 for (i = 0; i < length; i++) {
129 if (islower(new_p[i])) { /* a-z */
131 } else if (isupper(new_p[i])) { /* A-Z */
133 } else if (isdigit(new_p[i])) { /* 0-9 */
135 } else { /* special characters */
138 /* More than 50% similar characters ? */
142 if ((p = strchr(p, new_p[i])) == NULL) {
147 break; /* move past the matched char if possible */
151 if (c >= (length / 2)) {
152 return "too many similar characters";
156 if (mixed & (1<<i)) size -= 2;
160 if (old_p && old_p[0] != '\0') {
161 /* check vs. old password */
162 if (string_checker(new_p, old_p)) {
163 return "similar to old password";
169 int obscure(const char *old, const char *newval, const struct passwd *pwdp)
173 if ((msg = obscure_msg(old, newval, pwdp))) {
174 printf("Bad password: %s.\n", msg);
175 /* If user is root warn only */
176 return (getuid())? 1 : 0;