xref: /freebsd/crypto/heimdal/lib/kadm5/password_quality.c (revision 6a068746777241722b2b32c5d0bc443a2a64d80b)
1b528cefcSMark Murray /*
2ae771770SStanislav Sedov  * 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 #ifdef HAVE_SYS_WAIT_H
38c19800e8SDoug Rabson #include <sys/wait.h>
39c19800e8SDoug Rabson #endif
40b528cefcSMark Murray #ifdef HAVE_DLFCN_H
41b528cefcSMark Murray #include <dlfcn.h>
42b528cefcSMark Murray #endif
43b528cefcSMark Murray 
44c19800e8SDoug Rabson static int
min_length_passwd_quality(krb5_context context,krb5_principal principal,krb5_data * pwd,const char * opaque,char * message,size_t length)45c19800e8SDoug Rabson min_length_passwd_quality (krb5_context context,
46c19800e8SDoug Rabson 			   krb5_principal principal,
47c19800e8SDoug Rabson 			   krb5_data *pwd,
48c19800e8SDoug Rabson 			   const char *opaque,
49c19800e8SDoug Rabson 			   char *message,
50c19800e8SDoug Rabson 			   size_t length)
51c19800e8SDoug Rabson {
52c19800e8SDoug Rabson     uint32_t min_length = krb5_config_get_int_default(context, NULL, 6,
53c19800e8SDoug Rabson 						      "password_quality",
54c19800e8SDoug Rabson 						      "min_length",
55c19800e8SDoug Rabson 						      NULL);
56c19800e8SDoug Rabson 
57c19800e8SDoug Rabson     if (pwd->length < min_length) {
58c19800e8SDoug Rabson 	strlcpy(message, "Password too short", length);
59c19800e8SDoug Rabson 	return 1;
60c19800e8SDoug Rabson     } else
61c19800e8SDoug Rabson 	return 0;
62c19800e8SDoug Rabson }
63c19800e8SDoug Rabson 
64b528cefcSMark Murray static const char *
min_length_passwd_quality_v0(krb5_context context,krb5_principal principal,krb5_data * pwd)65c19800e8SDoug Rabson min_length_passwd_quality_v0 (krb5_context context,
66b528cefcSMark Murray 			      krb5_principal principal,
67b528cefcSMark Murray 			      krb5_data *pwd)
68b528cefcSMark Murray {
69c19800e8SDoug Rabson     static char message[1024];
70c19800e8SDoug Rabson     int ret;
71c19800e8SDoug Rabson 
72c19800e8SDoug Rabson     message[0] = '\0';
73c19800e8SDoug Rabson 
74c19800e8SDoug Rabson     ret = min_length_passwd_quality(context, principal, pwd, NULL,
75c19800e8SDoug Rabson 				    message, sizeof(message));
76c19800e8SDoug Rabson     if (ret)
77c19800e8SDoug Rabson 	return message;
78b528cefcSMark Murray     return NULL;
79b528cefcSMark Murray }
80b528cefcSMark Murray 
81b528cefcSMark Murray 
82c19800e8SDoug Rabson static int
char_class_passwd_quality(krb5_context context,krb5_principal principal,krb5_data * pwd,const char * opaque,char * message,size_t length)83c19800e8SDoug Rabson char_class_passwd_quality (krb5_context context,
84c19800e8SDoug Rabson 			   krb5_principal principal,
85c19800e8SDoug Rabson 			   krb5_data *pwd,
86c19800e8SDoug Rabson 			   const char *opaque,
87c19800e8SDoug Rabson 			   char *message,
88c19800e8SDoug Rabson 			   size_t length)
89c19800e8SDoug Rabson {
90c19800e8SDoug Rabson     const char *classes[] = {
91c19800e8SDoug Rabson 	"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
92c19800e8SDoug Rabson 	"abcdefghijklmnopqrstuvwxyz",
93c19800e8SDoug Rabson 	"1234567890",
94c19800e8SDoug Rabson 	"!@#$%^&*()/?<>,.{[]}\\|'~`\" "
95c19800e8SDoug Rabson     };
96ae771770SStanislav Sedov     int counter = 0, req_classes;
97ae771770SStanislav Sedov     size_t i, len;
98c19800e8SDoug Rabson     char *pw;
99b528cefcSMark Murray 
100c19800e8SDoug Rabson     req_classes = krb5_config_get_int_default(context, NULL, 3,
101c19800e8SDoug Rabson 					      "password_quality",
102c19800e8SDoug Rabson 					      "min_classes",
103c19800e8SDoug Rabson 					      NULL);
104b528cefcSMark Murray 
105c19800e8SDoug Rabson     len = pwd->length + 1;
106c19800e8SDoug Rabson     pw = malloc(len);
107c19800e8SDoug Rabson     if (pw == NULL) {
108c19800e8SDoug Rabson 	strlcpy(message, "out of memory", length);
109c19800e8SDoug Rabson 	return 1;
110c19800e8SDoug Rabson     }
111c19800e8SDoug Rabson     strlcpy(pw, pwd->data, len);
112c19800e8SDoug Rabson     len = strlen(pw);
113b528cefcSMark Murray 
114c19800e8SDoug Rabson     for (i = 0; i < sizeof(classes)/sizeof(classes[0]); i++) {
115c19800e8SDoug Rabson 	if (strcspn(pw, classes[i]) < len)
116c19800e8SDoug Rabson 	    counter++;
117c19800e8SDoug Rabson     }
118c19800e8SDoug Rabson     memset(pw, 0, pwd->length + 1);
119c19800e8SDoug Rabson     free(pw);
120c19800e8SDoug Rabson     if (counter < req_classes) {
121c19800e8SDoug Rabson 	snprintf(message, length,
122c19800e8SDoug Rabson 	    "Password doesn't meet complexity requirement.\n"
123c19800e8SDoug Rabson 	    "Add more characters from the following classes:\n"
124c19800e8SDoug Rabson 	    "1. English uppercase characters (A through Z)\n"
125c19800e8SDoug Rabson 	    "2. English lowercase characters (a through z)\n"
126c19800e8SDoug Rabson 	    "3. Base 10 digits (0 through 9)\n"
127c19800e8SDoug Rabson 	    "4. Nonalphanumeric characters (e.g., !, $, #, %%)");
128c19800e8SDoug Rabson 	return 1;
129c19800e8SDoug Rabson     }
130c19800e8SDoug Rabson     return 0;
131c19800e8SDoug Rabson }
132c19800e8SDoug Rabson 
133c19800e8SDoug Rabson static int
external_passwd_quality(krb5_context context,krb5_principal principal,krb5_data * pwd,const char * opaque,char * message,size_t length)134c19800e8SDoug Rabson external_passwd_quality (krb5_context context,
135c19800e8SDoug Rabson 			 krb5_principal principal,
136c19800e8SDoug Rabson 			 krb5_data *pwd,
137c19800e8SDoug Rabson 			 const char *opaque,
138c19800e8SDoug Rabson 			 char *message,
139c19800e8SDoug Rabson 			 size_t length)
140c19800e8SDoug Rabson {
141c19800e8SDoug Rabson     krb5_error_code ret;
142c19800e8SDoug Rabson     const char *program;
143c19800e8SDoug Rabson     char *p;
144c19800e8SDoug Rabson     pid_t child;
145c19800e8SDoug Rabson     int status;
146c19800e8SDoug Rabson     char reply[1024];
147c19800e8SDoug Rabson     FILE *in = NULL, *out = NULL, *error = NULL;
148c19800e8SDoug Rabson 
149ae771770SStanislav Sedov     if (memchr(pwd->data, '\n', pwd->length) != NULL) {
150c19800e8SDoug Rabson 	snprintf(message, length, "password contains newline, "
151c19800e8SDoug Rabson 		 "not valid for external test");
152c19800e8SDoug Rabson 	return 1;
153c19800e8SDoug Rabson     }
154c19800e8SDoug Rabson 
155c19800e8SDoug Rabson     program = krb5_config_get_string(context, NULL,
156c19800e8SDoug Rabson 				     "password_quality",
157c19800e8SDoug Rabson 				     "external_program",
158c19800e8SDoug Rabson 				     NULL);
159c19800e8SDoug Rabson     if (program == NULL) {
160c19800e8SDoug Rabson 	snprintf(message, length, "external password quality "
161c19800e8SDoug Rabson 		 "program not configured");
162c19800e8SDoug Rabson 	return 1;
163c19800e8SDoug Rabson     }
164c19800e8SDoug Rabson 
165c19800e8SDoug Rabson     ret = krb5_unparse_name(context, principal, &p);
166c19800e8SDoug Rabson     if (ret) {
167c19800e8SDoug Rabson 	strlcpy(message, "out of memory", length);
168c19800e8SDoug Rabson 	return 1;
169c19800e8SDoug Rabson     }
170c19800e8SDoug Rabson 
171ae771770SStanislav Sedov     child = pipe_execv(&in, &out, &error, program, program, p, NULL);
172c19800e8SDoug Rabson     if (child < 0) {
173c19800e8SDoug Rabson 	snprintf(message, length, "external password quality "
174c19800e8SDoug Rabson 		 "program failed to execute for principal %s", p);
175c19800e8SDoug Rabson 	free(p);
176c19800e8SDoug Rabson 	return 1;
177c19800e8SDoug Rabson     }
178c19800e8SDoug Rabson 
179c19800e8SDoug Rabson     fprintf(in, "principal: %s\n"
180c19800e8SDoug Rabson 	    "new-password: %.*s\n"
181c19800e8SDoug Rabson 	    "end\n",
182c19800e8SDoug Rabson 	    p, (int)pwd->length, (char *)pwd->data);
183c19800e8SDoug Rabson 
184c19800e8SDoug Rabson     fclose(in);
185c19800e8SDoug Rabson 
186c19800e8SDoug Rabson     if (fgets(reply, sizeof(reply), out) == NULL) {
187c19800e8SDoug Rabson 
188c19800e8SDoug Rabson 	if (fgets(reply, sizeof(reply), error) == NULL) {
189c19800e8SDoug Rabson 	    snprintf(message, length, "external password quality "
190c19800e8SDoug Rabson 		     "program failed without error");
191c19800e8SDoug Rabson 
192c19800e8SDoug Rabson 	} else {
193c19800e8SDoug Rabson 	    reply[strcspn(reply, "\n")] = '\0';
194c19800e8SDoug Rabson 	    snprintf(message, length, "External password quality "
195c19800e8SDoug Rabson 		     "program failed: %s", reply);
196c19800e8SDoug Rabson 	}
197c19800e8SDoug Rabson 
198c19800e8SDoug Rabson 	fclose(out);
199c19800e8SDoug Rabson 	fclose(error);
200ae771770SStanislav Sedov 	wait_for_process(child);
201c19800e8SDoug Rabson 	return 1;
202c19800e8SDoug Rabson     }
203c19800e8SDoug Rabson     reply[strcspn(reply, "\n")] = '\0';
204c19800e8SDoug Rabson 
205c19800e8SDoug Rabson     fclose(out);
206c19800e8SDoug Rabson     fclose(error);
207c19800e8SDoug Rabson 
208ae771770SStanislav Sedov     status = wait_for_process(child);
209ae771770SStanislav Sedov 
210ae771770SStanislav Sedov     if (SE_IS_ERROR(status) || SE_PROCSTATUS(status) != 0) {
211c19800e8SDoug Rabson 	snprintf(message, length, "external program failed: %s", reply);
212c19800e8SDoug Rabson 	free(p);
213c19800e8SDoug Rabson 	return 1;
214c19800e8SDoug Rabson     }
215c19800e8SDoug Rabson 
216c19800e8SDoug Rabson     if (strcmp(reply, "APPROVED") != 0) {
217c19800e8SDoug Rabson 	snprintf(message, length, "%s", reply);
218c19800e8SDoug Rabson 	free(p);
219c19800e8SDoug Rabson 	return 1;
220c19800e8SDoug Rabson     }
221c19800e8SDoug Rabson 
222c19800e8SDoug Rabson     free(p);
223c19800e8SDoug Rabson 
224c19800e8SDoug Rabson     return 0;
225c19800e8SDoug Rabson }
226c19800e8SDoug Rabson 
227c19800e8SDoug Rabson 
228c19800e8SDoug Rabson static kadm5_passwd_quality_check_func_v0 passwd_quality_check =
229c19800e8SDoug Rabson 	min_length_passwd_quality_v0;
230c19800e8SDoug Rabson 
231c19800e8SDoug Rabson struct kadm5_pw_policy_check_func builtin_funcs[] = {
232c19800e8SDoug Rabson     { "minimum-length", min_length_passwd_quality },
233c19800e8SDoug Rabson     { "character-class", char_class_passwd_quality },
234c19800e8SDoug Rabson     { "external-check", external_passwd_quality },
235ae771770SStanislav Sedov     { NULL, NULL }
236c19800e8SDoug Rabson };
237c19800e8SDoug Rabson struct kadm5_pw_policy_verifier builtin_verifier = {
238c19800e8SDoug Rabson     "builtin",
239c19800e8SDoug Rabson     KADM5_PASSWD_VERSION_V1,
240c19800e8SDoug Rabson     "Heimdal builtin",
241c19800e8SDoug Rabson     builtin_funcs
242c19800e8SDoug Rabson };
243c19800e8SDoug Rabson 
244c19800e8SDoug Rabson static struct kadm5_pw_policy_verifier **verifiers;
245c19800e8SDoug Rabson static int num_verifiers;
246b528cefcSMark Murray 
247b528cefcSMark Murray /*
248b528cefcSMark Murray  * setup the password quality hook
249b528cefcSMark Murray  */
250b528cefcSMark Murray 
251c19800e8SDoug Rabson #ifndef RTLD_NOW
252c19800e8SDoug Rabson #define RTLD_NOW 0
253c19800e8SDoug Rabson #endif
254c19800e8SDoug Rabson 
255b528cefcSMark Murray void
kadm5_setup_passwd_quality_check(krb5_context context,const char * check_library,const char * check_function)256b528cefcSMark Murray kadm5_setup_passwd_quality_check(krb5_context context,
257b528cefcSMark Murray 				 const char *check_library,
258b528cefcSMark Murray 				 const char *check_function)
259b528cefcSMark Murray {
260b528cefcSMark Murray #ifdef HAVE_DLOPEN
261b528cefcSMark Murray     void *handle;
262b528cefcSMark Murray     void *sym;
263b528cefcSMark Murray     int *version;
264b528cefcSMark Murray     const char *tmp;
265b528cefcSMark Murray 
266b528cefcSMark Murray     if(check_library == NULL) {
267b528cefcSMark Murray 	tmp = krb5_config_get_string(context, NULL,
268b528cefcSMark Murray 				     "password_quality",
269b528cefcSMark Murray 				     "check_library",
270b528cefcSMark Murray 				     NULL);
271b528cefcSMark Murray 	if(tmp != NULL)
272b528cefcSMark Murray 	    check_library = tmp;
273b528cefcSMark Murray     }
274b528cefcSMark Murray     if(check_function == NULL) {
275b528cefcSMark Murray 	tmp = krb5_config_get_string(context, NULL,
276b528cefcSMark Murray 				     "password_quality",
277b528cefcSMark Murray 				     "check_function",
278b528cefcSMark Murray 				     NULL);
279b528cefcSMark Murray 	if(tmp != NULL)
280b528cefcSMark Murray 	    check_function = tmp;
281b528cefcSMark Murray     }
282b528cefcSMark Murray     if(check_library != NULL && check_function == NULL)
283b528cefcSMark Murray 	check_function = "passwd_check";
284b528cefcSMark Murray 
285b528cefcSMark Murray     if(check_library == NULL)
286b528cefcSMark Murray 	return;
287c19800e8SDoug Rabson     handle = dlopen(check_library, RTLD_NOW);
288b528cefcSMark Murray     if(handle == NULL) {
289b528cefcSMark Murray 	krb5_warnx(context, "failed to open `%s'", check_library);
290b528cefcSMark Murray 	return;
291b528cefcSMark Murray     }
292ae771770SStanislav Sedov     version = (int *) dlsym(handle, "version");
293b528cefcSMark Murray     if(version == NULL) {
294b528cefcSMark Murray 	krb5_warnx(context,
295b528cefcSMark Murray 		   "didn't find `version' symbol in `%s'", check_library);
296b528cefcSMark Murray 	dlclose(handle);
297b528cefcSMark Murray 	return;
298b528cefcSMark Murray     }
299c19800e8SDoug Rabson     if(*version != KADM5_PASSWD_VERSION_V0) {
300b528cefcSMark Murray 	krb5_warnx(context,
301b528cefcSMark Murray 		   "version of loaded library is %d (expected %d)",
302c19800e8SDoug Rabson 		   *version, KADM5_PASSWD_VERSION_V0);
303b528cefcSMark Murray 	dlclose(handle);
304b528cefcSMark Murray 	return;
305b528cefcSMark Murray     }
306b528cefcSMark Murray     sym = dlsym(handle, check_function);
307b528cefcSMark Murray     if(sym == NULL) {
308b528cefcSMark Murray 	krb5_warnx(context,
309b528cefcSMark Murray 		   "didn't find `%s' symbol in `%s'",
310b528cefcSMark Murray 		   check_function, check_library);
311b528cefcSMark Murray 	dlclose(handle);
312b528cefcSMark Murray 	return;
313b528cefcSMark Murray     }
314c19800e8SDoug Rabson     passwd_quality_check = (kadm5_passwd_quality_check_func_v0) sym;
315b528cefcSMark Murray #endif /* HAVE_DLOPEN */
316b528cefcSMark Murray }
317b528cefcSMark Murray 
318c19800e8SDoug Rabson #ifdef HAVE_DLOPEN
319c19800e8SDoug Rabson 
320c19800e8SDoug Rabson static krb5_error_code
add_verifier(krb5_context context,const char * check_library)321c19800e8SDoug Rabson add_verifier(krb5_context context, const char *check_library)
322c19800e8SDoug Rabson {
323c19800e8SDoug Rabson     struct kadm5_pw_policy_verifier *v, **tmp;
324c19800e8SDoug Rabson     void *handle;
325c19800e8SDoug Rabson     int i;
326c19800e8SDoug Rabson 
327c19800e8SDoug Rabson     handle = dlopen(check_library, RTLD_NOW);
328c19800e8SDoug Rabson     if(handle == NULL) {
329c19800e8SDoug Rabson 	krb5_warnx(context, "failed to open `%s'", check_library);
330c19800e8SDoug Rabson 	return ENOENT;
331c19800e8SDoug Rabson     }
332ae771770SStanislav Sedov     v = (struct kadm5_pw_policy_verifier *) dlsym(handle, "kadm5_password_verifier");
333c19800e8SDoug Rabson     if(v == NULL) {
334c19800e8SDoug Rabson 	krb5_warnx(context,
335c19800e8SDoug Rabson 		   "didn't find `kadm5_password_verifier' symbol "
336c19800e8SDoug Rabson 		   "in `%s'", check_library);
337c19800e8SDoug Rabson 	dlclose(handle);
338c19800e8SDoug Rabson 	return ENOENT;
339c19800e8SDoug Rabson     }
340c19800e8SDoug Rabson     if(v->version != KADM5_PASSWD_VERSION_V1) {
341c19800e8SDoug Rabson 	krb5_warnx(context,
342c19800e8SDoug Rabson 		   "version of loaded library is %d (expected %d)",
343c19800e8SDoug Rabson 		   v->version, KADM5_PASSWD_VERSION_V1);
344c19800e8SDoug Rabson 	dlclose(handle);
345c19800e8SDoug Rabson 	return EINVAL;
346c19800e8SDoug Rabson     }
347c19800e8SDoug Rabson     for (i = 0; i < num_verifiers; i++) {
348c19800e8SDoug Rabson 	if (strcmp(v->name, verifiers[i]->name) == 0)
349c19800e8SDoug Rabson 	    break;
350c19800e8SDoug Rabson     }
351c19800e8SDoug Rabson     if (i < num_verifiers) {
352c19800e8SDoug Rabson 	krb5_warnx(context, "password verifier library `%s' is already loaded",
353c19800e8SDoug Rabson 		   v->name);
354c19800e8SDoug Rabson 	dlclose(handle);
355c19800e8SDoug Rabson 	return 0;
356c19800e8SDoug Rabson     }
357c19800e8SDoug Rabson 
358c19800e8SDoug Rabson     tmp = realloc(verifiers, (num_verifiers + 1) * sizeof(*verifiers));
359c19800e8SDoug Rabson     if (tmp == NULL) {
360c19800e8SDoug Rabson 	krb5_warnx(context, "out of memory");
361c19800e8SDoug Rabson 	dlclose(handle);
362c19800e8SDoug Rabson 	return 0;
363c19800e8SDoug Rabson     }
364c19800e8SDoug Rabson     verifiers = tmp;
365c19800e8SDoug Rabson     verifiers[num_verifiers] = v;
366c19800e8SDoug Rabson     num_verifiers++;
367c19800e8SDoug Rabson 
368c19800e8SDoug Rabson     return 0;
369c19800e8SDoug Rabson }
370c19800e8SDoug Rabson 
371c19800e8SDoug Rabson #endif
372c19800e8SDoug Rabson 
373c19800e8SDoug Rabson krb5_error_code
kadm5_add_passwd_quality_verifier(krb5_context context,const char * check_library)374c19800e8SDoug Rabson kadm5_add_passwd_quality_verifier(krb5_context context,
375c19800e8SDoug Rabson 				  const char *check_library)
376c19800e8SDoug Rabson {
377c19800e8SDoug Rabson #ifdef HAVE_DLOPEN
378c19800e8SDoug Rabson 
379c19800e8SDoug Rabson     if(check_library == NULL) {
380c19800e8SDoug Rabson 	krb5_error_code ret;
381c19800e8SDoug Rabson 	char **tmp;
382c19800e8SDoug Rabson 
383c19800e8SDoug Rabson 	tmp = krb5_config_get_strings(context, NULL,
384c19800e8SDoug Rabson 				      "password_quality",
385c19800e8SDoug Rabson 				      "policy_libraries",
386c19800e8SDoug Rabson 				      NULL);
387ae771770SStanislav Sedov 	if(tmp == NULL || *tmp == NULL)
388c19800e8SDoug Rabson 	    return 0;
389c19800e8SDoug Rabson 
390ae771770SStanislav Sedov 	while (*tmp) {
391c19800e8SDoug Rabson 	    ret = add_verifier(context, *tmp);
392c19800e8SDoug Rabson 	    if (ret)
393c19800e8SDoug Rabson 		return ret;
394c19800e8SDoug Rabson 	    tmp++;
395c19800e8SDoug Rabson 	}
396ae771770SStanislav Sedov 	return 0;
397ae771770SStanislav Sedov     } else {
398c19800e8SDoug Rabson 	return add_verifier(context, check_library);
399ae771770SStanislav Sedov     }
400c19800e8SDoug Rabson #else
401c19800e8SDoug Rabson     return 0;
402c19800e8SDoug Rabson #endif /* HAVE_DLOPEN */
403c19800e8SDoug Rabson }
404c19800e8SDoug Rabson 
405c19800e8SDoug Rabson /*
406c19800e8SDoug Rabson  *
407c19800e8SDoug Rabson  */
408c19800e8SDoug Rabson 
409c19800e8SDoug Rabson static const struct kadm5_pw_policy_check_func *
find_func(krb5_context context,const char * name)410c19800e8SDoug Rabson find_func(krb5_context context, const char *name)
411c19800e8SDoug Rabson {
412c19800e8SDoug Rabson     const struct kadm5_pw_policy_check_func *f;
413c19800e8SDoug Rabson     char *module = NULL;
414c19800e8SDoug Rabson     const char *p, *func;
415c19800e8SDoug Rabson     int i;
416c19800e8SDoug Rabson 
417c19800e8SDoug Rabson     p = strchr(name, ':');
418c19800e8SDoug Rabson     if (p) {
419ae771770SStanislav Sedov 	size_t len = p - name + 1;
420c19800e8SDoug Rabson 	func = p + 1;
421ae771770SStanislav Sedov 	module = malloc(len);
422c19800e8SDoug Rabson 	if (module == NULL)
423c19800e8SDoug Rabson 	    return NULL;
424ae771770SStanislav Sedov 	strlcpy(module, name, len);
425c19800e8SDoug Rabson     } else
426c19800e8SDoug Rabson 	func = name;
427c19800e8SDoug Rabson 
428c19800e8SDoug Rabson     /* Find module in loaded modules first */
429c19800e8SDoug Rabson     for (i = 0; i < num_verifiers; i++) {
430c19800e8SDoug Rabson 	if (module && strcmp(module, verifiers[i]->name) != 0)
431c19800e8SDoug Rabson 	    continue;
432c19800e8SDoug Rabson 	for (f = verifiers[i]->funcs; f->name ; f++)
433ae771770SStanislav Sedov 	    if (strcmp(func, f->name) == 0) {
434c19800e8SDoug Rabson 		if (module)
435c19800e8SDoug Rabson 		    free(module);
436c19800e8SDoug Rabson 		return f;
437c19800e8SDoug Rabson 	    }
438c19800e8SDoug Rabson     }
439c19800e8SDoug Rabson     /* Lets try try the builtin modules */
440c19800e8SDoug Rabson     if (module == NULL || strcmp(module, "builtin") == 0) {
441c19800e8SDoug Rabson 	for (f = builtin_verifier.funcs; f->name ; f++)
442c19800e8SDoug Rabson 	    if (strcmp(func, f->name) == 0) {
443c19800e8SDoug Rabson 		if (module)
444c19800e8SDoug Rabson 		    free(module);
445c19800e8SDoug Rabson 		return f;
446c19800e8SDoug Rabson 	    }
447c19800e8SDoug Rabson     }
448c19800e8SDoug Rabson     if (module)
449c19800e8SDoug Rabson 	free(module);
450c19800e8SDoug Rabson     return NULL;
451c19800e8SDoug Rabson }
452c19800e8SDoug Rabson 
453b528cefcSMark Murray const char *
kadm5_check_password_quality(krb5_context context,krb5_principal principal,krb5_data * pwd_data)454b528cefcSMark Murray kadm5_check_password_quality (krb5_context context,
455b528cefcSMark Murray 			      krb5_principal principal,
456b528cefcSMark Murray 			      krb5_data *pwd_data)
457b528cefcSMark Murray {
458c19800e8SDoug Rabson     const struct kadm5_pw_policy_check_func *proc;
459c19800e8SDoug Rabson     static char error_msg[1024];
460c19800e8SDoug Rabson     const char *msg;
461c19800e8SDoug Rabson     char **v, **vp;
462c19800e8SDoug Rabson     int ret;
463c19800e8SDoug Rabson 
464c19800e8SDoug Rabson     /*
465c19800e8SDoug Rabson      * Check if we should use the old version of policy function.
466c19800e8SDoug Rabson      */
467c19800e8SDoug Rabson 
468c19800e8SDoug Rabson     v = krb5_config_get_strings(context, NULL,
469c19800e8SDoug Rabson 				"password_quality",
470c19800e8SDoug Rabson 				"policies",
471c19800e8SDoug Rabson 				NULL);
472c19800e8SDoug Rabson     if (v == NULL) {
473c19800e8SDoug Rabson 	msg = (*passwd_quality_check) (context, principal, pwd_data);
474*cf771f22SStanislav Sedov 	if (msg)
475ae771770SStanislav Sedov 	    krb5_set_error_message(context, 0, "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";
486ae771770SStanislav Sedov 	    krb5_set_error_message(context, 0, "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) {
493ae771770SStanislav Sedov 	    krb5_set_error_message(context, 0, "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)
507ae771770SStanislav Sedov 	    krb5_set_error_message(context, 0, "(old) password policy "
508c19800e8SDoug Rabson 				   "failed with %s", msg);
509c19800e8SDoug Rabson 
510c19800e8SDoug Rabson     }
511c19800e8SDoug Rabson     return msg;
512b528cefcSMark Murray }
513