]> eyrie.org Git - kerberos/krb5-strength.git/blob - plugin/cdb.c
Add support for checking against a CDB database
[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 <rra@stanford.edu>
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
28 #include <plugin/api.h>
29 #include <util/macros.h>
30
31 /* Skip the rest of this file if CDB is not available. */
32 #ifdef HAVE_CDB
33
34 /*
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.
38  */
39 # define CHECK_PASSWORD(ctx, data, password)                    \
40     do {                                                        \
41         code = in_cdb_dictionary(ctx, data, password, &found);  \
42         if (code != 0)                                          \
43             goto fail;                                          \
44         if (found)                                              \
45             goto found;                                         \
46     } while (0)
47
48
49 /*
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.
53  */
54 static krb5_error_code
55 in_cdb_dictionary(krb5_context ctx, krb5_pwqual_moddata data,
56                   const char *password, bool *found)
57 {
58     int status, oerrno;
59
60     status = cdb_find(&data->cdb, password, strlen(password));
61     if (status < 0) {
62         oerrno = errno;
63         krb5_set_error_message(ctx, oerrno, "cannot query CDB database: %s",
64                                strerror(oerrno));
65         return oerrno;
66     } else {
67         *found = (status == 1);
68         return 0;
69     }
70 }
71
72
73 /*
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
77  * dictionary.
78  */
79 krb5_error_code
80 pwcheck_check_cdb(krb5_context ctx, krb5_pwqual_moddata data,
81                   const char *password)
82 {
83     krb5_error_code code;
84     bool found;
85     char *variant = NULL;
86     int oerrno;
87
88     /* Check the basic password. */
89     CHECK_PASSWORD(ctx, data, password);
90
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);
96     }
97
98     /*
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.
101      */
102     if (strlen(password) > 0) {
103         variant = strdup(password);
104         if (variant == NULL) {
105             oerrno = errno;
106             krb5_set_error_message(ctx, oerrno, "cannot allocate memory: %s",
107                                    strerror(oerrno));
108             return oerrno;
109         }
110         variant[strlen(variant) - 1] = '\0';
111         CHECK_PASSWORD(ctx, data, variant);
112         if (variant[0] != '\0')
113             CHECK_PASSWORD(ctx, data, variant + 1);
114
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);
119         }
120         free(variant);
121     }
122
123     /* Password not found. */
124     return 0;
125
126 found:
127     /* We found the password or a variant in the dictionary. */
128     if (variant != NULL)
129         free(variant);
130     krb5_set_error_message(ctx, KADM5_PASS_Q_DICT,
131                            "it is based on a dictionary word");
132     return KADM5_PASS_Q_DICT;
133
134 fail:
135     /* Some sort of failure during CDB lookup. */
136     if (variant != NULL)
137         free(variant);
138     return code;
139 }
140
141
142 /*
143  * Free internal TinyCDB state and close the CDB dictionary.
144  */
145 void
146 pwcheck_close_cdb(krb5_context ctx UNUSED, krb5_pwqual_moddata data)
147 {
148     if (data->have_cdb)
149         cdb_free(&data->cdb);
150     if (data->cdb_fd != -1)
151         close(data->cdb_fd);
152 }
153
154 #endif /* HAVE_CDB */