blob: 1841b27d6aff72335065b812bc7ab2ec7ec18986 [file] [log] [blame]
Eric Andersen27f64e12002-06-23 04:24:25 +00001/* vi: set sw=4 ts=4: */
2/*
Rob Landleya13cca92006-04-02 18:57:20 +00003 * Mini weak password checker implementation for busybox
Eric Andersen27f64e12002-06-23 04:24:25 +00004 *
Rob Landleya13cca92006-04-02 18:57:20 +00005 * Copyright (C) 2006 Tito Ragusa <farmatito@tiscali.it>
Eric Andersen27f64e12002-06-23 04:24:25 +00006 *
Rob Landleya13cca92006-04-02 18:57:20 +00007 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Eric Andersen27f64e12002-06-23 04:24:25 +00008 */
9
Rob Landleya13cca92006-04-02 18:57:20 +000010/* A good password:
11 1) should contain at least six characters (man passwd);
12 2) empty passwords are not permitted;
13 3) should contain a mix of four different types of characters
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)
Bernhard Reutner-Fischera2a647d2006-05-19 12:30:00 +000021 as palindromes, consecutive or repetitive letters
Rob Landleya13cca92006-04-02 18:57:20 +000022 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)
Bernhard Reutner-Fischera2a647d2006-05-19 12:30:00 +000026 d) common and obvious letter-number replacements
Rob Landleya13cca92006-04-02 18:57:20 +000027 (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).
Eric Andersen27f64e12002-06-23 04:24:25 +000030
Rob Landleya13cca92006-04-02 18:57:20 +000031 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*/
41
Eric Andersen27f64e12002-06-23 04:24:25 +000042#include "libbb.h"
43
Rob Landleya13cca92006-04-02 18:57:20 +000044static int string_checker_helper(const char *p1, const char *p2) __attribute__ ((__pure__));
45
46static int string_checker_helper(const char *p1, const char *p2)
Eric Andersen27f64e12002-06-23 04:24:25 +000047{
Rob Landleya13cca92006-04-02 18:57:20 +000048 /* as-is or capitalized */
49 if (strcasecmp(p1, p2) == 0
50 /* as sub-string */
51 || strcasestr(p2, p1) != NULL
52 /* invert in case haystack is shorter than needle */
53 || strcasestr(p1, p2) != NULL)
54 return 1;
55 return 0;
Eric Andersen27f64e12002-06-23 04:24:25 +000056}
57
Rob Landleya13cca92006-04-02 18:57:20 +000058static int string_checker(const char *p1, const char *p2)
Eric Andersen27f64e12002-06-23 04:24:25 +000059{
Eric Andersen27f64e12002-06-23 04:24:25 +000060 int size;
Rob Landleya13cca92006-04-02 18:57:20 +000061 /* check string */
62 int ret = string_checker_helper(p1, p2);
63 /* Make our own copy */
Rob Landleyd921b2e2006-08-03 15:41:12 +000064 char *p = xstrdup(p1);
Rob Landleya13cca92006-04-02 18:57:20 +000065 /* reverse string */
66 size = strlen(p);
67
68 while (size--) {
69 *p = p1[size];
70 p++;
71 }
72 /* restore pointer */
73 p -= strlen(p1);
74 /* check reversed string */
75 ret |= string_checker_helper(p, p2);
76 /* clean up */
77 memset(p, 0, strlen(p1));
78 free(p);
79 return ret;
80}
81
82#define LOWERCASE 1
83#define UPPERCASE 2
84#define NUMBERS 4
85#define SPECIAL 8
86
Bernhard Reutner-Fischera2a647d2006-05-19 12:30:00 +000087static const char *obscure_msg(const char *old_p, const char *new_p, const struct passwd *pw)
Rob Landleya13cca92006-04-02 18:57:20 +000088{
Eric Andersen27f64e12002-06-23 04:24:25 +000089 int i;
Rob Landleya13cca92006-04-02 18:57:20 +000090 int c;
91 int length;
92 int mixed = 0;
"Robert P. J. Day"c9f423a2006-07-02 19:52:52 +000093 /* Add 2 for each type of characters to the minlen of password */
"Robert P. J. Day"087b9d62006-07-02 18:35:39 +000094 int size = CONFIG_PASSWORD_MINLEN + 8;
Rob Landleya13cca92006-04-02 18:57:20 +000095 const char *p;
Denis Vlasenko6f1713f2008-02-25 23:23:58 +000096 char *hostname;
Eric Andersen27f64e12002-06-23 04:24:25 +000097
Rob Landleya13cca92006-04-02 18:57:20 +000098 /* size */
"Robert P. J. Day"087b9d62006-07-02 18:35:39 +000099 if (!new_p || (length = strlen(new_p)) < CONFIG_PASSWORD_MINLEN)
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000100 return "too short";
Bernhard Reutner-Fischera2a647d2006-05-19 12:30:00 +0000101
Rob Landleya13cca92006-04-02 18:57:20 +0000102 /* no username as-is, as sub-string, reversed, capitalized, doubled */
103 if (string_checker(new_p, pw->pw_name)) {
104 return "similar to username";
105 }
106 /* no gecos as-is, as sub-string, reversed, capitalized, doubled */
Mike Frysinger83169c62006-07-15 03:59:00 +0000107 if (*pw->pw_gecos && string_checker(new_p, pw->pw_gecos)) {
Rob Landleya13cca92006-04-02 18:57:20 +0000108 return "similar to gecos";
109 }
110 /* hostname as-is, as sub-string, reversed, capitalized, doubled */
Denis Vlasenko6f1713f2008-02-25 23:23:58 +0000111 hostname = safe_gethostname();
112 i = string_checker(new_p, hostname);
113 free(hostname);
114 if (i)
115 return "similar to hostname";
Eric Andersen27f64e12002-06-23 04:24:25 +0000116
Rob Landleya13cca92006-04-02 18:57:20 +0000117 /* Should / Must contain a mix of: */
118 for (i = 0; i < length; i++) {
119 if (islower(new_p[i])) { /* a-z */
120 mixed |= LOWERCASE;
121 } else if (isupper(new_p[i])) { /* A-Z */
122 mixed |= UPPERCASE;
123 } else if (isdigit(new_p[i])) { /* 0-9 */
124 mixed |= NUMBERS;
125 } else { /* special characters */
126 mixed |= SPECIAL;
127 }
128 /* More than 50% similar characters ? */
129 c = 0;
130 p = new_p;
131 while (1) {
Denis Vlasenko6bef3d12007-11-06 03:05:54 +0000132 p = strchr(p, new_p[i]);
133 if (p == NULL) {
Rob Landleya13cca92006-04-02 18:57:20 +0000134 break;
135 }
136 c++;
137 if (!++p) {
138 break; /* move past the matched char if possible */
139 }
140 }
Eric Andersen27f64e12002-06-23 04:24:25 +0000141
Rob Landleya13cca92006-04-02 18:57:20 +0000142 if (c >= (length / 2)) {
143 return "too many similar characters";
144 }
Eric Andersen3124a9e2003-07-30 07:57:06 +0000145 }
Bernhard Reutner-Fischera2a647d2006-05-19 12:30:00 +0000146 for (i=0; i<4; i++)
Rob Landleya13cca92006-04-02 18:57:20 +0000147 if (mixed & (1<<i)) size -= 2;
148 if (length < size)
149 return "too weak";
Bernhard Reutner-Fischera2a647d2006-05-19 12:30:00 +0000150
Rob Landleya13cca92006-04-02 18:57:20 +0000151 if (old_p && old_p[0] != '\0') {
152 /* check vs. old password */
153 if (string_checker(new_p, old_p)) {
154 return "similar to old password";
155 }
156 }
157 return NULL;
Eric Andersen27f64e12002-06-23 04:24:25 +0000158}
159
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000160int obscure(const char *old, const char *newval, const struct passwd *pw)
Eric Andersen27f64e12002-06-23 04:24:25 +0000161{
Rob Landleya13cca92006-04-02 18:57:20 +0000162 const char *msg;
Eric Andersen27f64e12002-06-23 04:24:25 +0000163
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000164 msg = obscure_msg(old, newval, pw);
165 if (msg) {
166 printf("Bad password: %s\n", msg);
167 return 1;
Eric Andersen27f64e12002-06-23 04:24:25 +0000168 }
Eric Andersen27f64e12002-06-23 04:24:25 +0000169 return 0;
170}