X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=libbb%2Fobscure.c;h=2599095dfca23e0228b3432aadc236d74d12b50f;hb=ab24e18c7a32ee1637be19f239e9dd9d7c7f6534;hp=dc7de751d7a4a7a6930a8a89865c7c693d0ee1ad;hpb=27f64e1f4eb4354844f6553e37501deffde8373e;p=oweals%2Fbusybox.git diff --git a/libbb/obscure.c b/libbb/obscure.c index dc7de751d..2599095df 100644 --- a/libbb/obscure.c +++ b/libbb/obscure.c @@ -1,246 +1,170 @@ /* vi: set sw=4 ts=4: */ /* - * Copyright 1989 - 1994, Julianne Frances Haugh - * All rights reserved. + * Mini weak password checker implementation for busybox * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Julianne F. Haugh nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. + * Copyright (C) 2006 Tito Ragusa * - * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. */ -/* - * This version of obscure.c contains modifications to support "cracklib" - * by Alec Muffet (alec.muffett@uk.sun.com). You must obtain the Cracklib - * library source code for this function to operate. - */ +/* 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 -#include -#include -#include #include "libbb.h" -/* - * can't be a palindrome - like `R A D A R' or `M A D A M' - */ - -static int palindrome(const char *old, const char *newval) -{ - int i, j; - - i = strlen(newval); +static int string_checker_helper(const char *p1, const char *p2) __attribute__ ((__pure__)); - for (j = 0; j < i; j++) - if (newval[i - j - 1] != newval[j]) - return 0; - - return 1; -} - -/* - * more than half of the characters are different ones. - */ - -static int similiar(const char *old, const char *newval) +static int string_checker_helper(const char *p1, const char *p2) { - int i, j; - - for (i = j = 0; newval[i] && old[i]; i++) - if (strchr(newval, old[i])) - j++; - - if (i >= j * 2) - return 0; - - return 1; + /* 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; } -/* - * a nice mix of characters. - */ - -static int simple(const char *old, const char *newval) +static int string_checker(const char *p1, const char *p2) { - int digits = 0; - int uppers = 0; - int lowers = 0; - int others = 0; int size; - int i; - - for (i = 0; newval[i]; i++) { - if (isdigit(newval[i])) - digits++; - else if (isupper(newval[i])) - uppers++; - else if (islower(newval[i])) - lowers++; - else - others++; + /* 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++; } - - /* - * The scam is this - a password of only one character type - * must be 8 letters long. Two types, 7, and so on. - */ - - size = 9; - if (digits) - size--; - if (uppers) - size--; - if (lowers) - size--; - if (others) - size--; - - if (size <= i) - return 0; - - return 1; + /* 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; } -static char *str_lower(char *string) -{ - char *cp; - - for (cp = string; *cp; cp++) - *cp = tolower(*cp); - return string; -} +#define LOWERCASE 1 +#define UPPERCASE 2 +#define NUMBERS 4 +#define SPECIAL 8 -static char *password_check(const char *old, const char *newval, const struct passwd *pwdp) +static const char *obscure_msg(const char *old_p, const char *new_p, const struct passwd *pw) { - char *msg = NULL; - char *oldmono, *newmono, *wrapped; - - if (strcmp(newval, old) == 0) - return "no change"; - - newmono = str_lower(xstrdup(newval)); - oldmono = str_lower(xstrdup(old)); - wrapped = (char *) xmalloc(strlen(oldmono) * 2 + 1); - strcpy(wrapped, oldmono); - strcat(wrapped, oldmono); - - if (palindrome(oldmono, newmono)) - msg = "a palindrome"; - - if (!msg && strcmp(oldmono, newmono) == 0) - msg = "case changes only"; - - if (!msg && similiar(oldmono, newmono)) - msg = "too similiar"; - - if (!msg && simple(old, newval)) - msg = "too simple"; - - if (!msg && strstr(wrapped, newmono)) - msg = "rotated"; - - bzero(newmono, strlen(newmono)); - bzero(oldmono, strlen(oldmono)); - bzero(wrapped, strlen(wrapped)); - free(newmono); - free(oldmono); - free(wrapped); - - return msg; -} - -static char *obscure_msg(const char *old, const char *newval, const struct passwd *pwdp) -{ - int maxlen, oldlen, newlen; - char *new1, *old1, *msg; - - oldlen = strlen(old); - newlen = strlen(newval); - -#if 0 /* why not check the password when set for the first time? --marekm */ - if (old[0] == '\0') - /* return (1); */ - return NULL; -#endif - - if (newlen < 5) + 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"; - /* - * Remaining checks are optional. - */ - /* Not for us -- Sean - *if (!getdef_bool("OBSCURE_CHECKS_ENAB")) - * return NULL; - */ - msg = password_check(old, newval, pwdp); - if (msg) - return msg; - - /* The traditional crypt() truncates passwords to 8 chars. It is - possible to circumvent the above checks by choosing an easy - 8-char password and adding some random characters to it... - Example: "password$%^&*123". So check it again, this time - truncated to the maximum length. Idea from npasswd. --marekm */ - - maxlen = 8; - if (oldlen <= maxlen && newlen <= maxlen) - return NULL; - - new1 = (char *) xstrdup(newval); - old1 = (char *) xstrdup(old); - if (newlen > maxlen) - new1[maxlen] = '\0'; - if (oldlen > maxlen) - old1[maxlen] = '\0'; - - msg = password_check(old1, new1, pwdp); - - bzero(new1, newlen); - bzero(old1, oldlen); - free(new1); - free(old1); - - return msg; -} + /* 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"; + } + } -/* - * Obscure - see if password is obscure enough. - * - * The programmer is encouraged to add as much complexity to this - * routine as desired. Included are some of my favorite ways to - * check passwords. - */ + /* 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<