]> eyrie.org Git - kerberos/krb5-strength.git/blob - plugin/cdb.c
Change CrackLib tests for system CrackLib
[kerberos/krb5-strength.git] / plugin / cdb.c
1 /*
2  * Check a CDB database for a password or some simple permutations.
3  *
4  * This file implements a much simpler variation on CrackLib checks intended
5  * for use with longer passwords where some of the CrackLib permutations don't
6  * make as much sense.  A CDB database with passwords as keys is checked for
7  * the password and for variations with one character removed from the start
8  * or end, two characters removed from the start, two from the end, or one
9  * character from both start and end.
10  *
11  * Written by Russ Allbery <eagle@eyrie.org>
12  * Copyright 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/kadmin.h>
20 #include <portable/krb5.h>
21 #include <portable/system.h>
22
23 #ifdef HAVE_CDB_H
24 # include <cdb.h>
25 #endif
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <sys/stat.h>
29
30 #include <plugin/internal.h>
31 #include <util/macros.h>
32
33
34 /*
35  * Stub for strength_init_cdb if not built with CDB support.
36  */
37 #ifndef HAVE_CDB
38 krb5_error_code
39 strength_init_cdb(krb5_context ctx, krb5_pwqual_moddata data UNUSED)
40 {
41     char *path = NULL;
42
43     /* Get CDB dictionary path from krb5.conf. */
44     strength_config_string(ctx, "password_dictionary_cdb", &path);
45
46     /* If it was set, report an error, since we don't have CDB support. */
47     if (path == NULL)
48         return 0;
49     free(path);
50     krb5_set_error_message(ctx, KADM5_BAD_SERVER_PARAMS, "CDB dictionary"
51                            " requested but not built with CDB support");
52     return KADM5_BAD_SERVER_PARAMS;
53 }
54 #endif
55
56
57 /* Skip the rest of this file if CDB is not available. */
58 #ifdef HAVE_CDB
59
60 /*
61  * Macro used to make password checks more readable.  Assumes that the found
62  * and fail labels are available for the abort cases of finding a password or
63  * failing to look it up.
64  */
65 # define CHECK_PASSWORD(ctx, data, password)                    \
66     do {                                                        \
67         code = in_cdb_dictionary(ctx, data, password, &found);  \
68         if (code != 0)                                          \
69             goto fail;                                          \
70         if (found)                                              \
71             goto found;                                         \
72     } while (0)
73
74
75 /*
76  * Look up a password in CDB and set the found parameter to true if it is
77  * found, false otherwise.  Returns a Kerberos status code, which will be 0 on
78  * success and something else on failure.
79  */
80 static krb5_error_code
81 in_cdb_dictionary(krb5_context ctx, krb5_pwqual_moddata data,
82                   const char *password, bool *found)
83 {
84     int status;
85
86     *found = false;
87     status = cdb_find(&data->cdb, password, strlen(password));
88     if (status < 0)
89         return strength_error_system(ctx, "cannot query CDB database");
90     else {
91         *found = (status == 1);
92         return 0;
93     }
94 }
95
96
97 /*
98  * Initialize the CDB dictionary.  Opens the dictionary and sets up the
99  * TinyCDB state.  Returns 0 on success, non-zero on failure (and sets the
100  * error in the Kerberos context).  If not built with CDB support, always
101  * returns an error.
102  */
103 krb5_error_code
104 strength_init_cdb(krb5_context ctx, krb5_pwqual_moddata data)
105 {
106     krb5_error_code code;
107     char *path = NULL;
108
109     /* Get CDB dictionary path from krb5.conf. */
110     strength_config_string(ctx, "password_dictionary_cdb", &path);
111
112     /* If there is no configured dictionary, nothing to do. */
113     if (path == NULL)
114         return 0;
115
116     /* Open the dictionary and initialize the CDB data. */
117     data->cdb_fd = open(path, O_RDONLY);
118     if (data->cdb_fd < 0)
119         return strength_error_system(ctx, "cannot open dictionary %s", path);
120     if (cdb_init(&data->cdb, data->cdb_fd) < 0) {
121         code = strength_error_system(ctx, "cannot init dictionary %s", path);
122         free(path);
123         close(data->cdb_fd);
124         data->cdb_fd = -1;
125         return code;
126     }
127     free(path);
128     data->have_cdb = true;
129     return 0;
130 }
131
132
133 /*
134  * Given a password, try the various transformations that we want to apply and
135  * check for each of them in the dictionary.  Returns a Kerberos status code,
136  * which will be KADM5_PASS_Q_DICT if the password was found in the
137  * dictionary.
138  */
139 krb5_error_code
140 strength_check_cdb(krb5_context ctx, krb5_pwqual_moddata data,
141                    const char *password)
142 {
143     krb5_error_code code;
144     bool found;
145     char *variant = NULL;
146
147     /* If we have no dictionary, there is nothing to do. */
148     if (!data->have_cdb)
149         return 0;
150
151     /* Check the basic password. */
152     CHECK_PASSWORD(ctx, data, password);
153
154     /* Check with one or two characters removed from the start. */
155     if (password[0] != '\0') {
156         CHECK_PASSWORD(ctx, data, password + 1);
157         if (password[1] != '\0')
158             CHECK_PASSWORD(ctx, data, password + 2);
159     }
160
161     /*
162      * Strip a character from the end and then check both that password and
163      * the one with a character taken from the start as well.
164      */
165     if (strlen(password) > 0) {
166         variant = strdup(password);
167         if (variant == NULL)
168             return strength_error_system(ctx, "cannot allocate memory");
169         variant[strlen(variant) - 1] = '\0';
170         CHECK_PASSWORD(ctx, data, variant);
171         if (variant[0] != '\0')
172             CHECK_PASSWORD(ctx, data, variant + 1);
173
174         /* Check the password with two characters removed. */
175         if (strlen(password) > 1) {
176             variant[strlen(variant) - 1] = '\0';
177             CHECK_PASSWORD(ctx, data, variant);
178         }
179     }
180
181     /* Password not found. */
182     free(variant);
183     return 0;
184
185 found:
186     /* We found the password or a variant in the dictionary. */
187     free(variant);
188     return strength_error_dict(ctx, ERROR_DICT);
189
190 fail:
191     /* Some sort of failure during CDB lookup. */
192     free(variant);
193     return code;
194 }
195
196
197 /*
198  * Free internal TinyCDB state and close the CDB dictionary.
199  */
200 void
201 strength_close_cdb(krb5_context ctx UNUSED, krb5_pwqual_moddata data)
202 {
203     if (data->have_cdb)
204         cdb_free(&data->cdb);
205     if (data->cdb_fd != -1)
206         close(data->cdb_fd);
207 }
208
209 #endif /* HAVE_CDB */