]> eyrie.org Git - kerberos/krb5-strength.git/blobdiff - plugin/general.c
Finalize changes for 3.3-1
[kerberos/krb5-strength.git] / plugin / general.c
index 752ca3d592f32309c6c6681334af86665767aaa2..325f785b7b2813b3949c494639c7a26f89dc2545 100644 (file)
@@ -6,13 +6,12 @@
  * are called by the implementation-specific code, and all other checks are
  * wrapped up in those interfaces.
  *
- * Developed by Derrick Brashear and Ken Hornstein of Sine Nomine Associates,
- *     on behalf of Stanford University
- * Extensive modifications by Russ Allbery <rra@stanford.edu>
- * Copyright 2006, 2007, 2009, 2012, 2013
- *     The Board of Trustees of the Leland Stanford Junior University
+ * Developed by Daria Phoebe Brashear and Ken Hornstein of Sine Nomine
+ * Associates, on behalf of Stanford University Extensive modifications by Russ
+ * Allbery <eagle@eyrie.org> Copyright 2006-2007, 2009, 2012-2014 The Board of
+ * Trustees of the Leland Stanford Junior University
  *
- * See LICENSE for licensing terms.
+ * SPDX-License-Identifier: MIT
  */
 
 #include <config.h>
@@ -47,22 +46,34 @@ strength_init(krb5_context ctx, const char *dictionary,
         return strength_error_system(ctx, "cannot allocate memory");
     data->cdb_fd = -1;
 
-    /* Get minimum length information from krb5.conf. */
+    /* Get minimum length and character information from krb5.conf. */
+    strength_config_number(ctx, "minimum_different", &data->minimum_different);
     strength_config_number(ctx, "minimum_length", &data->minimum_length);
 
-    /* Get character class restrictions from krb5.conf. */
+    /* Get simple character class restrictions from krb5.conf. */
     strength_config_boolean(ctx, "require_ascii_printable", &data->ascii);
     strength_config_boolean(ctx, "require_non_letter", &data->nonletter);
 
+    /* Get complex character class restrictions from krb5.conf. */
+    code = strength_config_classes(ctx, "require_classes", &data->rules);
+    if (code != 0)
+        goto fail;
+
+    /* Get CrackLib maximum length from krb5.conf. */
+    strength_config_number(ctx, "cracklib_maxlen", &data->cracklib_maxlen);
+
     /*
-     * Try to initialize CDB and CrackLib dictionaries.  Both functions handle
-     * their own configuration parsing and will do nothing if the
-     * corresponding dictionary is not configured.
+     * Try to initialize CDB, CrackLib, and SQLite dictionaries.  These
+     * functions handle their own configuration parsing and will do nothing if
+     * the corresponding dictionary is not configured.
      */
     code = strength_init_cracklib(ctx, data, dictionary);
     if (code != 0)
         goto fail;
     code = strength_init_cdb(ctx, data);
+    if (code != 0)
+        goto fail;
+    code = strength_init_sqlite(ctx, data);
     if (code != 0)
         goto fail;
 
@@ -108,6 +119,39 @@ only_alpha_space(const char *password)
 }
 
 
+/*
+ * Check if a password has a sufficient number of unique characters.  Takes
+ * the password and the required number of characters.
+ */
+static bool
+has_minimum_different(const char *password, long minimum)
+{
+    size_t unique;
+    const char *p;
+
+    /* Special cases for passwords of length 0 and a minimum <= 1. */
+    if (password == NULL || password[0] == '\0')
+        return minimum <= 0;
+    if (minimum <= 1)
+        return true;
+
+    /*
+     * Count the number of unique characters by incrementing the count if each
+     * subsequent character is not found in the previous password characters.
+     * This algorithm is O(n^2), but passwords are short enough it shouldn't
+     * matter.
+     */
+    unique = 1;
+    for (p = password + 1; *p != '\0'; p++)
+        if (memchr(password, *p, p - password) == NULL) {
+            unique++;
+            if (unique >= (size_t) minimum)
+                return true;
+        }
+    return false;
+}
+
+
 /*
  * Check a given password.  Takes a Kerberos context, our module data, the
  * password, the principal the password is for, and a buffer and buffer length
@@ -139,16 +183,32 @@ strength_check(krb5_context ctx UNUSED, krb5_pwqual_moddata data,
     if (data->nonletter && only_alpha_space(password))
         return strength_error_class(ctx, ERROR_LETTER);
 
+    /* If desired, check for enough unique characters. */
+    if (data->minimum_different > 0)
+        if (!has_minimum_different(password, data->minimum_different))
+            return strength_error_class(ctx, ERROR_MINDIFF);
+
+    /*
+     * If desired, check that the password satisfies character class
+     * restrictions.
+     */
+    code = strength_check_classes(ctx, data, password);
+    if (code != 0)
+        return code;
+
     /* Check if the password is based on the principal in some way. */
     code = strength_check_principal(ctx, data, principal, password);
     if (code != 0)
         return code;
 
-    /* Check the password against CDB and CrackLib if configured. */
+    /* Check the password against CDB, CrackLib, and SQLite if configured. */
     code = strength_check_cracklib(ctx, data, password);
     if (code != 0)
         return code;
     code = strength_check_cdb(ctx, data, password);
+    if (code != 0)
+        return code;
+    code = strength_check_sqlite(ctx, data, password);
     if (code != 0)
         return code;
 
@@ -164,9 +224,18 @@ strength_check(krb5_context ctx UNUSED, krb5_pwqual_moddata data,
 void
 strength_close(krb5_context ctx UNUSED, krb5_pwqual_moddata data)
 {
-    if (data != NULL) {
-        strength_close_cdb(ctx, data);
-        free(data->dictionary);
-        free(data);
+    struct class_rule *last, *tmp;
+
+    if (data == NULL)
+        return;
+    strength_close_cdb(ctx, data);
+    strength_close_sqlite(ctx, data);
+    last = data->rules;
+    while (last != NULL) {
+        tmp = last;
+        last = last->next;
+        free(tmp);
     }
+    free(data->dictionary);
+    free(data);
 }