File: cdb.c

package info (click to toggle)
krb5-strength 3.0-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 2,668 kB
  • ctags: 876
  • sloc: sh: 11,907; ansic: 8,234; perl: 1,208; makefile: 167
file content (209 lines) | stat: -rw-r--r-- 6,177 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*
 * Check a CDB database for a password or some simple permutations.
 *
 * This file implements a much simpler variation on CrackLib checks intended
 * for use with longer passwords where some of the CrackLib permutations don't
 * make as much sense.  A CDB database with passwords as keys is checked for
 * the password and for variations with one character removed from the start
 * or end, two characters removed from the start, two from the end, or one
 * character from both start and end.
 *
 * Written by Russ Allbery <eagle@eyrie.org>
 * Copyright 2013
 *     The Board of Trustees of the Leland Stanford Junior University
 *
 * See LICENSE for licensing terms.
 */

#include <config.h>
#include <portable/kadmin.h>
#include <portable/krb5.h>
#include <portable/system.h>

#ifdef HAVE_CDB_H
# include <cdb.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>

#include <plugin/internal.h>
#include <util/macros.h>


/*
 * Stub for strength_init_cdb if not built with CDB support.
 */
#ifndef HAVE_CDB
krb5_error_code
strength_init_cdb(krb5_context ctx, krb5_pwqual_moddata data UNUSED)
{
    char *path = NULL;

    /* Get CDB dictionary path from krb5.conf. */
    strength_config_string(ctx, "password_dictionary_cdb", &path);

    /* If it was set, report an error, since we don't have CDB support. */
    if (path == NULL)
        return 0;
    free(path);
    krb5_set_error_message(ctx, KADM5_BAD_SERVER_PARAMS, "CDB dictionary"
                           " requested but not built with CDB support");
    return KADM5_BAD_SERVER_PARAMS;
}
#endif


/* Skip the rest of this file if CDB is not available. */
#ifdef HAVE_CDB

/*
 * Macro used to make password checks more readable.  Assumes that the found
 * and fail labels are available for the abort cases of finding a password or
 * failing to look it up.
 */
# define CHECK_PASSWORD(ctx, data, password)                    \
    do {                                                        \
        code = in_cdb_dictionary(ctx, data, password, &found);  \
        if (code != 0)                                          \
            goto fail;                                          \
        if (found)                                              \
            goto found;                                         \
    } while (0)


/*
 * Look up a password in CDB and set the found parameter to true if it is
 * found, false otherwise.  Returns a Kerberos status code, which will be 0 on
 * success and something else on failure.
 */
static krb5_error_code
in_cdb_dictionary(krb5_context ctx, krb5_pwqual_moddata data,
                  const char *password, bool *found)
{
    int status;

    *found = false;
    status = cdb_find(&data->cdb, password, strlen(password));
    if (status < 0)
        return strength_error_system(ctx, "cannot query CDB database");
    else {
        *found = (status == 1);
        return 0;
    }
}


/*
 * Initialize the CDB dictionary.  Opens the dictionary and sets up the
 * TinyCDB state.  Returns 0 on success, non-zero on failure (and sets the
 * error in the Kerberos context).  If not built with CDB support, always
 * returns an error.
 */
krb5_error_code
strength_init_cdb(krb5_context ctx, krb5_pwqual_moddata data)
{
    krb5_error_code code;
    char *path = NULL;

    /* Get CDB dictionary path from krb5.conf. */
    strength_config_string(ctx, "password_dictionary_cdb", &path);

    /* If there is no configured dictionary, nothing to do. */
    if (path == NULL)
        return 0;

    /* Open the dictionary and initialize the CDB data. */
    data->cdb_fd = open(path, O_RDONLY);
    if (data->cdb_fd < 0)
        return strength_error_system(ctx, "cannot open dictionary %s", path);
    if (cdb_init(&data->cdb, data->cdb_fd) < 0) {
        code = strength_error_system(ctx, "cannot init dictionary %s", path);
        free(path);
        close(data->cdb_fd);
        data->cdb_fd = -1;
        return code;
    }
    free(path);
    data->have_cdb = true;
    return 0;
}


/*
 * Given a password, try the various transformations that we want to apply and
 * check for each of them in the dictionary.  Returns a Kerberos status code,
 * which will be KADM5_PASS_Q_DICT if the password was found in the
 * dictionary.
 */
krb5_error_code
strength_check_cdb(krb5_context ctx, krb5_pwqual_moddata data,
                   const char *password)
{
    krb5_error_code code;
    bool found;
    char *variant = NULL;

    /* If we have no dictionary, there is nothing to do. */
    if (!data->have_cdb)
        return 0;

    /* Check the basic password. */
    CHECK_PASSWORD(ctx, data, password);

    /* Check with one or two characters removed from the start. */
    if (password[0] != '\0') {
        CHECK_PASSWORD(ctx, data, password + 1);
        if (password[1] != '\0')
            CHECK_PASSWORD(ctx, data, password + 2);
    }

    /*
     * Strip a character from the end and then check both that password and
     * the one with a character taken from the start as well.
     */
    if (strlen(password) > 0) {
        variant = strdup(password);
        if (variant == NULL)
            return strength_error_system(ctx, "cannot allocate memory");
        variant[strlen(variant) - 1] = '\0';
        CHECK_PASSWORD(ctx, data, variant);
        if (variant[0] != '\0')
            CHECK_PASSWORD(ctx, data, variant + 1);

        /* Check the password with two characters removed. */
        if (strlen(password) > 1) {
            variant[strlen(variant) - 1] = '\0';
            CHECK_PASSWORD(ctx, data, variant);
        }
    }

    /* Password not found. */
    free(variant);
    return 0;

found:
    /* We found the password or a variant in the dictionary. */
    free(variant);
    return strength_error_dict(ctx, ERROR_DICT);

fail:
    /* Some sort of failure during CDB lookup. */
    free(variant);
    return code;
}


/*
 * Free internal TinyCDB state and close the CDB dictionary.
 */
void
strength_close_cdb(krb5_context ctx UNUSED, krb5_pwqual_moddata data)
{
    if (data->have_cdb)
        cdb_free(&data->cdb);
    if (data->cdb_fd != -1)
        close(data->cdb_fd);
}

#endif /* HAVE_CDB */