Patch from vodz to cleanup libbb/obscure.c:password_check()
[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  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 /*
32  * This version of obscure.c contains modifications to support "cracklib"
33  * by Alec Muffet (alec.muffett@uk.sun.com).  You must obtain the Cracklib
34  * library source code for this function to operate.
35  */
36
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include "libbb.h"
42
43 /*
44  * can't be a palindrome - like `R A D A R' or `M A D A M'
45  */
46
47 static int palindrome(const char *newval)
48 {
49         int i, j;
50
51         i = strlen(newval);
52
53         for (j = 0; j < i; j++)
54                 if (newval[i - j - 1] != newval[j])
55                         return 0;
56
57         return 1;
58 }
59
60 /*
61  * more than half of the characters are different ones.
62  */
63
64 static int similiar(const char *old, const char *newval)
65 {
66         int i, j;
67
68         for (i = j = 0; newval[i] && old[i]; i++)
69                 if (strchr(newval, old[i]))
70                         j++;
71
72         if (i >= j * 2)
73                 return 0;
74
75         return 1;
76 }
77
78 /*
79  * a nice mix of characters.
80  */
81
82 static int simple(const char *newval)
83 {
84         int digits = 0;
85         int uppers = 0;
86         int lowers = 0;
87         int others = 0;
88         int c;
89         int size;
90         int i;
91
92         for (i = 0; (c = *newval++) != 0; i++) {
93                 if (isdigit(c))
94                         digits = c;
95                 else if (isupper(c))
96                         uppers = c;
97                 else if (islower(c))
98                         lowers = c;
99                 else
100                         others = c;
101         }
102
103         /*
104          * The scam is this - a password of only one character type
105          * must be 8 letters long.  Two types, 7, and so on.
106          */
107
108         size = 9;
109         if (digits)
110                 size--;
111         if (uppers)
112                 size--;
113         if (lowers)
114                 size--;
115         if (others)
116                 size--;
117
118         if (size <= i)
119                 return 0;
120
121         return 1;
122 }
123
124 static char *str_lower(char *string)
125 {
126         char *cp;
127
128         for (cp = string; *cp; cp++)
129                 *cp = tolower(*cp);
130         return string;
131 }
132
133 static const char *
134 password_check(const char *old, const char *newval, const struct passwd *pwdp)
135 {
136         const char *msg;
137         char *newmono, *wrapped;
138         int lenwrap;
139
140         if (strcmp(newval, old) == 0)
141                 return "no change";
142         if (simple(newval))
143                 return "too simple";
144
145         msg = NULL;
146         newmono = str_lower(bb_xstrdup(newval));
147         lenwrap = strlen(old);
148         wrapped = (char *) xmalloc(lenwrap * 2 + 1);
149         str_lower(strcpy(wrapped, old));
150
151         if (palindrome(newmono))
152                 msg = "a palindrome";
153
154         else if (strcmp(wrapped, newmono) == 0)
155                 msg = "case changes only";
156
157         else if (similiar(wrapped, newmono))
158                 msg = "too similiar";
159
160         else {
161                 safe_strncpy(wrapped + lenwrap, wrapped, lenwrap + 1);
162                 if (strstr(wrapped, newmono))
163                         msg = "rotated";
164         }
165
166         bzero(newmono, strlen(newmono));
167         bzero(wrapped, lenwrap * 2);
168         free(newmono);
169         free(wrapped);
170
171         return msg;
172 }
173
174 static const char *
175 obscure_msg(const char *old, const char *newval, const struct passwd *pwdp)
176 {
177         int maxlen, oldlen, newlen;
178         char *new1, *old1;
179         const char *msg;
180
181         oldlen = strlen(old);
182         newlen = strlen(newval);
183
184 #if 0                                                   /* why not check the password when set for the first time?  --marekm */
185         if (old[0] == '\0')
186                 /* return (1); */
187                 return NULL;
188 #endif
189
190         if (newlen < 5)
191                 return "too short";
192
193         /*
194          * Remaining checks are optional.
195          */
196         /* Not for us -- Sean
197          *if (!getdef_bool("OBSCURE_CHECKS_ENAB"))
198          *      return NULL;
199          */
200         msg = password_check(old, newval, pwdp);
201         if (msg)
202                 return msg;
203
204         /* The traditional crypt() truncates passwords to 8 chars.  It is
205            possible to circumvent the above checks by choosing an easy
206            8-char password and adding some random characters to it...
207            Example: "password$%^&*123".  So check it again, this time
208            truncated to the maximum length.  Idea from npasswd.  --marekm */
209
210         maxlen = 8;
211         if (oldlen <= maxlen && newlen <= maxlen)
212                 return NULL;
213
214         new1 = (char *) bb_xstrdup(newval);
215         old1 = (char *) bb_xstrdup(old);
216         if (newlen > maxlen)
217                 new1[maxlen] = '\0';
218         if (oldlen > maxlen)
219                 old1[maxlen] = '\0';
220
221         msg = password_check(old1, new1, pwdp);
222
223         bzero(new1, newlen);
224         bzero(old1, oldlen);
225         free(new1);
226         free(old1);
227
228         return msg;
229 }
230
231 /*
232  * Obscure - see if password is obscure enough.
233  *
234  *      The programmer is encouraged to add as much complexity to this
235  *      routine as desired.  Included are some of my favorite ways to
236  *      check passwords.
237  */
238
239 extern int obscure(const char *old, const char *newval, const struct passwd *pwdp)
240 {
241         const char *msg = obscure_msg(old, newval, pwdp);
242
243         /*  if (msg) { */
244         if (msg != NULL) {
245                 printf("Bad password: %s.\n", msg);
246                 /* return 0; */
247                 return 1;
248         }
249         /* return 1; */
250         return 0;
251 }