Robert P. Day removed 8 gazillion occurrences of "extern" on function
[oweals/busybox.git] / libbb / obscure.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Copyright 1989 - 1994, Julianne Frances Haugh <jockgrrl@austin.rr.com>
4  * Copyright 2006, Bernhard Fischer <busybox@busybox.net>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
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  *
19  * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
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  */
31
32 /*
33  * This version of obscure.c contains modifications to support "cracklib"
34  * by Alec Muffet (alec.muffett@uk.sun.com).  You must obtain the Cracklib
35  * library source code for this function to operate.
36  */
37
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <ctype.h>
42 #include "libbb.h"
43
44 /*
45  * can't be a palindrome - like `R A D A R' or `M A D A M'
46  */
47
48 static int palindrome(const char *newval)
49 {
50         int i, j;
51
52         i = strlen(newval);
53
54         for (j = 0; j < i; j++)
55                 if (newval[i - j - 1] != newval[j])
56                         return 0;
57
58         return 1;
59 }
60
61 /*
62  * more than half of the characters are different ones.
63  */
64
65 static int similiar(const char *old, const char *newval)
66 {
67         int i, j;
68
69         for (i = j = 0; newval[i] && old[i]; i++)
70                 if (strchr(newval, old[i]))
71                         j++;
72
73         if (i >= j * 2)
74                 return 0;
75
76         return 1;
77 }
78
79 /*
80  * a nice mix of characters.
81  */
82
83 static int simple(const char *newval)
84 {
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;
91         int i;
92
93         for (i = 0; (c = *newval++) != 0; i++) {
94                 if (isdigit(c))
95                         is_simple |= digits;
96                 else if (isupper(c))
97                         is_simple |= uppers;
98                 else if (islower(c))
99                         is_simple |= lowers;
100                 else
101                         is_simple |= others;
102         }
103
104         /*
105          * The scam is this - a password of only one character type
106          * must be 8 letters long.  Two types, 7, and so on.
107          */
108
109         size = 9;
110         if (is_simple & digits)
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 }
128
129 static char *str_lower(char *string)
130 {
131         char *cp;
132
133         for (cp = string; *cp; cp++)
134                 *cp = tolower(*cp);
135         return string;
136 }
137
138 static const char *
139 password_check(const char *old, const char *newval, const struct passwd *pwdp)
140 {
141         const char *msg;
142         char *newmono, *wrapped;
143         int lenwrap;
144
145         if (strcmp(newval, old) == 0)
146                 return "no change";
147         if (simple(newval))
148                 return "too simple";
149
150         msg = NULL;
151         newmono = str_lower(bb_xstrdup(newval));
152         lenwrap = strlen(old);
153         wrapped = (char *) xmalloc(lenwrap * 2 + 1);
154         str_lower(strcpy(wrapped, old));
155
156         if (palindrome(newmono))
157                 msg = "a palindrome";
158
159         else if (strcmp(wrapped, newmono) == 0)
160                 msg = "case changes only";
161
162         else if (similiar(wrapped, newmono))
163                 msg = "too similiar";
164
165         else if ( strstr(newval, pwdp->pw_name) )
166                 msg = "username in password";
167
168         else {
169                 safe_strncpy(wrapped + lenwrap, wrapped, lenwrap + 1);
170                 if (strstr(wrapped, newmono))
171                         msg = "rotated";
172         }
173
174         memset(newmono, 0, strlen(newmono));
175         memset(wrapped, 0, lenwrap * 2);
176         free(newmono);
177         free(wrapped);
178
179         return msg;
180 }
181
182 static const char *
183 obscure_msg(const char *old, const char *newval, const struct passwd *pwdp)
184 {
185         int maxlen, oldlen, newlen;
186         char *new1, *old1;
187         const char *msg;
188
189         oldlen = strlen(old);
190         newlen = strlen(newval);
191
192 #if 0  /* why not check the password when set for the first time?  --marekm */
193         if (old[0] == '\0')
194                 /* return (1); */
195                 return NULL;
196 #endif
197
198         if (newlen < 5)
199                 return "too short";
200
201         /*
202          * Remaining checks are optional.
203          */
204         /* Not for us -- Sean
205          *if (!getdef_bool("OBSCURE_CHECKS_ENAB"))
206          *      return NULL;
207          */
208         msg = password_check(old, newval, pwdp);
209         if (msg)
210                 return msg;
211
212         /* The traditional crypt() truncates passwords to 8 chars.  It is
213            possible to circumvent the above checks by choosing an easy
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 }
238
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)
248 {
249         const char *msg = obscure_msg(old, newval, pwdp);
250
251         /*  if (msg) { */
252         if (msg != NULL) {
253                 printf("Bad password: %s.\n", msg);
254                 /* return 0; */
255                 return 1;
256         }
257         /* return 1; */
258         return 0;
259 }