]> eyrie.org Git - kerberos/krb5-strength.git/commitdiff
In CDB checks, check all passwords within edit distance one
authorRuss Allbery <eagle@eyrie.org>
Fri, 7 Feb 2014 23:44:44 +0000 (15:44 -0800)
committerRuss Allbery <eagle@eyrie.org>
Fri, 7 Feb 2014 23:44:44 +0000 (15:44 -0800)
When checking a password against a CDB dictionary, the dictionary will
be checked for all printable ASCII passwords within edit distance one,
in addition to checking the password with first and last characters,
first two characters, and last two characters removed.

NEWS
README
plugin/cdb.c

diff --git a/NEWS b/NEWS
index e8c73f17c1af04c3e9b4d1b933a9b2ceb0c0b180..a41835989edf9698b8c52a32798822e6bd2db3d6 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,11 @@ krb5-strength 3.0 (unreleased)
     mechanism.)  This program has more extensive Perl module dependencies
     than the other programs in this distribution.
 
+    When checking a password against a CDB dictionary, the dictionary will
+    be checked for all printable ASCII passwords within edit distance one,
+    in addition to checking the password with first and last characters,
+    first two characters, and last two characters removed.
+
 krb5-strength 2.2 (2013-12-16)
 
     More complex character class requirements can be specified with the
diff --git a/README b/README
index 7ca26f0b275dc91ac04dbf83bc0eba0cd1d9b6bc..e01a4a872ea6e8c747261b33df1891d465c6e6ef 100644 (file)
--- a/README
+++ b/README
@@ -3,8 +3,8 @@
 
                Maintained by Russ Allbery <eagle@eyrie.org>
 
-  Copyright 2006, 2007, 2009, 2010, 2012, 2013 The Board of Trustees of
-  the Leland Stanford Junior University.  Portions copyright 1993 Alec
+  Copyright 2006, 2007, 2009, 2010, 2012, 2013, 2014 The Board of Trustees
+  of the Leland Stanford Junior University.  Portions copyright 1993 Alec
   Muffett.  Developed by Derrick Brashear and Ken Hornstein of Sine Nomine
   Associates, on behalf of Stanford University.  This software is
   distributed under a BSD-style license and under the Artistic License.
@@ -257,10 +257,10 @@ CONFIGURATION
   files, omitting the trailing *.hwm, *.pwd, and *.pwi extensions for the
   CrackLib dictionary.  You can use either or both settings.  If you use
   both, CrackLib will be checked first, and then CDB.  When checking a CDB
-  database, the password, the password with the first character removed,
-  the last character removed, the first and last characters removed, the
-  first two characters removed, and the last two characters removed will
-  all be checked against the dictionary.
+  database, the password, all printable ASCII passwords within edit
+  distance one of the password, and the password with the first and last
+  characters removed, the first two characters removed, and the last two
+  characters removed will all be checked against the dictionary.
 
   Then, for the external password checking program, add a new section (or
   modify the existing [password_quality] section) to look like the
@@ -343,10 +343,10 @@ CONFIGURATION
   files, omitting the trailing *.hwm, *.pwd, and *.pwi extensions for the
   CrackLib dictionary.  You can use either or both settings.  If you use
   both, CrackLib will be checked first, and then CDB.  When checking a CDB
-  database, the password, the password with the first character removed,
-  the last character removed, the first and last characters removed, the
-  first two characters removed, and the last two characters removed will
-  all be checked against the dictionary.
+  database, the password, all printable ASCII passwords within edit
+  distance one of the password, and the password with the first and last
+  characters removed, the first two characters removed, and the last two
+  characters removed will all be checked against the dictionary.
 
   The second option is to use the normal dict_path setting.  In the
   [realms] section of your krb5.conf kdc.conf, under the appropriate realm
@@ -394,7 +394,10 @@ CONFIGURATION
       canonicalization or character set is defined for Kerberos passwords
       in general, so you may want to reject non-ASCII characters to avoid
       interoperability problems with computers with different default
-      character sets or Unicode normalization forms.
+      character sets or Unicode normalization forms.  Also be aware that,
+      when testing passwords within edit distance one against a CDB
+      database, only characters in the printable ASCII character set will
+      be attempted.
 
   require_classes
 
@@ -483,7 +486,7 @@ LICENSE
   The krb5-strength package as a whole is covered by the following
   copyright statement and license:
 
-    Copyright 2006, 2007, 2009, 2010, 2012, 2013
+    Copyright 2006, 2007, 2009, 2010, 2012, 2013, 2014
         The Board of Trustees of the Leland Stanford Junior University
 
     Permission is hereby granted, free of charge, to any person obtaining
index 92e08eb13dfdd6e78d2b0d0d73ef1b58c670da1d..d77785d9346678992be596a8702c385bbb369c41 100644 (file)
@@ -9,7 +9,7 @@
  * character from both start and end.
  *
  * Written by Russ Allbery <eagle@eyrie.org>
- * Copyright 2013
+ * Copyright 2013, 2014
  *     The Board of Trustees of the Leland Stanford Junior University
  *
  * See LICENSE for licensing terms.
@@ -23,6 +23,7 @@
 #ifdef HAVE_CDB_H
 # include <cdb.h>
 #endif
+#include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/stat.h>
@@ -58,7 +59,7 @@ strength_init_cdb(krb5_context ctx, krb5_pwqual_moddata data UNUSED)
 #ifdef HAVE_CDB
 
 /*
- * Macro used to make password checks more readable.  Assumes that the found
+ * Macros used to make password checks more readable.  Assumes that the found
  * and fail labels are available for the abort cases of finding a password or
  * failing to look it up.
  */
@@ -70,6 +71,14 @@ strength_init_cdb(krb5_context ctx, krb5_pwqual_moddata data UNUSED)
         if (found)                                              \
             goto found;                                         \
     } while (0)
+# define CHECK_PASSWORD_VARIANT(ctx, data, template, p)                   \
+    do {                                                                  \
+        code = variant_in_cdb_dictionary(ctx, data, template, p, &found); \
+        if (code != 0)                                                    \
+            goto fail;                                                    \
+        if (found)                                                        \
+            goto found;                                                   \
+    } while (0)
 
 
 /*
@@ -94,6 +103,32 @@ in_cdb_dictionary(krb5_context ctx, krb5_pwqual_moddata data,
 }
 
 
+/*
+ * Given a password template and a pointer to the character to change, check
+ * all versions of that template with that character replaced by all possible
+ * printable ASCII characters.  The template will be modified in place to try
+ * the various characters.  Sets the found parameter to true if some variation
+ * of the template is found, false otherwise.  Returns a Kerberos status code.
+ */
+static krb5_error_code
+variant_in_cdb_dictionary(krb5_context ctx, krb5_pwqual_moddata data,
+                          char *template, char *permute, bool *found)
+{
+    int c;
+    krb5_error_code code;
+
+    *found = false;
+    for (c = 0; c <= 127; c++)
+        if (isprint(c)) {
+            *permute = c;
+            code = in_cdb_dictionary(ctx, data, template, found);
+            if (code != 0 || found)
+                return code;
+        }
+    return 0;
+}
+
+
 /*
  * Initialize the CDB dictionary.  Opens the dictionary and sets up the
  * TinyCDB state.  Returns 0 on success, non-zero on failure (and sets the
@@ -142,6 +177,8 @@ strength_check_cdb(krb5_context ctx, krb5_pwqual_moddata data,
 {
     krb5_error_code code;
     bool found;
+    size_t length, i;
+    char *p;
     char *variant = NULL;
 
     /* If we have no dictionary, there is nothing to do. */
@@ -151,31 +188,50 @@ strength_check_cdb(krb5_context ctx, krb5_pwqual_moddata data,
     /* Check the basic password. */
     CHECK_PASSWORD(ctx, data, password);
 
-    /* Check with one or two characters removed from the start. */
-    if (password[0] != '\0') {
-        CHECK_PASSWORD(ctx, data, password + 1);
-        if (password[1] != '\0')
-            CHECK_PASSWORD(ctx, data, password + 2);
+    /* Allocate memory for password variations. */
+    length = strlen(password);
+    variant = malloc(length + 2);
+    if (variant == NULL)
+        return strength_error_system(ctx, "cannot allocate memory");
+
+    /* Check all one-character deletions. */
+    for (i = 0; i < length; i++) {
+        if (i > 0)
+            memcpy(variant, password, i);
+        if (i < length - 1)
+            memcpy(variant + i, password + i + 1, length - i - 1);
+        variant[length - 1] = '\0';
+        CHECK_PASSWORD(ctx, data, variant);
+    }
+
+    /* Check all one-character permutations. */
+    memcpy(variant, password, length + 1);
+    for (p = variant; *p != '\0'; p++)
+        CHECK_PASSWORD_VARIANT(ctx, data, variant, p);
+
+    /* Check all one-character additions. */
+    for (i = 0; i <= length; i++) {
+        if (i > 0)
+            memcpy(variant, password, i);
+        if (i < length)
+            memcpy(variant + i + 1, password + i, length - i);
+        variant[length + 1] = '\0';
+        CHECK_PASSWORD_VARIANT(ctx, data, variant, variant + i);
     }
 
     /*
-     * Strip a character from the end and then check both that password and
-     * the one with a character taken from the start as well.
+     * Check the password with first and last, two leading, or two trailing
+     * characters removed.
      */
-    if (strlen(password) > 0) {
-        variant = strdup(password);
-        if (variant == NULL)
-            return strength_error_system(ctx, "cannot allocate memory");
-        variant[strlen(variant) - 1] = '\0';
+    if (length > 2) {
+        memcpy(variant, password + 2, length - 1);
+        CHECK_PASSWORD(ctx, data, variant);
+        memcpy(variant, password + 1, length - 2);
+        variant[length - 2] = '\0';
+        CHECK_PASSWORD(ctx, data, variant);
+        memcpy(variant, password, length - 2);
+        variant[length - 2] = '\0';
         CHECK_PASSWORD(ctx, data, variant);
-        if (variant[0] != '\0')
-            CHECK_PASSWORD(ctx, data, variant + 1);
-
-        /* Check the password with two characters removed. */
-        if (strlen(password) > 1) {
-            variant[strlen(variant) - 1] = '\0';
-            CHECK_PASSWORD(ctx, data, variant);
-        }
     }
 
     /* Password not found. */