]> eyrie.org Git - kerberos/krb5-strength.git/blobdiff - plugin/config.c
Add dependabot configuration
[kerberos/krb5-strength.git] / plugin / config.c
index 414de048fc6ab268d2bdd6837e7729ca61d3f7cd..d9d2a5248bc58e2ec5c73b1b02cbbe8c8fc0dd97 100644 (file)
@@ -6,16 +6,18 @@
  * krb5_appdefaults_* functions.
  *
  * Written by Russ Allbery <eagle@eyrie.org>
+ * Copyright 2016 Russ Allbery <eagle@eyrie.org>
  * Copyright 2013
  *     The Board of Trustees of the Leland Stanford Junior University
  *
- * See LICENSE for licensing terms.
+ * SPDX-License-Identifier: MIT
  */
 
 #include <config.h>
 #include <portable/krb5.h>
 #include <portable/system.h>
 
+#include <ctype.h>
 #include <errno.h>
 
 #include <plugin/internal.h>
@@ -28,6 +30,9 @@ typedef krb5_realm realm_type;
 typedef krb5_data *realm_type;
 #endif
 
+/* Maximum number of character classes. */
+#define MAX_CLASSES 4
+
 
 /*
  * Obtain the default realm and translate it into the format required by
@@ -63,7 +68,7 @@ default_realm(krb5_context ctx)
         return NULL;
     code = krb5_get_default_realm(ctx, &realm);
     if (code != 0) {
-        free(realm);
+        free(realm_data);
         return NULL;
     }
     realm_data->magic = KV5M_DATA;
@@ -73,7 +78,7 @@ default_realm(krb5_context ctx)
         krb5_free_default_realm(ctx, realm);
         return NULL;
     }
-    realm_data->length = strlen(realm);
+    realm_data->length = (unsigned int) strlen(realm);
     krb5_free_default_realm(ctx, realm);
     return realm_data;
 }
@@ -105,6 +110,26 @@ free_default_realm(krb5_context ctx UNUSED, realm_type realm)
 #endif /* !HAVE_KRB5_REALM */
 
 
+/*
+ * Helper function to parse a number.  Takes the string to parse, the unsigned
+ * int in which to store the number, and the pointer to set to the first
+ * invalid character after the number.  Returns true if a number could be
+ * successfully parsed and false otherwise.
+ */
+static bool
+parse_number(const char *string, unsigned long *result, const char **end)
+{
+    unsigned long value;
+
+    errno = 0;
+    value = strtoul(string, (char **) end, 10);
+    if (errno != 0 || *end == string)
+        return false;
+    *result = value;
+    return true;
+}
+
+
 /*
  * Load a boolean option from Kerberos appdefaults.  Takes the Kerberos
  * context, the option, and the result location.
@@ -136,44 +161,84 @@ strength_config_boolean(krb5_context ctx, const char *opt, bool *result)
 static krb5_error_code
 parse_class(krb5_context ctx, const char *spec, struct class_rule **rule)
 {
-    struct vector *classes;
+    struct vector *classes = NULL;
     size_t i;
     krb5_error_code code;
-
-    /* Parse the required classes into a vector. */
-    classes = strength_vector_split_multi(spec, ",", NULL);
-    if (classes == NULL)
-        return strength_error_system(ctx, "cannot allocate memory");
+    const char *class, *end;
+    bool okay;
 
     /* Create the basic rule structure. */
     *rule = calloc(1, sizeof(struct class_rule));
-    (*rule)->min = 0;
-    (*rule)->max = 0;
+    if (*rule == NULL)
+        return strength_error_system(ctx, "cannot allocate memory");
+
+    /*
+     * If the rule starts with a digit and contains a '-', it starts
+     * with a range of affected password lengths.  Parse that range.
+     */
+    if (isdigit((unsigned char) *spec) && strchr(spec, '-') != NULL) {
+        okay = parse_number(spec, &(*rule)->min, &end);
+        if (okay)
+            okay = (*end == '-');
+        if (okay)
+            okay = parse_number(end + 1, &(*rule)->max, &end);
+        if (okay)
+            okay = (*end == ':');
+        if (okay)
+            spec = end + 1;
+        else {
+            code = strength_error_config(ctx,
+                                         "bad character class requirement in"
+                                         " configuration: %s",
+                                         spec);
+            goto fail;
+        }
+    }
+
+    /* Parse the required classes into a vector. */
+    classes = strength_vector_split_multi(spec, ",", NULL);
+    if (classes == NULL) {
+        code = strength_error_system(ctx, "cannot allocate memory");
+        goto fail;
+    }
 
     /*
      * Walk the list of required classes and set our flags, diagnosing an
      * unknown character class.
      */
     for (i = 0; i < classes->count; i++) {
-        if (strcmp(classes->strings[i], "upper") == 0)
+        class = classes->strings[i];
+        if (strcmp(class, "upper") == 0)
             (*rule)->upper = true;
-        else if (strcmp(classes->strings[i], "lower") == 0)
+        else if (strcmp(class, "lower") == 0)
             (*rule)->lower = true;
-        else if (strcmp(classes->strings[i], "digit") == 0)
+        else if (strcmp(class, "digit") == 0)
             (*rule)->digit = true;
-        else if (strcmp(classes->strings[i], "symbol") == 0)
+        else if (strcmp(class, "symbol") == 0)
             (*rule)->symbol = true;
-        else {
+        else if (isdigit((unsigned char) *class)) {
+            okay = parse_number(class, &(*rule)->num_classes, &end);
+            if (!okay || *end != '\0' || (*rule)->num_classes > MAX_CLASSES) {
+                code = strength_error_config(ctx,
+                                             "bad character class minimum in"
+                                             " configuration: %s",
+                                             class);
+                goto fail;
+            }
+        } else {
             code = strength_error_config(ctx, "unknown character class %s",
-                                         classes->strings[i]);
-            strength_vector_free(classes);
-            free(*rule);
-            *rule = NULL;
-            return code;
+                                         class);
+            goto fail;
         }
     }
     strength_vector_free(classes);
     return 0;
+
+fail:
+    strength_vector_free(classes);
+    free(*rule);
+    *rule = NULL;
+    return code;
 }
 
 
@@ -202,12 +267,12 @@ strength_config_classes(krb5_context ctx, const char *opt,
 
     /* Each word in the list will be a class rule. */
     code = parse_class(ctx, config->strings[0], &rules);
-    if (code != 0)
+    if (code != 0 || rules == NULL)
         goto fail;
     last = rules;
     for (i = 1; i < config->count; i++) {
         code = parse_class(ctx, config->strings[i], &last->next);
-        if (code != 0)
+        if (code != 0 || last->next == NULL)
             goto fail;
         last = last->next;
     }
@@ -240,8 +305,7 @@ fail:
  * allocation failed while parsing or while setting the default value.
  */
 krb5_error_code
-strength_config_list(krb5_context ctx, const char *opt,
-                     struct vector **result)
+strength_config_list(krb5_context ctx, const char *opt, struct vector **result)
 {
     realm_type realm;
     char *value = NULL;