blob: a152456b2e630a3376fc33ed97165ddd52c93c5a [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)
21 as palindromes, consecutive or repetitive letters
22 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)
26 d) common and obvious letter-number replacements
27 (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 <ctype.h>
Rob Landleya13cca92006-04-02 18:57:20 +000043#include <unistd.h>
44#include <string.h>
45
Eric Andersen27f64e12002-06-23 04:24:25 +000046#include "libbb.h"
47
Eric Andersen27f64e12002-06-23 04:24:25 +000048
Rob Landleya13cca92006-04-02 18:57:20 +000049/* passwords should consist of 6 (to 8 characters) */
50#define MINLEN 6
51
52
53static int string_checker_helper(const char *p1, const char *p2) __attribute__ ((__pure__));
54
55static int string_checker_helper(const char *p1, const char *p2)
Eric Andersen27f64e12002-06-23 04:24:25 +000056{
Rob Landleya13cca92006-04-02 18:57:20 +000057 /* as-is or capitalized */
58 if (strcasecmp(p1, p2) == 0
59 /* as sub-string */
60 || strcasestr(p2, p1) != NULL
61 /* invert in case haystack is shorter than needle */
62 || strcasestr(p1, p2) != NULL)
63 return 1;
64 return 0;
Eric Andersen27f64e12002-06-23 04:24:25 +000065}
66
Rob Landleya13cca92006-04-02 18:57:20 +000067static int string_checker(const char *p1, const char *p2)
Eric Andersen27f64e12002-06-23 04:24:25 +000068{
Eric Andersen27f64e12002-06-23 04:24:25 +000069 int size;
Rob Landleya13cca92006-04-02 18:57:20 +000070 /* check string */
71 int ret = string_checker_helper(p1, p2);
72 /* Make our own copy */
73 char *p = bb_xstrdup(p1);
74 /* reverse string */
75 size = strlen(p);
76
77 while (size--) {
78 *p = p1[size];
79 p++;
80 }
81 /* restore pointer */
82 p -= strlen(p1);
83 /* check reversed string */
84 ret |= string_checker_helper(p, p2);
85 /* clean up */
86 memset(p, 0, strlen(p1));
87 free(p);
88 return ret;
89}
90
91#define LOWERCASE 1
92#define UPPERCASE 2
93#define NUMBERS 4
94#define SPECIAL 8
95
96static const char *obscure_msg(const char *old_p, const char *new_p, const struct passwd *pw)
97{
Eric Andersen27f64e12002-06-23 04:24:25 +000098 int i;
Rob Landleya13cca92006-04-02 18:57:20 +000099 int c;
100 int length;
101 int mixed = 0;
102 /* Add 1 for each type of characters to the minlen of password */
103 int size = MINLEN + 8;
104 const char *p;
105 char hostname[255];
Eric Andersen27f64e12002-06-23 04:24:25 +0000106
Rob Landleya13cca92006-04-02 18:57:20 +0000107 /* size */
108 if (!new_p || (length = strlen(new_p)) < MINLEN)
109 return("too short");
110
111 /* no username as-is, as sub-string, reversed, capitalized, doubled */
112 if (string_checker(new_p, pw->pw_name)) {
113 return "similar to username";
114 }
115 /* no gecos as-is, as sub-string, reversed, capitalized, doubled */
116 if (string_checker(new_p, pw->pw_gecos)) {
117 return "similar to gecos";
118 }
119 /* hostname as-is, as sub-string, reversed, capitalized, doubled */
120 if (gethostname(hostname, 255) == 0) {
121 hostname[254] = '\0';
122 if (string_checker(new_p, hostname)) {
123 return "similar to hostname";
124 }
Eric Andersen27f64e12002-06-23 04:24:25 +0000125 }
126
Rob Landleya13cca92006-04-02 18:57:20 +0000127 /* Should / Must contain a mix of: */
128 for (i = 0; i < length; i++) {
129 if (islower(new_p[i])) { /* a-z */
130 mixed |= LOWERCASE;
131 } else if (isupper(new_p[i])) { /* A-Z */
132 mixed |= UPPERCASE;
133 } else if (isdigit(new_p[i])) { /* 0-9 */
134 mixed |= NUMBERS;
135 } else { /* special characters */
136 mixed |= SPECIAL;
137 }
138 /* More than 50% similar characters ? */
139 c = 0;
140 p = new_p;
141 while (1) {
142 if ((p = strchr(p, new_p[i])) == NULL) {
143 break;
144 }
145 c++;
146 if (!++p) {
147 break; /* move past the matched char if possible */
148 }
149 }
Eric Andersen27f64e12002-06-23 04:24:25 +0000150
Rob Landleya13cca92006-04-02 18:57:20 +0000151 if (c >= (length / 2)) {
152 return "too many similar characters";
153 }
Eric Andersen3124a9e2003-07-30 07:57:06 +0000154 }
Rob Landleya13cca92006-04-02 18:57:20 +0000155 for(i=0;i<4;i++)
156 if (mixed & (1<<i)) size -= 2;
157 if (length < size)
158 return "too weak";
159
160 if (old_p && old_p[0] != '\0') {
161 /* check vs. old password */
162 if (string_checker(new_p, old_p)) {
163 return "similar to old password";
164 }
165 }
166 return NULL;
Eric Andersen27f64e12002-06-23 04:24:25 +0000167}
168
Rob Landleydfba7412006-03-06 20:47:33 +0000169int obscure(const char *old, const char *newval, const struct passwd *pwdp)
Eric Andersen27f64e12002-06-23 04:24:25 +0000170{
Rob Landleya13cca92006-04-02 18:57:20 +0000171 const char *msg;
Eric Andersen27f64e12002-06-23 04:24:25 +0000172
Rob Landleya13cca92006-04-02 18:57:20 +0000173 if ((msg = obscure_msg(old, newval, pwdp))) {
Eric Andersen27f64e12002-06-23 04:24:25 +0000174 printf("Bad password: %s.\n", msg);
Rob Landleya13cca92006-04-02 18:57:20 +0000175 /* If user is root warn only */
176 return (getuid())? 1 : 0;
Eric Andersen27f64e12002-06-23 04:24:25 +0000177 }
Eric Andersen27f64e12002-06-23 04:24:25 +0000178 return 0;
179}