4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/types.h>
27 #include <sys/varargs.h>
28 #include <sys/param.h>
29 #include <sys/sysmacros.h>
33 #include <security/pam_appl.h>
34 #include <security/pam_modules.h>
35 #include <security/pam_impl.h>
45 #include <passwdutil.h>
47 #define PWADMIN "/etc/default/passwd"
54 mutex_t dictlock
= DEFAULTMUTEX
;
58 * PASSLENGTH (int) minimum password length
59 * NAMECHECK (yes/no) perform comparison of password and loginname
60 * MINDIFF (int) minimum number of character-positions in which
61 * the old and the new password should differ.
62 * MINALPHA (int) minimum number of Alpha characters
63 * MINUPPER (int) minimum number of upper-case characters
64 * MINLOWER (int) minimum number of lower-case characters
65 * MAXREPEATS (int) maximum number of consecutively repeating chars
66 * WHITESPACE (yes/no) Are whitespaces allowed?
68 * Furthermore, these two mutualy exclusive groups of options are allowed:
70 * MINNONALPHA (int) minimum number of characters from the
71 * character classes [ punct, space, digit ]
72 * if WHITESPACE == NO, whitespaces don't count.
74 * MINSPECIAL (int) minimum number of punctuation characters.
75 * if WHITESPACE != NO, whitespace is seen as
76 * a "special" character.
77 * MINDIGIT (int) minimum number of digits
79 * specifying options from both groups results in an error to syslog and
80 * failure to change the password.
83 * HISTORY is implemented at the repository level (passwdutil).
87 * default password-strength-values, compiled-in or stored in PWADMIN
91 boolean_t server_policy
; /* server policy flag from pam.conf */
92 uint_t minlength
; /* minimum password lenght */
93 uint_t maxlength
; /* maximum (significant) length */
94 boolean_t do_namecheck
; /* check password against user's gecos */
95 char db_location
[MAXPATHLEN
]; /* location of the generated database */
96 boolean_t do_dictcheck
; /* perform dictionary lookup */
97 char *dicts
; /* list of dictionaries configured */
98 uint_t mindiff
; /* old and new should differ by this much */
99 uint_t minalpha
; /* minimum alpha characters required */
100 uint_t minupper
; /* minimum uppercase characters required */
101 uint_t minlower
; /* minimum lowercase characters required */
102 uint_t minnonalpha
; /* minimum special (non alpha) required */
103 uint_t maxrepeat
; /* maximum number of repeating chars allowed */
104 uint_t minspecial
; /* punctuation characters */
105 uint_t mindigit
; /* minimum number of digits required */
106 boolean_t whitespace
; /* is whitespace allowed in a password */
112 error(pam_handle_t
*pamh
, int flags
, char *fmt
, ...)
115 char msg
[1][PAM_MAX_MSG_SIZE
];
118 (void) vsnprintf(msg
[0], sizeof (msg
[0]), fmt
, ap
);
120 if ((flags
& PAM_SILENT
) == 0)
121 (void) __pam_display_msg(pamh
, PAM_ERROR_MSG
, 1, msg
, NULL
);
125 defread_int(char *name
, uint_t
*ip
, void *defp
)
129 if ((q
= defread_r(name
, defp
)) != NULL
) {
131 syslog(LOG_ERR
, "pam_authtok_check: %s contains "
132 "non-integer value for %s: %s. "
133 "Using default instead.", PWADMIN
, name
, q
);
143 * fill in static defaults, and augment with settings from PWADMIN
144 * get system defaults with regard to maximum password length
147 get_passwd_defaults(pam_handle_t
*pamh
, char *user
, struct pwdefaults
*p
)
150 boolean_t minnonalpha_defined
= B_FALSE
;
151 pwu_repository_t
*pwu_rep
;
152 struct pam_repository
*pam_rep
;
158 (void) pam_get_item(pamh
, PAM_SERVICE
, (void **)&progname
);
160 /* Module defaults */
161 p
->minlength
= MINLENGTH
;
162 p
->do_namecheck
= B_TRUE
;
163 p
->do_dictcheck
= B_FALSE
;
165 p
->mindiff
= MINDIFF
;
166 p
->minalpha
= MINALPHA
;
167 p
->minnonalpha
= MINNONALPHA
;
168 p
->minupper
= 0; /* not configured by default */
169 p
->minlower
= 0; /* not configured by default */
170 p
->maxrepeat
= 0; /* not configured by default */
174 p
->whitespace
= B_TRUE
;
176 if ((defp
= defopen_r(PWADMIN
)) == NULL
)
177 return (PAM_SUCCESS
);
179 (void) defread_int("PASSLENGTH=", &p
->minlength
, defp
);
181 if ((q
= defread_r("NAMECHECK=", defp
)) != NULL
&&
182 strcasecmp(q
, "NO") == 0)
183 p
->do_namecheck
= B_FALSE
;
185 if ((q
= defread_r("DICTIONLIST=", defp
)) != NULL
) {
186 if ((p
->dicts
= strdup(q
)) == NULL
) {
187 syslog(LOG_ERR
, "pam_authtok_check: out of memory");
189 return (PAM_BUF_ERR
);
192 p
->do_dictcheck
= B_TRUE
;
197 if ((q
= defread_r("DICTIONDBDIR=", defp
)) != 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.");
203 return (PAM_SYSTEM_ERR
);
205 p
->do_dictcheck
= B_TRUE
;
207 (void) strlcpy(p
->db_location
, CRACK_DIR
,
208 sizeof (p
->db_location
));
211 (void) defread_int("MINDIFF=", &p
->mindiff
, defp
);
212 (void) defread_int("MINALPHA=", &p
->minalpha
, defp
);
213 (void) defread_int("MINUPPER=", &p
->minupper
, defp
);
214 (void) defread_int("MINLOWER=", &p
->minlower
, defp
);
215 if (defread_int("MINNONALPHA=", &p
->minnonalpha
, defp
))
216 minnonalpha_defined
= B_TRUE
;
217 (void) defread_int("MAXREPEATS=", &p
->maxrepeat
, defp
);
219 if (defread_int("MINSPECIAL=", &p
->minspecial
, defp
)) {
220 if (minnonalpha_defined
) {
221 syslog(LOG_ERR
, "pam_authtok_check: %s contains "
222 "definition for MINNONALPHA and for MINSPECIAL. "
223 "These options are mutually exclusive.", PWADMIN
);
225 return (PAM_SYSTEM_ERR
);
230 if (defread_int("MINDIGIT=", &p
->mindigit
, defp
)) {
231 if (minnonalpha_defined
) {
232 syslog(LOG_ERR
, "pam_authtok_check: %s contains "
233 "definition for MINNONALPHA and for MINDIGIT. "
234 "These options are mutually exclusive.", PWADMIN
);
236 return (PAM_SYSTEM_ERR
);
241 if ((q
= defread_r("WHITESPACE=", defp
)) != NULL
)
243 (strcasecmp(q
, "no") == 0 || strcmp(q
, "0") == 0)
249 * Determine the number of significant characters in a password
251 * we find out where the user information came from (which repository),
252 * and which password-crypt-algorithm is to be used (based on the
253 * old password, or the system default).
255 * If the user comes from a repository other than FILES/NIS
256 * the module-flag "server_policy" means that we don't perform
257 * any checks on the user, but let the repository decide instead.
260 (void) pam_get_item(pamh
, PAM_REPOSITORY
, (void **)&pam_rep
);
261 if (pam_rep
!= NULL
) {
262 if ((pwu_rep
= calloc(1, sizeof (*pwu_rep
))) == NULL
)
263 return (PAM_BUF_ERR
);
264 pwu_rep
->type
= pam_rep
->type
;
265 pwu_rep
->scope
= pam_rep
->scope
;
266 pwu_rep
->scope_len
= pam_rep
->scope_len
;
268 pwu_rep
= PWU_DEFAULT_REP
;
271 attr
[0].type
= ATTR_PASSWD
; attr
[0].next
= &attr
[1];
272 attr
[1].type
= ATTR_REP_NAME
; attr
[1].next
= NULL
;
273 result
= __get_authtoken_attr(user
, pwu_rep
, attr
);
274 if (pwu_rep
!= PWU_DEFAULT_REP
)
277 if (result
!= PWU_SUCCESS
) {
279 * In the unlikely event that we can't obtain any info about
280 * the users password, we assume the most strict scenario.
282 p
->maxlength
= _PASS_MAX_XPG
;
284 char *oldpw
= attr
[0].data
.val_s
;
285 char *repository
= attr
[1].data
.val_s
;
286 if ((strcmp(repository
, "files") == 0 ||
287 strcmp(repository
, "nis") == 0) ||
288 p
->server_policy
== B_FALSE
) {
291 * We currently need to supply this dummy to
292 * crypt_gensalt(). This will change RSN.
296 dummy
.pw_name
= user
;
298 salt
= crypt_gensalt(oldpw
, &dummy
);
299 if (salt
&& *salt
== '$')
300 p
->maxlength
= _PASS_MAX
;
302 p
->maxlength
= _PASS_MAX_XPG
;
306 p
->server_policy
= B_FALSE
; /* we perform checks */
308 /* not files or nis AND server_policy is set */
309 p
->maxlength
= _PASS_MAX
;
311 free(attr
[0].data
.val_s
);
312 free(attr
[1].data
.val_s
);
315 /* sanity check of the configured parameters */
316 if (p
->minlength
< p
->mindigit
+ p
->minspecial
+ p
->minnonalpha
+
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
,
322 p
->mindigit
+ p
->minspecial
+ p
->minnonalpha
+ p
->minalpha
);
323 p
->minlength
= p
->mindigit
+ p
->minspecial
+ p
->minnonalpha
+
325 syslog(LOG_ERR
, "%s: pam_authtok_check: effective "
326 "PASSLENGTH set to %d.", progname
, p
->minlength
);
327 /* this won't lead to failure */
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 */
339 return (PAM_SUCCESS
);
343 * free_passwd_defaults(struct pwdefaults *p)
345 * free space occupied by the defaults read from PWADMIN
348 free_passwd_defaults(struct pwdefaults
*p
)
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".
365 char c
, *p
, *o
, *r
, *buff
, *ubuff
, *pubuff
;
366 unsigned int i
, j
, k
, l
, m
;
378 pubuff
= malloc(len
);
380 if (buff
== NULL
|| ubuff
== NULL
|| pubuff
== NULL
) {
381 syslog(LOG_ERR
, "pam_authtok_check: out of memory.");
387 for (p
= s
; c
= *p
++; *o
++ = c
)
392 for (p
= t
; c
= *p
++; *o
++ = c
)
400 for (k
= 0; k
< i
; k
++) {
410 if (strcmp(p
, pubuff
) == 0) {
419 *--p
= *r
++; /* reverse test-string for m==0 pass */
422 (void) memset(buff
, 0, len
);
423 (void) memset(ubuff
, 0, len
);
424 (void) memset(pubuff
, 0, len
);
433 * count the different character classes present in the password.
436 check_composition(char *pw
, struct pwdefaults
*pwdef
, pam_handle_t
*pamh
,
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;
451 uint_t significant
= pwdef
->maxlength
;
454 (void) pam_get_item(pamh
, PAM_SERVICE
, (void **)&progname
);
456 /* go over the password gathering statistics */
457 for (w
= pw
; significant
!= 0 && *w
!= '\0'; w
++, significant
--) {
465 } else if (isspace(*w
))
467 else if (isdigit(*w
))
472 if (++repeat
> maxrepeat
)
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
491 if (pwdef
->maxlength
< strlen(pw
))
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)"
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)"
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
);
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)"
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)"
524 (void) snprintf(errmsg
, sizeof (errmsg
), dgettext(TEXT_DOMAIN
,
525 "%s: The password must contain at least %%d %%s."),
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
);
537 * Once we get here, whitespace_cnt is either 0, or whitespaces are
538 * to be treated a special characters.
541 if (alpha_cnt
< pwdef
->minalpha
) {
542 error(pamh
, flags
, errmsg
, pwdef
->minalpha
,
543 dgettext(TEXT_DOMAIN
, "alphabetic character(s)"));
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)"));
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)"));
567 if (digit_cnt
< pwdef
->mindigit
) {
568 error(pamh
, flags
, errmsg
, pwdef
->mindigit
,
569 dgettext(TEXT_DOMAIN
, "digit(s)"));
575 if (upper_cnt
< pwdef
->minupper
) {
576 error(pamh
, flags
, errmsg
, pwdef
->minupper
,
577 dgettext(TEXT_DOMAIN
, "uppercase alpha character(s)"));
581 if (lower_cnt
< pwdef
->minlower
) {
582 error(pamh
, flags
, errmsg
, pwdef
->minlower
,
583 dgettext(TEXT_DOMAIN
, "lowercase alpha character(s)"));
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
);
599 * make sure that old and new password differ by at least 'mindiff'
600 * positions. Return 0 if OK, 1 otherwise
603 check_diff(char *pw
, char *opw
, struct pwdefaults
*pwdef
, pam_handle_t
*pamh
,
606 size_t pwlen
, opwlen
, max
;
607 unsigned int diff
; /* difference between old and new */
612 max
= pwdef
->maxlength
;
613 pwlen
= MIN(strlen(pw
), max
);
614 opwlen
= MIN(strlen(opw
), max
);
617 diff
= pwlen
- opwlen
;
619 diff
= opwlen
- pwlen
;
621 while (*opw
!= '\0' && *pw
!= '\0' && max
-- != 0) {
628 if (diff
< pwdef
->mindiff
) {
631 (void) pam_get_item(pamh
, PAM_SERVICE
, (void **)&progname
);
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
);
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.
649 check_dictionary(char *pw
, struct pwdefaults
*pwdef
, pam_handle_t
*pamh
,
656 (void) pam_get_item(pamh
, PAM_SERVICE
, (void **)&progname
);
658 /* dictionary check isn't MT-safe */
659 (void) mutex_lock(&dictlock
);
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
);
671 crack_ret
= DictCheck(pw
, pwdef
->db_location
);
673 (void) mutex_unlock(&dictlock
);
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
;
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
;
688 case REVERSE_DICTIONARY_WORD
:
689 error(pamh
, flags
, dgettext(TEXT_DOMAIN
,
690 "%s: password is based on a reversed dictionary word."),
692 ret
= PAM_AUTHTOK_ERR
;
702 pam_sm_chauthtok(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
)
710 char *pwbuf
, *opwbuf
;
711 pwu_repository_t
*pwu_rep
= PWU_DEFAULT_REP
;
712 pam_repository_t
*pwd_rep
= NULL
;
713 struct pwdefaults pwdef
;
716 /* needs to be set before option processing */
717 pwdef
.server_policy
= B_FALSE
;
719 for (i
= 0; i
< argc
; i
++) {
720 if (strcmp(argv
[i
], "debug") == 0)
722 if (strcmp(argv
[i
], "force_check") == 0)
724 if (strcmp(argv
[i
], "server_policy") == 0)
725 pwdef
.server_policy
= B_TRUE
;
729 syslog(LOG_AUTH
| LOG_DEBUG
,
730 "pam_authtok_check: pam_sm_chauthok called(%x) "
731 "force_check = %d", flags
, force_check
);
733 if ((flags
& PAM_PRELIM_CHECK
) == 0)
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
);
743 (void) pam_get_item(pamh
, PAM_AUTHTOK
, (void **)&pwbuf
);
744 (void) pam_get_item(pamh
, PAM_OLDAUTHTOK
, (void **)&opwbuf
);
746 return (PAM_AUTHTOK_ERR
);
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
);
752 /* read system-defaults */
753 retcode
= get_passwd_defaults(pamh
, usrname
, &pwdef
);
754 if (retcode
!= PAM_SUCCESS
)
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",
776 syslog(LOG_AUTH
| LOG_DEBUG
, "pam_authtok_check: MINDIFF=%d",
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",
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.
798 if (pwdef
.server_policy
== B_TRUE
) {
799 free_passwd_defaults(&pwdef
);
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?
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
815 * Make sure new password is long enough
817 pwlen
= strlen(pwbuf
);
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
);
827 /* Make sure the password doesn't equal--a shift of--the username */
828 if (pwdef
.do_namecheck
) {
829 switch (check_circular(usrname
, pwbuf
)) {
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
);
837 free_passwd_defaults(&pwdef
);
838 return (PAM_BUF_ERR
);
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
;
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
)
860 free_passwd_defaults(&pwdef
);
861 return (PAM_AUTHTOK_ERR
);
864 if (pwu_rep
!= PWU_DEFAULT_REP
)
867 /* check MINALPHA, MINLOWER, etc. */
868 if (check_composition(pwbuf
, &pwdef
, pamh
, flags
) != 0) {
869 free_passwd_defaults(&pwdef
);
870 return (PAM_AUTHTOK_ERR
);
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
);
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
);
888 free_passwd_defaults(&pwdef
);
889 /* password has passed all tests: it's strong enough */
890 return (PAM_SUCCESS
);