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 tarball for details.
   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-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;
  56}
  57
  58static int string_checker(const char *p1, const char *p2)
  59{
  60        int size;
  61        /* check string */
  62        int ret = string_checker_helper(p1, p2);
  63        /* Make our own copy */
  64        char *p = xstrdup(p1);
  65        /* 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
  87static const char *obscure_msg(const char *old_p, const char *new_p, const struct passwd *pw)
  88{
  89        int i;
  90        int c;
  91        int length;
  92        int mixed = 0;
  93        /* Add 2 for each type of characters to the minlen of password */
  94        int size = CONFIG_PASSWORD_MINLEN + 8;
  95        const char *p;
  96        char *hostname;
  97
  98        /* size */
  99        if (!new_p || (length = strlen(new_p)) < CONFIG_PASSWORD_MINLEN)
 100                return "too short";
 101
 102        /* 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 */
 107        if (*pw->pw_gecos && string_checker(new_p, pw->pw_gecos)) {
 108                return "similar to gecos";
 109        }
 110        /* hostname as-is, as sub-string, reversed, capitalized, doubled */
 111        hostname = safe_gethostname();
 112        i = string_checker(new_p, hostname);
 113        free(hostname);
 114        if (i)
 115                return "similar to hostname";
 116
 117        /* 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) {
 132                        p = strchr(p, new_p[i]);
 133                        if (p == NULL) {
 134                                break;
 135                        }
 136                        c++;
 137                        if (!++p) {
 138                                break; /* move past the matched char if possible */
 139                        }
 140                }
 141
 142                if (c >= (length / 2)) {
 143                        return "too many similar characters";
 144                }
 145        }
 146        for (i=0; i<4; i++)
 147                if (mixed & (1<<i)) size -= 2;
 148        if (length < size)
 149                return "too weak";
 150
 151        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;
 158}
 159
 160int FAST_FUNC obscure(const char *old, const char *newval, const struct passwd *pw)
 161{
 162        const char *msg;
 163
 164        msg = obscure_msg(old, newval, pw);
 165        if (msg) {
 166                printf("Bad password: %s\n", msg);
 167                return 1;
 168        }
 169        return 0;
 170}
 171