xref: /freebsd/crypto/krb5/src/lib/kadm5/srv/pwqual_dict.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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