]> eyrie.org Git - kerberos/krb5-strength.git/blob - plugin/classes.c
Fix various character class check mistakes, add test suite
[kerberos/krb5-strength.git] / plugin / classes.c
1 /*
2  * Password strength checks for character classes.
3  *
4  * Checks whether the password satisfies a set of character class rules.
5  *
6  * Written by Russ Allbery <eagle@eyrie.org>
7  * Copyright 2013
8  *     The Board of Trustees of the Leland Stanford Junior University
9  *
10  * See LICENSE for licensing terms.
11  */
12
13 #include <config.h>
14 #include <portable/system.h>
15
16 #include <ctype.h>
17
18 #include <plugin/internal.h>
19
20 /* Stores the characteristics of a particular password as boolean flags. */
21 struct password_classes {
22     bool lower;
23     bool upper;
24     bool digit;
25     bool symbol;
26 };
27
28 /* Abbreviate the most common error reporting syntax. */
29 #define MUST_HAVE(ctx, err) \
30     strength_error_class((ctx), "password must contain " err)
31
32
33 /*
34  * Analyze a password and fill out a struct with flags indicating which
35  * character classes are present in the password.
36  */
37 static void
38 analyze_password(const char *password, struct password_classes *classes)
39 {
40     const char *p;
41
42     memset(classes, 0, sizeof(struct password_classes));
43     for (p = password; *p != '\0'; p++) {
44         if (islower((unsigned char) *p))
45             classes->lower = true;
46         else if (isupper((unsigned char) *p))
47             classes->upper = true;
48         else if (isdigit((unsigned char) *p))
49             classes->digit = true;
50         else
51             classes->symbol = true;
52     }
53 }
54
55
56 /*
57  * Check whether a password satisfies a required character class rule, given
58  * the length of the password and the classes.  Returns 0 if it does and a
59  * Kerberos error code if it does not.
60  */
61 static krb5_error_code
62 check_rule(krb5_context ctx, struct class_rule *rule, size_t length,
63            struct password_classes *classes)
64 {
65     if (length < rule->min || (rule->max > 0 && length > rule->max))
66         return 0;
67     if (rule->lower && !classes->lower)
68         return MUST_HAVE(ctx, "a lowercase letter");
69     if (rule->upper && !classes->upper)
70         return MUST_HAVE(ctx, "an uppercase letter");
71     if (rule->digit && !classes->digit)
72         return MUST_HAVE(ctx, "a number");
73     if (rule->symbol && !classes->symbol)
74         return MUST_HAVE(ctx, "a space or punctuation character");
75     return 0;
76 }
77
78
79 /*
80  * Check whether a password satisfies the configured character class
81  * restrictions.
82  */
83 krb5_error_code
84 strength_check_classes(krb5_context ctx, krb5_pwqual_moddata data,
85                        const char *password)
86 {
87     struct password_classes classes;
88     size_t length;
89     struct class_rule *rule;
90     krb5_error_code code;
91
92     if (data->rules == NULL)
93         return 0;
94     analyze_password(password, &classes);
95     length = strlen(password);
96     for (rule = data->rules; rule != NULL; rule = rule->next) {
97         code = check_rule(ctx, rule, length, &classes);
98         if (code != 0)
99             return code;
100     }
101     return 0;
102 }