2 * Copyright (c) 1997-2000, 2003-2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include "kadm5_locl.h"
35 #include "kadm5-pwcheck.h"
37 #ifdef HAVE_SYS_WAIT_H
42 min_length_passwd_quality (krb5_context context
,
43 krb5_principal principal
,
49 uint32_t min_length
= krb5_config_get_int_default(context
, NULL
, 6,
54 if (pwd
->length
< min_length
) {
55 strlcpy(message
, "Password too short", length
);
62 min_length_passwd_quality_v0 (krb5_context context
,
63 krb5_principal principal
,
66 static char message
[1024];
71 ret
= min_length_passwd_quality(context
, principal
, pwd
, NULL
,
72 message
, sizeof(message
));
80 char_class_passwd_quality (krb5_context context
,
81 krb5_principal principal
,
87 const char *classes
[] = {
88 "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
89 "abcdefghijklmnopqrstuvwxyz",
91 " !\"#$%&'()*+,-./:;<=>?@\\]^_`{|}~"
93 int counter
= 0, req_classes
;
97 req_classes
= krb5_config_get_int_default(context
, NULL
, 3,
102 len
= pwd
->length
+ 1;
105 strlcpy(message
, "out of memory", length
);
108 strlcpy(pw
, pwd
->data
, len
);
111 for (i
= 0; i
< sizeof(classes
)/sizeof(classes
[0]); i
++) {
112 if (strcspn(pw
, classes
[i
]) < len
)
115 memset(pw
, 0, pwd
->length
+ 1);
117 if (counter
< req_classes
) {
118 snprintf(message
, length
,
119 "Password doesn't meet complexity requirement.\n"
120 "Add more characters from at least %d of the\n"
121 "following classes:\n"
122 "1. English uppercase characters (A through Z)\n"
123 "2. English lowercase characters (a through z)\n"
124 "3. Base 10 digits (0 through 9)\n"
125 "4. Nonalphanumeric characters (e.g., !, $, #, %%)", req_classes
);
132 external_passwd_quality (krb5_context context
,
133 krb5_principal principal
,
145 FILE *in
= NULL
, *out
= NULL
, *error
= NULL
;
147 if (memchr(pwd
->data
, '\n', pwd
->length
) != NULL
) {
148 snprintf(message
, length
, "password contains newline, "
149 "not valid for external test");
153 program
= krb5_config_get_string(context
, NULL
,
157 if (program
== NULL
) {
158 snprintf(message
, length
, "external password quality "
159 "program not configured");
163 ret
= krb5_unparse_name(context
, principal
, &p
);
165 strlcpy(message
, "out of memory", length
);
169 child
= pipe_execv(&in
, &out
, &error
, program
, program
, p
, NULL
);
171 snprintf(message
, length
, "external password quality "
172 "program failed to execute for principal %s", p
);
177 fprintf(in
, "principal: %s\n"
178 "new-password: %.*s\n"
180 p
, (int)pwd
->length
, (char *)pwd
->data
);
184 if (fgets(reply
, sizeof(reply
), out
) == NULL
) {
186 if (fgets(reply
, sizeof(reply
), error
) == NULL
) {
187 snprintf(message
, length
, "external password quality "
188 "program failed without error");
191 reply
[strcspn(reply
, "\n")] = '\0';
192 snprintf(message
, length
, "External password quality "
193 "program failed: %s", reply
);
198 wait_for_process(child
);
202 reply
[strcspn(reply
, "\n")] = '\0';
207 status
= wait_for_process(child
);
209 if (SE_IS_ERROR(status
) || SE_PROCSTATUS(status
) != 0) {
210 snprintf(message
, length
, "external program failed: %s", reply
);
215 if (strcmp(reply
, "APPROVED") != 0) {
216 snprintf(message
, length
, "%s", reply
);
227 static kadm5_passwd_quality_check_func_v0 passwd_quality_check
=
228 min_length_passwd_quality_v0
;
230 struct kadm5_pw_policy_check_func builtin_funcs
[] = {
231 { "minimum-length", min_length_passwd_quality
},
232 { "character-class", char_class_passwd_quality
},
233 { "external-check", external_passwd_quality
},
236 struct kadm5_pw_policy_verifier builtin_verifier
= {
238 KADM5_PASSWD_VERSION_V1
,
243 static struct kadm5_pw_policy_verifier
**verifiers
;
244 static int num_verifiers
;
247 * setup the password quality hook
251 kadm5_setup_passwd_quality_check(krb5_context context
,
252 const char *check_library
,
253 const char *check_function
)
261 if(check_library
== NULL
) {
262 tmp
= krb5_config_get_string(context
, NULL
,
269 if(check_function
== NULL
) {
270 tmp
= krb5_config_get_string(context
, NULL
,
275 check_function
= tmp
;
277 if(check_library
!= NULL
&& check_function
== NULL
)
278 check_function
= "passwd_check";
280 if(check_library
== NULL
)
282 handle
= dlopen(check_library
, RTLD_NOW
| RTLD_LOCAL
| RTLD_GROUP
);
284 krb5_warnx(context
, "failed to open `%s'", check_library
);
287 version
= (int *) dlsym(handle
, "version");
288 if(version
== NULL
) {
290 "didn't find `version' symbol in `%s'", check_library
);
294 if(*version
!= KADM5_PASSWD_VERSION_V0
) {
296 "version of loaded library is %d (expected %d)",
297 *version
, KADM5_PASSWD_VERSION_V0
);
301 sym
= dlsym(handle
, check_function
);
304 "didn't find `%s' symbol in `%s'",
305 check_function
, check_library
);
309 passwd_quality_check
= (kadm5_passwd_quality_check_func_v0
) sym
;
310 #endif /* HAVE_DLOPEN */
315 static krb5_error_code
316 add_verifier(krb5_context context
, const char *check_library
)
318 struct kadm5_pw_policy_verifier
*v
, **tmp
;
322 handle
= dlopen(check_library
, RTLD_NOW
| RTLD_LOCAL
| RTLD_GROUP
);
324 krb5_warnx(context
, "failed to open `%s'", check_library
);
327 v
= (struct kadm5_pw_policy_verifier
*) dlsym(handle
, "kadm5_password_verifier");
330 "didn't find `kadm5_password_verifier' symbol "
331 "in `%s'", check_library
);
335 if(v
->version
!= KADM5_PASSWD_VERSION_V1
) {
337 "version of loaded library is %d (expected %d)",
338 v
->version
, KADM5_PASSWD_VERSION_V1
);
342 for (i
= 0; i
< num_verifiers
; i
++) {
343 if (strcmp(v
->name
, verifiers
[i
]->name
) == 0)
346 if (i
< num_verifiers
) {
347 krb5_warnx(context
, "password verifier library `%s' is already loaded",
353 tmp
= realloc(verifiers
, (num_verifiers
+ 1) * sizeof(*verifiers
));
355 krb5_warnx(context
, "out of memory");
360 verifiers
[num_verifiers
] = v
;
369 kadm5_add_passwd_quality_verifier(krb5_context context
,
370 const char *check_library
)
374 if(check_library
== NULL
) {
375 krb5_error_code ret
= 0;
379 strs
= krb5_config_get_strings(context
, NULL
,
386 for (tmp
= strs
; *tmp
; tmp
++) {
387 ret
= add_verifier(context
, *tmp
);
391 krb5_config_free_strings(strs
);
394 return add_verifier(context
, check_library
);
398 #endif /* HAVE_DLOPEN */
405 static const struct kadm5_pw_policy_check_func
*
406 find_func(krb5_context context
, const char *name
)
408 const struct kadm5_pw_policy_check_func
*f
;
410 const char *p
, *func
;
413 p
= strchr(name
, ':');
415 size_t len
= p
- name
+ 1;
417 module
= malloc(len
);
420 strlcpy(module
, name
, len
);
424 /* Find module in loaded modules first */
425 for (i
= 0; i
< num_verifiers
; i
++) {
426 if (module
&& strcmp(module
, verifiers
[i
]->name
) != 0)
428 for (f
= verifiers
[i
]->funcs
; f
->name
; f
++)
429 if (strcmp(func
, f
->name
) == 0) {
435 /* Lets try try the builtin modules */
436 if (module
== NULL
|| strcmp(module
, "builtin") == 0) {
437 for (f
= builtin_verifier
.funcs
; f
->name
; f
++)
438 if (strcmp(func
, f
->name
) == 0) {
450 kadm5_check_password_quality (krb5_context context
,
451 krb5_principal principal
,
454 const struct kadm5_pw_policy_check_func
*proc
;
455 static char error_msg
[1024];
461 * Check if we should use the old version of policy function.
464 v
= krb5_config_get_strings(context
, NULL
,
469 msg
= (*passwd_quality_check
) (context
, principal
, pwd_data
);
471 krb5_set_error_message(context
, 0, "password policy failed: %s", msg
);
478 for(vp
= v
; *vp
; vp
++) {
479 proc
= find_func(context
, *vp
);
481 msg
= "failed to find password verifier function";
482 krb5_set_error_message(context
, 0, "Failed to find password policy "
483 "function: %s", *vp
);
486 ret
= (proc
->func
)(context
, principal
, pwd_data
, NULL
,
487 error_msg
, sizeof(error_msg
));
489 krb5_set_error_message(context
, 0, "Password policy "
491 proc
->name
, error_msg
);
496 krb5_config_free_strings(v
);
498 /* If the default quality check isn't used, lets check that the
499 * old quality function the user have set too */
500 if (msg
== NULL
&& passwd_quality_check
!= min_length_passwd_quality_v0
) {
501 msg
= (*passwd_quality_check
) (context
, principal
, pwd_data
);
503 krb5_set_error_message(context
, 0, "(old) password policy "
504 "failed with %s", msg
);