xref: /illumos-gate/usr/src/lib/pam_modules/authtok_check/authtok_check.c (revision bdfc6d18da790deeec2e0eb09c625902defe2498)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/varargs.h>
31 #include <sys/param.h>
32 #include <sys/sysmacros.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <deflt.h>
36 #include <security/pam_appl.h>
37 #include <security/pam_modules.h>
38 #include <security/pam_impl.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <unistd.h>
42 #include <syslog.h>
43 #include <libintl.h>
44 #include <errno.h>
45 #include <pwd.h>
46 #include "packer.h"
47 
48 #include <passwdutil.h>
49 
50 #define	PWADMIN "/etc/default/passwd"
51 
52 #define	MINLENGTH	6
53 #define	MINDIFF		3
54 #define	MINALPHA	2
55 #define	MINNONALPHA	1
56 
57 mutex_t dictlock = DEFAULTMUTEX;
58 
59 /*
60  * We implement:
61  *	PASSLENGTH (int)	minimum password length
62  *	NAMECHECK (yes/no)	perform comparison of password and loginname
63  *	MINDIFF (int)		minimum number of character-positions in which
64  *				the old	and the new password should differ.
65  *	MINALPHA (int)		minimum number of Alpha characters
66  *	MINUPPER (int)		minimum number of upper-case characters
67  *	MINLOWER (int)		minimum number of lower-case characters
68  *	MAXREPEATS (int)	maximum number of consecutively repeating chars
69  *	WHITESPACE (yes/no)	Are whitespaces allowed?
70  *
71  * Furthermore, these two mutualy exclusive groups of options are allowed:
72  *
73  *	MINNONALPHA (int)	minimum number of characters from the
74  *				character classes [ punct, space, digit ]
75  *				if WHITESPACE == NO, whitespaces don't count.
76  * and
77  *	MINSPECIAL (int)	minimum number of punctuation characters.
78  *				if WHITESPACE != NO, whitespace is seen as
79  *				a "special" character.
80  *	MINDIGIT (int)		minimum number of digits
81  *
82  * specifying options from both groups results in an error to syslog and
83  * failure to change the password.
84  *
85  * NOTE:
86  *	HISTORY is implemented at the repository level (passwdutil).
87  */
88 
89 /*
90  * default password-strength-values, compiled-in or stored in PWADMIN
91  * are kept in here
92  */
93 struct pwdefaults {
94 	boolean_t server_policy;	/* server policy flag from pam.conf */
95 	uint_t minlength;	/* minimum password lenght */
96 	uint_t maxlength;	/* maximum (significant) length */
97 	boolean_t do_namecheck;	/* check password against user's gecos */
98 	char db_location[MAXPATHLEN]; /* location of the generated database */
99 	boolean_t do_dictcheck;	/* perform dictionary lookup */
100 	char *dicts;		/* list of dictionaries configured */
101 	uint_t mindiff;		/* old and new should differ by this much */
102 	uint_t minalpha;	/* minimum alpha characters required */
103 	uint_t minupper;	/* minimum uppercase characters required */
104 	uint_t minlower;	/* minimum lowercase characters required */
105 	uint_t minnonalpha; 	/* minimum special (non alpha) required */
106 	uint_t maxrepeat;	/* maximum number of repeating chars allowed */
107 	uint_t minspecial;	/* punctuation characters */
108 	uint_t mindigit;	/* minimum number of digits required */
109 	boolean_t whitespace;	/* is whitespace allowed in a password */
110 };
111 
112 
113 /*PRINTFLIKE3*/
114 void
115 error(pam_handle_t *pamh, int flags, char *fmt, ...)
116 {
117 	va_list ap;
118 	char msg[1][PAM_MAX_MSG_SIZE];
119 
120 	va_start(ap, fmt);
121 	(void) vsnprintf(msg[0], sizeof (msg[0]), fmt, ap);
122 	va_end(ap);
123 	if ((flags & PAM_SILENT) == 0)
124 		(void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, msg, NULL);
125 }
126 
127 int
128 defread_int(char *name, uint_t *ip)
129 {
130 	char *q;
131 	int r = 0;
132 	if ((q = defread(name)) != NULL) {
133 		if (!isdigit(*q)) {
134 			syslog(LOG_ERR, "pam_authtok_check: %s contains "
135 			    "non-integer value for %s: %s. "
136 			    "Using default instead.", PWADMIN, name, q);
137 		} else {
138 			*ip = atoi(q);
139 			r = 1;
140 		}
141 	}
142 	return (r);
143 }
144 
145 /*
146  * fill in static defaults, and augment with settings from PWADMIN
147  * get system defaults with regard to maximum password length
148  */
149 int
150 get_passwd_defaults(pam_handle_t *pamh, char *user, struct pwdefaults *p)
151 {
152 	char *q;
153 	boolean_t minnonalpha_defined = B_FALSE;
154 	pwu_repository_t *pwu_rep;
155 	struct pam_repository *pam_rep;
156 	attrlist attr[2];
157 	int result;
158 	char *progname;
159 
160 	(void) pam_get_item(pamh, PAM_SERVICE, (void **)&progname);
161 
162 	/* Module defaults */
163 	p->minlength = MINLENGTH;
164 	p->do_namecheck = B_TRUE;
165 	p->do_dictcheck = B_FALSE;
166 	p->dicts = NULL;
167 	p->mindiff = MINDIFF;
168 	p->minalpha = MINALPHA;
169 	p->minnonalpha = MINNONALPHA;
170 	p->minupper = 0;	/* not configured by default */
171 	p->minlower = 0;	/* not configured by default */
172 	p->maxrepeat = 0;	/* not configured by default */
173 
174 	p->minspecial = 0;
175 	p->mindigit = 0;
176 	p->whitespace = B_TRUE;
177 
178 	if (defopen(PWADMIN) != 0)
179 		return (PAM_SUCCESS);
180 
181 	(void) defread_int("PASSLENGTH=", &p->minlength);
182 
183 	if ((q = defread("NAMECHECK=")) != NULL && strcasecmp(q, "NO") == 0)
184 		p->do_namecheck = B_FALSE;
185 
186 	if ((q = defread("DICTIONLIST=")) != NULL) {
187 		if ((p->dicts = strdup(q)) == NULL) {
188 			syslog(LOG_ERR, "pam_authtok_check: out of memory");
189 			(void) defopen(NULL);
190 			return (PAM_BUF_ERR);
191 
192 		}
193 		p->do_dictcheck = B_TRUE;
194 	} else
195 		p->dicts = NULL;
196 
197 	if ((q = defread("DICTIONDBDIR=")) != NULL) {
198 		if (strlcpy(p->db_location, q, sizeof (p->db_location)) >=
199 		    sizeof (p->db_location)) {
200 			syslog(LOG_ERR, "pam_authtok_check: value for "
201 			    "DICTIONDBDIR too large.");
202 			(void) defopen(NULL);
203 			return (PAM_SYSTEM_ERR);
204 		}
205 		p->do_dictcheck = B_TRUE;
206 	} else
207 		(void) strlcpy(p->db_location, CRACK_DIR,
208 		    sizeof (p->db_location));
209 
210 	(void) defread_int("MINDIFF=", &p->mindiff);
211 	(void) defread_int("MINALPHA=", &p->minalpha);
212 	(void) defread_int("MINUPPER=", &p->minupper);
213 	(void) defread_int("MINLOWER=", &p->minlower);
214 	if (defread_int("MINNONALPHA=", &p->minnonalpha))
215 		minnonalpha_defined = B_TRUE;
216 	(void) defread_int("MAXREPEATS=", &p->maxrepeat);
217 
218 	if (defread_int("MINSPECIAL=", &p->minspecial)) {
219 		if (minnonalpha_defined) {
220 			syslog(LOG_ERR, "pam_authtok_check: %s contains "
221 			    "definition for MINNONALPHA and for MINSPECIAL. "
222 			    "These options are mutually exclusive.", PWADMIN);
223 			(void) defopen(NULL);
224 			return (PAM_SYSTEM_ERR);
225 		}
226 		p->minnonalpha = 0;
227 	}
228 
229 	if (defread_int("MINDIGIT=", &p->mindigit)) {
230 		if (minnonalpha_defined) {
231 			syslog(LOG_ERR, "pam_authtok_check: %s contains "
232 			    "definition for MINNONALPHA and for MINDIGIT. "
233 			    "These options are mutually exclusive.", PWADMIN);
234 			(void) defopen(NULL);
235 			return (PAM_SYSTEM_ERR);
236 		}
237 		p->minnonalpha = 0;
238 	}
239 
240 	if ((q = defread("WHITESPACE=")) != NULL)
241 		p->whitespace =
242 		    (strcasecmp(q, "no") == 0 || strcmp(q, "0") == 0)
243 		    ? B_FALSE : B_TRUE;
244 
245 	(void) defopen(NULL);
246 
247 	/*
248 	 * Determine the number of significant characters in a password
249 	 *
250 	 * we find out where the user information came from (which repository),
251 	 * and which password-crypt-algorithm is to be used (based on the
252 	 * old password, or the system default).
253 	 *
254 	 * If the user comes from a repository other than FILES/NIS/NIS+,
255 	 * the module-flag "server_policy" means that we don't perform
256 	 * any checks on the user, but let the repository decide instead.
257 	 */
258 
259 	(void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&pam_rep);
260 	if (pam_rep != NULL) {
261 		if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL)
262 			return (PAM_BUF_ERR);
263 		pwu_rep->type = pam_rep->type;
264 		pwu_rep->scope = pam_rep->scope;
265 		pwu_rep->scope_len = pam_rep->scope_len;
266 	} else {
267 		pwu_rep = PWU_DEFAULT_REP;
268 	}
269 
270 	attr[0].type = ATTR_PASSWD; attr[0].next = &attr[1];
271 	attr[1].type = ATTR_REP_NAME; attr[1].next = NULL;
272 	result = __get_authtoken_attr(user, pwu_rep, attr);
273 	if (pwu_rep != PWU_DEFAULT_REP)
274 		free(pwu_rep);
275 
276 	if (result != PWU_SUCCESS) {
277 		/*
278 		 * In the unlikely event that we can't obtain any info about
279 		 * the users password, we assume the most strict scenario.
280 		 */
281 		p->maxlength = _PASS_MAX_XPG;
282 	} else {
283 		char *oldpw = attr[0].data.val_s;
284 		char *repository = attr[1].data.val_s;
285 		if ((strcmp(repository, "files") == 0 ||
286 		    strcmp(repository, "nis") == 0 ||
287 		    strcmp(repository, "nisplus") == 0) ||
288 		    p->server_policy == B_FALSE) {
289 			char *salt;
290 			/*
291 			 * We currently need to supply this dummy to
292 			 * crypt_gensalt(). This will change RSN.
293 			 */
294 			struct passwd dummy;
295 
296 			dummy.pw_name = user;
297 
298 			salt = crypt_gensalt(oldpw, &dummy);
299 			if (salt && *salt == '$')
300 				p->maxlength = _PASS_MAX;
301 			else
302 				p->maxlength = _PASS_MAX_XPG;
303 
304 			free(salt);
305 
306 			p->server_policy = B_FALSE; /* we perform checks */
307 		} else {
308 			/* not files, nis or nisplus AND server_policy is set */
309 			p->maxlength = _PASS_MAX;
310 		}
311 		free(attr[0].data.val_s);
312 		free(attr[1].data.val_s);
313 	}
314 
315 	/* sanity check of the configured parameters */
316 	if (p->minlength < p->mindigit + p->minspecial + p->minnonalpha +
317 	    p->minalpha) {
318 		syslog(LOG_ERR, "%s: pam_authtok_check: Defined minimum "
319 		    "password length (PASSLENGTH=%d) is less then minimum "
320 		    "characters in the various classes (%d)", progname,
321 		    p->minlength,
322 		    p->mindigit + p->minspecial + p->minnonalpha + p->minalpha);
323 		p->minlength = p->mindigit + p->minspecial + p->minnonalpha +
324 		    p->minalpha;
325 		syslog(LOG_ERR, "%s: pam_authtok_check: effective "
326 		    "PASSLENGTH set to %d.", progname, p->minlength);
327 		/* this won't lead to failure */
328 	}
329 
330 	if (p->maxlength < p->minlength) {
331 		syslog(LOG_ERR, "%s: pam_authtok_check: The configured "
332 		    "minimum password length (PASSLENGTH=%d) is larger than "
333 		    "the number of significant characters the current "
334 		    "encryption algorithm uses (%d). See policy.conf(4) for "
335 		    "alternative password encryption algorithms.", progname);
336 		/* this won't lead to failure */
337 	}
338 
339 	return (PAM_SUCCESS);
340 }
341 
342 /*
343  * free_passwd_defaults(struct pwdefaults *p)
344  *
345  * free space occupied by the defaults read from PWADMIN
346  */
347 void
348 free_passwd_defaults(struct pwdefaults *p)
349 {
350 	if (p && p->dicts)
351 		free(p->dicts);
352 }
353 
354 /*
355  * check_circular():
356  * This function return 1 if string "t" is a circular shift of
357  * string "s", else it returns 0. -1 is returned on failure.
358  * We also check to see if string "t" is a reversed-circular shift
359  * of string "s", i.e. "ABCDE" vs. "DCBAE".
360  */
361 static int
362 check_circular(s, t)
363 	char *s, *t;
364 {
365 	char c, *p, *o, *r, *buff, *ubuff, *pubuff;
366 	unsigned int i, j, k, l, m;
367 	size_t len;
368 	int ret = 0;
369 
370 	i = strlen(s);
371 	l = strlen(t);
372 	if (i != l)
373 		return (0);
374 	len = i + 1;
375 
376 	buff = malloc(len);
377 	ubuff = malloc(len);
378 	pubuff = malloc(len);
379 
380 	if (buff == NULL || ubuff == NULL || pubuff == NULL) {
381 		syslog(LOG_ERR, "pam_authtok_check: out of memory.");
382 		return (-1);
383 	}
384 
385 	m = 2;
386 	o = &ubuff[0];
387 	for (p = s; c = *p++; *o++ = c)
388 		if (islower(c))
389 			c = toupper(c);
390 	*o = '\0';
391 	o = &pubuff[0];
392 	for (p = t; c = *p++; *o++ = c)
393 		if (islower(c))
394 			c = toupper(c);
395 
396 	*o = '\0';
397 
398 	p = &ubuff[0];
399 	while (m--) {
400 		for (k = 0; k  <  i; k++) {
401 			c = *p++;
402 			o = p;
403 			l = i;
404 			r = &buff[0];
405 			while (--l)
406 				*r++ = *o++;
407 			*r++ = c;
408 			*r = '\0';
409 			p = &buff[0];
410 			if (strcmp(p, pubuff) == 0) {
411 				ret = 1;
412 				goto out;
413 			}
414 		}
415 		p = p + i;
416 		r = &ubuff[0];
417 		j = i;
418 		while (j--)
419 			*--p = *r++;	/* reverse test-string for m==0 pass */
420 	}
421 out:
422 	(void) memset(buff, 0, len);
423 	(void) memset(ubuff, 0, len);
424 	(void) memset(pubuff, 0, len);
425 	free(buff);
426 	free(ubuff);
427 	free(pubuff);
428 	return (ret);
429 }
430 
431 
432 /*
433  * count the different character classes present in the password.
434  */
435 int
436 check_composition(char *pw, struct pwdefaults *pwdef, pam_handle_t *pamh,
437     int flags)
438 {
439 	uint_t alpha_cnt = 0;
440 	uint_t upper_cnt = 0;
441 	uint_t lower_cnt = 0;
442 	uint_t special_cnt = 0;
443 	uint_t whitespace_cnt = 0;
444 	uint_t digit_cnt = 0;
445 	uint_t maxrepeat = 0;
446 	uint_t repeat = 1;
447 	int ret = 0;
448 	char *progname;
449 	char errmsg[256];
450 	char lastc = '\0';
451 	uint_t significant = pwdef->maxlength;
452 	char *w;
453 
454 	(void) pam_get_item(pamh, PAM_SERVICE, (void **)&progname);
455 
456 	/* go over the password gathering statistics */
457 	for (w = pw; significant != 0 && *w != '\0'; w++, significant--) {
458 		if (isalpha(*w)) {
459 			alpha_cnt++;
460 			if (isupper(*w)) {
461 				upper_cnt++;
462 			} else {
463 				lower_cnt++;
464 			}
465 		} else if (isspace(*w))
466 			whitespace_cnt++;
467 		else if (isdigit(*w))
468 			digit_cnt++;
469 		else
470 			special_cnt++;
471 		if (*w == lastc) {
472 			if (++repeat > maxrepeat)
473 				maxrepeat = repeat;
474 		} else {
475 			repeat = 1;
476 		}
477 		lastc = *w;
478 	}
479 
480 	/*
481 	 * If we only consider part of the password (the first maxlength
482 	 * characters) we give a modified error message. Otherwise, a
483 	 * user entering FooBar1234 with PASSLENGTH=6, MINDIGIT=4, while
484 	 * we're using the default UNIX crypt (8 chars significant),
485 	 * would not understand what's going on when he's told that
486 	 * "The password should contain at least 4 digits"...
487 	 * Instead, we now well him
488 	 * "The first 8 characters of the password should contain at least
489 	 *  4 digits."
490 	 */
491 	if (pwdef->maxlength < strlen(pw))
492 		/*
493 		 * TRANSLATION_NOTE
494 		 * - Make sure the % and %% come over intact
495 		 * - The last %%s will be replaced by strings like
496 		 *	"alphabetic character(s)"
497 		 *	"numeric or special character(s)"
498 		 *	"special character(s)"
499 		 *	"digit(s)"
500 		 *	"uppercase alpha character(s)"
501 		 *	"lowercase alpha character(s)"
502 		 *   So the final string written to the user might become
503 		 * "passwd: The first 8 characters of the password must contain
504 		 *   at least 4 uppercase alpha characters(s)"
505 		 */
506 		(void) snprintf(errmsg, sizeof (errmsg), dgettext(TEXT_DOMAIN,
507 		    "%s: The first %d characters of the password must "
508 		    "contain at least %%d %%s."), progname, pwdef->maxlength);
509 	else
510 		/*
511 		 * TRANSLATION_NOTE
512 		 * - Make sure the % and %% come over intact
513 		 * - The last %%s will be replaced by strings like
514 		 *	"alphabetic character(s)"
515 		 *	"numeric or special character(s)"
516 		 *	"special character(s)"
517 		 *	"digit(s)"
518 		 *	"uppercase alpha character(s)"
519 		 *	"lowercase alpha character(s)"
520 		 *   So the final string written to the user might become
521 		 * "passwd: The password must contain at least 4 uppercase
522 		 *   alpha characters(s)"
523 		 */
524 		(void) snprintf(errmsg, sizeof (errmsg), dgettext(TEXT_DOMAIN,
525 		    "%s: The password must contain at least %%d %%s."),
526 		    progname);
527 
528 	/* Check for whitespace first since it influences special counts */
529 	if (whitespace_cnt > 0 && pwdef->whitespace == B_FALSE) {
530 		error(pamh, flags, dgettext(TEXT_DOMAIN,
531 		    "%s: Whitespace characters are not allowed."), progname);
532 		ret = 1;
533 		goto out;
534 	}
535 
536 	/*
537 	 * Once we get here, whitespace_cnt is either 0, or whitespaces are
538 	 * to be treated a special characters.
539 	 */
540 
541 	if (alpha_cnt < pwdef->minalpha) {
542 		error(pamh, flags, errmsg, pwdef->minalpha,
543 		    dgettext(TEXT_DOMAIN, "alphabetic character(s)"));
544 		ret = 1;
545 		goto out;
546 	}
547 
548 	if (pwdef->minnonalpha > 0) {
549 		/* specials are defined by MINNONALPHA */
550 		/* nonalpha = special+whitespace+digit */
551 		if ((special_cnt + whitespace_cnt + digit_cnt) <
552 		    pwdef->minnonalpha) {
553 			error(pamh, flags, errmsg, pwdef->minnonalpha,
554 			    dgettext(TEXT_DOMAIN,
555 				"numeric or special character(s)"));
556 			ret = 1;
557 			goto out;
558 		}
559 	} else {
560 		/* specials are defined by MINSPECIAL and/or MINDIGIT */
561 		if ((special_cnt + whitespace_cnt) < pwdef->minspecial) {
562 			error(pamh, flags, errmsg, pwdef->minspecial,
563 			    dgettext(TEXT_DOMAIN, "special character(s)"));
564 			ret = 1;
565 			goto out;
566 		}
567 		if (digit_cnt < pwdef->mindigit) {
568 			error(pamh, flags, errmsg, pwdef->mindigit,
569 			    dgettext(TEXT_DOMAIN, "digit(s)"));
570 			ret = 1;
571 			goto out;
572 		}
573 	}
574 
575 	if (upper_cnt < pwdef->minupper) {
576 		error(pamh, flags, errmsg, pwdef->minupper,
577 		    dgettext(TEXT_DOMAIN, "uppercase alpha character(s)"));
578 		ret = 1;
579 		goto out;
580 	}
581 	if (lower_cnt < pwdef->minlower) {
582 		error(pamh, flags, errmsg, pwdef->minlower,
583 		    dgettext(TEXT_DOMAIN, "lowercase alpha character(s)"));
584 		ret = 1;
585 		goto out;
586 	}
587 
588 	if (pwdef->maxrepeat > 0 && maxrepeat > pwdef->maxrepeat) {
589 		error(pamh, flags, dgettext(TEXT_DOMAIN,
590 		    "%s: Too many consecutively repeating characters. "
591 		    "Maximum allowed is %d."), progname, pwdef->maxrepeat);
592 		ret = 1;
593 	}
594 out:
595 	return (ret);
596 }
597 
598 /*
599  * make sure that old and new password differ by at least 'mindiff'
600  * positions. Return 0 if OK, 1 otherwise
601  */
602 int
603 check_diff(char *pw, char *opw, struct pwdefaults *pwdef, pam_handle_t *pamh,
604     int flags)
605 {
606 	size_t pwlen, opwlen, max;
607 	unsigned int diff;	/* difference between old and new */
608 
609 	if (opw == NULL)
610 		opw = "";
611 
612 	max = pwdef->maxlength;
613 	pwlen = MIN(strlen(pw), max);
614 	opwlen = MIN(strlen(opw), max);
615 
616 	if (pwlen > opwlen)
617 		diff = pwlen - opwlen;
618 	else
619 		diff = opwlen - pwlen;
620 
621 	while (*opw != '\0' && *pw != '\0' && max-- != 0) {
622 		if (*opw != *pw)
623 			diff++;
624 		opw++;
625 		pw++;
626 	}
627 
628 	if (diff  < pwdef->mindiff) {
629 		char *progname;
630 
631 		(void) pam_get_item(pamh, PAM_SERVICE, (void **)&progname);
632 
633 		error(pamh, flags, dgettext(TEXT_DOMAIN,
634 		    "%s: The first %d characters of the old and new passwords "
635 		    "must differ by at least %d positions."), progname,
636 		    pwdef->maxlength, pwdef->mindiff);
637 		return (1);
638 	}
639 
640 	return (0);
641 }
642 
643 /*
644  * check to see if password is in one way or another based on a
645  * dictionary word. Returns 0 if password is OK, 1 if it is based
646  * on a dictionary word and hence should be rejected.
647  */
648 int
649 check_dictionary(char *pw, struct pwdefaults *pwdef, pam_handle_t *pamh,
650     int flags)
651 {
652 	int crack_ret;
653 	int ret;
654 	char *progname;
655 
656 	(void) pam_get_item(pamh, PAM_SERVICE, (void **)&progname);
657 
658 	/* dictionary check isn't MT-safe */
659 	(void) mutex_lock(&dictlock);
660 
661 	if (pwdef->dicts &&
662 	    make_dict_database(pwdef->dicts, pwdef->db_location) != 0) {
663 		(void) mutex_unlock(&dictlock);
664 		syslog(LOG_ERR, "pam_authtok_check:pam_sm_chauthtok: "
665 		    "Dictionary database not present.");
666 		error(pamh, flags, dgettext(TEXT_DOMAIN,
667 		    "%s: password dictionary missing."), progname);
668 		return (PAM_SYSTEM_ERR);
669 	}
670 
671 	crack_ret = DictCheck(pw, pwdef->db_location);
672 
673 	(void) mutex_unlock(&dictlock);
674 
675 	switch (crack_ret) {
676 	case DATABASE_OPEN_FAIL:
677 		syslog(LOG_ERR, "pam_authtok_check:pam_sm_chauthtok: "
678 		    "dictionary database open failure: %s", strerror(errno));
679 		error(pamh, flags, dgettext(TEXT_DOMAIN,
680 		    "%s: failed to open dictionary database."), progname);
681 		ret = PAM_SYSTEM_ERR;
682 		break;
683 	case DICTIONARY_WORD:
684 		error(pamh, flags, dgettext(TEXT_DOMAIN,
685 		    "%s: password is based on a dictionary word."), progname);
686 		ret = PAM_AUTHTOK_ERR;
687 		break;
688 	case REVERSE_DICTIONARY_WORD:
689 		error(pamh, flags, dgettext(TEXT_DOMAIN,
690 		    "%s: password is based on a reversed dictionary word."),
691 		    progname);
692 		ret = PAM_AUTHTOK_ERR;
693 		break;
694 	default:
695 		ret = PAM_SUCCESS;
696 		break;
697 	}
698 	return (ret);
699 }
700 
701 int
702 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
703 {
704 	int	debug = 0;
705 	int	retcode = 0;
706 	int	force_check = 0;
707 	int 	i;
708 	size_t	pwlen;
709 	char	*usrname;
710 	char	*pwbuf, *opwbuf;
711 	pwu_repository_t *pwu_rep = PWU_DEFAULT_REP;
712 	pam_repository_t *pwd_rep = NULL;
713 	struct pwdefaults pwdef;
714 	char *progname;
715 
716 	/* needs to be set before option processing */
717 	pwdef.server_policy = B_FALSE;
718 
719 	for (i = 0; i < argc; i++) {
720 		if (strcmp(argv[i], "debug") == 0)
721 			debug = 1;
722 		if (strcmp(argv[i], "force_check") == 0)
723 			force_check = 1;
724 		if (strcmp(argv[i], "server_policy") == 0)
725 			pwdef.server_policy = B_TRUE;
726 	}
727 
728 	if (debug)
729 		syslog(LOG_AUTH | LOG_DEBUG,
730 		    "pam_authtok_check: pam_sm_chauthok called(%x) "
731 		    "force_check = %d", flags, force_check);
732 
733 	if ((flags & PAM_PRELIM_CHECK) == 0)
734 		return (PAM_IGNORE);
735 
736 	(void) pam_get_item(pamh, PAM_SERVICE, (void **)&progname);
737 	(void) pam_get_item(pamh, PAM_USER, (void **)&usrname);
738 	if (usrname == NULL || *usrname == '\0') {
739 		syslog(LOG_ERR, "pam_authtok_check: username name is empty");
740 		return (PAM_USER_UNKNOWN);
741 	}
742 
743 	(void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&pwbuf);
744 	(void) pam_get_item(pamh, PAM_OLDAUTHTOK, (void **)&opwbuf);
745 	if (pwbuf == NULL)
746 		return (PAM_AUTHTOK_ERR);
747 
748 	/* none of these checks holds if caller say so */
749 	if ((flags & PAM_NO_AUTHTOK_CHECK) != 0 && force_check == 0)
750 		return (PAM_SUCCESS);
751 
752 	/* read system-defaults */
753 	retcode = get_passwd_defaults(pamh, usrname, &pwdef);
754 	if (retcode != PAM_SUCCESS)
755 		return (retcode);
756 
757 	if (debug) {
758 		syslog(LOG_AUTH | LOG_DEBUG,
759 		    "pam_authtok_check: MAXLENGTH= %d, server_policy = %s",
760 		    pwdef.maxlength, pwdef.server_policy ? "true" : "false");
761 		syslog(LOG_AUTH | LOG_DEBUG,
762 		    "pam_authtok_check: PASSLENGTH= %d", pwdef.minlength);
763 		syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: NAMECHECK=%s",
764 		    pwdef.do_namecheck == B_TRUE ? "Yes" : "No");
765 		syslog(LOG_AUTH | LOG_DEBUG,
766 		    "pam_authtok_check: do_dictcheck = %s\n",
767 		    pwdef.do_dictcheck ? "true" : "false");
768 		if (pwdef.do_dictcheck) {
769 			syslog(LOG_AUTH | LOG_DEBUG,
770 			    "pam_authtok_check: DICTIONLIST=%s",
771 			    (pwdef.dicts != NULL) ? pwdef.dicts : "<not set>");
772 			syslog(LOG_AUTH | LOG_DEBUG,
773 			    "pam_authtok_check: DICTIONDBDIR=%s",
774 			    pwdef.db_location);
775 		}
776 		syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: MINDIFF=%d",
777 		    pwdef.mindiff);
778 		syslog(LOG_AUTH | LOG_DEBUG,
779 		    "pam_authtok_check: MINALPHA=%d, MINNONALPHA=%d",
780 		    pwdef.minalpha, pwdef.minnonalpha);
781 		syslog(LOG_AUTH | LOG_DEBUG,
782 		    "pam_authtok_check: MINSPECIAL=%d, MINDIGIT=%d",
783 		    pwdef.minspecial, pwdef.mindigit);
784 		syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: WHITESPACE=%s",
785 		    pwdef.whitespace ? "YES" : "NO");
786 		syslog(LOG_AUTH | LOG_DEBUG,
787 		    "pam_authtok_check: MINUPPER=%d, MINLOWER=%d",
788 		    pwdef.minupper, pwdef.minlower);
789 		syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: MAXREPEATS=%d",
790 		    pwdef.maxrepeat);
791 	}
792 
793 	/*
794 	 * If server policy is still true (might be changed from the
795 	 * value specified in /etc/pam.conf by get_passwd_defaults()),
796 	 * we return ignore and let the server do all the checks.
797 	 */
798 	if (pwdef.server_policy == B_TRUE) {
799 		free_passwd_defaults(&pwdef);
800 		return (PAM_IGNORE);
801 	}
802 
803 	/*
804 	 * XXX: JV: we can't really make any assumption on the length of
805 	 *	the password that will be used by the crypto algorithm.
806 	 *	for UNIX-style encryption, minalpha=5,minnonalpha=5 might
807 	 *	be impossible, but not for MD5 style hashes... what to do?
808 	 *
809 	 *	since we don't know what alg. will be used, we operate on
810 	 *	the password as entered, so we don't sanity check anything
811 	 *	for now.
812 	 */
813 
814 	/*
815 	 * Make sure new password is long enough
816 	 */
817 	pwlen = strlen(pwbuf);
818 
819 	if (pwlen < pwdef.minlength) {
820 		error(pamh, flags, dgettext(TEXT_DOMAIN,
821 		    "%s: Password too short - must be at least %d "
822 		    "characters."), progname, pwdef.minlength);
823 		free_passwd_defaults(&pwdef);
824 		return (PAM_AUTHTOK_ERR);
825 	}
826 
827 	/* Make sure the password doesn't equal--a shift of--the username */
828 	if (pwdef.do_namecheck) {
829 		switch (check_circular(usrname, pwbuf)) {
830 		case 1:
831 			error(pamh, flags, dgettext(TEXT_DOMAIN,
832 			    "%s: Password cannot be circular shift of "
833 			    "logonid."), progname);
834 			free_passwd_defaults(&pwdef);
835 			return (PAM_AUTHTOK_ERR);
836 		case -1:
837 			free_passwd_defaults(&pwdef);
838 			return (PAM_BUF_ERR);
839 		default:
840 			break;
841 		}
842 	}
843 
844 	/* Check if new password is in history list. */
845 	(void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&pwd_rep);
846 	if (pwd_rep != NULL) {
847 		if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL)
848 			return (PAM_BUF_ERR);
849 		pwu_rep->type = pwd_rep->type;
850 		pwu_rep->scope = pwd_rep->scope;
851 		pwu_rep->scope_len = pwd_rep->scope_len;
852 	}
853 
854 	if (__check_history(usrname, pwbuf, pwu_rep) == PWU_SUCCESS) {
855 		/* password found in history */
856 		error(pamh, flags, dgettext(TEXT_DOMAIN,
857 		    "%s: Password in history list."), progname);
858 		if (pwu_rep != PWU_DEFAULT_REP)
859 			free(pwu_rep);
860 		free_passwd_defaults(&pwdef);
861 		return (PAM_AUTHTOK_ERR);
862 	}
863 
864 	if (pwu_rep != PWU_DEFAULT_REP)
865 		free(pwu_rep);
866 
867 	/* check MINALPHA, MINLOWER, etc. */
868 	if (check_composition(pwbuf, &pwdef, pamh, flags) != 0) {
869 		free_passwd_defaults(&pwdef);
870 		return (PAM_AUTHTOK_ERR);
871 	}
872 
873 	/* make sure the old and new password are not too much alike */
874 	if (check_diff(pwbuf, opwbuf, &pwdef, pamh, flags) != 0) {
875 		free_passwd_defaults(&pwdef);
876 		return (PAM_AUTHTOK_ERR);
877 	}
878 
879 	/* dictionary check */
880 	if (pwdef.do_dictcheck) {
881 		retcode = check_dictionary(pwbuf, &pwdef, pamh, flags);
882 		if (retcode != PAM_SUCCESS) {
883 			free_passwd_defaults(&pwdef);
884 			return (retcode);
885 		}
886 	}
887 
888 	free_passwd_defaults(&pwdef);
889 	/* password has passed all tests: it's strong enough */
890 	return (PAM_SUCCESS);
891 }
892