diff options
author | Rob Landley <rob@landley.net> | 2006-04-02 18:57:20 +0000 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2006-04-02 18:57:20 +0000 |
commit | a13cca9cf44ab36a8da90a343a6cd68ab7743345 (patch) | |
tree | 4c91358dbeae69befc39ad2fe8b81507a813ce90 | |
parent | 819b56b64a5ac838512825a04d708acd948dfc0e (diff) | |
download | busybox-w32-a13cca9cf44ab36a8da90a343a6cd68ab7743345.tar.gz busybox-w32-a13cca9cf44ab36a8da90a343a6cd68ab7743345.tar.bz2 busybox-w32-a13cca9cf44ab36a8da90a343a6cd68ab7743345.zip |
New version from Tito.
-rw-r--r-- | libbb/obscure.c | 368 |
1 files changed, 144 insertions, 224 deletions
diff --git a/libbb/obscure.c b/libbb/obscure.c index 6244970de..a152456b2 100644 --- a/libbb/obscure.c +++ b/libbb/obscure.c | |||
@@ -1,259 +1,179 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | 1 | /* vi: set sw=4 ts=4: */ |
2 | /* | 2 | /* |
3 | * Copyright 1989 - 1994, Julianne Frances Haugh <jockgrrl@austin.rr.com> | 3 | * Mini weak password checker implementation for busybox |
4 | * Copyright 2006, Bernhard Fischer <busybox@busybox.net> | ||
5 | * All rights reserved. | ||
6 | * | 4 | * |
7 | * Redistribution and use in source and binary forms, with or without | 5 | * Copyright (C) 2006 Tito Ragusa <farmatito@tiscali.it> |
8 | * modification, are permitted provided that the following conditions | ||
9 | * are met: | ||
10 | * 1. Redistributions of source code must retain the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer. | ||
12 | * 2. Redistributions in binary form must reproduce the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer in the | ||
14 | * documentation and/or other materials provided with the distribution. | ||
15 | * 3. Neither the name of Julianne F. Haugh nor the names of its contributors | ||
16 | * may be used to endorse or promote products derived from this software | ||
17 | * without specific prior written permission. | ||
18 | * | 6 | * |
19 | * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND | 7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
22 | * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE | ||
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
29 | * SUCH DAMAGE. | ||
30 | */ | 8 | */ |
31 | 9 | ||
32 | /* | 10 | /* A good password: |
33 | * This version of obscure.c contains modifications to support "cracklib" | 11 | 1) should contain at least six characters (man passwd); |
34 | * by Alec Muffet (alec.muffett@uk.sun.com). You must obtain the Cracklib | 12 | 2) empty passwords are not permitted; |
35 | * library source code for this function to operate. | 13 | 3) should contain a mix of four different types of characters |
36 | */ | 14 | upper case letters, |
15 | lower case letters, | ||
16 | numbers, | ||
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). | ||
30 | |||
31 | For each missing type of characters an increase of password length is | ||
32 | requested. | ||
33 | |||
34 | If user is root we warn only. | ||
35 | |||
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. | ||
40 | */ | ||
37 | 41 | ||
38 | #include <stdlib.h> | ||
39 | #include <stdio.h> | ||
40 | #include <string.h> | ||
41 | #include <ctype.h> | 42 | #include <ctype.h> |
42 | #include "libbb.h" | 43 | #include <unistd.h> |
43 | 44 | #include <string.h> | |
44 | /* | ||
45 | * can't be a palindrome - like `R A D A R' or `M A D A M' | ||
46 | */ | ||
47 | 45 | ||
48 | static int palindrome(const char *newval) | 46 | #include "libbb.h" |
49 | { | ||
50 | int i, j; | ||
51 | 47 | ||
52 | i = strlen(newval); | ||
53 | 48 | ||
54 | for (j = 0; j < i; j++) | 49 | /* passwords should consist of 6 (to 8 characters) */ |
55 | if (newval[i - j - 1] != newval[j]) | 50 | #define MINLEN 6 |
56 | return 0; | ||
57 | 51 | ||
58 | return 1; | ||
59 | } | ||
60 | 52 | ||
61 | /* | 53 | static int string_checker_helper(const char *p1, const char *p2) __attribute__ ((__pure__)); |
62 | * more than half of the characters are different ones. | ||
63 | */ | ||
64 | 54 | ||
65 | static int similiar(const char *old, const char *newval) | 55 | static int string_checker_helper(const char *p1, const char *p2) |
66 | { | 56 | { |
67 | int i, j; | 57 | /* as-is or capitalized */ |
68 | 58 | if (strcasecmp(p1, p2) == 0 | |
69 | for (i = j = 0; newval[i] && old[i]; i++) | 59 | /* as sub-string */ |
70 | if (strchr(newval, old[i])) | 60 | || strcasestr(p2, p1) != NULL |
71 | j++; | 61 | /* invert in case haystack is shorter than needle */ |
72 | 62 | || strcasestr(p1, p2) != NULL) | |
73 | if (i >= j * 2) | 63 | return 1; |
74 | return 0; | 64 | return 0; |
75 | |||
76 | return 1; | ||
77 | } | 65 | } |
78 | 66 | ||
79 | /* | 67 | static int string_checker(const char *p1, const char *p2) |
80 | * a nice mix of characters. | ||
81 | */ | ||
82 | |||
83 | static int simple(const char *newval) | ||
84 | { | 68 | { |
85 | #define digits 1 | ||
86 | #define uppers 2 | ||
87 | #define lowers 4 | ||
88 | #define others 8 | ||
89 | int c, is_simple = 0; | ||
90 | int size; | 69 | int size; |
91 | int i; | 70 | /* check string */ |
92 | 71 | int ret = string_checker_helper(p1, p2); | |
93 | for (i = 0; (c = *newval++) != 0; i++) { | 72 | /* Make our own copy */ |
94 | if (isdigit(c)) | 73 | char *p = bb_xstrdup(p1); |
95 | is_simple |= digits; | 74 | /* reverse string */ |
96 | else if (isupper(c)) | 75 | size = strlen(p); |
97 | is_simple |= uppers; | 76 | |
98 | else if (islower(c)) | 77 | while (size--) { |
99 | is_simple |= lowers; | 78 | *p = p1[size]; |
100 | else | 79 | p++; |
101 | is_simple |= others; | ||
102 | } | 80 | } |
103 | 81 | /* restore pointer */ | |
104 | /* | 82 | p -= strlen(p1); |
105 | * The scam is this - a password of only one character type | 83 | /* check reversed string */ |
106 | * must be 8 letters long. Two types, 7, and so on. | 84 | ret |= string_checker_helper(p, p2); |
107 | */ | 85 | /* clean up */ |
108 | 86 | memset(p, 0, strlen(p1)); | |
109 | size = 9; | 87 | free(p); |
110 | if (is_simple & digits) | 88 | return ret; |
111 | size--; | ||
112 | if (is_simple & uppers) | ||
113 | size--; | ||
114 | if (is_simple & lowers) | ||
115 | size--; | ||
116 | if (is_simple & others) | ||
117 | size--; | ||
118 | |||
119 | if (size <= i) | ||
120 | return 0; | ||
121 | |||
122 | return 1; | ||
123 | #undef digits | ||
124 | #undef uppers | ||
125 | #undef lowers | ||
126 | #undef others | ||
127 | } | 89 | } |
128 | 90 | ||
129 | static char *str_lower(char *string) | 91 | #define LOWERCASE 1 |
130 | { | 92 | #define UPPERCASE 2 |
131 | char *cp; | 93 | #define NUMBERS 4 |
132 | 94 | #define SPECIAL 8 | |
133 | for (cp = string; *cp; cp++) | ||
134 | *cp = tolower(*cp); | ||
135 | return string; | ||
136 | } | ||
137 | 95 | ||
138 | static const char * | 96 | static const char *obscure_msg(const char *old_p, const char *new_p, const struct passwd *pw) |
139 | password_check(const char *old, const char *newval, const struct passwd *pwdp) | ||
140 | { | 97 | { |
141 | const char *msg; | 98 | int i; |
142 | char *newmono, *wrapped; | 99 | int c; |
143 | int lenwrap; | 100 | int length; |
144 | 101 | int mixed = 0; | |
145 | if (strcmp(newval, old) == 0) | 102 | /* Add 1 for each type of characters to the minlen of password */ |
146 | return "no change"; | 103 | int size = MINLEN + 8; |
147 | if (simple(newval)) | 104 | const char *p; |
148 | return "too simple"; | 105 | char hostname[255]; |
149 | 106 | ||
150 | msg = NULL; | 107 | /* size */ |
151 | newmono = str_lower(bb_xstrdup(newval)); | 108 | if (!new_p || (length = strlen(new_p)) < MINLEN) |
152 | lenwrap = strlen(old); | 109 | return("too short"); |
153 | wrapped = (char *) xmalloc(lenwrap * 2 + 1); | 110 | |
154 | str_lower(strcpy(wrapped, old)); | 111 | /* no username as-is, as sub-string, reversed, capitalized, doubled */ |
155 | 112 | if (string_checker(new_p, pw->pw_name)) { | |
156 | if (palindrome(newmono)) | 113 | return "similar to username"; |
157 | msg = "a palindrome"; | 114 | } |
158 | 115 | /* no gecos as-is, as sub-string, reversed, capitalized, doubled */ | |
159 | else if (strcmp(wrapped, newmono) == 0) | 116 | if (string_checker(new_p, pw->pw_gecos)) { |
160 | msg = "case changes only"; | 117 | return "similar to gecos"; |
161 | 118 | } | |
162 | else if (similiar(wrapped, newmono)) | 119 | /* hostname as-is, as sub-string, reversed, capitalized, doubled */ |
163 | msg = "too similiar"; | 120 | if (gethostname(hostname, 255) == 0) { |
164 | 121 | hostname[254] = '\0'; | |
165 | else if ( strstr(newval, pwdp->pw_name) ) | 122 | if (string_checker(new_p, hostname)) { |
166 | msg = "username in password"; | 123 | return "similar to hostname"; |
167 | 124 | } | |
168 | else { | ||
169 | safe_strncpy(wrapped + lenwrap, wrapped, lenwrap + 1); | ||
170 | if (strstr(wrapped, newmono)) | ||
171 | msg = "rotated"; | ||
172 | } | 125 | } |
173 | 126 | ||
174 | memset(newmono, 0, strlen(newmono)); | 127 | /* Should / Must contain a mix of: */ |
175 | memset(wrapped, 0, lenwrap * 2); | 128 | for (i = 0; i < length; i++) { |
176 | free(newmono); | 129 | if (islower(new_p[i])) { /* a-z */ |
177 | free(wrapped); | 130 | mixed |= LOWERCASE; |
178 | 131 | } else if (isupper(new_p[i])) { /* A-Z */ | |
179 | return msg; | 132 | mixed |= UPPERCASE; |
180 | } | 133 | } else if (isdigit(new_p[i])) { /* 0-9 */ |
181 | 134 | mixed |= NUMBERS; | |
182 | static const char * | 135 | } else { /* special characters */ |
183 | obscure_msg(const char *old, const char *newval, const struct passwd *pwdp) | 136 | mixed |= SPECIAL; |
184 | { | 137 | } |
185 | int maxlen, oldlen, newlen; | 138 | /* More than 50% similar characters ? */ |
186 | char *new1, *old1; | 139 | c = 0; |
187 | const char *msg; | 140 | p = new_p; |
188 | 141 | while (1) { | |
189 | oldlen = strlen(old); | 142 | if ((p = strchr(p, new_p[i])) == NULL) { |
190 | newlen = strlen(newval); | 143 | break; |
191 | 144 | } | |
192 | #if 0 /* why not check the password when set for the first time? --marekm */ | 145 | c++; |
193 | if (old[0] == '\0') | 146 | if (!++p) { |
194 | /* return (1); */ | 147 | break; /* move past the matched char if possible */ |
195 | return NULL; | 148 | } |
196 | #endif | 149 | } |
197 | 150 | ||
198 | if (newlen < 5) | 151 | if (c >= (length / 2)) { |
199 | return "too short"; | 152 | return "too many similar characters"; |
200 | 153 | } | |
201 | /* | 154 | } |
202 | * Remaining checks are optional. | 155 | for(i=0;i<4;i++) |
203 | */ | 156 | if (mixed & (1<<i)) size -= 2; |
204 | /* Not for us -- Sean | 157 | if (length < size) |
205 | *if (!getdef_bool("OBSCURE_CHECKS_ENAB")) | 158 | return "too weak"; |
206 | * return NULL; | 159 | |
207 | */ | 160 | if (old_p && old_p[0] != '\0') { |
208 | msg = password_check(old, newval, pwdp); | 161 | /* check vs. old password */ |
209 | if (msg) | 162 | if (string_checker(new_p, old_p)) { |
210 | return msg; | 163 | return "similar to old password"; |
211 | 164 | } | |
212 | /* The traditional crypt() truncates passwords to 8 chars. It is | 165 | } |
213 | possible to circumvent the above checks by choosing an easy | 166 | return NULL; |
214 | 8-char password and adding some random characters to it... | ||
215 | Example: "password$%^&*123". So check it again, this time | ||
216 | truncated to the maximum length. Idea from npasswd. --marekm */ | ||
217 | |||
218 | maxlen = 8; | ||
219 | if (oldlen <= maxlen && newlen <= maxlen) | ||
220 | return NULL; | ||
221 | |||
222 | new1 = (char *) bb_xstrdup(newval); | ||
223 | old1 = (char *) bb_xstrdup(old); | ||
224 | if (newlen > maxlen) | ||
225 | new1[maxlen] = '\0'; | ||
226 | if (oldlen > maxlen) | ||
227 | old1[maxlen] = '\0'; | ||
228 | |||
229 | msg = password_check(old1, new1, pwdp); | ||
230 | |||
231 | memset(new1, 0, newlen); | ||
232 | memset(old1, 0, oldlen); | ||
233 | free(new1); | ||
234 | free(old1); | ||
235 | |||
236 | return msg; | ||
237 | } | 167 | } |
238 | 168 | ||
239 | /* | ||
240 | * Obscure - see if password is obscure enough. | ||
241 | * | ||
242 | * The programmer is encouraged to add as much complexity to this | ||
243 | * routine as desired. Included are some of my favorite ways to | ||
244 | * check passwords. | ||
245 | */ | ||
246 | |||
247 | int obscure(const char *old, const char *newval, const struct passwd *pwdp) | 169 | int obscure(const char *old, const char *newval, const struct passwd *pwdp) |
248 | { | 170 | { |
249 | const char *msg = obscure_msg(old, newval, pwdp); | 171 | const char *msg; |
250 | 172 | ||
251 | /* if (msg) { */ | 173 | if ((msg = obscure_msg(old, newval, pwdp))) { |
252 | if (msg != NULL) { | ||
253 | printf("Bad password: %s.\n", msg); | 174 | printf("Bad password: %s.\n", msg); |
254 | /* return 0; */ | 175 | /* If user is root warn only */ |
255 | return 1; | 176 | return (getuid())? 1 : 0; |
256 | } | 177 | } |
257 | /* return 1; */ | ||
258 | return 0; | 178 | return 0; |
259 | } | 179 | } |