ash: builtin: Mark more regular built-ins
[oweals/busybox.git] / libbb / obscure.c
index 22bcb9decfb0af9c37f2f538f3770ffdeb5d0571..ad17d1ff1b27c6051ee7ef18920c0eb701ef8525 100644 (file)
@@ -45,53 +45,59 @@ static int string_checker_helper(const char *p1, const char *p2) __attribute__ (
 
 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
+       if (strcasestr(p2, p1) != NULL
        /* invert in case haystack is shorter than needle */
-       || strcasestr(p1, p2) != NULL)
+        || strcasestr(p1, p2) != NULL
+       /* as-is or capitalized */
+       /* || strcasecmp(p1, p2) == 0 - 1st strcasestr should catch this too */
+       ) {
                return 1;
+       }
        return 0;
 }
 
 static int string_checker(const char *p1, const char *p2)
 {
-       int size;
+       int size, i;
        /* check string */
        int ret = string_checker_helper(p1, p2);
-       /* Make our own copy */
+       /* make our own copy */
        char *p = xstrdup(p1);
-       /* reverse string */
-       size = strlen(p);
 
-       while (size--) {
-               *p = p1[size];
-               p++;
+       /* reverse string */
+       i = size = strlen(p1);
+       while (--i >= 0) {
+               *p++ = p1[i];
        }
-       /* restore pointer */
-       p -= strlen(p1);
+       p -= size; /* restore pointer */
+
        /* check reversed string */
        ret |= string_checker_helper(p, p2);
+
        /* clean up */
-       memset(p, 0, strlen(p1));
+       nuke_str(p);
        free(p);
+
        return ret;
 }
 
-#define LOWERCASE          1
-#define UPPERCASE          2
-#define NUMBERS            4
-#define SPECIAL            8
+#define CATEGORIES  4
+
+#define LOWERCASE   1
+#define UPPERCASE   2
+#define NUMBERS     4
+#define SPECIAL     8
+
+#define LAST_CAT    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;
+       unsigned length;
+       unsigned size;
+       unsigned mixed;
+       unsigned c;
+       unsigned i;
        const char *p;
        char *hostname;
 
@@ -103,10 +109,12 @@ static const char *obscure_msg(const char *old_p, const char *new_p, const struc
        if (string_checker(new_p, pw->pw_name)) {
                return "similar to username";
        }
+#ifndef __BIONIC__
        /* no gecos as-is, as sub-string, reversed, capitalized, doubled */
-       if (*pw->pw_gecos && string_checker(new_p, pw->pw_gecos)) {
+       if (pw->pw_gecos[0] && string_checker(new_p, pw->pw_gecos)) {
                return "similar to gecos";
        }
+#endif
        /* hostname as-is, as sub-string, reversed, capitalized, doubled */
        hostname = safe_gethostname();
        i = string_checker(new_p, hostname);
@@ -115,6 +123,7 @@ static const char *obscure_msg(const char *old_p, const char *new_p, const struc
                return "similar to hostname";
 
        /* Should / Must contain a mix of: */
+       mixed = 0;
        for (i = 0; i < length; i++) {
                if (islower(new_p[i])) {        /* a-z */
                        mixed |= LOWERCASE;
@@ -125,7 +134,7 @@ static const char *obscure_msg(const char *old_p, const char *new_p, const struc
                } else  {                       /* special characters */
                        mixed |= SPECIAL;
                }
-               /* More than 50% similar characters ? */
+               /* Count i'th char */
                c = 0;
                p = new_p;
                while (1) {
@@ -134,26 +143,31 @@ static const char *obscure_msg(const char *old_p, const char *new_p, const struc
                                break;
                        }
                        c++;
-                       if (!++p) {
-                               break; /* move past the matched char if possible */
+                       p++;
+                       if (!*p) {
+                               break;
                        }
                }
-
-               if (c >= (length / 2)) {
+               /* More than 50% similar characters ? */
+               if (c*2 >= length) {
                        return "too many similar characters";
                }
        }
-       for (i=0; i<4; i++)
-               if (mixed & (1<<i)) size -= 2;
+
+       size = CONFIG_PASSWORD_MINLEN + 2*CATEGORIES;
+       for (i = 1; i <= LAST_CAT; i <<= 1)
+               if (mixed & i)
+                       size -= 2;
        if (length < size)
                return "too weak";
 
-       if (old_p && old_p[0] != '\0') {
+       if (old_p && old_p[0]) {
                /* check vs. old password */
                if (string_checker(new_p, old_p)) {
                        return "similar to old password";
                }
        }
+
        return NULL;
 }
 
@@ -168,3 +182,41 @@ int FAST_FUNC obscure(const char *old, const char *newval, const struct passwd *
        }
        return 0;
 }
+
+#if ENABLE_UNIT_TEST
+
+/* Test obscure_msg() instead of obscure() in order not to print anything. */
+
+static const struct passwd pw = {
+       .pw_name = (char *)"johndoe",
+       .pw_gecos = (char *)"John Doe",
+};
+
+BBUNIT_DEFINE_TEST(obscure_weak_pass)
+{
+       /* Empty password */
+       BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "", &pw));
+       /* Pure numbers */
+       BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "23577315", &pw));
+       /* Similar to pw_name */
+       BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "johndoe123%", &pw));
+       /* Similar to pw_gecos, reversed */
+       BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "eoD nhoJ^44@", &pw));
+       /* Similar to the old password */
+       BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "d4#21?'S", &pw));
+       /* adjacent letters */
+       BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "qwerty123", &pw));
+       /* Many similar chars */
+       BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "^33Daaaaaa1", &pw));
+
+       BBUNIT_ENDTEST;
+}
+
+BBUNIT_DEFINE_TEST(obscure_strong_pass)
+{
+       BBUNIT_ASSERT_NULL(obscure_msg("Rt4##2&:'|", "}(^#rrSX3S*22", &pw));
+
+       BBUNIT_ENDTEST;
+}
+
+#endif /* ENABLE_UNIT_TEST */