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.
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
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
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
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
* 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.
#ifdef HAVE_CDB_H
# include <cdb.h>
#endif
+#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#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.
*/
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)
/*
}
+/*
+ * 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
{
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. */
/* 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. */