aboutsummaryrefslogtreecommitdiff
path: root/src/lib/kadm5/srv/pwqual_dict.c
blob: dddd7f5f0585b0599960104a7807676e61999448 (plain)
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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
 *
 * $Header$
 */

/* Password quality module to look up paswords within the realm dictionary. */

#if !defined(lint) && !defined(__CODECENTER__)
static char *rcsid = "$Header$";
#endif

#include "k5-platform.h"
#include <krb5/pwqual_plugin.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <kadm5/admin.h>
#include "adm_proto.h"
#include <syslog.h>
#include "server_internal.h"

typedef struct dict_moddata_st {
    char **word_list;        /* list of word pointers */
    char *word_block;        /* actual word data */
    unsigned int word_count; /* number of words */
} *dict_moddata;


/*
 * Function: word_compare
 *
 * Purpose: compare two words in the dictionary.
 *
 * Arguments:
 *      w1              (input) pointer to first word
 *      w2              (input) pointer to second word
 *      <return value>  result of strcmp
 *
 * Requires:
 *      w1 and w2 to point to valid memory
 *
 */

static int
word_compare(const void *s1, const void *s2)
{
    return (strcasecmp(*(const char **)s1, *(const char **)s2));
}

/*
 * Function: init-dict
 *
 * Purpose: Initialize in memory word dictionary
 *
 * Arguments:
 *          none
 *          <return value> KADM5_OK on success errno on failure;
 *                         (but success on ENOENT)
 *
 * Requires:
 *      If WORDFILE exists, it must contain a list of words,
 *      one word per-line.
 *
 * Effects:
 *      If WORDFILE exists, it is read into memory sorted for future
 * use.  If it does not exist, it syslogs an error message and returns
 * success.
 *
 * Modifies:
 *      word_list to point to a chunck of allocated memory containing
 *      pointers to words
 *      word_block to contain the dictionary.
 *
 */

static int
init_dict(dict_moddata dict, const char *dict_file)
{
    int fd;
    size_t len, i;
    char *p, *t;
    struct stat sb;

    if (dict_file == NULL) {
        krb5_klog_syslog(LOG_INFO, "No dictionary file specified, continuing "
                         "without one.");
        return KADM5_OK;
    }
    if ((fd = open(dict_file, O_RDONLY)) == -1) {
        if (errno == ENOENT) {
            krb5_klog_syslog(LOG_ERR,
                             "WARNING!  Cannot find dictionary file %s, "
                             "continuing without one.", dict_file);
            return KADM5_OK;
        } else
            return errno;
    }
    set_cloexec_fd(fd);
    if (fstat(fd, &sb) == -1) {
        close(fd);
        return errno;
    }
    if ((dict->word_block = malloc(sb.st_size + 1)) == NULL)
        return ENOMEM;
    if (read(fd, dict->word_block, sb.st_size) != sb.st_size)
        return errno;
    (void) close(fd);
    dict->word_block[sb.st_size] = '\0';

    p = dict->word_block;
    len = sb.st_size;
    while(len > 0 && (t = memchr(p, '\n', len)) != NULL) {
        *t = '\0';
        len -= t - p + 1;
        p = t + 1;
        dict->word_count++;
    }
    if ((dict->word_list = malloc(dict->word_count * sizeof(char *))) == NULL)
        return ENOMEM;
    p = dict->word_block;
    for (i = 0; i < dict->word_count; i++) {
        dict->word_list[i] = p;
        p += strlen(p) + 1;
    }
    qsort(dict->word_list, dict->word_count, sizeof(char *), word_compare);
    return KADM5_OK;
}

/*
 * Function: destroy_dict
 *
 * Purpose: destroy in-core copy of dictionary.
 *
 * Arguments:
 *          none
 *          <return value>  none
 * Requires:
 *          nothing
 * Effects:
 *      frees up memory occupied by word_list and word_block
 *      sets count back to 0, and resets the pointers to NULL
 *
 * Modifies:
 *      word_list, word_block, and word_count.
 *
 */

static void
destroy_dict(dict_moddata dict)
{
    if (dict == NULL)
        return;
    free(dict->word_list);
    free(dict->word_block);
    free(dict);
    return;
}

/* Implement the password quality open method by reading in dict_file. */
static krb5_error_code
dict_open(krb5_context context, const char *dict_file,
          krb5_pwqual_moddata *data)
{
    krb5_error_code ret;
    dict_moddata dict;

    *data = NULL;

    /* Allocate and initialize a dictionary structure. */
    dict = malloc(sizeof(*dict));
    if (dict == NULL)
        return ENOMEM;
    dict->word_list = NULL;
    dict->word_block = NULL;
    dict->word_count = 0;

    /* Fill in the dictionary structure with data from dict_file. */
    ret = init_dict(dict, dict_file);
    if (ret != 0) {
        destroy_dict(dict);
        return ret;
    }

    *data = (krb5_pwqual_moddata)dict;
    return 0;
}

/* Implement the password quality check method by checking the password
 * against the dictionary, as well as against principal components. */
static krb5_error_code
dict_check(krb5_context context, krb5_pwqual_moddata data,
           const char *password, kadm5_policy_ent_t policy,
           krb5_principal princ)
{
    dict_moddata dict = (dict_moddata)data;
    int i, n;
    char *cp;

    /* Don't check the dictionary for principals with no password policy. */
    if (policy == NULL)
        return 0;

    /* Check against words in the dictionary if we successfully loaded one. */
    if (dict->word_list != NULL &&
        bsearch(&password, dict->word_list, dict->word_count, sizeof(char *),
                word_compare) != NULL)
        return KADM5_PASS_Q_DICT;

    /* Check against components of the principal. */
    n = krb5_princ_size(handle->context, princ);
    cp = krb5_princ_realm(handle->context, princ)->data;
    if (strcasecmp(cp, password) == 0)
	return KADM5_PASS_Q_DICT;
    for (i = 0; i < n; i++) {
	cp = krb5_princ_component(handle->context, princ, i)->data;
	if (strcasecmp(cp, password) == 0)
	    return KADM5_PASS_Q_DICT;
    }
    return 0;
}

/* Implement the password quality close method. */
static void
dict_close(krb5_context context, krb5_pwqual_moddata data)
{
    destroy_dict((dict_moddata)data);
}

krb5_error_code
pwqual_dict_init(krb5_context context, int maj_ver, int min_ver,
                 krb5_plugin_vtable vtable)
{
    krb5_pwqual_vtable vt;

    if (maj_ver != 1)
        return EINVAL; /* XXX create error code */
    vt = (krb5_pwqual_vtable)vtable;
    vt->open = dict_open;
    vt->check = dict_check;
    vt->close = dict_close;
    return 0;
}