]> eyrie.org Git - kerberos/krb5-strength.git/blob - plugin/classes.c
Update hash iterations in heimdal-history
[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, 2023 Russ Allbery <eagle@eyrie.org>
8  * Copyright 2013-2014
9  *     The Board of Trustees of the Leland Stanford Junior University
10  *
11  * SPDX-License-Identifier: MIT
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)
52         classes->num_classes++;
53     if (classes->upper)
54         classes->num_classes++;
55     if (classes->digit)
56         classes->num_classes++;
57     if (classes->symbol)
58         classes->num_classes++;
59 }
60
61
62 /*
63  * Check whether a password satisfies a required character class rule, given
64  * the length of the password and the classes.  Returns 0 if it does and a
65  * Kerberos error code if it does not.
66  */
67 static krb5_error_code
68 check_rule(krb5_context ctx, struct class_rule *rule, size_t length,
69            const struct password_classes *classes)
70 {
71     if (length < rule->min || (rule->max > 0 && length > rule->max))
72         return 0;
73     if (classes->num_classes < rule->num_classes)
74         return strength_error_class(ctx, ERROR_CLASS_MIN, rule->num_classes);
75     if (rule->lower && !classes->lower)
76         return strength_error_class(ctx, ERROR_CLASS_LOWER);
77     if (rule->upper && !classes->upper)
78         return strength_error_class(ctx, ERROR_CLASS_UPPER);
79     if (rule->digit && !classes->digit)
80         return strength_error_class(ctx, ERROR_CLASS_DIGIT);
81     if (rule->symbol && !classes->symbol)
82         return strength_error_class(ctx, ERROR_CLASS_SYMBOL);
83     return 0;
84 }
85
86
87 /*
88  * Check whether a password satisfies the configured character class
89  * restrictions.
90  */
91 krb5_error_code
92 strength_check_classes(krb5_context ctx, krb5_pwqual_moddata data,
93                        const char *password)
94 {
95     struct password_classes classes;
96     size_t length;
97     struct class_rule *rule;
98     krb5_error_code code;
99
100     if (data->rules == NULL)
101         return 0;
102     analyze_password(password, &classes);
103     length = strlen(password);
104     for (rule = data->rules; rule != NULL; rule = rule->next) {
105         code = check_rule(ctx, rule, length, &classes);
106         if (code != 0)
107             return code;
108     }
109     return 0;
110 }