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