xref: /freebsd/crypto/heimdal/lib/kadm5/password_quality.c (revision c19800e8cd5640693f36f2040db4ab5e8d738146)
1b528cefcSMark Murray /*
2c19800e8SDoug Rabson  * Copyright (c) 1997-2000, 2003-2005 Kungliga Tekniska H�gskolan
3b528cefcSMark Murray  * (Royal Institute of Technology, Stockholm, Sweden).
4b528cefcSMark Murray  * All rights reserved.
5b528cefcSMark Murray  *
6b528cefcSMark Murray  * Redistribution and use in source and binary forms, with or without
7b528cefcSMark Murray  * modification, are permitted provided that the following conditions
8b528cefcSMark Murray  * are met:
9b528cefcSMark Murray  *
10b528cefcSMark Murray  * 1. Redistributions of source code must retain the above copyright
11b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer.
12b528cefcSMark Murray  *
13b528cefcSMark Murray  * 2. Redistributions in binary form must reproduce the above copyright
14b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer in the
15b528cefcSMark Murray  *    documentation and/or other materials provided with the distribution.
16b528cefcSMark Murray  *
17b528cefcSMark Murray  * 3. Neither the name of the Institute nor the names of its contributors
18b528cefcSMark Murray  *    may be used to endorse or promote products derived from this software
19b528cefcSMark Murray  *    without specific prior written permission.
20b528cefcSMark Murray  *
21b528cefcSMark Murray  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22b528cefcSMark Murray  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23b528cefcSMark Murray  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24b528cefcSMark Murray  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25b528cefcSMark Murray  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26b528cefcSMark Murray  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27b528cefcSMark Murray  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28b528cefcSMark Murray  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29b528cefcSMark Murray  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30b528cefcSMark Murray  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31b528cefcSMark Murray  * SUCH DAMAGE.
32b528cefcSMark Murray  */
33b528cefcSMark Murray 
34b528cefcSMark Murray #include "kadm5_locl.h"
35c19800e8SDoug Rabson #include "kadm5-pwcheck.h"
36b528cefcSMark Murray 
37c19800e8SDoug Rabson RCSID("$Id: password_quality.c 17595 2006-05-30 21:51:55Z lha $");
38b528cefcSMark Murray 
39c19800e8SDoug Rabson #ifdef HAVE_SYS_WAIT_H
40c19800e8SDoug Rabson #include <sys/wait.h>
41c19800e8SDoug Rabson #endif
42b528cefcSMark Murray #ifdef HAVE_DLFCN_H
43b528cefcSMark Murray #include <dlfcn.h>
44b528cefcSMark Murray #endif
45b528cefcSMark Murray 
46c19800e8SDoug Rabson static int
47c19800e8SDoug Rabson min_length_passwd_quality (krb5_context context,
48c19800e8SDoug Rabson 			   krb5_principal principal,
49c19800e8SDoug Rabson 			   krb5_data *pwd,
50c19800e8SDoug Rabson 			   const char *opaque,
51c19800e8SDoug Rabson 			   char *message,
52c19800e8SDoug Rabson 			   size_t length)
53c19800e8SDoug Rabson {
54c19800e8SDoug Rabson     uint32_t min_length = krb5_config_get_int_default(context, NULL, 6,
55c19800e8SDoug Rabson 						      "password_quality",
56c19800e8SDoug Rabson 						      "min_length",
57c19800e8SDoug Rabson 						      NULL);
58c19800e8SDoug Rabson 
59c19800e8SDoug Rabson     if (pwd->length < min_length) {
60c19800e8SDoug Rabson 	strlcpy(message, "Password too short", length);
61c19800e8SDoug Rabson 	return 1;
62c19800e8SDoug Rabson     } else
63c19800e8SDoug Rabson 	return 0;
64c19800e8SDoug Rabson }
65c19800e8SDoug Rabson 
66b528cefcSMark Murray static const char *
67c19800e8SDoug Rabson min_length_passwd_quality_v0 (krb5_context context,
68b528cefcSMark Murray 			      krb5_principal principal,
69b528cefcSMark Murray 			      krb5_data *pwd)
70b528cefcSMark Murray {
71c19800e8SDoug Rabson     static char message[1024];
72c19800e8SDoug Rabson     int ret;
73c19800e8SDoug Rabson 
74c19800e8SDoug Rabson     message[0] = '\0';
75c19800e8SDoug Rabson 
76c19800e8SDoug Rabson     ret = min_length_passwd_quality(context, principal, pwd, NULL,
77c19800e8SDoug Rabson 				    message, sizeof(message));
78c19800e8SDoug Rabson     if (ret)
79c19800e8SDoug Rabson 	return message;
80b528cefcSMark Murray     return NULL;
81b528cefcSMark Murray }
82b528cefcSMark Murray 
83b528cefcSMark Murray 
84c19800e8SDoug Rabson static int
85c19800e8SDoug Rabson char_class_passwd_quality (krb5_context context,
86c19800e8SDoug Rabson 			   krb5_principal principal,
87c19800e8SDoug Rabson 			   krb5_data *pwd,
88c19800e8SDoug Rabson 			   const char *opaque,
89c19800e8SDoug Rabson 			   char *message,
90c19800e8SDoug Rabson 			   size_t length)
91c19800e8SDoug Rabson {
92c19800e8SDoug Rabson     const char *classes[] = {
93c19800e8SDoug Rabson 	"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
94c19800e8SDoug Rabson 	"abcdefghijklmnopqrstuvwxyz",
95c19800e8SDoug Rabson 	"1234567890",
96c19800e8SDoug Rabson 	"!@#$%^&*()/?<>,.{[]}\\|'~`\" "
97c19800e8SDoug Rabson     };
98c19800e8SDoug Rabson     int i, counter = 0, req_classes;
99c19800e8SDoug Rabson     size_t len;
100c19800e8SDoug Rabson     char *pw;
101b528cefcSMark Murray 
102c19800e8SDoug Rabson     req_classes = krb5_config_get_int_default(context, NULL, 3,
103c19800e8SDoug Rabson 					      "password_quality",
104c19800e8SDoug Rabson 					      "min_classes",
105c19800e8SDoug Rabson 					      NULL);
106b528cefcSMark Murray 
107c19800e8SDoug Rabson     len = pwd->length + 1;
108c19800e8SDoug Rabson     pw = malloc(len);
109c19800e8SDoug Rabson     if (pw == NULL) {
110c19800e8SDoug Rabson 	strlcpy(message, "out of memory", length);
111c19800e8SDoug Rabson 	return 1;
112c19800e8SDoug Rabson     }
113c19800e8SDoug Rabson     strlcpy(pw, pwd->data, len);
114c19800e8SDoug Rabson     len = strlen(pw);
115b528cefcSMark Murray 
116c19800e8SDoug Rabson     for (i = 0; i < sizeof(classes)/sizeof(classes[0]); i++) {
117c19800e8SDoug Rabson 	if (strcspn(pw, classes[i]) < len)
118c19800e8SDoug Rabson 	    counter++;
119c19800e8SDoug Rabson     }
120c19800e8SDoug Rabson     memset(pw, 0, pwd->length + 1);
121c19800e8SDoug Rabson     free(pw);
122c19800e8SDoug Rabson     if (counter < req_classes) {
123c19800e8SDoug Rabson 	snprintf(message, length,
124c19800e8SDoug Rabson 	    "Password doesn't meet complexity requirement.\n"
125c19800e8SDoug Rabson 	    "Add more characters from the following classes:\n"
126c19800e8SDoug Rabson 	    "1. English uppercase characters (A through Z)\n"
127c19800e8SDoug Rabson 	    "2. English lowercase characters (a through z)\n"
128c19800e8SDoug Rabson 	    "3. Base 10 digits (0 through 9)\n"
129c19800e8SDoug Rabson 	    "4. Nonalphanumeric characters (e.g., !, $, #, %%)");
130c19800e8SDoug Rabson 	return 1;
131c19800e8SDoug Rabson     }
132c19800e8SDoug Rabson     return 0;
133c19800e8SDoug Rabson }
134c19800e8SDoug Rabson 
135c19800e8SDoug Rabson static int
136c19800e8SDoug Rabson external_passwd_quality (krb5_context context,
137c19800e8SDoug Rabson 			 krb5_principal principal,
138c19800e8SDoug Rabson 			 krb5_data *pwd,
139c19800e8SDoug Rabson 			 const char *opaque,
140c19800e8SDoug Rabson 			 char *message,
141c19800e8SDoug Rabson 			 size_t length)
142c19800e8SDoug Rabson {
143c19800e8SDoug Rabson     krb5_error_code ret;
144c19800e8SDoug Rabson     const char *program;
145c19800e8SDoug Rabson     char *p;
146c19800e8SDoug Rabson     pid_t child;
147c19800e8SDoug Rabson     int status;
148c19800e8SDoug Rabson     char reply[1024];
149c19800e8SDoug Rabson     FILE *in = NULL, *out = NULL, *error = NULL;
150c19800e8SDoug Rabson 
151c19800e8SDoug Rabson     if (memchr(pwd->data, pwd->length, '\n') != NULL) {
152c19800e8SDoug Rabson 	snprintf(message, length, "password contains newline, "
153c19800e8SDoug Rabson 		 "not valid for external test");
154c19800e8SDoug Rabson 	return 1;
155c19800e8SDoug Rabson     }
156c19800e8SDoug Rabson 
157c19800e8SDoug Rabson     program = krb5_config_get_string(context, NULL,
158c19800e8SDoug Rabson 				     "password_quality",
159c19800e8SDoug Rabson 				     "external_program",
160c19800e8SDoug Rabson 				     NULL);
161c19800e8SDoug Rabson     if (program == NULL) {
162c19800e8SDoug Rabson 	snprintf(message, length, "external password quality "
163c19800e8SDoug Rabson 		 "program not configured");
164c19800e8SDoug Rabson 	return 1;
165c19800e8SDoug Rabson     }
166c19800e8SDoug Rabson 
167c19800e8SDoug Rabson     ret = krb5_unparse_name(context, principal, &p);
168c19800e8SDoug Rabson     if (ret) {
169c19800e8SDoug Rabson 	strlcpy(message, "out of memory", length);
170c19800e8SDoug Rabson 	return 1;
171c19800e8SDoug Rabson     }
172c19800e8SDoug Rabson 
173c19800e8SDoug Rabson     child = pipe_execv(&in, &out, &error, program, p, NULL);
174c19800e8SDoug Rabson     if (child < 0) {
175c19800e8SDoug Rabson 	snprintf(message, length, "external password quality "
176c19800e8SDoug Rabson 		 "program failed to execute for principal %s", p);
177c19800e8SDoug Rabson 	free(p);
178c19800e8SDoug Rabson 	return 1;
179c19800e8SDoug Rabson     }
180c19800e8SDoug Rabson 
181c19800e8SDoug Rabson     fprintf(in, "principal: %s\n"
182c19800e8SDoug Rabson 	    "new-password: %.*s\n"
183c19800e8SDoug Rabson 	    "end\n",
184c19800e8SDoug Rabson 	    p, (int)pwd->length, (char *)pwd->data);
185c19800e8SDoug Rabson 
186c19800e8SDoug Rabson     fclose(in);
187c19800e8SDoug Rabson 
188c19800e8SDoug Rabson     if (fgets(reply, sizeof(reply), out) == NULL) {
189c19800e8SDoug Rabson 
190c19800e8SDoug Rabson 	if (fgets(reply, sizeof(reply), error) == NULL) {
191c19800e8SDoug Rabson 	    snprintf(message, length, "external password quality "
192c19800e8SDoug Rabson 		     "program failed without error");
193c19800e8SDoug Rabson 
194c19800e8SDoug Rabson 	} else {
195c19800e8SDoug Rabson 	    reply[strcspn(reply, "\n")] = '\0';
196c19800e8SDoug Rabson 	    snprintf(message, length, "External password quality "
197c19800e8SDoug Rabson 		     "program failed: %s", reply);
198c19800e8SDoug Rabson 	}
199c19800e8SDoug Rabson 
200c19800e8SDoug Rabson 	fclose(out);
201c19800e8SDoug Rabson 	fclose(error);
202c19800e8SDoug Rabson 	waitpid(child, &status, 0);
203c19800e8SDoug Rabson 	return 1;
204c19800e8SDoug Rabson     }
205c19800e8SDoug Rabson     reply[strcspn(reply, "\n")] = '\0';
206c19800e8SDoug Rabson 
207c19800e8SDoug Rabson     fclose(out);
208c19800e8SDoug Rabson     fclose(error);
209c19800e8SDoug Rabson 
210c19800e8SDoug Rabson     if (waitpid(child, &status, 0) < 0) {
211c19800e8SDoug Rabson 	snprintf(message, length, "external program failed: %s", reply);
212c19800e8SDoug Rabson 	free(p);
213c19800e8SDoug Rabson 	return 1;
214c19800e8SDoug Rabson     }
215c19800e8SDoug Rabson     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
216c19800e8SDoug Rabson 	snprintf(message, length, "external program failed: %s", reply);
217c19800e8SDoug Rabson 	free(p);
218c19800e8SDoug Rabson 	return 1;
219c19800e8SDoug Rabson     }
220c19800e8SDoug Rabson 
221c19800e8SDoug Rabson     if (strcmp(reply, "APPROVED") != 0) {
222c19800e8SDoug Rabson 	snprintf(message, length, "%s", reply);
223c19800e8SDoug Rabson 	free(p);
224c19800e8SDoug Rabson 	return 1;
225c19800e8SDoug Rabson     }
226c19800e8SDoug Rabson 
227c19800e8SDoug Rabson     free(p);
228c19800e8SDoug Rabson 
229c19800e8SDoug Rabson     return 0;
230c19800e8SDoug Rabson }
231c19800e8SDoug Rabson 
232c19800e8SDoug Rabson 
233c19800e8SDoug Rabson static kadm5_passwd_quality_check_func_v0 passwd_quality_check =
234c19800e8SDoug Rabson 	min_length_passwd_quality_v0;
235c19800e8SDoug Rabson 
236c19800e8SDoug Rabson struct kadm5_pw_policy_check_func builtin_funcs[] = {
237c19800e8SDoug Rabson     { "minimum-length", min_length_passwd_quality },
238c19800e8SDoug Rabson     { "character-class", char_class_passwd_quality },
239c19800e8SDoug Rabson     { "external-check", external_passwd_quality },
240c19800e8SDoug Rabson     { NULL }
241c19800e8SDoug Rabson };
242c19800e8SDoug Rabson struct kadm5_pw_policy_verifier builtin_verifier = {
243c19800e8SDoug Rabson     "builtin",
244c19800e8SDoug Rabson     KADM5_PASSWD_VERSION_V1,
245c19800e8SDoug Rabson     "Heimdal builtin",
246c19800e8SDoug Rabson     builtin_funcs
247c19800e8SDoug Rabson };
248c19800e8SDoug Rabson 
249c19800e8SDoug Rabson static struct kadm5_pw_policy_verifier **verifiers;
250c19800e8SDoug Rabson static int num_verifiers;
251b528cefcSMark Murray 
252b528cefcSMark Murray /*
253b528cefcSMark Murray  * setup the password quality hook
254b528cefcSMark Murray  */
255b528cefcSMark Murray 
256c19800e8SDoug Rabson #ifndef RTLD_NOW
257c19800e8SDoug Rabson #define RTLD_NOW 0
258c19800e8SDoug Rabson #endif
259c19800e8SDoug Rabson 
260b528cefcSMark Murray void
261b528cefcSMark Murray kadm5_setup_passwd_quality_check(krb5_context context,
262b528cefcSMark Murray 				 const char *check_library,
263b528cefcSMark Murray 				 const char *check_function)
264b528cefcSMark Murray {
265b528cefcSMark Murray #ifdef HAVE_DLOPEN
266b528cefcSMark Murray     void *handle;
267b528cefcSMark Murray     void *sym;
268b528cefcSMark Murray     int *version;
269b528cefcSMark Murray     const char *tmp;
270b528cefcSMark Murray 
271b528cefcSMark Murray     if(check_library == NULL) {
272b528cefcSMark Murray 	tmp = krb5_config_get_string(context, NULL,
273b528cefcSMark Murray 				     "password_quality",
274b528cefcSMark Murray 				     "check_library",
275b528cefcSMark Murray 				     NULL);
276b528cefcSMark Murray 	if(tmp != NULL)
277b528cefcSMark Murray 	    check_library = tmp;
278b528cefcSMark Murray     }
279b528cefcSMark Murray     if(check_function == NULL) {
280b528cefcSMark Murray 	tmp = krb5_config_get_string(context, NULL,
281b528cefcSMark Murray 				     "password_quality",
282b528cefcSMark Murray 				     "check_function",
283b528cefcSMark Murray 				     NULL);
284b528cefcSMark Murray 	if(tmp != NULL)
285b528cefcSMark Murray 	    check_function = tmp;
286b528cefcSMark Murray     }
287b528cefcSMark Murray     if(check_library != NULL && check_function == NULL)
288b528cefcSMark Murray 	check_function = "passwd_check";
289b528cefcSMark Murray 
290b528cefcSMark Murray     if(check_library == NULL)
291b528cefcSMark Murray 	return;
292c19800e8SDoug Rabson     handle = dlopen(check_library, RTLD_NOW);
293b528cefcSMark Murray     if(handle == NULL) {
294b528cefcSMark Murray 	krb5_warnx(context, "failed to open `%s'", check_library);
295b528cefcSMark Murray 	return;
296b528cefcSMark Murray     }
297b528cefcSMark Murray     version = dlsym(handle, "version");
298b528cefcSMark Murray     if(version == NULL) {
299b528cefcSMark Murray 	krb5_warnx(context,
300b528cefcSMark Murray 		   "didn't find `version' symbol in `%s'", check_library);
301b528cefcSMark Murray 	dlclose(handle);
302b528cefcSMark Murray 	return;
303b528cefcSMark Murray     }
304c19800e8SDoug Rabson     if(*version != KADM5_PASSWD_VERSION_V0) {
305b528cefcSMark Murray 	krb5_warnx(context,
306b528cefcSMark Murray 		   "version of loaded library is %d (expected %d)",
307c19800e8SDoug Rabson 		   *version, KADM5_PASSWD_VERSION_V0);
308b528cefcSMark Murray 	dlclose(handle);
309b528cefcSMark Murray 	return;
310b528cefcSMark Murray     }
311b528cefcSMark Murray     sym = dlsym(handle, check_function);
312b528cefcSMark Murray     if(sym == NULL) {
313b528cefcSMark Murray 	krb5_warnx(context,
314b528cefcSMark Murray 		   "didn't find `%s' symbol in `%s'",
315b528cefcSMark Murray 		   check_function, check_library);
316b528cefcSMark Murray 	dlclose(handle);
317b528cefcSMark Murray 	return;
318b528cefcSMark Murray     }
319c19800e8SDoug Rabson     passwd_quality_check = (kadm5_passwd_quality_check_func_v0) sym;
320b528cefcSMark Murray #endif /* HAVE_DLOPEN */
321b528cefcSMark Murray }
322b528cefcSMark Murray 
323c19800e8SDoug Rabson #ifdef HAVE_DLOPEN
324c19800e8SDoug Rabson 
325c19800e8SDoug Rabson static krb5_error_code
326c19800e8SDoug Rabson add_verifier(krb5_context context, const char *check_library)
327c19800e8SDoug Rabson {
328c19800e8SDoug Rabson     struct kadm5_pw_policy_verifier *v, **tmp;
329c19800e8SDoug Rabson     void *handle;
330c19800e8SDoug Rabson     int i;
331c19800e8SDoug Rabson 
332c19800e8SDoug Rabson     handle = dlopen(check_library, RTLD_NOW);
333c19800e8SDoug Rabson     if(handle == NULL) {
334c19800e8SDoug Rabson 	krb5_warnx(context, "failed to open `%s'", check_library);
335c19800e8SDoug Rabson 	return ENOENT;
336c19800e8SDoug Rabson     }
337c19800e8SDoug Rabson     v = dlsym(handle, "kadm5_password_verifier");
338c19800e8SDoug Rabson     if(v == NULL) {
339c19800e8SDoug Rabson 	krb5_warnx(context,
340c19800e8SDoug Rabson 		   "didn't find `kadm5_password_verifier' symbol "
341c19800e8SDoug Rabson 		   "in `%s'", check_library);
342c19800e8SDoug Rabson 	dlclose(handle);
343c19800e8SDoug Rabson 	return ENOENT;
344c19800e8SDoug Rabson     }
345c19800e8SDoug Rabson     if(v->version != KADM5_PASSWD_VERSION_V1) {
346c19800e8SDoug Rabson 	krb5_warnx(context,
347c19800e8SDoug Rabson 		   "version of loaded library is %d (expected %d)",
348c19800e8SDoug Rabson 		   v->version, KADM5_PASSWD_VERSION_V1);
349c19800e8SDoug Rabson 	dlclose(handle);
350c19800e8SDoug Rabson 	return EINVAL;
351c19800e8SDoug Rabson     }
352c19800e8SDoug Rabson     for (i = 0; i < num_verifiers; i++) {
353c19800e8SDoug Rabson 	if (strcmp(v->name, verifiers[i]->name) == 0)
354c19800e8SDoug Rabson 	    break;
355c19800e8SDoug Rabson     }
356c19800e8SDoug Rabson     if (i < num_verifiers) {
357c19800e8SDoug Rabson 	krb5_warnx(context, "password verifier library `%s' is already loaded",
358c19800e8SDoug Rabson 		   v->name);
359c19800e8SDoug Rabson 	dlclose(handle);
360c19800e8SDoug Rabson 	return 0;
361c19800e8SDoug Rabson     }
362c19800e8SDoug Rabson 
363c19800e8SDoug Rabson     tmp = realloc(verifiers, (num_verifiers + 1) * sizeof(*verifiers));
364c19800e8SDoug Rabson     if (tmp == NULL) {
365c19800e8SDoug Rabson 	krb5_warnx(context, "out of memory");
366c19800e8SDoug Rabson 	dlclose(handle);
367c19800e8SDoug Rabson 	return 0;
368c19800e8SDoug Rabson     }
369c19800e8SDoug Rabson     verifiers = tmp;
370c19800e8SDoug Rabson     verifiers[num_verifiers] = v;
371c19800e8SDoug Rabson     num_verifiers++;
372c19800e8SDoug Rabson 
373c19800e8SDoug Rabson     return 0;
374c19800e8SDoug Rabson }
375c19800e8SDoug Rabson 
376c19800e8SDoug Rabson #endif
377c19800e8SDoug Rabson 
378c19800e8SDoug Rabson krb5_error_code
379c19800e8SDoug Rabson kadm5_add_passwd_quality_verifier(krb5_context context,
380c19800e8SDoug Rabson 				  const char *check_library)
381c19800e8SDoug Rabson {
382c19800e8SDoug Rabson #ifdef HAVE_DLOPEN
383c19800e8SDoug Rabson 
384c19800e8SDoug Rabson     if(check_library == NULL) {
385c19800e8SDoug Rabson 	krb5_error_code ret;
386c19800e8SDoug Rabson 	char **tmp;
387c19800e8SDoug Rabson 
388c19800e8SDoug Rabson 	tmp = krb5_config_get_strings(context, NULL,
389c19800e8SDoug Rabson 				      "password_quality",
390c19800e8SDoug Rabson 				      "policy_libraries",
391c19800e8SDoug Rabson 				      NULL);
392c19800e8SDoug Rabson 	if(tmp == NULL)
393c19800e8SDoug Rabson 	    return 0;
394c19800e8SDoug Rabson 
395c19800e8SDoug Rabson 	while(tmp) {
396c19800e8SDoug Rabson 	    ret = add_verifier(context, *tmp);
397c19800e8SDoug Rabson 	    if (ret)
398c19800e8SDoug Rabson 		return ret;
399c19800e8SDoug Rabson 	    tmp++;
400c19800e8SDoug Rabson 	}
401c19800e8SDoug Rabson     }
402c19800e8SDoug Rabson     return add_verifier(context, check_library);
403c19800e8SDoug Rabson #else
404c19800e8SDoug Rabson     return 0;
405c19800e8SDoug Rabson #endif /* HAVE_DLOPEN */
406c19800e8SDoug Rabson }
407c19800e8SDoug Rabson 
408c19800e8SDoug Rabson /*
409c19800e8SDoug Rabson  *
410c19800e8SDoug Rabson  */
411c19800e8SDoug Rabson 
412c19800e8SDoug Rabson static const struct kadm5_pw_policy_check_func *
413c19800e8SDoug Rabson find_func(krb5_context context, const char *name)
414c19800e8SDoug Rabson {
415c19800e8SDoug Rabson     const struct kadm5_pw_policy_check_func *f;
416c19800e8SDoug Rabson     char *module = NULL;
417c19800e8SDoug Rabson     const char *p, *func;
418c19800e8SDoug Rabson     int i;
419c19800e8SDoug Rabson 
420c19800e8SDoug Rabson     p = strchr(name, ':');
421c19800e8SDoug Rabson     if (p) {
422c19800e8SDoug Rabson 	func = p + 1;
423c19800e8SDoug Rabson 	module = strndup(name, p - name);
424c19800e8SDoug Rabson 	if (module == NULL)
425c19800e8SDoug Rabson 	    return NULL;
426c19800e8SDoug Rabson     } else
427c19800e8SDoug Rabson 	func = name;
428c19800e8SDoug Rabson 
429c19800e8SDoug Rabson     /* Find module in loaded modules first */
430c19800e8SDoug Rabson     for (i = 0; i < num_verifiers; i++) {
431c19800e8SDoug Rabson 	if (module && strcmp(module, verifiers[i]->name) != 0)
432c19800e8SDoug Rabson 	    continue;
433c19800e8SDoug Rabson 	for (f = verifiers[i]->funcs; f->name ; f++)
434c19800e8SDoug Rabson 	    if (strcmp(name, f->name) == 0) {
435c19800e8SDoug Rabson 		if (module)
436c19800e8SDoug Rabson 		    free(module);
437c19800e8SDoug Rabson 		return f;
438c19800e8SDoug Rabson 	    }
439c19800e8SDoug Rabson     }
440c19800e8SDoug Rabson     /* Lets try try the builtin modules */
441c19800e8SDoug Rabson     if (module == NULL || strcmp(module, "builtin") == 0) {
442c19800e8SDoug Rabson 	for (f = builtin_verifier.funcs; f->name ; f++)
443c19800e8SDoug Rabson 	    if (strcmp(func, f->name) == 0) {
444c19800e8SDoug Rabson 		if (module)
445c19800e8SDoug Rabson 		    free(module);
446c19800e8SDoug Rabson 		return f;
447c19800e8SDoug Rabson 	    }
448c19800e8SDoug Rabson     }
449c19800e8SDoug Rabson     if (module)
450c19800e8SDoug Rabson 	free(module);
451c19800e8SDoug Rabson     return NULL;
452c19800e8SDoug Rabson }
453c19800e8SDoug Rabson 
454b528cefcSMark Murray const char *
455b528cefcSMark Murray kadm5_check_password_quality (krb5_context context,
456b528cefcSMark Murray 			      krb5_principal principal,
457b528cefcSMark Murray 			      krb5_data *pwd_data)
458b528cefcSMark Murray {
459c19800e8SDoug Rabson     const struct kadm5_pw_policy_check_func *proc;
460c19800e8SDoug Rabson     static char error_msg[1024];
461c19800e8SDoug Rabson     const char *msg;
462c19800e8SDoug Rabson     char **v, **vp;
463c19800e8SDoug Rabson     int ret;
464c19800e8SDoug Rabson 
465c19800e8SDoug Rabson     /*
466c19800e8SDoug Rabson      * Check if we should use the old version of policy function.
467c19800e8SDoug Rabson      */
468c19800e8SDoug Rabson 
469c19800e8SDoug Rabson     v = krb5_config_get_strings(context, NULL,
470c19800e8SDoug Rabson 				"password_quality",
471c19800e8SDoug Rabson 				"policies",
472c19800e8SDoug Rabson 				NULL);
473c19800e8SDoug Rabson     if (v == NULL) {
474c19800e8SDoug Rabson 	msg = (*passwd_quality_check) (context, principal, pwd_data);
475c19800e8SDoug Rabson 	krb5_set_error_string(context, "password policy failed: %s", msg);
476c19800e8SDoug Rabson 	return msg;
477c19800e8SDoug Rabson     }
478c19800e8SDoug Rabson 
479c19800e8SDoug Rabson     error_msg[0] = '\0';
480c19800e8SDoug Rabson 
481c19800e8SDoug Rabson     msg = NULL;
482c19800e8SDoug Rabson     for(vp = v; *vp; vp++) {
483c19800e8SDoug Rabson 	proc = find_func(context, *vp);
484c19800e8SDoug Rabson 	if (proc == NULL) {
485c19800e8SDoug Rabson 	    msg = "failed to find password verifier function";
486c19800e8SDoug Rabson 	    krb5_set_error_string(context, "Failed to find password policy "
487c19800e8SDoug Rabson 				  "function: %s", *vp);
488c19800e8SDoug Rabson 	    break;
489c19800e8SDoug Rabson 	}
490c19800e8SDoug Rabson 	ret = (proc->func)(context, principal, pwd_data, NULL,
491c19800e8SDoug Rabson 			   error_msg, sizeof(error_msg));
492c19800e8SDoug Rabson 	if (ret) {
493c19800e8SDoug Rabson 	    krb5_set_error_string(context, "Password policy "
494c19800e8SDoug Rabson 				  "%s failed with %s",
495c19800e8SDoug Rabson 				  proc->name, error_msg);
496c19800e8SDoug Rabson 	    msg = error_msg;
497c19800e8SDoug Rabson 	    break;
498c19800e8SDoug Rabson 	}
499c19800e8SDoug Rabson     }
500c19800e8SDoug Rabson     krb5_config_free_strings(v);
501c19800e8SDoug Rabson 
502c19800e8SDoug Rabson     /* If the default quality check isn't used, lets check that the
503c19800e8SDoug Rabson      * old quality function the user have set too */
504c19800e8SDoug Rabson     if (msg == NULL && passwd_quality_check != min_length_passwd_quality_v0) {
505c19800e8SDoug Rabson 	msg = (*passwd_quality_check) (context, principal, pwd_data);
506c19800e8SDoug Rabson 	if (msg)
507c19800e8SDoug Rabson 	    krb5_set_error_string(context, "(old) password policy "
508c19800e8SDoug Rabson 				  "failed with %s", msg);
509c19800e8SDoug Rabson 
510c19800e8SDoug Rabson     }
511c19800e8SDoug Rabson     return msg;
512b528cefcSMark Murray }
513