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 * See LICENSE for licensing terms.
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, "CDB dictionary"
51 " requested but not built with CDB support");
52 return KADM5_BAD_SERVER_PARAMS;
57 /* Skip the rest of this file if CDB is not available. */
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.
65 # define CHECK_PASSWORD(ctx, data, password) \
67 code = in_cdb_dictionary(ctx, data, password, &found); \
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.
80 static krb5_error_code
81 in_cdb_dictionary(krb5_context ctx, krb5_pwqual_moddata data,
82 const char *password, bool *found)
87 status = cdb_find(&data->cdb, password, strlen(password));
89 return strength_error_system(ctx, "cannot query CDB database");
91 *found = (status == 1);
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
104 strength_init_cdb(krb5_context ctx, krb5_pwqual_moddata data)
106 krb5_error_code code;
109 /* Get CDB dictionary path from krb5.conf. */
110 strength_config_string(ctx, "password_dictionary_cdb", &path);
112 /* If there is no configured dictionary, nothing to do. */
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);
128 data->have_cdb = true;
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
140 strength_check_cdb(krb5_context ctx, krb5_pwqual_moddata data,
141 const char *password)
143 krb5_error_code code;
145 char *variant = NULL;
147 /* If we have no dictionary, there is nothing to do. */
151 /* Check the basic password. */
152 CHECK_PASSWORD(ctx, data, password);
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);
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.
165 if (strlen(password) > 0) {
166 variant = strdup(password);
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);
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);
181 /* Password not found. */
186 /* We found the password or a variant in the dictionary. */
188 return strength_error_dict(ctx, ERROR_DICT);
191 /* Some sort of failure during CDB lookup. */
198 * Free internal TinyCDB state and close the CDB dictionary.
201 strength_close_cdb(krb5_context ctx UNUSED, krb5_pwqual_moddata data)
204 cdb_free(&data->cdb);
205 if (data->cdb_fd != -1)
209 #endif /* HAVE_CDB */