1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42#include "libbb.h"
43
44static int string_checker_helper(const char *p1, const char *p2) __attribute__ ((__pure__));
45
46static int string_checker_helper(const char *p1, const char *p2)
47{
48
49 if (strcasestr(p2, p1) != NULL
50
51 || strcasestr(p1, p2) != NULL
52
53
54 ) {
55 return 1;
56 }
57 return 0;
58}
59
60static int string_checker(const char *p1, const char *p2)
61{
62 int size, i;
63
64 int ret = string_checker_helper(p1, p2);
65
66 char *p = xstrdup(p1);
67
68
69 i = size = strlen(p1);
70 while (--i >= 0) {
71 *p++ = p1[i];
72 }
73 p -= size;
74
75
76 ret |= string_checker_helper(p, p2);
77
78
79 nuke_str(p);
80 free(p);
81
82 return ret;
83}
84
85#define CATEGORIES 4
86
87#define LOWERCASE 1
88#define UPPERCASE 2
89#define NUMBERS 4
90#define SPECIAL 8
91
92#define LAST_CAT 8
93
94static const char *obscure_msg(const char *old_p, const char *new_p, const struct passwd *pw)
95{
96 unsigned length;
97 unsigned size;
98 unsigned mixed;
99 unsigned c;
100 unsigned i;
101 const char *p;
102 char *hostname;
103
104
105 if (!new_p || (length = strlen(new_p)) < CONFIG_PASSWORD_MINLEN)
106 return "too short";
107
108
109 if (string_checker(new_p, pw->pw_name)) {
110 return "similar to username";
111 }
112#ifndef __BIONIC__
113
114 if (pw->pw_gecos[0] && string_checker(new_p, pw->pw_gecos)) {
115 return "similar to gecos";
116 }
117#endif
118
119 hostname = safe_gethostname();
120 i = string_checker(new_p, hostname);
121 free(hostname);
122 if (i)
123 return "similar to hostname";
124
125
126 mixed = 0;
127 for (i = 0; i < length; i++) {
128 if (islower(new_p[i])) {
129 mixed |= LOWERCASE;
130 } else if (isupper(new_p[i])) {
131 mixed |= UPPERCASE;
132 } else if (isdigit(new_p[i])) {
133 mixed |= NUMBERS;
134 } else {
135 mixed |= SPECIAL;
136 }
137
138 c = 0;
139 p = new_p;
140 while (1) {
141 p = strchr(p, new_p[i]);
142 if (p == NULL) {
143 break;
144 }
145 c++;
146 p++;
147 if (!*p) {
148 break;
149 }
150 }
151
152 if (c*2 >= length) {
153 return "too many similar characters";
154 }
155 }
156
157 size = CONFIG_PASSWORD_MINLEN + 2*CATEGORIES;
158 for (i = 1; i <= LAST_CAT; i <<= 1)
159 if (mixed & i)
160 size -= 2;
161 if (length < size)
162 return "too weak";
163
164 if (old_p && old_p[0]) {
165
166 if (string_checker(new_p, old_p)) {
167 return "similar to old password";
168 }
169 }
170
171 return NULL;
172}
173
174int FAST_FUNC obscure(const char *old, const char *newval, const struct passwd *pw)
175{
176 const char *msg;
177
178 msg = obscure_msg(old, newval, pw);
179 if (msg) {
180 printf("Bad password: %s\n", msg);
181 return 1;
182 }
183 return 0;
184}
185
186#if ENABLE_UNIT_TEST
187
188
189
190static const struct passwd pw = {
191 .pw_name = (char *)"johndoe",
192 .pw_gecos = (char *)"John Doe",
193};
194
195BBUNIT_DEFINE_TEST(obscure_weak_pass)
196{
197
198 BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "", &pw));
199
200 BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "23577315", &pw));
201
202 BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "johndoe123%", &pw));
203
204 BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "eoD nhoJ^44@", &pw));
205
206 BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "d4#21?'S", &pw));
207
208 BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "qwerty123", &pw));
209
210 BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "^33Daaaaaa1", &pw));
211
212 BBUNIT_ENDTEST;
213}
214
215BBUNIT_DEFINE_TEST(obscure_strong_pass)
216{
217 BBUNIT_ASSERT_NULL(obscure_msg("Rt4##2&:'|", "}(^#rrSX3S*22", &pw));
218
219 BBUNIT_ENDTEST;
220}
221
222#endif
223