xref: /illumos-gate/usr/src/lib/pam_modules/authtok_check/authtok_check.c (revision cbea7aca3fd7787405cbdbd93752998f03dfc25f)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5b9175c69SKenjiro Tsuji  * Common Development and Distribution License (the "License").
6b9175c69SKenjiro Tsuji  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22b9175c69SKenjiro Tsuji  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
2448bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
25*cbea7acaSDominik Hassler  * Copyright 2023 OmniOS Community Edition (OmniOSce) Association.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include <sys/types.h>
297c478bd9Sstevel@tonic-gate #include <sys/varargs.h>
307c478bd9Sstevel@tonic-gate #include <sys/param.h>
317c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
327c478bd9Sstevel@tonic-gate #include <stdio.h>
337c478bd9Sstevel@tonic-gate #include <stdlib.h>
347c478bd9Sstevel@tonic-gate #include <deflt.h>
357c478bd9Sstevel@tonic-gate #include <security/pam_appl.h>
367c478bd9Sstevel@tonic-gate #include <security/pam_modules.h>
377c478bd9Sstevel@tonic-gate #include <security/pam_impl.h>
387c478bd9Sstevel@tonic-gate #include <string.h>
397c478bd9Sstevel@tonic-gate #include <ctype.h>
407c478bd9Sstevel@tonic-gate #include <unistd.h>
417c478bd9Sstevel@tonic-gate #include <syslog.h>
427c478bd9Sstevel@tonic-gate #include <libintl.h>
437c478bd9Sstevel@tonic-gate #include <errno.h>
447c478bd9Sstevel@tonic-gate #include <pwd.h>
457c478bd9Sstevel@tonic-gate #include "packer.h"
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate #include <passwdutil.h>
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate #define	PWADMIN "/etc/default/passwd"
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate #define	MINLENGTH	6
527c478bd9Sstevel@tonic-gate #define	MINDIFF		3
537c478bd9Sstevel@tonic-gate #define	MINALPHA	2
547c478bd9Sstevel@tonic-gate #define	MINNONALPHA	1
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate mutex_t dictlock = DEFAULTMUTEX;
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate /*
597c478bd9Sstevel@tonic-gate  * We implement:
607c478bd9Sstevel@tonic-gate  *	PASSLENGTH (int)	minimum password length
617c478bd9Sstevel@tonic-gate  *	NAMECHECK (yes/no)	perform comparison of password and loginname
627c478bd9Sstevel@tonic-gate  *	MINDIFF (int)		minimum number of character-positions in which
637c478bd9Sstevel@tonic-gate  *				the old	and the new password should differ.
647c478bd9Sstevel@tonic-gate  *	MINALPHA (int)		minimum number of Alpha characters
657c478bd9Sstevel@tonic-gate  *	MINUPPER (int)		minimum number of upper-case characters
667c478bd9Sstevel@tonic-gate  *	MINLOWER (int)		minimum number of lower-case characters
677c478bd9Sstevel@tonic-gate  *	MAXREPEATS (int)	maximum number of consecutively repeating chars
687c478bd9Sstevel@tonic-gate  *	WHITESPACE (yes/no)	Are whitespaces allowed?
697c478bd9Sstevel@tonic-gate  *
707c478bd9Sstevel@tonic-gate  * Furthermore, these two mutualy exclusive groups of options are allowed:
717c478bd9Sstevel@tonic-gate  *
727c478bd9Sstevel@tonic-gate  *	MINNONALPHA (int)	minimum number of characters from the
737c478bd9Sstevel@tonic-gate  *				character classes [ punct, space, digit ]
747c478bd9Sstevel@tonic-gate  *				if WHITESPACE == NO, whitespaces don't count.
757c478bd9Sstevel@tonic-gate  * and
767c478bd9Sstevel@tonic-gate  *	MINSPECIAL (int)	minimum number of punctuation characters.
777c478bd9Sstevel@tonic-gate  *				if WHITESPACE != NO, whitespace is seen as
787c478bd9Sstevel@tonic-gate  *				a "special" character.
797c478bd9Sstevel@tonic-gate  *	MINDIGIT (int)		minimum number of digits
807c478bd9Sstevel@tonic-gate  *
817c478bd9Sstevel@tonic-gate  * specifying options from both groups results in an error to syslog and
827c478bd9Sstevel@tonic-gate  * failure to change the password.
837c478bd9Sstevel@tonic-gate  *
847c478bd9Sstevel@tonic-gate  * NOTE:
857c478bd9Sstevel@tonic-gate  *	HISTORY is implemented at the repository level (passwdutil).
867c478bd9Sstevel@tonic-gate  */
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate /*
897c478bd9Sstevel@tonic-gate  * default password-strength-values, compiled-in or stored in PWADMIN
907c478bd9Sstevel@tonic-gate  * are kept in here
917c478bd9Sstevel@tonic-gate  */
927c478bd9Sstevel@tonic-gate struct pwdefaults {
937c478bd9Sstevel@tonic-gate 	boolean_t server_policy;	/* server policy flag from pam.conf */
947c478bd9Sstevel@tonic-gate 	uint_t minlength;	/* minimum password lenght */
957c478bd9Sstevel@tonic-gate 	uint_t maxlength;	/* maximum (significant) length */
967c478bd9Sstevel@tonic-gate 	boolean_t do_namecheck;	/* check password against user's gecos */
977c478bd9Sstevel@tonic-gate 	char db_location[MAXPATHLEN]; /* location of the generated database */
987c478bd9Sstevel@tonic-gate 	boolean_t do_dictcheck;	/* perform dictionary lookup */
997c478bd9Sstevel@tonic-gate 	char *dicts;		/* list of dictionaries configured */
1007c478bd9Sstevel@tonic-gate 	uint_t mindiff;		/* old and new should differ by this much */
1017c478bd9Sstevel@tonic-gate 	uint_t minalpha;	/* minimum alpha characters required */
1027c478bd9Sstevel@tonic-gate 	uint_t minupper;	/* minimum uppercase characters required */
1037c478bd9Sstevel@tonic-gate 	uint_t minlower;	/* minimum lowercase characters required */
1047c478bd9Sstevel@tonic-gate 	uint_t minnonalpha; 	/* minimum special (non alpha) required */
1057c478bd9Sstevel@tonic-gate 	uint_t maxrepeat;	/* maximum number of repeating chars allowed */
1067c478bd9Sstevel@tonic-gate 	uint_t minspecial;	/* punctuation characters */
1077c478bd9Sstevel@tonic-gate 	uint_t mindigit;	/* minimum number of digits required */
1087c478bd9Sstevel@tonic-gate 	boolean_t whitespace;	/* is whitespace allowed in a password */
1097c478bd9Sstevel@tonic-gate };
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate /*PRINTFLIKE3*/
1137c478bd9Sstevel@tonic-gate void
error(pam_handle_t * pamh,int flags,char * fmt,...)1147c478bd9Sstevel@tonic-gate error(pam_handle_t *pamh, int flags, char *fmt, ...)
1157c478bd9Sstevel@tonic-gate {
1167c478bd9Sstevel@tonic-gate 	va_list ap;
1177c478bd9Sstevel@tonic-gate 	char msg[1][PAM_MAX_MSG_SIZE];
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate 	va_start(ap, fmt);
1207c478bd9Sstevel@tonic-gate 	(void) vsnprintf(msg[0], sizeof (msg[0]), fmt, ap);
1217c478bd9Sstevel@tonic-gate 	va_end(ap);
1227c478bd9Sstevel@tonic-gate 	if ((flags & PAM_SILENT) == 0)
1237c478bd9Sstevel@tonic-gate 		(void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, msg, NULL);
1247c478bd9Sstevel@tonic-gate }
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate int
defread_int(char * name,uint_t * ip,void * defp)127b9175c69SKenjiro Tsuji defread_int(char *name, uint_t *ip, void *defp)
1287c478bd9Sstevel@tonic-gate {
1297c478bd9Sstevel@tonic-gate 	char *q;
1307c478bd9Sstevel@tonic-gate 	int r = 0;
131b9175c69SKenjiro Tsuji 	if ((q = defread_r(name, defp)) != NULL) {
1327c478bd9Sstevel@tonic-gate 		if (!isdigit(*q)) {
1337c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, "pam_authtok_check: %s contains "
1347c478bd9Sstevel@tonic-gate 			    "non-integer value for %s: %s. "
1357c478bd9Sstevel@tonic-gate 			    "Using default instead.", PWADMIN, name, q);
1367c478bd9Sstevel@tonic-gate 		} else {
1377c478bd9Sstevel@tonic-gate 			*ip = atoi(q);
1387c478bd9Sstevel@tonic-gate 			r = 1;
1397c478bd9Sstevel@tonic-gate 		}
1407c478bd9Sstevel@tonic-gate 	}
1417c478bd9Sstevel@tonic-gate 	return (r);
1427c478bd9Sstevel@tonic-gate }
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate /*
1457c478bd9Sstevel@tonic-gate  * fill in static defaults, and augment with settings from PWADMIN
1467c478bd9Sstevel@tonic-gate  * get system defaults with regard to maximum password length
1477c478bd9Sstevel@tonic-gate  */
1487c478bd9Sstevel@tonic-gate int
get_passwd_defaults(pam_handle_t * pamh,const char * user,struct pwdefaults * p)149*cbea7acaSDominik Hassler get_passwd_defaults(pam_handle_t *pamh, const char *user, struct pwdefaults *p)
1507c478bd9Sstevel@tonic-gate {
1517c478bd9Sstevel@tonic-gate 	char *q;
1527c478bd9Sstevel@tonic-gate 	boolean_t minnonalpha_defined = B_FALSE;
1537c478bd9Sstevel@tonic-gate 	pwu_repository_t *pwu_rep;
154*cbea7acaSDominik Hassler 	const struct pam_repository *pam_rep;
1557c478bd9Sstevel@tonic-gate 	attrlist attr[2];
1567c478bd9Sstevel@tonic-gate 	int result;
157*cbea7acaSDominik Hassler 	const char *progname;
158b9175c69SKenjiro Tsuji 	void *defp;
1597c478bd9Sstevel@tonic-gate 
160*cbea7acaSDominik Hassler 	(void) pam_get_item(pamh, PAM_SERVICE, (const void **)&progname);
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate 	/* Module defaults */
1637c478bd9Sstevel@tonic-gate 	p->minlength = MINLENGTH;
1647c478bd9Sstevel@tonic-gate 	p->do_namecheck = B_TRUE;
1657c478bd9Sstevel@tonic-gate 	p->do_dictcheck = B_FALSE;
1667c478bd9Sstevel@tonic-gate 	p->dicts = NULL;
1677c478bd9Sstevel@tonic-gate 	p->mindiff = MINDIFF;
1687c478bd9Sstevel@tonic-gate 	p->minalpha = MINALPHA;
1697c478bd9Sstevel@tonic-gate 	p->minnonalpha = MINNONALPHA;
1707c478bd9Sstevel@tonic-gate 	p->minupper = 0;	/* not configured by default */
1717c478bd9Sstevel@tonic-gate 	p->minlower = 0;	/* not configured by default */
1727c478bd9Sstevel@tonic-gate 	p->maxrepeat = 0;	/* not configured by default */
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 	p->minspecial = 0;
1757c478bd9Sstevel@tonic-gate 	p->mindigit = 0;
1767c478bd9Sstevel@tonic-gate 	p->whitespace = B_TRUE;
1777c478bd9Sstevel@tonic-gate 
178b9175c69SKenjiro Tsuji 	if ((defp = defopen_r(PWADMIN)) == NULL)
1797c478bd9Sstevel@tonic-gate 		return (PAM_SUCCESS);
1807c478bd9Sstevel@tonic-gate 
181b9175c69SKenjiro Tsuji 	(void) defread_int("PASSLENGTH=", &p->minlength, defp);
1827c478bd9Sstevel@tonic-gate 
183b9175c69SKenjiro Tsuji 	if ((q = defread_r("NAMECHECK=", defp)) != NULL &&
184b9175c69SKenjiro Tsuji 	    strcasecmp(q, "NO") == 0)
1857c478bd9Sstevel@tonic-gate 		p->do_namecheck = B_FALSE;
1867c478bd9Sstevel@tonic-gate 
187b9175c69SKenjiro Tsuji 	if ((q = defread_r("DICTIONLIST=", defp)) != NULL) {
1887c478bd9Sstevel@tonic-gate 		if ((p->dicts = strdup(q)) == NULL) {
1897c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, "pam_authtok_check: out of memory");
190b9175c69SKenjiro Tsuji 			defclose_r(defp);
1917c478bd9Sstevel@tonic-gate 			return (PAM_BUF_ERR);
1927c478bd9Sstevel@tonic-gate 
1937c478bd9Sstevel@tonic-gate 		}
1947c478bd9Sstevel@tonic-gate 		p->do_dictcheck = B_TRUE;
195b9175c69SKenjiro Tsuji 	} else {
1967c478bd9Sstevel@tonic-gate 		p->dicts = NULL;
197b9175c69SKenjiro Tsuji 	}
1987c478bd9Sstevel@tonic-gate 
199b9175c69SKenjiro Tsuji 	if ((q = defread_r("DICTIONDBDIR=", defp)) != NULL) {
2007c478bd9Sstevel@tonic-gate 		if (strlcpy(p->db_location, q, sizeof (p->db_location)) >=
2017c478bd9Sstevel@tonic-gate 		    sizeof (p->db_location)) {
2027c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, "pam_authtok_check: value for "
2037c478bd9Sstevel@tonic-gate 			    "DICTIONDBDIR too large.");
204b9175c69SKenjiro Tsuji 			defclose_r(defp);
2057c478bd9Sstevel@tonic-gate 			return (PAM_SYSTEM_ERR);
2067c478bd9Sstevel@tonic-gate 		}
2077c478bd9Sstevel@tonic-gate 		p->do_dictcheck = B_TRUE;
208b9175c69SKenjiro Tsuji 	} else {
2097c478bd9Sstevel@tonic-gate 		(void) strlcpy(p->db_location, CRACK_DIR,
2107c478bd9Sstevel@tonic-gate 		    sizeof (p->db_location));
211b9175c69SKenjiro Tsuji 	}
2127c478bd9Sstevel@tonic-gate 
213b9175c69SKenjiro Tsuji 	(void) defread_int("MINDIFF=", &p->mindiff, defp);
214b9175c69SKenjiro Tsuji 	(void) defread_int("MINALPHA=", &p->minalpha, defp);
215b9175c69SKenjiro Tsuji 	(void) defread_int("MINUPPER=", &p->minupper, defp);
216b9175c69SKenjiro Tsuji 	(void) defread_int("MINLOWER=", &p->minlower, defp);
217b9175c69SKenjiro Tsuji 	if (defread_int("MINNONALPHA=", &p->minnonalpha, defp))
2187c478bd9Sstevel@tonic-gate 		minnonalpha_defined = B_TRUE;
219b9175c69SKenjiro Tsuji 	(void) defread_int("MAXREPEATS=", &p->maxrepeat, defp);
2207c478bd9Sstevel@tonic-gate 
221b9175c69SKenjiro Tsuji 	if (defread_int("MINSPECIAL=", &p->minspecial, defp)) {
2227c478bd9Sstevel@tonic-gate 		if (minnonalpha_defined) {
2237c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, "pam_authtok_check: %s contains "
2247c478bd9Sstevel@tonic-gate 			    "definition for MINNONALPHA and for MINSPECIAL. "
2257c478bd9Sstevel@tonic-gate 			    "These options are mutually exclusive.", PWADMIN);
226b9175c69SKenjiro Tsuji 			defclose_r(defp);
2277c478bd9Sstevel@tonic-gate 			return (PAM_SYSTEM_ERR);
2287c478bd9Sstevel@tonic-gate 		}
2297c478bd9Sstevel@tonic-gate 		p->minnonalpha = 0;
2307c478bd9Sstevel@tonic-gate 	}
2317c478bd9Sstevel@tonic-gate 
232b9175c69SKenjiro Tsuji 	if (defread_int("MINDIGIT=", &p->mindigit, defp)) {
2337c478bd9Sstevel@tonic-gate 		if (minnonalpha_defined) {
2347c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, "pam_authtok_check: %s contains "
2357c478bd9Sstevel@tonic-gate 			    "definition for MINNONALPHA and for MINDIGIT. "
2367c478bd9Sstevel@tonic-gate 			    "These options are mutually exclusive.", PWADMIN);
237b9175c69SKenjiro Tsuji 			defclose_r(defp);
2387c478bd9Sstevel@tonic-gate 			return (PAM_SYSTEM_ERR);
2397c478bd9Sstevel@tonic-gate 		}
2407c478bd9Sstevel@tonic-gate 		p->minnonalpha = 0;
2417c478bd9Sstevel@tonic-gate 	}
2427c478bd9Sstevel@tonic-gate 
243b9175c69SKenjiro Tsuji 	if ((q = defread_r("WHITESPACE=", defp)) != NULL)
2447c478bd9Sstevel@tonic-gate 		p->whitespace =
2457c478bd9Sstevel@tonic-gate 		    (strcasecmp(q, "no") == 0 || strcmp(q, "0") == 0)
2467c478bd9Sstevel@tonic-gate 		    ? B_FALSE : B_TRUE;
2477c478bd9Sstevel@tonic-gate 
248b9175c69SKenjiro Tsuji 	defclose_r(defp);
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate 	/*
2517c478bd9Sstevel@tonic-gate 	 * Determine the number of significant characters in a password
2527c478bd9Sstevel@tonic-gate 	 *
2537c478bd9Sstevel@tonic-gate 	 * we find out where the user information came from (which repository),
2547c478bd9Sstevel@tonic-gate 	 * and which password-crypt-algorithm is to be used (based on the
2557c478bd9Sstevel@tonic-gate 	 * old password, or the system default).
2567c478bd9Sstevel@tonic-gate 	 *
25736e852a1SRaja Andra 	 * If the user comes from a repository other than FILES/NIS
2587c478bd9Sstevel@tonic-gate 	 * the module-flag "server_policy" means that we don't perform
2597c478bd9Sstevel@tonic-gate 	 * any checks on the user, but let the repository decide instead.
2607c478bd9Sstevel@tonic-gate 	 */
2617c478bd9Sstevel@tonic-gate 
262*cbea7acaSDominik Hassler 	(void) pam_get_item(pamh, PAM_REPOSITORY, (const void **)&pam_rep);
2637c478bd9Sstevel@tonic-gate 	if (pam_rep != NULL) {
2647c478bd9Sstevel@tonic-gate 		if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL)
2657c478bd9Sstevel@tonic-gate 			return (PAM_BUF_ERR);
2667c478bd9Sstevel@tonic-gate 		pwu_rep->type = pam_rep->type;
2677c478bd9Sstevel@tonic-gate 		pwu_rep->scope = pam_rep->scope;
2687c478bd9Sstevel@tonic-gate 		pwu_rep->scope_len = pam_rep->scope_len;
2697c478bd9Sstevel@tonic-gate 	} else {
2707c478bd9Sstevel@tonic-gate 		pwu_rep = PWU_DEFAULT_REP;
2717c478bd9Sstevel@tonic-gate 	}
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate 	attr[0].type = ATTR_PASSWD; attr[0].next = &attr[1];
2747c478bd9Sstevel@tonic-gate 	attr[1].type = ATTR_REP_NAME; attr[1].next = NULL;
2757c478bd9Sstevel@tonic-gate 	result = __get_authtoken_attr(user, pwu_rep, attr);
2767c478bd9Sstevel@tonic-gate 	if (pwu_rep != PWU_DEFAULT_REP)
2777c478bd9Sstevel@tonic-gate 		free(pwu_rep);
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate 	if (result != PWU_SUCCESS) {
2807c478bd9Sstevel@tonic-gate 		/*
2817c478bd9Sstevel@tonic-gate 		 * In the unlikely event that we can't obtain any info about
2827c478bd9Sstevel@tonic-gate 		 * the users password, we assume the most strict scenario.
2837c478bd9Sstevel@tonic-gate 		 */
2847c478bd9Sstevel@tonic-gate 		p->maxlength = _PASS_MAX_XPG;
2857c478bd9Sstevel@tonic-gate 	} else {
2867c478bd9Sstevel@tonic-gate 		char *oldpw = attr[0].data.val_s;
2877c478bd9Sstevel@tonic-gate 		char *repository = attr[1].data.val_s;
2887c478bd9Sstevel@tonic-gate 		if ((strcmp(repository, "files") == 0 ||
28936e852a1SRaja Andra 		    strcmp(repository, "nis") == 0) ||
2907c478bd9Sstevel@tonic-gate 		    p->server_policy == B_FALSE) {
2917c478bd9Sstevel@tonic-gate 			char *salt;
2927c478bd9Sstevel@tonic-gate 			/*
2937c478bd9Sstevel@tonic-gate 			 * We currently need to supply this dummy to
2947c478bd9Sstevel@tonic-gate 			 * crypt_gensalt(). This will change RSN.
2957c478bd9Sstevel@tonic-gate 			 */
2967c478bd9Sstevel@tonic-gate 			struct passwd dummy;
2977c478bd9Sstevel@tonic-gate 
298*cbea7acaSDominik Hassler 			dummy.pw_name = strdup(user);
299*cbea7acaSDominik Hassler 			if (dummy.pw_name == NULL) {
300*cbea7acaSDominik Hassler 				free(attr[0].data.val_s);
301*cbea7acaSDominik Hassler 				free(attr[1].data.val_s);
302*cbea7acaSDominik Hassler 				return (PAM_BUF_ERR);
303*cbea7acaSDominik Hassler 			}
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 			salt = crypt_gensalt(oldpw, &dummy);
306*cbea7acaSDominik Hassler 			free(dummy.pw_name);
3077c478bd9Sstevel@tonic-gate 			if (salt && *salt == '$')
3087c478bd9Sstevel@tonic-gate 				p->maxlength = _PASS_MAX;
3097c478bd9Sstevel@tonic-gate 			else
3107c478bd9Sstevel@tonic-gate 				p->maxlength = _PASS_MAX_XPG;
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 			free(salt);
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 			p->server_policy = B_FALSE; /* we perform checks */
3157c478bd9Sstevel@tonic-gate 		} else {
31636e852a1SRaja Andra 			/* not files or nis AND server_policy is set */
3177c478bd9Sstevel@tonic-gate 			p->maxlength = _PASS_MAX;
3187c478bd9Sstevel@tonic-gate 		}
3197c478bd9Sstevel@tonic-gate 		free(attr[0].data.val_s);
3207c478bd9Sstevel@tonic-gate 		free(attr[1].data.val_s);
3217c478bd9Sstevel@tonic-gate 	}
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	/* sanity check of the configured parameters */
3247c478bd9Sstevel@tonic-gate 	if (p->minlength < p->mindigit + p->minspecial + p->minnonalpha +
3257c478bd9Sstevel@tonic-gate 	    p->minalpha) {
3267c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "%s: pam_authtok_check: Defined minimum "
3277c478bd9Sstevel@tonic-gate 		    "password length (PASSLENGTH=%d) is less then minimum "
3287c478bd9Sstevel@tonic-gate 		    "characters in the various classes (%d)", progname,
3297c478bd9Sstevel@tonic-gate 		    p->minlength,
3307c478bd9Sstevel@tonic-gate 		    p->mindigit + p->minspecial + p->minnonalpha + p->minalpha);
3317c478bd9Sstevel@tonic-gate 		p->minlength = p->mindigit + p->minspecial + p->minnonalpha +
3327c478bd9Sstevel@tonic-gate 		    p->minalpha;
3337c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "%s: pam_authtok_check: effective "
3347c478bd9Sstevel@tonic-gate 		    "PASSLENGTH set to %d.", progname, p->minlength);
3357c478bd9Sstevel@tonic-gate 		/* this won't lead to failure */
3367c478bd9Sstevel@tonic-gate 	}
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 	if (p->maxlength < p->minlength) {
3397c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "%s: pam_authtok_check: The configured "
3407c478bd9Sstevel@tonic-gate 		    "minimum password length (PASSLENGTH=%d) is larger than "
3417c478bd9Sstevel@tonic-gate 		    "the number of significant characters the current "
342bbf21555SRichard Lowe 		    "encryption algorithm uses (%d). See policy.conf(5) for "
3437c478bd9Sstevel@tonic-gate 		    "alternative password encryption algorithms.", progname);
3447c478bd9Sstevel@tonic-gate 		/* this won't lead to failure */
3457c478bd9Sstevel@tonic-gate 	}
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 	return (PAM_SUCCESS);
3487c478bd9Sstevel@tonic-gate }
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate /*
3517c478bd9Sstevel@tonic-gate  * free_passwd_defaults(struct pwdefaults *p)
3527c478bd9Sstevel@tonic-gate  *
3537c478bd9Sstevel@tonic-gate  * free space occupied by the defaults read from PWADMIN
3547c478bd9Sstevel@tonic-gate  */
3557c478bd9Sstevel@tonic-gate void
free_passwd_defaults(struct pwdefaults * p)3567c478bd9Sstevel@tonic-gate free_passwd_defaults(struct pwdefaults *p)
3577c478bd9Sstevel@tonic-gate {
3587c478bd9Sstevel@tonic-gate 	if (p && p->dicts)
3597c478bd9Sstevel@tonic-gate 		free(p->dicts);
3607c478bd9Sstevel@tonic-gate }
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate /*
3637c478bd9Sstevel@tonic-gate  * check_circular():
3647c478bd9Sstevel@tonic-gate  * This function return 1 if string "t" is a circular shift of
3657c478bd9Sstevel@tonic-gate  * string "s", else it returns 0. -1 is returned on failure.
3667c478bd9Sstevel@tonic-gate  * We also check to see if string "t" is a reversed-circular shift
3677c478bd9Sstevel@tonic-gate  * of string "s", i.e. "ABCDE" vs. "DCBAE".
3687c478bd9Sstevel@tonic-gate  */
3697c478bd9Sstevel@tonic-gate static int
check_circular(s,t)3707c478bd9Sstevel@tonic-gate check_circular(s, t)
3717c478bd9Sstevel@tonic-gate 	char *s, *t;
3727c478bd9Sstevel@tonic-gate {
3737c478bd9Sstevel@tonic-gate 	char c, *p, *o, *r, *buff, *ubuff, *pubuff;
3747c478bd9Sstevel@tonic-gate 	unsigned int i, j, k, l, m;
3757c478bd9Sstevel@tonic-gate 	size_t len;
3767c478bd9Sstevel@tonic-gate 	int ret = 0;
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 	i = strlen(s);
3797c478bd9Sstevel@tonic-gate 	l = strlen(t);
3807c478bd9Sstevel@tonic-gate 	if (i != l)
3817c478bd9Sstevel@tonic-gate 		return (0);
3827c478bd9Sstevel@tonic-gate 	len = i + 1;
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 	buff = malloc(len);
3857c478bd9Sstevel@tonic-gate 	ubuff = malloc(len);
3867c478bd9Sstevel@tonic-gate 	pubuff = malloc(len);
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate 	if (buff == NULL || ubuff == NULL || pubuff == NULL) {
3897c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "pam_authtok_check: out of memory.");
3907c478bd9Sstevel@tonic-gate 		return (-1);
3917c478bd9Sstevel@tonic-gate 	}
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 	m = 2;
3947c478bd9Sstevel@tonic-gate 	o = &ubuff[0];
3957c478bd9Sstevel@tonic-gate 	for (p = s; c = *p++; *o++ = c)
3967c478bd9Sstevel@tonic-gate 		if (islower(c))
3977c478bd9Sstevel@tonic-gate 			c = toupper(c);
3987c478bd9Sstevel@tonic-gate 	*o = '\0';
3997c478bd9Sstevel@tonic-gate 	o = &pubuff[0];
4007c478bd9Sstevel@tonic-gate 	for (p = t; c = *p++; *o++ = c)
4017c478bd9Sstevel@tonic-gate 		if (islower(c))
4027c478bd9Sstevel@tonic-gate 			c = toupper(c);
4037c478bd9Sstevel@tonic-gate 
4047c478bd9Sstevel@tonic-gate 	*o = '\0';
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	p = &ubuff[0];
4077c478bd9Sstevel@tonic-gate 	while (m--) {
4087c478bd9Sstevel@tonic-gate 		for (k = 0; k  <  i; k++) {
4097c478bd9Sstevel@tonic-gate 			c = *p++;
4107c478bd9Sstevel@tonic-gate 			o = p;
4117c478bd9Sstevel@tonic-gate 			l = i;
4127c478bd9Sstevel@tonic-gate 			r = &buff[0];
4137c478bd9Sstevel@tonic-gate 			while (--l)
4147c478bd9Sstevel@tonic-gate 				*r++ = *o++;
4157c478bd9Sstevel@tonic-gate 			*r++ = c;
4167c478bd9Sstevel@tonic-gate 			*r = '\0';
4177c478bd9Sstevel@tonic-gate 			p = &buff[0];
4187c478bd9Sstevel@tonic-gate 			if (strcmp(p, pubuff) == 0) {
4197c478bd9Sstevel@tonic-gate 				ret = 1;
4207c478bd9Sstevel@tonic-gate 				goto out;
4217c478bd9Sstevel@tonic-gate 			}
4227c478bd9Sstevel@tonic-gate 		}
4237c478bd9Sstevel@tonic-gate 		p = p + i;
4247c478bd9Sstevel@tonic-gate 		r = &ubuff[0];
4257c478bd9Sstevel@tonic-gate 		j = i;
4267c478bd9Sstevel@tonic-gate 		while (j--)
4277c478bd9Sstevel@tonic-gate 			*--p = *r++;	/* reverse test-string for m==0 pass */
4287c478bd9Sstevel@tonic-gate 	}
4297c478bd9Sstevel@tonic-gate out:
4307c478bd9Sstevel@tonic-gate 	(void) memset(buff, 0, len);
4317c478bd9Sstevel@tonic-gate 	(void) memset(ubuff, 0, len);
4327c478bd9Sstevel@tonic-gate 	(void) memset(pubuff, 0, len);
4337c478bd9Sstevel@tonic-gate 	free(buff);
4347c478bd9Sstevel@tonic-gate 	free(ubuff);
4357c478bd9Sstevel@tonic-gate 	free(pubuff);
4367c478bd9Sstevel@tonic-gate 	return (ret);
4377c478bd9Sstevel@tonic-gate }
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate /*
4417c478bd9Sstevel@tonic-gate  * count the different character classes present in the password.
4427c478bd9Sstevel@tonic-gate  */
4437c478bd9Sstevel@tonic-gate int
check_composition(const char * pw,struct pwdefaults * pwdef,pam_handle_t * pamh,int flags)444*cbea7acaSDominik Hassler check_composition(const char *pw, struct pwdefaults *pwdef, pam_handle_t *pamh,
4457c478bd9Sstevel@tonic-gate     int flags)
4467c478bd9Sstevel@tonic-gate {
4477c478bd9Sstevel@tonic-gate 	uint_t alpha_cnt = 0;
4487c478bd9Sstevel@tonic-gate 	uint_t upper_cnt = 0;
4497c478bd9Sstevel@tonic-gate 	uint_t lower_cnt = 0;
4507c478bd9Sstevel@tonic-gate 	uint_t special_cnt = 0;
4517c478bd9Sstevel@tonic-gate 	uint_t whitespace_cnt = 0;
4527c478bd9Sstevel@tonic-gate 	uint_t digit_cnt = 0;
4537c478bd9Sstevel@tonic-gate 	uint_t maxrepeat = 0;
4547c478bd9Sstevel@tonic-gate 	uint_t repeat = 1;
4557c478bd9Sstevel@tonic-gate 	int ret = 0;
456*cbea7acaSDominik Hassler 	const char *progname;
4577c478bd9Sstevel@tonic-gate 	char errmsg[256];
4587c478bd9Sstevel@tonic-gate 	char lastc = '\0';
4597c478bd9Sstevel@tonic-gate 	uint_t significant = pwdef->maxlength;
460*cbea7acaSDominik Hassler 	const char *w;
4617c478bd9Sstevel@tonic-gate 
462*cbea7acaSDominik Hassler 	(void) pam_get_item(pamh, PAM_SERVICE, (const void **)&progname);
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate 	/* go over the password gathering statistics */
4657c478bd9Sstevel@tonic-gate 	for (w = pw; significant != 0 && *w != '\0'; w++, significant--) {
4667c478bd9Sstevel@tonic-gate 		if (isalpha(*w)) {
4677c478bd9Sstevel@tonic-gate 			alpha_cnt++;
4687c478bd9Sstevel@tonic-gate 			if (isupper(*w)) {
4697c478bd9Sstevel@tonic-gate 				upper_cnt++;
4707c478bd9Sstevel@tonic-gate 			} else {
4717c478bd9Sstevel@tonic-gate 				lower_cnt++;
4727c478bd9Sstevel@tonic-gate 			}
4737c478bd9Sstevel@tonic-gate 		} else if (isspace(*w))
4747c478bd9Sstevel@tonic-gate 			whitespace_cnt++;
4757c478bd9Sstevel@tonic-gate 		else if (isdigit(*w))
4767c478bd9Sstevel@tonic-gate 			digit_cnt++;
4777c478bd9Sstevel@tonic-gate 		else
4787c478bd9Sstevel@tonic-gate 			special_cnt++;
4797c478bd9Sstevel@tonic-gate 		if (*w == lastc) {
4807c478bd9Sstevel@tonic-gate 			if (++repeat > maxrepeat)
4817c478bd9Sstevel@tonic-gate 				maxrepeat = repeat;
4827c478bd9Sstevel@tonic-gate 		} else {
4837c478bd9Sstevel@tonic-gate 			repeat = 1;
4847c478bd9Sstevel@tonic-gate 		}
4857c478bd9Sstevel@tonic-gate 		lastc = *w;
4867c478bd9Sstevel@tonic-gate 	}
4877c478bd9Sstevel@tonic-gate 
4887c478bd9Sstevel@tonic-gate 	/*
4897c478bd9Sstevel@tonic-gate 	 * If we only consider part of the password (the first maxlength
4907c478bd9Sstevel@tonic-gate 	 * characters) we give a modified error message. Otherwise, a
4917c478bd9Sstevel@tonic-gate 	 * user entering FooBar1234 with PASSLENGTH=6, MINDIGIT=4, while
4927c478bd9Sstevel@tonic-gate 	 * we're using the default UNIX crypt (8 chars significant),
49348bbca81SDaniel Hoffman 	 * would not understand what's going on when they're told that
4947c478bd9Sstevel@tonic-gate 	 * "The password should contain at least 4 digits"...
49548bbca81SDaniel Hoffman 	 * Instead, we now tell them
4967c478bd9Sstevel@tonic-gate 	 * "The first 8 characters of the password should contain at least
4977c478bd9Sstevel@tonic-gate 	 *  4 digits."
4987c478bd9Sstevel@tonic-gate 	 */
4997c478bd9Sstevel@tonic-gate 	if (pwdef->maxlength < strlen(pw))
5007c478bd9Sstevel@tonic-gate 		/*
5017c478bd9Sstevel@tonic-gate 		 * TRANSLATION_NOTE
5027c478bd9Sstevel@tonic-gate 		 * - Make sure the % and %% come over intact
5037c478bd9Sstevel@tonic-gate 		 * - The last %%s will be replaced by strings like
5047c478bd9Sstevel@tonic-gate 		 *	"alphabetic character(s)"
5057c478bd9Sstevel@tonic-gate 		 *	"numeric or special character(s)"
5067c478bd9Sstevel@tonic-gate 		 *	"special character(s)"
5077c478bd9Sstevel@tonic-gate 		 *	"digit(s)"
5087c478bd9Sstevel@tonic-gate 		 *	"uppercase alpha character(s)"
5097c478bd9Sstevel@tonic-gate 		 *	"lowercase alpha character(s)"
5107c478bd9Sstevel@tonic-gate 		 *   So the final string written to the user might become
5117c478bd9Sstevel@tonic-gate 		 * "passwd: The first 8 characters of the password must contain
5127c478bd9Sstevel@tonic-gate 		 *   at least 4 uppercase alpha characters(s)"
5137c478bd9Sstevel@tonic-gate 		 */
5147c478bd9Sstevel@tonic-gate 		(void) snprintf(errmsg, sizeof (errmsg), dgettext(TEXT_DOMAIN,
5157c478bd9Sstevel@tonic-gate 		    "%s: The first %d characters of the password must "
5167c478bd9Sstevel@tonic-gate 		    "contain at least %%d %%s."), progname, pwdef->maxlength);
5177c478bd9Sstevel@tonic-gate 	else
5187c478bd9Sstevel@tonic-gate 		/*
5197c478bd9Sstevel@tonic-gate 		 * TRANSLATION_NOTE
5207c478bd9Sstevel@tonic-gate 		 * - Make sure the % and %% come over intact
5217c478bd9Sstevel@tonic-gate 		 * - The last %%s will be replaced by strings like
5227c478bd9Sstevel@tonic-gate 		 *	"alphabetic character(s)"
5237c478bd9Sstevel@tonic-gate 		 *	"numeric or special character(s)"
5247c478bd9Sstevel@tonic-gate 		 *	"special character(s)"
5257c478bd9Sstevel@tonic-gate 		 *	"digit(s)"
5267c478bd9Sstevel@tonic-gate 		 *	"uppercase alpha character(s)"
5277c478bd9Sstevel@tonic-gate 		 *	"lowercase alpha character(s)"
5287c478bd9Sstevel@tonic-gate 		 *   So the final string written to the user might become
5297c478bd9Sstevel@tonic-gate 		 * "passwd: The password must contain at least 4 uppercase
5307c478bd9Sstevel@tonic-gate 		 *   alpha characters(s)"
5317c478bd9Sstevel@tonic-gate 		 */
5327c478bd9Sstevel@tonic-gate 		(void) snprintf(errmsg, sizeof (errmsg), dgettext(TEXT_DOMAIN,
5337c478bd9Sstevel@tonic-gate 		    "%s: The password must contain at least %%d %%s."),
5347c478bd9Sstevel@tonic-gate 		    progname);
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 	/* Check for whitespace first since it influences special counts */
5377c478bd9Sstevel@tonic-gate 	if (whitespace_cnt > 0 && pwdef->whitespace == B_FALSE) {
5387c478bd9Sstevel@tonic-gate 		error(pamh, flags, dgettext(TEXT_DOMAIN,
5397c478bd9Sstevel@tonic-gate 		    "%s: Whitespace characters are not allowed."), progname);
5407c478bd9Sstevel@tonic-gate 		ret = 1;
5417c478bd9Sstevel@tonic-gate 		goto out;
5427c478bd9Sstevel@tonic-gate 	}
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	/*
5457c478bd9Sstevel@tonic-gate 	 * Once we get here, whitespace_cnt is either 0, or whitespaces are
5467c478bd9Sstevel@tonic-gate 	 * to be treated a special characters.
5477c478bd9Sstevel@tonic-gate 	 */
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 	if (alpha_cnt < pwdef->minalpha) {
5507c478bd9Sstevel@tonic-gate 		error(pamh, flags, errmsg, pwdef->minalpha,
5517c478bd9Sstevel@tonic-gate 		    dgettext(TEXT_DOMAIN, "alphabetic character(s)"));
5527c478bd9Sstevel@tonic-gate 		ret = 1;
5537c478bd9Sstevel@tonic-gate 		goto out;
5547c478bd9Sstevel@tonic-gate 	}
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate 	if (pwdef->minnonalpha > 0) {
5577c478bd9Sstevel@tonic-gate 		/* specials are defined by MINNONALPHA */
5587c478bd9Sstevel@tonic-gate 		/* nonalpha = special+whitespace+digit */
5597c478bd9Sstevel@tonic-gate 		if ((special_cnt + whitespace_cnt + digit_cnt) <
5607c478bd9Sstevel@tonic-gate 		    pwdef->minnonalpha) {
5617c478bd9Sstevel@tonic-gate 			error(pamh, flags, errmsg, pwdef->minnonalpha,
5627c478bd9Sstevel@tonic-gate 			    dgettext(TEXT_DOMAIN,
5637c478bd9Sstevel@tonic-gate 			    "numeric or special character(s)"));
5647c478bd9Sstevel@tonic-gate 			ret = 1;
5657c478bd9Sstevel@tonic-gate 			goto out;
5667c478bd9Sstevel@tonic-gate 		}
5677c478bd9Sstevel@tonic-gate 	} else {
5687c478bd9Sstevel@tonic-gate 		/* specials are defined by MINSPECIAL and/or MINDIGIT */
5697c478bd9Sstevel@tonic-gate 		if ((special_cnt + whitespace_cnt) < pwdef->minspecial) {
5707c478bd9Sstevel@tonic-gate 			error(pamh, flags, errmsg, pwdef->minspecial,
5717c478bd9Sstevel@tonic-gate 			    dgettext(TEXT_DOMAIN, "special character(s)"));
5727c478bd9Sstevel@tonic-gate 			ret = 1;
5737c478bd9Sstevel@tonic-gate 			goto out;
5747c478bd9Sstevel@tonic-gate 		}
5757c478bd9Sstevel@tonic-gate 		if (digit_cnt < pwdef->mindigit) {
5767c478bd9Sstevel@tonic-gate 			error(pamh, flags, errmsg, pwdef->mindigit,
5777c478bd9Sstevel@tonic-gate 			    dgettext(TEXT_DOMAIN, "digit(s)"));
5787c478bd9Sstevel@tonic-gate 			ret = 1;
5797c478bd9Sstevel@tonic-gate 			goto out;
5807c478bd9Sstevel@tonic-gate 		}
5817c478bd9Sstevel@tonic-gate 	}
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate 	if (upper_cnt < pwdef->minupper) {
5847c478bd9Sstevel@tonic-gate 		error(pamh, flags, errmsg, pwdef->minupper,
5857c478bd9Sstevel@tonic-gate 		    dgettext(TEXT_DOMAIN, "uppercase alpha character(s)"));
5867c478bd9Sstevel@tonic-gate 		ret = 1;
5877c478bd9Sstevel@tonic-gate 		goto out;
5887c478bd9Sstevel@tonic-gate 	}
5897c478bd9Sstevel@tonic-gate 	if (lower_cnt < pwdef->minlower) {
5907c478bd9Sstevel@tonic-gate 		error(pamh, flags, errmsg, pwdef->minlower,
5917c478bd9Sstevel@tonic-gate 		    dgettext(TEXT_DOMAIN, "lowercase alpha character(s)"));
5927c478bd9Sstevel@tonic-gate 		ret = 1;
5937c478bd9Sstevel@tonic-gate 		goto out;
5947c478bd9Sstevel@tonic-gate 	}
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate 	if (pwdef->maxrepeat > 0 && maxrepeat > pwdef->maxrepeat) {
5977c478bd9Sstevel@tonic-gate 		error(pamh, flags, dgettext(TEXT_DOMAIN,
5987c478bd9Sstevel@tonic-gate 		    "%s: Too many consecutively repeating characters. "
5997c478bd9Sstevel@tonic-gate 		    "Maximum allowed is %d."), progname, pwdef->maxrepeat);
6007c478bd9Sstevel@tonic-gate 		ret = 1;
6017c478bd9Sstevel@tonic-gate 	}
6027c478bd9Sstevel@tonic-gate out:
6037c478bd9Sstevel@tonic-gate 	return (ret);
6047c478bd9Sstevel@tonic-gate }
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate /*
6077c478bd9Sstevel@tonic-gate  * make sure that old and new password differ by at least 'mindiff'
6087c478bd9Sstevel@tonic-gate  * positions. Return 0 if OK, 1 otherwise
6097c478bd9Sstevel@tonic-gate  */
6107c478bd9Sstevel@tonic-gate int
check_diff(const char * pw,const char * opw,struct pwdefaults * pwdef,pam_handle_t * pamh,int flags)611*cbea7acaSDominik Hassler check_diff(const char *pw, const char *opw, struct pwdefaults *pwdef,
612*cbea7acaSDominik Hassler     pam_handle_t *pamh, int flags)
6137c478bd9Sstevel@tonic-gate {
6147c478bd9Sstevel@tonic-gate 	size_t pwlen, opwlen, max;
6157c478bd9Sstevel@tonic-gate 	unsigned int diff;	/* difference between old and new */
6167c478bd9Sstevel@tonic-gate 
6177c478bd9Sstevel@tonic-gate 	if (opw == NULL)
6187c478bd9Sstevel@tonic-gate 		opw = "";
6197c478bd9Sstevel@tonic-gate 
6207c478bd9Sstevel@tonic-gate 	max = pwdef->maxlength;
6217c478bd9Sstevel@tonic-gate 	pwlen = MIN(strlen(pw), max);
6227c478bd9Sstevel@tonic-gate 	opwlen = MIN(strlen(opw), max);
6237c478bd9Sstevel@tonic-gate 
6247c478bd9Sstevel@tonic-gate 	if (pwlen > opwlen)
6257c478bd9Sstevel@tonic-gate 		diff = pwlen - opwlen;
6267c478bd9Sstevel@tonic-gate 	else
6277c478bd9Sstevel@tonic-gate 		diff = opwlen - pwlen;
6287c478bd9Sstevel@tonic-gate 
6297c478bd9Sstevel@tonic-gate 	while (*opw != '\0' && *pw != '\0' && max-- != 0) {
6307c478bd9Sstevel@tonic-gate 		if (*opw != *pw)
6317c478bd9Sstevel@tonic-gate 			diff++;
6327c478bd9Sstevel@tonic-gate 		opw++;
6337c478bd9Sstevel@tonic-gate 		pw++;
6347c478bd9Sstevel@tonic-gate 	}
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate 	if (diff  < pwdef->mindiff) {
637*cbea7acaSDominik Hassler 		const char *progname;
6387c478bd9Sstevel@tonic-gate 
639*cbea7acaSDominik Hassler 		(void) pam_get_item(pamh, PAM_SERVICE,
640*cbea7acaSDominik Hassler 		    (const void **)&progname);
6417c478bd9Sstevel@tonic-gate 
6427c478bd9Sstevel@tonic-gate 		error(pamh, flags, dgettext(TEXT_DOMAIN,
6437c478bd9Sstevel@tonic-gate 		    "%s: The first %d characters of the old and new passwords "
6447c478bd9Sstevel@tonic-gate 		    "must differ by at least %d positions."), progname,
6457c478bd9Sstevel@tonic-gate 		    pwdef->maxlength, pwdef->mindiff);
6467c478bd9Sstevel@tonic-gate 		return (1);
6477c478bd9Sstevel@tonic-gate 	}
6487c478bd9Sstevel@tonic-gate 
6497c478bd9Sstevel@tonic-gate 	return (0);
6507c478bd9Sstevel@tonic-gate }
6517c478bd9Sstevel@tonic-gate 
6527c478bd9Sstevel@tonic-gate /*
6537c478bd9Sstevel@tonic-gate  * check to see if password is in one way or another based on a
6547c478bd9Sstevel@tonic-gate  * dictionary word. Returns 0 if password is OK, 1 if it is based
6557c478bd9Sstevel@tonic-gate  * on a dictionary word and hence should be rejected.
6567c478bd9Sstevel@tonic-gate  */
6577c478bd9Sstevel@tonic-gate int
check_dictionary(const char * pw,struct pwdefaults * pwdef,pam_handle_t * pamh,int flags)658*cbea7acaSDominik Hassler check_dictionary(const char *pw, struct pwdefaults *pwdef, pam_handle_t *pamh,
6597c478bd9Sstevel@tonic-gate     int flags)
6607c478bd9Sstevel@tonic-gate {
6617c478bd9Sstevel@tonic-gate 	int crack_ret;
6627c478bd9Sstevel@tonic-gate 	int ret;
663*cbea7acaSDominik Hassler 	const char *progname;
6647c478bd9Sstevel@tonic-gate 
665*cbea7acaSDominik Hassler 	(void) pam_get_item(pamh, PAM_SERVICE, (const void **)&progname);
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate 	/* dictionary check isn't MT-safe */
6687c478bd9Sstevel@tonic-gate 	(void) mutex_lock(&dictlock);
6697c478bd9Sstevel@tonic-gate 
6707c478bd9Sstevel@tonic-gate 	if (pwdef->dicts &&
6717c478bd9Sstevel@tonic-gate 	    make_dict_database(pwdef->dicts, pwdef->db_location) != 0) {
6727c478bd9Sstevel@tonic-gate 		(void) mutex_unlock(&dictlock);
6737c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "pam_authtok_check:pam_sm_chauthtok: "
6747c478bd9Sstevel@tonic-gate 		    "Dictionary database not present.");
6757c478bd9Sstevel@tonic-gate 		error(pamh, flags, dgettext(TEXT_DOMAIN,
6767c478bd9Sstevel@tonic-gate 		    "%s: password dictionary missing."), progname);
6777c478bd9Sstevel@tonic-gate 		return (PAM_SYSTEM_ERR);
6787c478bd9Sstevel@tonic-gate 	}
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 	crack_ret = DictCheck(pw, pwdef->db_location);
6817c478bd9Sstevel@tonic-gate 
6827c478bd9Sstevel@tonic-gate 	(void) mutex_unlock(&dictlock);
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 	switch (crack_ret) {
6857c478bd9Sstevel@tonic-gate 	case DATABASE_OPEN_FAIL:
6867c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "pam_authtok_check:pam_sm_chauthtok: "
6877c478bd9Sstevel@tonic-gate 		    "dictionary database open failure: %s", strerror(errno));
6887c478bd9Sstevel@tonic-gate 		error(pamh, flags, dgettext(TEXT_DOMAIN,
6897c478bd9Sstevel@tonic-gate 		    "%s: failed to open dictionary database."), progname);
6907c478bd9Sstevel@tonic-gate 		ret = PAM_SYSTEM_ERR;
6917c478bd9Sstevel@tonic-gate 		break;
6927c478bd9Sstevel@tonic-gate 	case DICTIONARY_WORD:
6937c478bd9Sstevel@tonic-gate 		error(pamh, flags, dgettext(TEXT_DOMAIN,
6947c478bd9Sstevel@tonic-gate 		    "%s: password is based on a dictionary word."), progname);
6957c478bd9Sstevel@tonic-gate 		ret = PAM_AUTHTOK_ERR;
6967c478bd9Sstevel@tonic-gate 		break;
6977c478bd9Sstevel@tonic-gate 	case REVERSE_DICTIONARY_WORD:
6987c478bd9Sstevel@tonic-gate 		error(pamh, flags, dgettext(TEXT_DOMAIN,
6997c478bd9Sstevel@tonic-gate 		    "%s: password is based on a reversed dictionary word."),
7007c478bd9Sstevel@tonic-gate 		    progname);
7017c478bd9Sstevel@tonic-gate 		ret = PAM_AUTHTOK_ERR;
7027c478bd9Sstevel@tonic-gate 		break;
7037c478bd9Sstevel@tonic-gate 	default:
7047c478bd9Sstevel@tonic-gate 		ret = PAM_SUCCESS;
7057c478bd9Sstevel@tonic-gate 		break;
7067c478bd9Sstevel@tonic-gate 	}
7077c478bd9Sstevel@tonic-gate 	return (ret);
7087c478bd9Sstevel@tonic-gate }
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate int
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc,const char ** argv)7117c478bd9Sstevel@tonic-gate pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
7127c478bd9Sstevel@tonic-gate {
7137c478bd9Sstevel@tonic-gate 	int debug = 0;
7147c478bd9Sstevel@tonic-gate 	int retcode = 0;
7157c478bd9Sstevel@tonic-gate 	int force_check = 0;
7167c478bd9Sstevel@tonic-gate 	int i;
7177c478bd9Sstevel@tonic-gate 	size_t pwlen;
718*cbea7acaSDominik Hassler 	const char *usrname;
719*cbea7acaSDominik Hassler 	const char *pwbuf, *opwbuf;
7207c478bd9Sstevel@tonic-gate 	pwu_repository_t *pwu_rep = PWU_DEFAULT_REP;
721*cbea7acaSDominik Hassler 	const pam_repository_t *pwd_rep = NULL;
7227c478bd9Sstevel@tonic-gate 	struct pwdefaults pwdef;
723*cbea7acaSDominik Hassler 	const char *progname;
7247c478bd9Sstevel@tonic-gate 
7257c478bd9Sstevel@tonic-gate 	/* needs to be set before option processing */
7267c478bd9Sstevel@tonic-gate 	pwdef.server_policy = B_FALSE;
7277c478bd9Sstevel@tonic-gate 
7287c478bd9Sstevel@tonic-gate 	for (i = 0; i < argc; i++) {
7297c478bd9Sstevel@tonic-gate 		if (strcmp(argv[i], "debug") == 0)
7307c478bd9Sstevel@tonic-gate 			debug = 1;
7317c478bd9Sstevel@tonic-gate 		if (strcmp(argv[i], "force_check") == 0)
7327c478bd9Sstevel@tonic-gate 			force_check = 1;
7337c478bd9Sstevel@tonic-gate 		if (strcmp(argv[i], "server_policy") == 0)
7347c478bd9Sstevel@tonic-gate 			pwdef.server_policy = B_TRUE;
7357c478bd9Sstevel@tonic-gate 	}
7367c478bd9Sstevel@tonic-gate 
7377c478bd9Sstevel@tonic-gate 	if (debug)
7387c478bd9Sstevel@tonic-gate 		syslog(LOG_AUTH | LOG_DEBUG,
7397c478bd9Sstevel@tonic-gate 		    "pam_authtok_check: pam_sm_chauthok called(%x) "
7407c478bd9Sstevel@tonic-gate 		    "force_check = %d", flags, force_check);
7417c478bd9Sstevel@tonic-gate 
7427c478bd9Sstevel@tonic-gate 	if ((flags & PAM_PRELIM_CHECK) == 0)
7437c478bd9Sstevel@tonic-gate 		return (PAM_IGNORE);
7447c478bd9Sstevel@tonic-gate 
745*cbea7acaSDominik Hassler 	(void) pam_get_item(pamh, PAM_SERVICE, (const void **)&progname);
746*cbea7acaSDominik Hassler 	(void) pam_get_item(pamh, PAM_USER, (const void **)&usrname);
7477c478bd9Sstevel@tonic-gate 	if (usrname == NULL || *usrname == '\0') {
7487c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "pam_authtok_check: username name is empty");
7497c478bd9Sstevel@tonic-gate 		return (PAM_USER_UNKNOWN);
7507c478bd9Sstevel@tonic-gate 	}
7517c478bd9Sstevel@tonic-gate 
752*cbea7acaSDominik Hassler 	(void) pam_get_item(pamh, PAM_AUTHTOK, (const void **)&pwbuf);
753*cbea7acaSDominik Hassler 	(void) pam_get_item(pamh, PAM_OLDAUTHTOK, (const void **)&opwbuf);
7547c478bd9Sstevel@tonic-gate 	if (pwbuf == NULL)
7557c478bd9Sstevel@tonic-gate 		return (PAM_AUTHTOK_ERR);
7567c478bd9Sstevel@tonic-gate 
7577c478bd9Sstevel@tonic-gate 	/* none of these checks holds if caller say so */
7587c478bd9Sstevel@tonic-gate 	if ((flags & PAM_NO_AUTHTOK_CHECK) != 0 && force_check == 0)
7597c478bd9Sstevel@tonic-gate 		return (PAM_SUCCESS);
7607c478bd9Sstevel@tonic-gate 
7617c478bd9Sstevel@tonic-gate 	/* read system-defaults */
7627c478bd9Sstevel@tonic-gate 	retcode = get_passwd_defaults(pamh, usrname, &pwdef);
7637c478bd9Sstevel@tonic-gate 	if (retcode != PAM_SUCCESS)
7647c478bd9Sstevel@tonic-gate 		return (retcode);
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 	if (debug) {
7677c478bd9Sstevel@tonic-gate 		syslog(LOG_AUTH | LOG_DEBUG,
7687c478bd9Sstevel@tonic-gate 		    "pam_authtok_check: MAXLENGTH= %d, server_policy = %s",
7697c478bd9Sstevel@tonic-gate 		    pwdef.maxlength, pwdef.server_policy ? "true" : "false");
7707c478bd9Sstevel@tonic-gate 		syslog(LOG_AUTH | LOG_DEBUG,
7717c478bd9Sstevel@tonic-gate 		    "pam_authtok_check: PASSLENGTH= %d", pwdef.minlength);
7727c478bd9Sstevel@tonic-gate 		syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: NAMECHECK=%s",
7737c478bd9Sstevel@tonic-gate 		    pwdef.do_namecheck == B_TRUE ? "Yes" : "No");
7747c478bd9Sstevel@tonic-gate 		syslog(LOG_AUTH | LOG_DEBUG,
7757c478bd9Sstevel@tonic-gate 		    "pam_authtok_check: do_dictcheck = %s\n",
7767c478bd9Sstevel@tonic-gate 		    pwdef.do_dictcheck ? "true" : "false");
7777c478bd9Sstevel@tonic-gate 		if (pwdef.do_dictcheck) {
7787c478bd9Sstevel@tonic-gate 			syslog(LOG_AUTH | LOG_DEBUG,
7797c478bd9Sstevel@tonic-gate 			    "pam_authtok_check: DICTIONLIST=%s",
7807c478bd9Sstevel@tonic-gate 			    (pwdef.dicts != NULL) ? pwdef.dicts : "<not set>");
7817c478bd9Sstevel@tonic-gate 			syslog(LOG_AUTH | LOG_DEBUG,
7827c478bd9Sstevel@tonic-gate 			    "pam_authtok_check: DICTIONDBDIR=%s",
7837c478bd9Sstevel@tonic-gate 			    pwdef.db_location);
7847c478bd9Sstevel@tonic-gate 		}
7857c478bd9Sstevel@tonic-gate 		syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: MINDIFF=%d",
7867c478bd9Sstevel@tonic-gate 		    pwdef.mindiff);
7877c478bd9Sstevel@tonic-gate 		syslog(LOG_AUTH | LOG_DEBUG,
7887c478bd9Sstevel@tonic-gate 		    "pam_authtok_check: MINALPHA=%d, MINNONALPHA=%d",
7897c478bd9Sstevel@tonic-gate 		    pwdef.minalpha, pwdef.minnonalpha);
7907c478bd9Sstevel@tonic-gate 		syslog(LOG_AUTH | LOG_DEBUG,
7917c478bd9Sstevel@tonic-gate 		    "pam_authtok_check: MINSPECIAL=%d, MINDIGIT=%d",
7927c478bd9Sstevel@tonic-gate 		    pwdef.minspecial, pwdef.mindigit);
7937c478bd9Sstevel@tonic-gate 		syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: WHITESPACE=%s",
7947c478bd9Sstevel@tonic-gate 		    pwdef.whitespace ? "YES" : "NO");
7957c478bd9Sstevel@tonic-gate 		syslog(LOG_AUTH | LOG_DEBUG,
7967c478bd9Sstevel@tonic-gate 		    "pam_authtok_check: MINUPPER=%d, MINLOWER=%d",
7977c478bd9Sstevel@tonic-gate 		    pwdef.minupper, pwdef.minlower);
7987c478bd9Sstevel@tonic-gate 		syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: MAXREPEATS=%d",
7997c478bd9Sstevel@tonic-gate 		    pwdef.maxrepeat);
8007c478bd9Sstevel@tonic-gate 	}
8017c478bd9Sstevel@tonic-gate 
8027c478bd9Sstevel@tonic-gate 	/*
8037c478bd9Sstevel@tonic-gate 	 * If server policy is still true (might be changed from the
8047c478bd9Sstevel@tonic-gate 	 * value specified in /etc/pam.conf by get_passwd_defaults()),
8057c478bd9Sstevel@tonic-gate 	 * we return ignore and let the server do all the checks.
8067c478bd9Sstevel@tonic-gate 	 */
8077c478bd9Sstevel@tonic-gate 	if (pwdef.server_policy == B_TRUE) {
8087c478bd9Sstevel@tonic-gate 		free_passwd_defaults(&pwdef);
8097c478bd9Sstevel@tonic-gate 		return (PAM_IGNORE);
8107c478bd9Sstevel@tonic-gate 	}
8117c478bd9Sstevel@tonic-gate 
8127c478bd9Sstevel@tonic-gate 	/*
8137c478bd9Sstevel@tonic-gate 	 * XXX: JV: we can't really make any assumption on the length of
8147c478bd9Sstevel@tonic-gate 	 *	the password that will be used by the crypto algorithm.
8157c478bd9Sstevel@tonic-gate 	 *	for UNIX-style encryption, minalpha=5,minnonalpha=5 might
8167c478bd9Sstevel@tonic-gate 	 *	be impossible, but not for MD5 style hashes... what to do?
8177c478bd9Sstevel@tonic-gate 	 *
8187c478bd9Sstevel@tonic-gate 	 *	since we don't know what alg. will be used, we operate on
8197c478bd9Sstevel@tonic-gate 	 *	the password as entered, so we don't sanity check anything
8207c478bd9Sstevel@tonic-gate 	 *	for now.
8217c478bd9Sstevel@tonic-gate 	 */
8227c478bd9Sstevel@tonic-gate 
8237c478bd9Sstevel@tonic-gate 	/*
8247c478bd9Sstevel@tonic-gate 	 * Make sure new password is long enough
8257c478bd9Sstevel@tonic-gate 	 */
8267c478bd9Sstevel@tonic-gate 	pwlen = strlen(pwbuf);
8277c478bd9Sstevel@tonic-gate 
8287c478bd9Sstevel@tonic-gate 	if (pwlen < pwdef.minlength) {
8297c478bd9Sstevel@tonic-gate 		error(pamh, flags, dgettext(TEXT_DOMAIN,
8307c478bd9Sstevel@tonic-gate 		    "%s: Password too short - must be at least %d "
8317c478bd9Sstevel@tonic-gate 		    "characters."), progname, pwdef.minlength);
8327c478bd9Sstevel@tonic-gate 		free_passwd_defaults(&pwdef);
8337c478bd9Sstevel@tonic-gate 		return (PAM_AUTHTOK_ERR);
8347c478bd9Sstevel@tonic-gate 	}
8357c478bd9Sstevel@tonic-gate 
8367c478bd9Sstevel@tonic-gate 	/* Make sure the password doesn't equal--a shift of--the username */
8377c478bd9Sstevel@tonic-gate 	if (pwdef.do_namecheck) {
8387c478bd9Sstevel@tonic-gate 		switch (check_circular(usrname, pwbuf)) {
8397c478bd9Sstevel@tonic-gate 		case 1:
8407c478bd9Sstevel@tonic-gate 			error(pamh, flags, dgettext(TEXT_DOMAIN,
8417c478bd9Sstevel@tonic-gate 			    "%s: Password cannot be circular shift of "
8427c478bd9Sstevel@tonic-gate 			    "logonid."), progname);
8437c478bd9Sstevel@tonic-gate 			free_passwd_defaults(&pwdef);
8447c478bd9Sstevel@tonic-gate 			return (PAM_AUTHTOK_ERR);
8457c478bd9Sstevel@tonic-gate 		case -1:
8467c478bd9Sstevel@tonic-gate 			free_passwd_defaults(&pwdef);
8477c478bd9Sstevel@tonic-gate 			return (PAM_BUF_ERR);
8487c478bd9Sstevel@tonic-gate 		default:
8497c478bd9Sstevel@tonic-gate 			break;
8507c478bd9Sstevel@tonic-gate 		}
8517c478bd9Sstevel@tonic-gate 	}
8527c478bd9Sstevel@tonic-gate 
8537c478bd9Sstevel@tonic-gate 	/* Check if new password is in history list. */
854*cbea7acaSDominik Hassler 	(void) pam_get_item(pamh, PAM_REPOSITORY, (const void **)&pwd_rep);
8557c478bd9Sstevel@tonic-gate 	if (pwd_rep != NULL) {
8567c478bd9Sstevel@tonic-gate 		if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL)
8577c478bd9Sstevel@tonic-gate 			return (PAM_BUF_ERR);
8587c478bd9Sstevel@tonic-gate 		pwu_rep->type = pwd_rep->type;
8597c478bd9Sstevel@tonic-gate 		pwu_rep->scope = pwd_rep->scope;
8607c478bd9Sstevel@tonic-gate 		pwu_rep->scope_len = pwd_rep->scope_len;
8617c478bd9Sstevel@tonic-gate 	}
8627c478bd9Sstevel@tonic-gate 
8637c478bd9Sstevel@tonic-gate 	if (__check_history(usrname, pwbuf, pwu_rep) == PWU_SUCCESS) {
8647c478bd9Sstevel@tonic-gate 		/* password found in history */
8657c478bd9Sstevel@tonic-gate 		error(pamh, flags, dgettext(TEXT_DOMAIN,
8667c478bd9Sstevel@tonic-gate 		    "%s: Password in history list."), progname);
8677c478bd9Sstevel@tonic-gate 		if (pwu_rep != PWU_DEFAULT_REP)
8687c478bd9Sstevel@tonic-gate 			free(pwu_rep);
8697c478bd9Sstevel@tonic-gate 		free_passwd_defaults(&pwdef);
8707c478bd9Sstevel@tonic-gate 		return (PAM_AUTHTOK_ERR);
8717c478bd9Sstevel@tonic-gate 	}
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 	if (pwu_rep != PWU_DEFAULT_REP)
8747c478bd9Sstevel@tonic-gate 		free(pwu_rep);
8757c478bd9Sstevel@tonic-gate 
8767c478bd9Sstevel@tonic-gate 	/* check MINALPHA, MINLOWER, etc. */
8777c478bd9Sstevel@tonic-gate 	if (check_composition(pwbuf, &pwdef, pamh, flags) != 0) {
8787c478bd9Sstevel@tonic-gate 		free_passwd_defaults(&pwdef);
8797c478bd9Sstevel@tonic-gate 		return (PAM_AUTHTOK_ERR);
8807c478bd9Sstevel@tonic-gate 	}
8817c478bd9Sstevel@tonic-gate 
8827c478bd9Sstevel@tonic-gate 	/* make sure the old and new password are not too much alike */
8837c478bd9Sstevel@tonic-gate 	if (check_diff(pwbuf, opwbuf, &pwdef, pamh, flags) != 0) {
8847c478bd9Sstevel@tonic-gate 		free_passwd_defaults(&pwdef);
8857c478bd9Sstevel@tonic-gate 		return (PAM_AUTHTOK_ERR);
8867c478bd9Sstevel@tonic-gate 	}
8877c478bd9Sstevel@tonic-gate 
8887c478bd9Sstevel@tonic-gate 	/* dictionary check */
8897c478bd9Sstevel@tonic-gate 	if (pwdef.do_dictcheck) {
8907c478bd9Sstevel@tonic-gate 		retcode = check_dictionary(pwbuf, &pwdef, pamh, flags);
8917c478bd9Sstevel@tonic-gate 		if (retcode != PAM_SUCCESS) {
8927c478bd9Sstevel@tonic-gate 			free_passwd_defaults(&pwdef);
8937c478bd9Sstevel@tonic-gate 			return (retcode);
8947c478bd9Sstevel@tonic-gate 		}
8957c478bd9Sstevel@tonic-gate 	}
8967c478bd9Sstevel@tonic-gate 
8977c478bd9Sstevel@tonic-gate 	free_passwd_defaults(&pwdef);
8987c478bd9Sstevel@tonic-gate 	/* password has passed all tests: it's strong enough */
8997c478bd9Sstevel@tonic-gate 	return (PAM_SUCCESS);
9007c478bd9Sstevel@tonic-gate }
901