mirror of
https://github.com/sheumann/hush.git
synced 2024-09-12 15:54:56 +00:00
things like xasprintf() into xfuncs.c, remove xprint_file_by_name() (it only had one user), clean up lots of #includes... General cleanup pass. What I've been doing for the last couple days. And it conflicts! I've removed httpd.c from this checkin due to somebody else touching that file. It builds for me. I have to catch a bus. (Now you know why I'm looking forward to Mercurial.)
171 lines
4.5 KiB
C
171 lines
4.5 KiB
C
/* vi: set sw=4 ts=4: */
|
|
/*
|
|
* Mini weak password checker implementation for busybox
|
|
*
|
|
* Copyright (C) 2006 Tito Ragusa <farmatito@tiscali.it>
|
|
*
|
|
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
|
|
*/
|
|
|
|
/* A good password:
|
|
1) should contain at least six characters (man passwd);
|
|
2) empty passwords are not permitted;
|
|
3) should contain a mix of four different types of characters
|
|
upper case letters,
|
|
lower case letters,
|
|
numbers,
|
|
special characters such as !@#$%^&*,;".
|
|
This password types should not be permitted:
|
|
a) pure numbers: birthdates, social security number, license plate, phone numbers;
|
|
b) words and all letters only passwords (uppercase, lowercase or mixed)
|
|
as palindromes, consecutive or repetitive letters
|
|
or adjacent letters on your keyboard;
|
|
c) username, real name, company name or (e-mail?) address
|
|
in any form (as-is, reversed, capitalized, doubled, etc.).
|
|
(we can check only against username, gecos and hostname)
|
|
d) common and obvious letter-number replacements
|
|
(e.g. replace the letter O with number 0)
|
|
such as "M1cr0$0ft" or "P@ssw0rd" (CAVEAT: we cannot check for them
|
|
without the use of a dictionary).
|
|
|
|
For each missing type of characters an increase of password length is
|
|
requested.
|
|
|
|
If user is root we warn only.
|
|
|
|
CAVEAT: some older versions of crypt() truncates passwords to 8 chars,
|
|
so that aaaaaaaa1Q$ is equal to aaaaaaaa making it possible to fool
|
|
some of our checks. We don't test for this special case as newer versions
|
|
of crypt do not truncate passwords.
|
|
*/
|
|
|
|
#include "libbb.h"
|
|
|
|
static int string_checker_helper(const char *p1, const char *p2) __attribute__ ((__pure__));
|
|
|
|
static int string_checker_helper(const char *p1, const char *p2)
|
|
{
|
|
/* as-is or capitalized */
|
|
if (strcasecmp(p1, p2) == 0
|
|
/* as sub-string */
|
|
|| strcasestr(p2, p1) != NULL
|
|
/* invert in case haystack is shorter than needle */
|
|
|| strcasestr(p1, p2) != NULL)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static int string_checker(const char *p1, const char *p2)
|
|
{
|
|
int size;
|
|
/* check string */
|
|
int ret = string_checker_helper(p1, p2);
|
|
/* Make our own copy */
|
|
char *p = xstrdup(p1);
|
|
/* reverse string */
|
|
size = strlen(p);
|
|
|
|
while (size--) {
|
|
*p = p1[size];
|
|
p++;
|
|
}
|
|
/* restore pointer */
|
|
p -= strlen(p1);
|
|
/* check reversed string */
|
|
ret |= string_checker_helper(p, p2);
|
|
/* clean up */
|
|
memset(p, 0, strlen(p1));
|
|
free(p);
|
|
return ret;
|
|
}
|
|
|
|
#define LOWERCASE 1
|
|
#define UPPERCASE 2
|
|
#define NUMBERS 4
|
|
#define SPECIAL 8
|
|
|
|
static const char *obscure_msg(const char *old_p, const char *new_p, const struct passwd *pw)
|
|
{
|
|
int i;
|
|
int c;
|
|
int length;
|
|
int mixed = 0;
|
|
/* Add 2 for each type of characters to the minlen of password */
|
|
int size = CONFIG_PASSWORD_MINLEN + 8;
|
|
const char *p;
|
|
char hostname[255];
|
|
|
|
/* size */
|
|
if (!new_p || (length = strlen(new_p)) < CONFIG_PASSWORD_MINLEN)
|
|
return("too short");
|
|
|
|
/* no username as-is, as sub-string, reversed, capitalized, doubled */
|
|
if (string_checker(new_p, pw->pw_name)) {
|
|
return "similar to username";
|
|
}
|
|
/* no gecos as-is, as sub-string, reversed, capitalized, doubled */
|
|
if (*pw->pw_gecos && string_checker(new_p, pw->pw_gecos)) {
|
|
return "similar to gecos";
|
|
}
|
|
/* hostname as-is, as sub-string, reversed, capitalized, doubled */
|
|
if (gethostname(hostname, 255) == 0) {
|
|
hostname[254] = '\0';
|
|
if (string_checker(new_p, hostname)) {
|
|
return "similar to hostname";
|
|
}
|
|
}
|
|
|
|
/* Should / Must contain a mix of: */
|
|
for (i = 0; i < length; i++) {
|
|
if (islower(new_p[i])) { /* a-z */
|
|
mixed |= LOWERCASE;
|
|
} else if (isupper(new_p[i])) { /* A-Z */
|
|
mixed |= UPPERCASE;
|
|
} else if (isdigit(new_p[i])) { /* 0-9 */
|
|
mixed |= NUMBERS;
|
|
} else { /* special characters */
|
|
mixed |= SPECIAL;
|
|
}
|
|
/* More than 50% similar characters ? */
|
|
c = 0;
|
|
p = new_p;
|
|
while (1) {
|
|
if ((p = strchr(p, new_p[i])) == NULL) {
|
|
break;
|
|
}
|
|
c++;
|
|
if (!++p) {
|
|
break; /* move past the matched char if possible */
|
|
}
|
|
}
|
|
|
|
if (c >= (length / 2)) {
|
|
return "too many similar characters";
|
|
}
|
|
}
|
|
for (i=0; i<4; i++)
|
|
if (mixed & (1<<i)) size -= 2;
|
|
if (length < size)
|
|
return "too weak";
|
|
|
|
if (old_p && old_p[0] != '\0') {
|
|
/* check vs. old password */
|
|
if (string_checker(new_p, old_p)) {
|
|
return "similar to old password";
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int obscure(const char *old, const char *newval, const struct passwd *pwdp)
|
|
{
|
|
const char *msg;
|
|
|
|
if ((msg = obscure_msg(old, newval, pwdp))) {
|
|
printf("Bad password: %s.\n", msg);
|
|
/* If user is root warn only */
|
|
return (getuid())? 1 : 0;
|
|
}
|
|
return 0;
|
|
}
|