busybox/libbb/obscure.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini weak password checker implementation for busybox
   4 *
   5 * Copyright (C) 2006 Tito Ragusa <farmatito@tiscali.it>
   6 *
   7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   8 */
   9
  10/*      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).
  30
  31        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
  42#include "libbb.h"
  43
  44static int string_checker_helper(const char *p1, const char *p2) __attribute__ ((__pure__));
  45
  46static int string_checker_helper(const char *p1, const char *p2)
  47{
  48        /* as sub-string */
  49        if (strcasestr(p2, p1) != NULL
  50        /* invert in case haystack is shorter than needle */
  51         || strcasestr(p1, p2) != NULL
  52        /* as-is or capitalized */
  53        /* || strcasecmp(p1, p2) == 0 - 1st strcasestr should catch this too */
  54        ) {
  55                return 1;
  56        }
  57        return 0;
  58}
  59
  60static int string_checker(const char *p1, const char *p2)
  61{
  62        int size, i;
  63        /* check string */
  64        int ret = string_checker_helper(p1, p2);
  65        /* make our own copy */
  66        char *p = xstrdup(p1);
  67
  68        /* reverse string */
  69        i = size = strlen(p1);
  70        while (--i >= 0) {
  71                *p++ = p1[i];
  72        }
  73        p -= size; /* restore pointer */
  74
  75        /* check reversed string */
  76        ret |= string_checker_helper(p, p2);
  77
  78        /* clean up */
  79        memset(p, 0, size);
  80        free(p);
  81
  82        return ret;
  83}
  84
  85#define CATEGORIES  4
  86
  87#define LOWERCASE   1
  88#define UPPERCASE   2
  89#define NUMBERS     4
  90#define SPECIAL     8
  91
  92#define LAST_CAT    8
  93
  94static const char *obscure_msg(const char *old_p, const char *new_p, const struct passwd *pw)
  95{
  96        unsigned length;
  97        unsigned size;
  98        unsigned mixed;
  99        unsigned c;
 100        unsigned i;
 101        const char *p;
 102        char *hostname;
 103
 104        /* size */
 105        if (!new_p || (length = strlen(new_p)) < CONFIG_PASSWORD_MINLEN)
 106                return "too short";
 107
 108        /* no username as-is, as sub-string, reversed, capitalized, doubled */
 109        if (string_checker(new_p, pw->pw_name)) {
 110                return "similar to username";
 111        }
 112        /* no gecos as-is, as sub-string, reversed, capitalized, doubled */
 113        if (pw->pw_gecos[0] && string_checker(new_p, pw->pw_gecos)) {
 114                return "similar to gecos";
 115        }
 116        /* hostname as-is, as sub-string, reversed, capitalized, doubled */
 117        hostname = safe_gethostname();
 118        i = string_checker(new_p, hostname);
 119        free(hostname);
 120        if (i)
 121                return "similar to hostname";
 122
 123        /* Should / Must contain a mix of: */
 124        mixed = 0;
 125        for (i = 0; i < length; i++) {
 126                if (islower(new_p[i])) {        /* a-z */
 127                        mixed |= LOWERCASE;
 128                } else if (isupper(new_p[i])) { /* A-Z */
 129                        mixed |= UPPERCASE;
 130                } else if (isdigit(new_p[i])) { /* 0-9 */
 131                        mixed |= NUMBERS;
 132                } else  {                       /* special characters */
 133                        mixed |= SPECIAL;
 134                }
 135                /* Count i'th char */
 136                c = 0;
 137                p = new_p;
 138                while (1) {
 139                        p = strchr(p, new_p[i]);
 140                        if (p == NULL) {
 141                                break;
 142                        }
 143                        c++;
 144                        p++;
 145                        if (!*p) {
 146                                break;
 147                        }
 148                }
 149                /* More than 50% similar characters ? */
 150                if (c*2 >= length) {
 151                        return "too many similar characters";
 152                }
 153        }
 154
 155        size = CONFIG_PASSWORD_MINLEN + 2*CATEGORIES;
 156        for (i = 1; i <= LAST_CAT; i <<= 1)
 157                if (mixed & i)
 158                        size -= 2;
 159        if (length < size)
 160                return "too weak";
 161
 162        if (old_p && old_p[0]) {
 163                /* check vs. old password */
 164                if (string_checker(new_p, old_p)) {
 165                        return "similar to old password";
 166                }
 167        }
 168
 169        return NULL;
 170}
 171
 172int FAST_FUNC obscure(const char *old, const char *newval, const struct passwd *pw)
 173{
 174        const char *msg;
 175
 176        msg = obscure_msg(old, newval, pw);
 177        if (msg) {
 178                printf("Bad password: %s\n", msg);
 179                return 1;
 180        }
 181        return 0;
 182}
 183