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