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