aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2006-04-02 18:57:20 +0000
committerRob Landley <rob@landley.net>2006-04-02 18:57:20 +0000
commita13cca9cf44ab36a8da90a343a6cd68ab7743345 (patch)
tree4c91358dbeae69befc39ad2fe8b81507a813ce90
parent819b56b64a5ac838512825a04d708acd948dfc0e (diff)
downloadbusybox-w32-a13cca9cf44ab36a8da90a343a6cd68ab7743345.tar.gz
busybox-w32-a13cca9cf44ab36a8da90a343a6cd68ab7743345.tar.bz2
busybox-w32-a13cca9cf44ab36a8da90a343a6cd68ab7743345.zip
New version from Tito.
-rw-r--r--libbb/obscure.c368
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
48static 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/* 53static 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
65static int similiar(const char *old, const char *newval) 55static 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/* 67static int string_checker(const char *p1, const char *p2)
80 * a nice mix of characters.
81 */
82
83static 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
129static 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
138static const char * 96static const char *obscure_msg(const char *old_p, const char *new_p, const struct passwd *pw)
139password_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;
182static const char * 135 } else { /* special characters */
183obscure_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
247int obscure(const char *old, const char *newval, const struct passwd *pwdp) 169int 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}