]> eyrie.org Git - kerberos/krb5-strength.git/blob - plugin/general.c
Don't use sysbail to report libdl errors in test suite
[kerberos/krb5-strength.git] / plugin / general.c
1 /*
2  * The general entry points for password strength checking.
3  *
4  * Provides the strength_init, strength_check, and strength_close entry points
5  * for doing password strength checking.  These are the only interfaces that
6  * are called by the implementation-specific code, and all other checks are
7  * wrapped up in those interfaces.
8  *
9  * Developed by Derrick Brashear and Ken Hornstein of Sine Nomine Associates,
10  *     on behalf of Stanford University
11  * Extensive modifications by Russ Allbery <eagle@eyrie.org>
12  * Copyright 2006, 2007, 2009, 2012, 2013
13  *     The Board of Trustees of the Leland Stanford Junior University
14  *
15  * See LICENSE for licensing terms.
16  */
17
18 #include <config.h>
19 #include <portable/krb5.h>
20 #include <portable/system.h>
21
22 #include <ctype.h>
23
24 #include <plugin/internal.h>
25 #include <util/macros.h>
26
27
28 /*
29  * Initialize the module.  Ensure that the dictionary file exists and is
30  * readable and store the path in the module context.  Returns 0 on success,
31  * non-zero on failure.  This function returns failure only if it could not
32  * allocate memory or internal Kerberos calls that shouldn't fail do.
33  *
34  * The dictionary file should not include the trailing .pwd extension.
35  * Currently, we don't cope with a NULL dictionary path.
36  */
37 krb5_error_code
38 strength_init(krb5_context ctx, const char *dictionary,
39               krb5_pwqual_moddata *moddata)
40 {
41     krb5_pwqual_moddata data = NULL;
42     krb5_error_code code;
43
44     /* Allocate our internal data. */
45     data = calloc(1, sizeof(*data));
46     if (data == NULL)
47         return strength_error_system(ctx, "cannot allocate memory");
48     data->cdb_fd = -1;
49
50     /* Get minimum length information from krb5.conf. */
51     strength_config_number(ctx, "minimum_length", &data->minimum_length);
52
53     /* Get character class restrictions from krb5.conf. */
54     strength_config_boolean(ctx, "require_ascii_printable", &data->ascii);
55     strength_config_boolean(ctx, "require_non_letter", &data->nonletter);
56
57     /*
58      * Try to initialize CDB and CrackLib dictionaries.  Both functions handle
59      * their own configuration parsing and will do nothing if the
60      * corresponding dictionary is not configured.
61      */
62     code = strength_init_cracklib(ctx, data, dictionary);
63     if (code != 0)
64         goto fail;
65     code = strength_init_cdb(ctx, data);
66     if (code != 0)
67         goto fail;
68
69     /* Initialized.  Set moddata and return. */
70     *moddata = data;
71     return 0;
72
73 fail:
74     if (data != NULL)
75         strength_close(ctx, data);
76     *moddata = NULL;
77     return code;
78 }
79
80
81 /*
82  * Check if a password contains only printable ASCII characters.
83  */
84 static bool
85 only_printable_ascii(const char *password)
86 {
87     const char *p;
88
89     for (p = password; *p != '\0'; p++)
90         if (!isascii((unsigned char) *p) || !isprint((unsigned char) *p))
91             return false;
92     return true;
93 }
94
95
96 /*
97  * Check if a password contains only letters and spaces.
98  */
99 static bool
100 only_alpha_space(const char *password)
101 {
102     const char *p;
103
104     for (p = password; *p != '\0'; p++)
105         if (!isalpha((unsigned char) *p) && *p != ' ')
106             return false;
107     return true;
108 }
109
110
111 /*
112  * Check a given password.  Takes a Kerberos context, our module data, the
113  * password, the principal the password is for, and a buffer and buffer length
114  * into which to put any failure message.
115  */
116 krb5_error_code
117 strength_check(krb5_context ctx UNUSED, krb5_pwqual_moddata data,
118                const char *principal, const char *password)
119 {
120     krb5_error_code code;
121
122     /* Check minimum length first, since that's easy. */
123     if ((long) strlen(password) < data->minimum_length)
124         return strength_error_tooshort(ctx, ERROR_SHORT);
125
126     /*
127      * If desired, check whether the password contains non-ASCII or
128      * non-printable ASCII characters.
129      */
130     if (data->ascii && !only_printable_ascii(password))
131         return strength_error_generic(ctx, ERROR_ASCII);
132
133     /*
134      * If desired, ensure the password has a non-letter (and non-space)
135      * character.  This requires that people using phrases at least include a
136      * digit or punctuation to make phrase dictionary attacks or dictionary
137      * attacks via combinations of words harder.
138      */
139     if (data->nonletter && only_alpha_space(password))
140         return strength_error_class(ctx, ERROR_LETTER);
141
142     /* Check if the password is based on the principal in some way. */
143     code = strength_check_principal(ctx, data, principal, password);
144     if (code != 0)
145         return code;
146
147     /* Check the password against CDB and CrackLib if configured. */
148     code = strength_check_cracklib(ctx, data, password);
149     if (code != 0)
150         return code;
151     code = strength_check_cdb(ctx, data, password);
152     if (code != 0)
153         return code;
154
155     /* Success.  Password accepted. */
156     return 0;
157 }
158
159
160 /*
161  * Cleanly shut down the password strength plugin.  The only thing we have to
162  * do is free the memory allocated for our internal data.
163  */
164 void
165 strength_close(krb5_context ctx UNUSED, krb5_pwqual_moddata data)
166 {
167     if (data != NULL) {
168         strength_close_cdb(ctx, data);
169         free(data->dictionary);
170         free(data);
171     }
172 }