2 * Check a CDB database for a password or some simple permutations.
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.
11 * Written by Russ Allbery <eagle@eyrie.org>
13 * The Board of Trustees of the Leland Stanford Junior University
15 * SPDX-License-Identifier: MIT
19 #include <portable/kadmin.h>
20 #include <portable/krb5.h>
21 #include <portable/system.h>
30 #include <plugin/internal.h>
31 #include <util/macros.h>
35 * Stub for strength_init_cdb if not built with CDB support.
39 strength_init_cdb(krb5_context ctx, krb5_pwqual_moddata data UNUSED)
43 /* Get CDB dictionary path from krb5.conf. */
44 strength_config_string(ctx, "password_dictionary_cdb", &path);
46 /* If it was set, report an error, since we don't have CDB support. */
50 krb5_set_error_message(ctx, KADM5_BAD_SERVER_PARAMS,
51 "CDB dictionary requested but not built with CDB"
53 return KADM5_BAD_SERVER_PARAMS;
58 /* Skip the rest of this file if CDB is not available. */
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.
66 static krb5_error_code
67 in_cdb_dictionary(krb5_context ctx, krb5_pwqual_moddata data,
68 const char *password, bool *found)
73 status = cdb_find(&data->cdb, password, (unsigned int) strlen(password));
75 return strength_error_system(ctx, "cannot query CDB database");
77 *found = (status == 1);
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
90 strength_init_cdb(krb5_context ctx, krb5_pwqual_moddata data)
95 /* Get CDB dictionary path from krb5.conf. */
96 strength_config_string(ctx, "password_dictionary_cdb", &path);
98 /* If there is no configured dictionary, nothing to do. */
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);
114 data->have_cdb = true;
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.
124 # define CHECK_PASSWORD(ctx, data, password) \
126 code = in_cdb_dictionary(ctx, data, password, &found); \
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
141 strength_check_cdb(krb5_context ctx, krb5_pwqual_moddata data,
142 const char *password)
144 krb5_error_code code;
146 char *variant = NULL;
148 /* If we have no dictionary, there is nothing to do. */
152 /* Check the basic password. */
153 CHECK_PASSWORD(ctx, data, password);
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);
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.
166 if (strlen(password) > 0) {
167 variant = strdup(password);
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);
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);
182 /* Password not found. */
187 /* We found the password or a variant in the dictionary. */
189 return strength_error_dict(ctx, ERROR_DICT);
192 /* Some sort of failure during CDB lookup. */
199 * Free internal TinyCDB state and close the CDB dictionary.
202 strength_close_cdb(krb5_context ctx UNUSED, krb5_pwqual_moddata data)
205 cdb_free(&data->cdb);
206 if (data->cdb_fd != -1)
210 #endif /* HAVE_CDB */