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 <rra@stanford.edu>
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>
28 #include <plugin/api.h>
29 #include <util/macros.h>
31 /* Skip the rest of this file if CDB is not available. */
35 * Macro used to make password checks more readable. Assumes that the found
36 * and fail labels are available for the abort cases of finding a password or
37 * failing to look it up.
39 # define CHECK_PASSWORD(ctx, data, password) \
41 code = in_cdb_dictionary(ctx, data, password, &found); \
50 * Look up a password in CDB and set the found parameter to true if it is
51 * found, false otherwise. Returns a Kerberos status code, which will be 0 on
52 * success and something else on failure.
54 static krb5_error_code
55 in_cdb_dictionary(krb5_context ctx, krb5_pwqual_moddata data,
56 const char *password, bool *found)
60 status = cdb_find(&data->cdb, password, strlen(password));
63 krb5_set_error_message(ctx, oerrno, "cannot query CDB database: %s",
67 *found = (status == 1);
74 * Given a password, try the various transformations that we want to apply and
75 * check for each of them in the dictionary. Returns a Kerberos status code,
76 * which will be KADM5_PASS_Q_DICT if the password was found in the
80 pwcheck_check_cdb(krb5_context ctx, krb5_pwqual_moddata data,
88 /* Check the basic password. */
89 CHECK_PASSWORD(ctx, data, password);
91 /* Check with one or two characters removed from the start. */
92 if (password[0] != '\0') {
93 CHECK_PASSWORD(ctx, data, password + 1);
94 if (password[1] != '\0')
95 CHECK_PASSWORD(ctx, data, password + 2);
99 * Strip a character from the end and then check both that password and
100 * the one with a character taken from the start as well.
102 if (strlen(password) > 0) {
103 variant = strdup(password);
104 if (variant == NULL) {
106 krb5_set_error_message(ctx, oerrno, "cannot allocate memory: %s",
110 variant[strlen(variant) - 1] = '\0';
111 CHECK_PASSWORD(ctx, data, variant);
112 if (variant[0] != '\0')
113 CHECK_PASSWORD(ctx, data, variant + 1);
115 /* Check the password with two characters removed. */
116 if (strlen(password) > 1) {
117 variant[strlen(variant) - 1] = '\0';
118 CHECK_PASSWORD(ctx, data, variant);
123 /* Password not found. */
127 /* We found the password or a variant in the dictionary. */
130 krb5_set_error_message(ctx, KADM5_PASS_Q_DICT,
131 "it is based on a dictionary word");
132 return KADM5_PASS_Q_DICT;
135 /* Some sort of failure during CDB lookup. */
143 * Free internal TinyCDB state and close the CDB dictionary.
146 pwcheck_close_cdb(krb5_context ctx UNUSED, krb5_pwqual_moddata data)
149 cdb_free(&data->cdb);
150 if (data->cdb_fd != -1)
154 #endif /* HAVE_CDB */