1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/kadm5/srv/pwqual_dict.c */
3 /*
4 * Copyright (C) 2010 by the Massachusetts Institute of Technology.
5 * All rights reserved.
6 *
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
25 */
26 /*
27 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
28 */
29
30 /* Password quality module to look up passwords within the realm dictionary. */
31
32 #include "k5-platform.h"
33 #include <krb5/pwqual_plugin.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37 #include <kadm5/admin.h>
38 #include "adm_proto.h"
39 #include <syslog.h>
40 #include "server_internal.h"
41
42 typedef struct dict_moddata_st {
43 char **word_list; /* list of word pointers */
44 char *word_block; /* actual word data */
45 unsigned int word_count; /* number of words */
46 } *dict_moddata;
47
48
49 /*
50 * Function: word_compare
51 *
52 * Purpose: compare two words in the dictionary.
53 *
54 * Arguments:
55 * w1 (input) pointer to first word
56 * w2 (input) pointer to second word
57 * <return value> result of strcmp
58 *
59 * Requires:
60 * w1 and w2 to point to valid memory
61 *
62 */
63
64 static int
word_compare(const void * s1,const void * s2)65 word_compare(const void *s1, const void *s2)
66 {
67 return (strcasecmp(*(const char **)s1, *(const char **)s2));
68 }
69
70 /*
71 * Function: init-dict
72 *
73 * Purpose: Initialize in memory word dictionary
74 *
75 * Arguments:
76 * none
77 * <return value> KADM5_OK on success errno on failure;
78 * (but success on ENOENT)
79 *
80 * Requires:
81 * If WORDFILE exists, it must contain a list of words,
82 * one word per-line.
83 *
84 * Effects:
85 * If WORDFILE exists, it is read into memory sorted for future
86 * use. If it does not exist, it syslogs an error message and returns
87 * success.
88 *
89 * Modifies:
90 * word_list to point to a chunk of allocated memory containing
91 * pointers to words
92 * word_block to contain the dictionary.
93 *
94 */
95
96 static int
init_dict(dict_moddata dict,const char * dict_file)97 init_dict(dict_moddata dict, const char *dict_file)
98 {
99 int fd;
100 size_t len, i;
101 char *p, *t;
102 struct stat sb;
103
104 if (dict_file == NULL) {
105 krb5_klog_syslog(LOG_INFO,
106 _("No dictionary file specified, continuing without "
107 "one."));
108 return KADM5_OK;
109 }
110 if ((fd = open(dict_file, O_RDONLY)) == -1) {
111 if (errno == ENOENT) {
112 krb5_klog_syslog(LOG_ERR,
113 _("WARNING! Cannot find dictionary file %s, "
114 "continuing without one."), dict_file);
115 return KADM5_OK;
116 } else
117 return errno;
118 }
119 set_cloexec_fd(fd);
120 if (fstat(fd, &sb) == -1) {
121 close(fd);
122 return errno;
123 }
124 dict->word_block = malloc(sb.st_size + 1);
125 if (dict->word_block == NULL) {
126 (void)close(fd);
127 return ENOMEM;
128 }
129 if (read(fd, dict->word_block, sb.st_size) != sb.st_size) {
130 (void)close(fd);
131 return errno;
132 }
133 (void)close(fd);
134 dict->word_block[sb.st_size] = '\0';
135
136 p = dict->word_block;
137 len = sb.st_size;
138 while(len > 0 && (t = memchr(p, '\n', len)) != NULL) {
139 *t = '\0';
140 len -= t - p + 1;
141 p = t + 1;
142 dict->word_count++;
143 }
144 if ((dict->word_list = malloc(dict->word_count * sizeof(char *))) == NULL)
145 return ENOMEM;
146 p = dict->word_block;
147 for (i = 0; i < dict->word_count; i++) {
148 dict->word_list[i] = p;
149 p += strlen(p) + 1;
150 }
151 qsort(dict->word_list, dict->word_count, sizeof(char *), word_compare);
152 return KADM5_OK;
153 }
154
155 /*
156 * Function: destroy_dict
157 *
158 * Purpose: destroy in-core copy of dictionary.
159 *
160 * Arguments:
161 * none
162 * <return value> none
163 * Requires:
164 * nothing
165 * Effects:
166 * frees up memory occupied by word_list and word_block
167 * sets count back to 0, and resets the pointers to NULL
168 *
169 * Modifies:
170 * word_list, word_block, and word_count.
171 *
172 */
173
174 static void
destroy_dict(dict_moddata dict)175 destroy_dict(dict_moddata dict)
176 {
177 if (dict == NULL)
178 return;
179 free(dict->word_list);
180 free(dict->word_block);
181 free(dict);
182 return;
183 }
184
185 /* Implement the password quality open method by reading in dict_file. */
186 static krb5_error_code
dict_open(krb5_context context,const char * dict_file,krb5_pwqual_moddata * data)187 dict_open(krb5_context context, const char *dict_file,
188 krb5_pwqual_moddata *data)
189 {
190 krb5_error_code ret;
191 dict_moddata dict;
192
193 *data = NULL;
194
195 /* Allocate and initialize a dictionary structure. */
196 dict = malloc(sizeof(*dict));
197 if (dict == NULL)
198 return ENOMEM;
199 dict->word_list = NULL;
200 dict->word_block = NULL;
201 dict->word_count = 0;
202
203 /* Fill in the dictionary structure with data from dict_file. */
204 ret = init_dict(dict, dict_file);
205 if (ret != 0) {
206 destroy_dict(dict);
207 return ret;
208 }
209
210 *data = (krb5_pwqual_moddata)dict;
211 return 0;
212 }
213
214 /* Implement the password quality check method by checking the password
215 * against the dictionary, as well as against principal components. */
216 static krb5_error_code
dict_check(krb5_context context,krb5_pwqual_moddata data,const char * password,const char * policy_name,krb5_principal princ,const char ** languages)217 dict_check(krb5_context context, krb5_pwqual_moddata data,
218 const char *password, const char *policy_name,
219 krb5_principal princ, const char **languages)
220 {
221 dict_moddata dict = (dict_moddata)data;
222
223 /* Don't check the dictionary for principals with no password policy. */
224 if (policy_name == NULL)
225 return 0;
226
227 /* Check against words in the dictionary if we successfully loaded one. */
228 if (dict->word_list != NULL &&
229 bsearch(&password, dict->word_list, dict->word_count, sizeof(char *),
230 word_compare) != NULL)
231 return KADM5_PASS_Q_DICT;
232
233 return 0;
234 }
235
236 /* Implement the password quality close method. */
237 static void
dict_close(krb5_context context,krb5_pwqual_moddata data)238 dict_close(krb5_context context, krb5_pwqual_moddata data)
239 {
240 destroy_dict((dict_moddata)data);
241 }
242
243 krb5_error_code
pwqual_dict_initvt(krb5_context context,int maj_ver,int min_ver,krb5_plugin_vtable vtable)244 pwqual_dict_initvt(krb5_context context, int maj_ver, int min_ver,
245 krb5_plugin_vtable vtable)
246 {
247 krb5_pwqual_vtable vt;
248
249 if (maj_ver != 1)
250 return KRB5_PLUGIN_VER_NOTSUPP;
251 vt = (krb5_pwqual_vtable)vtable;
252 vt->name = "dict";
253 vt->open = dict_open;
254 vt->check = dict_check;
255 vt->close = dict_close;
256 return 0;
257 }
258