]> eyrie.org Git - kerberos/krb5-strength.git/blob - plugin/cdb.c
Update package installations for CI
[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  * SPDX-License-Identifier: MIT
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,
51                            "CDB dictionary requested but not built with CDB"
52                            " support");
53     return KADM5_BAD_SERVER_PARAMS;
54 }
55 #endif
56
57
58 /* Skip the rest of this file if CDB is not available. */
59 #ifdef HAVE_CDB
60
61 /*
62  * Look up a password in CDB and set the found parameter to true if it is
63  * found, false otherwise.  Returns a Kerberos status code, which will be 0 on
64  * success and something else on failure.
65  */
66 static krb5_error_code
67 in_cdb_dictionary(krb5_context ctx, krb5_pwqual_moddata data,
68                   const char *password, bool *found)
69 {
70     int status;
71
72     *found = false;
73     status = cdb_find(&data->cdb, password, (unsigned int) strlen(password));
74     if (status < 0)
75         return strength_error_system(ctx, "cannot query CDB database");
76     else {
77         *found = (status == 1);
78         return 0;
79     }
80 }
81
82
83 /*
84  * Initialize the CDB dictionary.  Opens the dictionary and sets up the
85  * TinyCDB state.  Returns 0 on success, non-zero on failure (and sets the
86  * error in the Kerberos context).  If not built with CDB support, always
87  * returns an error.
88  */
89 krb5_error_code
90 strength_init_cdb(krb5_context ctx, krb5_pwqual_moddata data)
91 {
92     krb5_error_code code;
93     char *path = NULL;
94
95     /* Get CDB dictionary path from krb5.conf. */
96     strength_config_string(ctx, "password_dictionary_cdb", &path);
97
98     /* If there is no configured dictionary, nothing to do. */
99     if (path == NULL)
100         return 0;
101
102     /* Open the dictionary and initialize the CDB data. */
103     data->cdb_fd = open(path, O_RDONLY);
104     if (data->cdb_fd < 0)
105         return strength_error_system(ctx, "cannot open dictionary %s", path);
106     if (cdb_init(&data->cdb, data->cdb_fd) < 0) {
107         code = strength_error_system(ctx, "cannot init dictionary %s", path);
108         free(path);
109         close(data->cdb_fd);
110         data->cdb_fd = -1;
111         return code;
112     }
113     free(path);
114     data->have_cdb = true;
115     return 0;
116 }
117
118
119 /*
120  * Macro used to make password checks more readable.  Assumes that the found
121  * and fail labels are available for the abort cases of finding a password or
122  * failing to look it up.
123  */
124 #    define CHECK_PASSWORD(ctx, data, password)                    \
125         do {                                                       \
126             code = in_cdb_dictionary(ctx, data, password, &found); \
127             if (code != 0)                                         \
128                 goto fail;                                         \
129             if (found)                                             \
130                 goto found;                                        \
131         } while (0)
132
133
134 /*
135  * Given a password, try the various transformations that we want to apply and
136  * check for each of them in the dictionary.  Returns a Kerberos status code,
137  * which will be KADM5_PASS_Q_DICT if the password was found in the
138  * dictionary.
139  */
140 krb5_error_code
141 strength_check_cdb(krb5_context ctx, krb5_pwqual_moddata data,
142                    const char *password)
143 {
144     krb5_error_code code;
145     bool found;
146     char *variant = NULL;
147
148     /* If we have no dictionary, there is nothing to do. */
149     if (!data->have_cdb)
150         return 0;
151
152     /* Check the basic password. */
153     CHECK_PASSWORD(ctx, data, password);
154
155     /* Check with one or two characters removed from the start. */
156     if (password[0] != '\0') {
157         CHECK_PASSWORD(ctx, data, password + 1);
158         if (password[1] != '\0')
159             CHECK_PASSWORD(ctx, data, password + 2);
160     }
161
162     /*
163      * Strip a character from the end and then check both that password and
164      * the one with a character taken from the start as well.
165      */
166     if (strlen(password) > 0) {
167         variant = strdup(password);
168         if (variant == NULL)
169             return strength_error_system(ctx, "cannot allocate memory");
170         variant[strlen(variant) - 1] = '\0';
171         CHECK_PASSWORD(ctx, data, variant);
172         if (variant[0] != '\0')
173             CHECK_PASSWORD(ctx, data, variant + 1);
174
175         /* Check the password with two characters removed. */
176         if (strlen(password) > 1) {
177             variant[strlen(variant) - 1] = '\0';
178             CHECK_PASSWORD(ctx, data, variant);
179         }
180     }
181
182     /* Password not found. */
183     free(variant);
184     return 0;
185
186 found:
187     /* We found the password or a variant in the dictionary. */
188     free(variant);
189     return strength_error_dict(ctx, ERROR_DICT);
190
191 fail:
192     /* Some sort of failure during CDB lookup. */
193     free(variant);
194     return code;
195 }
196
197
198 /*
199  * Free internal TinyCDB state and close the CDB dictionary.
200  */
201 void
202 strength_close_cdb(krb5_context ctx UNUSED, krb5_pwqual_moddata data)
203 {
204     if (data->have_cdb)
205         cdb_free(&data->cdb);
206     if (data->cdb_fd != -1)
207         close(data->cdb_fd);
208 }
209
210 #endif /* HAVE_CDB */