Port over the last of the tinylogin applets
[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 *old, 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 *old, const char *newval)
83 {
84         int digits = 0;
85         int uppers = 0;
86         int lowers = 0;
87         int others = 0;
88         int size;
89         int i;
90
91         for (i = 0; newval[i]; i++) {
92                 if (isdigit(newval[i]))
93                         digits++;
94                 else if (isupper(newval[i]))
95                         uppers++;
96                 else if (islower(newval[i]))
97                         lowers++;
98                 else
99                         others++;
100         }
101
102         /*
103          * The scam is this - a password of only one character type
104          * must be 8 letters long.  Two types, 7, and so on.
105          */
106
107         size = 9;
108         if (digits)
109                 size--;
110         if (uppers)
111                 size--;
112         if (lowers)
113                 size--;
114         if (others)
115                 size--;
116
117         if (size <= i)
118                 return 0;
119
120         return 1;
121 }
122
123 static char *str_lower(char *string)
124 {
125         char *cp;
126
127         for (cp = string; *cp; cp++)
128                 *cp = tolower(*cp);
129         return string;
130 }
131
132 static char *password_check(const char *old, const char *newval, const struct passwd *pwdp)
133 {
134         char *msg = NULL;
135         char *oldmono, *newmono, *wrapped;
136
137         if (strcmp(newval, old) == 0)
138                 return "no change";
139
140         newmono = str_lower(xstrdup(newval));
141         oldmono = str_lower(xstrdup(old));
142         wrapped = (char *) xmalloc(strlen(oldmono) * 2 + 1);
143         strcpy(wrapped, oldmono);
144         strcat(wrapped, oldmono);
145
146         if (palindrome(oldmono, newmono))
147                 msg = "a palindrome";
148
149         if (!msg && strcmp(oldmono, newmono) == 0)
150                 msg = "case changes only";
151
152         if (!msg && similiar(oldmono, newmono))
153                 msg = "too similiar";
154
155         if (!msg && simple(old, newval))
156                 msg = "too simple";
157
158         if (!msg && strstr(wrapped, newmono))
159                 msg = "rotated";
160
161         bzero(newmono, strlen(newmono));
162         bzero(oldmono, strlen(oldmono));
163         bzero(wrapped, strlen(wrapped));
164         free(newmono);
165         free(oldmono);
166         free(wrapped);
167
168         return msg;
169 }
170
171 static char *obscure_msg(const char *old, const char *newval, const struct passwd *pwdp)
172 {
173         int maxlen, oldlen, newlen;
174         char *new1, *old1, *msg;
175
176         oldlen = strlen(old);
177         newlen = strlen(newval);
178
179 #if 0                                                   /* why not check the password when set for the first time?  --marekm */
180         if (old[0] == '\0')
181                 /* return (1); */
182                 return NULL;
183 #endif
184
185         if (newlen < 5)
186                 return "too short";
187
188         /*
189          * Remaining checks are optional.
190          */
191         /* Not for us -- Sean
192          *if (!getdef_bool("OBSCURE_CHECKS_ENAB"))
193          *      return NULL;
194          */
195         msg = password_check(old, newval, pwdp);
196         if (msg)
197                 return msg;
198
199         /* The traditional crypt() truncates passwords to 8 chars.  It is
200            possible to circumvent the above checks by choosing an easy
201            8-char password and adding some random characters to it...
202            Example: "password$%^&*123".  So check it again, this time
203            truncated to the maximum length.  Idea from npasswd.  --marekm */
204
205         maxlen = 8;
206         if (oldlen <= maxlen && newlen <= maxlen)
207                 return NULL;
208
209         new1 = (char *) xstrdup(newval);
210         old1 = (char *) xstrdup(old);
211         if (newlen > maxlen)
212                 new1[maxlen] = '\0';
213         if (oldlen > maxlen)
214                 old1[maxlen] = '\0';
215
216         msg = password_check(old1, new1, pwdp);
217
218         bzero(new1, newlen);
219         bzero(old1, oldlen);
220         free(new1);
221         free(old1);
222
223         return msg;
224 }
225
226 /*
227  * Obscure - see if password is obscure enough.
228  *
229  *      The programmer is encouraged to add as much complexity to this
230  *      routine as desired.  Included are some of my favorite ways to
231  *      check passwords.
232  */
233
234 extern int obscure(const char *old, const char *newval, const struct passwd *pwdp)
235 {
236         char *msg = obscure_msg(old, newval, pwdp);
237
238         /*  if (msg) { */
239         if (msg != NULL) {
240                 printf("Bad password: %s.\n", msg);
241                 /* return 0; */
242                 return 1;
243         }
244         /* return 1; */
245         return 0;
246 }