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.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
27 #include <sys/types.h>
28 #include <sys/varargs.h>
29 #include <sys/param.h>
30 #include <sys/sysmacros.h>
34 #include <security/pam_appl.h>
35 #include <security/pam_modules.h>
36 #include <security/pam_impl.h>
46 #include <passwdutil.h>
48 #define PWADMIN "/etc/default/passwd"
55 mutex_t dictlock
= DEFAULTMUTEX
;
59 * PASSLENGTH (int) minimum password length
60 * NAMECHECK (yes/no) perform comparison of password and loginname
61 * MINDIFF (int) minimum number of character-positions in which
62 * the old and the new password should differ.
63 * MINALPHA (int) minimum number of Alpha characters
64 * MINUPPER (int) minimum number of upper-case characters
65 * MINLOWER (int) minimum number of lower-case characters
66 * MAXREPEATS (int) maximum number of consecutively repeating chars
67 * WHITESPACE (yes/no) Are whitespaces allowed?
69 * Furthermore, these two mutualy exclusive groups of options are allowed:
71 * MINNONALPHA (int) minimum number of characters from the
72 * character classes [ punct, space, digit ]
73 * if WHITESPACE == NO, whitespaces don't count.
75 * MINSPECIAL (int) minimum number of punctuation characters.
76 * if WHITESPACE != NO, whitespace is seen as
77 * a "special" character.
78 * MINDIGIT (int) minimum number of digits
80 * specifying options from both groups results in an error to syslog and
81 * failure to change the password.
84 * HISTORY is implemented at the repository level (passwdutil).
88 * default password-strength-values, compiled-in or stored in PWADMIN
92 boolean_t server_policy
; /* server policy flag from pam.conf */
93 uint_t minlength
; /* minimum password lenght */
94 uint_t maxlength
; /* maximum (significant) length */
95 boolean_t do_namecheck
; /* check password against user's gecos */
96 char db_location
[MAXPATHLEN
]; /* location of the generated database */
97 boolean_t do_dictcheck
; /* perform dictionary lookup */
98 char *dicts
; /* list of dictionaries configured */
99 uint_t mindiff
; /* old and new should differ by this much */
100 uint_t minalpha
; /* minimum alpha characters required */
101 uint_t minupper
; /* minimum uppercase characters required */
102 uint_t minlower
; /* minimum lowercase characters required */
103 uint_t minnonalpha
; /* minimum special (non alpha) required */
104 uint_t maxrepeat
; /* maximum number of repeating chars allowed */
105 uint_t minspecial
; /* punctuation characters */
106 uint_t mindigit
; /* minimum number of digits required */
107 boolean_t whitespace
; /* is whitespace allowed in a password */
113 error(pam_handle_t
*pamh
, int flags
, char *fmt
, ...)
116 char msg
[1][PAM_MAX_MSG_SIZE
];
119 (void) vsnprintf(msg
[0], sizeof (msg
[0]), fmt
, ap
);
121 if ((flags
& PAM_SILENT
) == 0)
122 (void) __pam_display_msg(pamh
, PAM_ERROR_MSG
, 1, msg
, NULL
);
126 defread_int(char *name
, uint_t
*ip
, void *defp
)
130 if ((q
= defread_r(name
, defp
)) != NULL
) {
132 syslog(LOG_ERR
, "pam_authtok_check: %s contains "
133 "non-integer value for %s: %s. "
134 "Using default instead.", PWADMIN
, name
, q
);
144 * fill in static defaults, and augment with settings from PWADMIN
145 * get system defaults with regard to maximum password length
148 get_passwd_defaults(pam_handle_t
*pamh
, char *user
, struct pwdefaults
*p
)
151 boolean_t minnonalpha_defined
= B_FALSE
;
152 pwu_repository_t
*pwu_rep
;
153 struct pam_repository
*pam_rep
;
159 (void) pam_get_item(pamh
, PAM_SERVICE
, (void **)&progname
);
161 /* Module defaults */
162 p
->minlength
= MINLENGTH
;
163 p
->do_namecheck
= B_TRUE
;
164 p
->do_dictcheck
= B_FALSE
;
166 p
->mindiff
= MINDIFF
;
167 p
->minalpha
= MINALPHA
;
168 p
->minnonalpha
= MINNONALPHA
;
169 p
->minupper
= 0; /* not configured by default */
170 p
->minlower
= 0; /* not configured by default */
171 p
->maxrepeat
= 0; /* not configured by default */
175 p
->whitespace
= B_TRUE
;
177 if ((defp
= defopen_r(PWADMIN
)) == NULL
)
178 return (PAM_SUCCESS
);
180 (void) defread_int("PASSLENGTH=", &p
->minlength
, defp
);
182 if ((q
= defread_r("NAMECHECK=", defp
)) != NULL
&&
183 strcasecmp(q
, "NO") == 0)
184 p
->do_namecheck
= B_FALSE
;
186 if ((q
= defread_r("DICTIONLIST=", defp
)) != NULL
) {
187 if ((p
->dicts
= strdup(q
)) == NULL
) {
188 syslog(LOG_ERR
, "pam_authtok_check: out of memory");
190 return (PAM_BUF_ERR
);
193 p
->do_dictcheck
= B_TRUE
;
198 if ((q
= defread_r("DICTIONDBDIR=", defp
)) != NULL
) {
199 if (strlcpy(p
->db_location
, q
, sizeof (p
->db_location
)) >=
200 sizeof (p
->db_location
)) {
201 syslog(LOG_ERR
, "pam_authtok_check: value for "
202 "DICTIONDBDIR too large.");
204 return (PAM_SYSTEM_ERR
);
206 p
->do_dictcheck
= B_TRUE
;
208 (void) strlcpy(p
->db_location
, CRACK_DIR
,
209 sizeof (p
->db_location
));
212 (void) defread_int("MINDIFF=", &p
->mindiff
, defp
);
213 (void) defread_int("MINALPHA=", &p
->minalpha
, defp
);
214 (void) defread_int("MINUPPER=", &p
->minupper
, defp
);
215 (void) defread_int("MINLOWER=", &p
->minlower
, defp
);
216 if (defread_int("MINNONALPHA=", &p
->minnonalpha
, defp
))
217 minnonalpha_defined
= B_TRUE
;
218 (void) defread_int("MAXREPEATS=", &p
->maxrepeat
, defp
);
220 if (defread_int("MINSPECIAL=", &p
->minspecial
, defp
)) {
221 if (minnonalpha_defined
) {
222 syslog(LOG_ERR
, "pam_authtok_check: %s contains "
223 "definition for MINNONALPHA and for MINSPECIAL. "
224 "These options are mutually exclusive.", PWADMIN
);
226 return (PAM_SYSTEM_ERR
);
231 if (defread_int("MINDIGIT=", &p
->mindigit
, defp
)) {
232 if (minnonalpha_defined
) {
233 syslog(LOG_ERR
, "pam_authtok_check: %s contains "
234 "definition for MINNONALPHA and for MINDIGIT. "
235 "These options are mutually exclusive.", PWADMIN
);
237 return (PAM_SYSTEM_ERR
);
242 if ((q
= defread_r("WHITESPACE=", defp
)) != NULL
)
244 (strcasecmp(q
, "no") == 0 || strcmp(q
, "0") == 0)
250 * Determine the number of significant characters in a password
252 * we find out where the user information came from (which repository),
253 * and which password-crypt-algorithm is to be used (based on the
254 * old password, or the system default).
256 * If the user comes from a repository other than FILES/NIS
257 * the module-flag "server_policy" means that we don't perform
258 * any checks on the user, but let the repository decide instead.
261 (void) pam_get_item(pamh
, PAM_REPOSITORY
, (void **)&pam_rep
);
262 if (pam_rep
!= NULL
) {
263 if ((pwu_rep
= calloc(1, sizeof (*pwu_rep
))) == NULL
)
264 return (PAM_BUF_ERR
);
265 pwu_rep
->type
= pam_rep
->type
;
266 pwu_rep
->scope
= pam_rep
->scope
;
267 pwu_rep
->scope_len
= pam_rep
->scope_len
;
269 pwu_rep
= PWU_DEFAULT_REP
;
272 attr
[0].type
= ATTR_PASSWD
; attr
[0].next
= &attr
[1];
273 attr
[1].type
= ATTR_REP_NAME
; attr
[1].next
= NULL
;
274 result
= __get_authtoken_attr(user
, pwu_rep
, attr
);
275 if (pwu_rep
!= PWU_DEFAULT_REP
)
278 if (result
!= PWU_SUCCESS
) {
280 * In the unlikely event that we can't obtain any info about
281 * the users password, we assume the most strict scenario.
283 p
->maxlength
= _PASS_MAX_XPG
;
285 char *oldpw
= attr
[0].data
.val_s
;
286 char *repository
= attr
[1].data
.val_s
;
287 if ((strcmp(repository
, "files") == 0 ||
288 strcmp(repository
, "nis") == 0) ||
289 p
->server_policy
== B_FALSE
) {
292 * We currently need to supply this dummy to
293 * crypt_gensalt(). This will change RSN.
297 dummy
.pw_name
= user
;
299 salt
= crypt_gensalt(oldpw
, &dummy
);
300 if (salt
&& *salt
== '$')
301 p
->maxlength
= _PASS_MAX
;
303 p
->maxlength
= _PASS_MAX_XPG
;
307 p
->server_policy
= B_FALSE
; /* we perform checks */
309 /* not files or nis AND server_policy is set */
310 p
->maxlength
= _PASS_MAX
;
312 free(attr
[0].data
.val_s
);
313 free(attr
[1].data
.val_s
);
316 /* sanity check of the configured parameters */
317 if (p
->minlength
< p
->mindigit
+ p
->minspecial
+ p
->minnonalpha
+
319 syslog(LOG_ERR
, "%s: pam_authtok_check: Defined minimum "
320 "password length (PASSLENGTH=%d) is less then minimum "
321 "characters in the various classes (%d)", progname
,
323 p
->mindigit
+ p
->minspecial
+ p
->minnonalpha
+ p
->minalpha
);
324 p
->minlength
= p
->mindigit
+ p
->minspecial
+ p
->minnonalpha
+
326 syslog(LOG_ERR
, "%s: pam_authtok_check: effective "
327 "PASSLENGTH set to %d.", progname
, p
->minlength
);
328 /* this won't lead to failure */
331 if (p
->maxlength
< p
->minlength
) {
332 syslog(LOG_ERR
, "%s: pam_authtok_check: The configured "
333 "minimum password length (PASSLENGTH=%d) is larger than "
334 "the number of significant characters the current "
335 "encryption algorithm uses (%d). See policy.conf(4) for "
336 "alternative password encryption algorithms.", progname
);
337 /* this won't lead to failure */
340 return (PAM_SUCCESS
);
344 * free_passwd_defaults(struct pwdefaults *p)
346 * free space occupied by the defaults read from PWADMIN
349 free_passwd_defaults(struct pwdefaults
*p
)
357 * This function return 1 if string "t" is a circular shift of
358 * string "s", else it returns 0. -1 is returned on failure.
359 * We also check to see if string "t" is a reversed-circular shift
360 * of string "s", i.e. "ABCDE" vs. "DCBAE".
366 char c
, *p
, *o
, *r
, *buff
, *ubuff
, *pubuff
;
367 unsigned int i
, j
, k
, l
, m
;
379 pubuff
= malloc(len
);
381 if (buff
== NULL
|| ubuff
== NULL
|| pubuff
== NULL
) {
382 syslog(LOG_ERR
, "pam_authtok_check: out of memory.");
388 for (p
= s
; c
= *p
++; *o
++ = c
)
393 for (p
= t
; c
= *p
++; *o
++ = c
)
401 for (k
= 0; k
< i
; k
++) {
411 if (strcmp(p
, pubuff
) == 0) {
420 *--p
= *r
++; /* reverse test-string for m==0 pass */
423 (void) memset(buff
, 0, len
);
424 (void) memset(ubuff
, 0, len
);
425 (void) memset(pubuff
, 0, len
);
434 * count the different character classes present in the password.
437 check_composition(char *pw
, struct pwdefaults
*pwdef
, pam_handle_t
*pamh
,
440 uint_t alpha_cnt
= 0;
441 uint_t upper_cnt
= 0;
442 uint_t lower_cnt
= 0;
443 uint_t special_cnt
= 0;
444 uint_t whitespace_cnt
= 0;
445 uint_t digit_cnt
= 0;
446 uint_t maxrepeat
= 0;
452 uint_t significant
= pwdef
->maxlength
;
455 (void) pam_get_item(pamh
, PAM_SERVICE
, (void **)&progname
);
457 /* go over the password gathering statistics */
458 for (w
= pw
; significant
!= 0 && *w
!= '\0'; w
++, significant
--) {
466 } else if (isspace(*w
))
468 else if (isdigit(*w
))
473 if (++repeat
> maxrepeat
)
482 * If we only consider part of the password (the first maxlength
483 * characters) we give a modified error message. Otherwise, a
484 * user entering FooBar1234 with PASSLENGTH=6, MINDIGIT=4, while
485 * we're using the default UNIX crypt (8 chars significant),
486 * would not understand what's going on when they're told that
487 * "The password should contain at least 4 digits"...
488 * Instead, we now tell them
489 * "The first 8 characters of the password should contain at least
492 if (pwdef
->maxlength
< strlen(pw
))
495 * - Make sure the % and %% come over intact
496 * - The last %%s will be replaced by strings like
497 * "alphabetic character(s)"
498 * "numeric or special character(s)"
499 * "special character(s)"
501 * "uppercase alpha character(s)"
502 * "lowercase alpha character(s)"
503 * So the final string written to the user might become
504 * "passwd: The first 8 characters of the password must contain
505 * at least 4 uppercase alpha characters(s)"
507 (void) snprintf(errmsg
, sizeof (errmsg
), dgettext(TEXT_DOMAIN
,
508 "%s: The first %d characters of the password must "
509 "contain at least %%d %%s."), progname
, pwdef
->maxlength
);
513 * - Make sure the % and %% come over intact
514 * - The last %%s will be replaced by strings like
515 * "alphabetic character(s)"
516 * "numeric or special character(s)"
517 * "special character(s)"
519 * "uppercase alpha character(s)"
520 * "lowercase alpha character(s)"
521 * So the final string written to the user might become
522 * "passwd: The password must contain at least 4 uppercase
523 * alpha characters(s)"
525 (void) snprintf(errmsg
, sizeof (errmsg
), dgettext(TEXT_DOMAIN
,
526 "%s: The password must contain at least %%d %%s."),
529 /* Check for whitespace first since it influences special counts */
530 if (whitespace_cnt
> 0 && pwdef
->whitespace
== B_FALSE
) {
531 error(pamh
, flags
, dgettext(TEXT_DOMAIN
,
532 "%s: Whitespace characters are not allowed."), progname
);
538 * Once we get here, whitespace_cnt is either 0, or whitespaces are
539 * to be treated a special characters.
542 if (alpha_cnt
< pwdef
->minalpha
) {
543 error(pamh
, flags
, errmsg
, pwdef
->minalpha
,
544 dgettext(TEXT_DOMAIN
, "alphabetic character(s)"));
549 if (pwdef
->minnonalpha
> 0) {
550 /* specials are defined by MINNONALPHA */
551 /* nonalpha = special+whitespace+digit */
552 if ((special_cnt
+ whitespace_cnt
+ digit_cnt
) <
553 pwdef
->minnonalpha
) {
554 error(pamh
, flags
, errmsg
, pwdef
->minnonalpha
,
555 dgettext(TEXT_DOMAIN
,
556 "numeric or special character(s)"));
561 /* specials are defined by MINSPECIAL and/or MINDIGIT */
562 if ((special_cnt
+ whitespace_cnt
) < pwdef
->minspecial
) {
563 error(pamh
, flags
, errmsg
, pwdef
->minspecial
,
564 dgettext(TEXT_DOMAIN
, "special character(s)"));
568 if (digit_cnt
< pwdef
->mindigit
) {
569 error(pamh
, flags
, errmsg
, pwdef
->mindigit
,
570 dgettext(TEXT_DOMAIN
, "digit(s)"));
576 if (upper_cnt
< pwdef
->minupper
) {
577 error(pamh
, flags
, errmsg
, pwdef
->minupper
,
578 dgettext(TEXT_DOMAIN
, "uppercase alpha character(s)"));
582 if (lower_cnt
< pwdef
->minlower
) {
583 error(pamh
, flags
, errmsg
, pwdef
->minlower
,
584 dgettext(TEXT_DOMAIN
, "lowercase alpha character(s)"));
589 if (pwdef
->maxrepeat
> 0 && maxrepeat
> pwdef
->maxrepeat
) {
590 error(pamh
, flags
, dgettext(TEXT_DOMAIN
,
591 "%s: Too many consecutively repeating characters. "
592 "Maximum allowed is %d."), progname
, pwdef
->maxrepeat
);
600 * make sure that old and new password differ by at least 'mindiff'
601 * positions. Return 0 if OK, 1 otherwise
604 check_diff(char *pw
, char *opw
, struct pwdefaults
*pwdef
, pam_handle_t
*pamh
,
607 size_t pwlen
, opwlen
, max
;
608 unsigned int diff
; /* difference between old and new */
613 max
= pwdef
->maxlength
;
614 pwlen
= MIN(strlen(pw
), max
);
615 opwlen
= MIN(strlen(opw
), max
);
618 diff
= pwlen
- opwlen
;
620 diff
= opwlen
- pwlen
;
622 while (*opw
!= '\0' && *pw
!= '\0' && max
-- != 0) {
629 if (diff
< pwdef
->mindiff
) {
632 (void) pam_get_item(pamh
, PAM_SERVICE
, (void **)&progname
);
634 error(pamh
, flags
, dgettext(TEXT_DOMAIN
,
635 "%s: The first %d characters of the old and new passwords "
636 "must differ by at least %d positions."), progname
,
637 pwdef
->maxlength
, pwdef
->mindiff
);
645 * check to see if password is in one way or another based on a
646 * dictionary word. Returns 0 if password is OK, 1 if it is based
647 * on a dictionary word and hence should be rejected.
650 check_dictionary(char *pw
, struct pwdefaults
*pwdef
, pam_handle_t
*pamh
,
657 (void) pam_get_item(pamh
, PAM_SERVICE
, (void **)&progname
);
659 /* dictionary check isn't MT-safe */
660 (void) mutex_lock(&dictlock
);
663 make_dict_database(pwdef
->dicts
, pwdef
->db_location
) != 0) {
664 (void) mutex_unlock(&dictlock
);
665 syslog(LOG_ERR
, "pam_authtok_check:pam_sm_chauthtok: "
666 "Dictionary database not present.");
667 error(pamh
, flags
, dgettext(TEXT_DOMAIN
,
668 "%s: password dictionary missing."), progname
);
669 return (PAM_SYSTEM_ERR
);
672 crack_ret
= DictCheck(pw
, pwdef
->db_location
);
674 (void) mutex_unlock(&dictlock
);
677 case DATABASE_OPEN_FAIL
:
678 syslog(LOG_ERR
, "pam_authtok_check:pam_sm_chauthtok: "
679 "dictionary database open failure: %s", strerror(errno
));
680 error(pamh
, flags
, dgettext(TEXT_DOMAIN
,
681 "%s: failed to open dictionary database."), progname
);
682 ret
= PAM_SYSTEM_ERR
;
684 case DICTIONARY_WORD
:
685 error(pamh
, flags
, dgettext(TEXT_DOMAIN
,
686 "%s: password is based on a dictionary word."), progname
);
687 ret
= PAM_AUTHTOK_ERR
;
689 case REVERSE_DICTIONARY_WORD
:
690 error(pamh
, flags
, dgettext(TEXT_DOMAIN
,
691 "%s: password is based on a reversed dictionary word."),
693 ret
= PAM_AUTHTOK_ERR
;
703 pam_sm_chauthtok(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
)
711 char *pwbuf
, *opwbuf
;
712 pwu_repository_t
*pwu_rep
= PWU_DEFAULT_REP
;
713 pam_repository_t
*pwd_rep
= NULL
;
714 struct pwdefaults pwdef
;
717 /* needs to be set before option processing */
718 pwdef
.server_policy
= B_FALSE
;
720 for (i
= 0; i
< argc
; i
++) {
721 if (strcmp(argv
[i
], "debug") == 0)
723 if (strcmp(argv
[i
], "force_check") == 0)
725 if (strcmp(argv
[i
], "server_policy") == 0)
726 pwdef
.server_policy
= B_TRUE
;
730 syslog(LOG_AUTH
| LOG_DEBUG
,
731 "pam_authtok_check: pam_sm_chauthok called(%x) "
732 "force_check = %d", flags
, force_check
);
734 if ((flags
& PAM_PRELIM_CHECK
) == 0)
737 (void) pam_get_item(pamh
, PAM_SERVICE
, (void **)&progname
);
738 (void) pam_get_item(pamh
, PAM_USER
, (void **)&usrname
);
739 if (usrname
== NULL
|| *usrname
== '\0') {
740 syslog(LOG_ERR
, "pam_authtok_check: username name is empty");
741 return (PAM_USER_UNKNOWN
);
744 (void) pam_get_item(pamh
, PAM_AUTHTOK
, (void **)&pwbuf
);
745 (void) pam_get_item(pamh
, PAM_OLDAUTHTOK
, (void **)&opwbuf
);
747 return (PAM_AUTHTOK_ERR
);
749 /* none of these checks holds if caller say so */
750 if ((flags
& PAM_NO_AUTHTOK_CHECK
) != 0 && force_check
== 0)
751 return (PAM_SUCCESS
);
753 /* read system-defaults */
754 retcode
= get_passwd_defaults(pamh
, usrname
, &pwdef
);
755 if (retcode
!= PAM_SUCCESS
)
759 syslog(LOG_AUTH
| LOG_DEBUG
,
760 "pam_authtok_check: MAXLENGTH= %d, server_policy = %s",
761 pwdef
.maxlength
, pwdef
.server_policy
? "true" : "false");
762 syslog(LOG_AUTH
| LOG_DEBUG
,
763 "pam_authtok_check: PASSLENGTH= %d", pwdef
.minlength
);
764 syslog(LOG_AUTH
| LOG_DEBUG
, "pam_authtok_check: NAMECHECK=%s",
765 pwdef
.do_namecheck
== B_TRUE
? "Yes" : "No");
766 syslog(LOG_AUTH
| LOG_DEBUG
,
767 "pam_authtok_check: do_dictcheck = %s\n",
768 pwdef
.do_dictcheck
? "true" : "false");
769 if (pwdef
.do_dictcheck
) {
770 syslog(LOG_AUTH
| LOG_DEBUG
,
771 "pam_authtok_check: DICTIONLIST=%s",
772 (pwdef
.dicts
!= NULL
) ? pwdef
.dicts
: "<not set>");
773 syslog(LOG_AUTH
| LOG_DEBUG
,
774 "pam_authtok_check: DICTIONDBDIR=%s",
777 syslog(LOG_AUTH
| LOG_DEBUG
, "pam_authtok_check: MINDIFF=%d",
779 syslog(LOG_AUTH
| LOG_DEBUG
,
780 "pam_authtok_check: MINALPHA=%d, MINNONALPHA=%d",
781 pwdef
.minalpha
, pwdef
.minnonalpha
);
782 syslog(LOG_AUTH
| LOG_DEBUG
,
783 "pam_authtok_check: MINSPECIAL=%d, MINDIGIT=%d",
784 pwdef
.minspecial
, pwdef
.mindigit
);
785 syslog(LOG_AUTH
| LOG_DEBUG
, "pam_authtok_check: WHITESPACE=%s",
786 pwdef
.whitespace
? "YES" : "NO");
787 syslog(LOG_AUTH
| LOG_DEBUG
,
788 "pam_authtok_check: MINUPPER=%d, MINLOWER=%d",
789 pwdef
.minupper
, pwdef
.minlower
);
790 syslog(LOG_AUTH
| LOG_DEBUG
, "pam_authtok_check: MAXREPEATS=%d",
795 * If server policy is still true (might be changed from the
796 * value specified in /etc/pam.conf by get_passwd_defaults()),
797 * we return ignore and let the server do all the checks.
799 if (pwdef
.server_policy
== B_TRUE
) {
800 free_passwd_defaults(&pwdef
);
805 * XXX: JV: we can't really make any assumption on the length of
806 * the password that will be used by the crypto algorithm.
807 * for UNIX-style encryption, minalpha=5,minnonalpha=5 might
808 * be impossible, but not for MD5 style hashes... what to do?
810 * since we don't know what alg. will be used, we operate on
811 * the password as entered, so we don't sanity check anything
816 * Make sure new password is long enough
818 pwlen
= strlen(pwbuf
);
820 if (pwlen
< pwdef
.minlength
) {
821 error(pamh
, flags
, dgettext(TEXT_DOMAIN
,
822 "%s: Password too short - must be at least %d "
823 "characters."), progname
, pwdef
.minlength
);
824 free_passwd_defaults(&pwdef
);
825 return (PAM_AUTHTOK_ERR
);
828 /* Make sure the password doesn't equal--a shift of--the username */
829 if (pwdef
.do_namecheck
) {
830 switch (check_circular(usrname
, pwbuf
)) {
832 error(pamh
, flags
, dgettext(TEXT_DOMAIN
,
833 "%s: Password cannot be circular shift of "
834 "logonid."), progname
);
835 free_passwd_defaults(&pwdef
);
836 return (PAM_AUTHTOK_ERR
);
838 free_passwd_defaults(&pwdef
);
839 return (PAM_BUF_ERR
);
845 /* Check if new password is in history list. */
846 (void) pam_get_item(pamh
, PAM_REPOSITORY
, (void **)&pwd_rep
);
847 if (pwd_rep
!= NULL
) {
848 if ((pwu_rep
= calloc(1, sizeof (*pwu_rep
))) == NULL
)
849 return (PAM_BUF_ERR
);
850 pwu_rep
->type
= pwd_rep
->type
;
851 pwu_rep
->scope
= pwd_rep
->scope
;
852 pwu_rep
->scope_len
= pwd_rep
->scope_len
;
855 if (__check_history(usrname
, pwbuf
, pwu_rep
) == PWU_SUCCESS
) {
856 /* password found in history */
857 error(pamh
, flags
, dgettext(TEXT_DOMAIN
,
858 "%s: Password in history list."), progname
);
859 if (pwu_rep
!= PWU_DEFAULT_REP
)
861 free_passwd_defaults(&pwdef
);
862 return (PAM_AUTHTOK_ERR
);
865 if (pwu_rep
!= PWU_DEFAULT_REP
)
868 /* check MINALPHA, MINLOWER, etc. */
869 if (check_composition(pwbuf
, &pwdef
, pamh
, flags
) != 0) {
870 free_passwd_defaults(&pwdef
);
871 return (PAM_AUTHTOK_ERR
);
874 /* make sure the old and new password are not too much alike */
875 if (check_diff(pwbuf
, opwbuf
, &pwdef
, pamh
, flags
) != 0) {
876 free_passwd_defaults(&pwdef
);
877 return (PAM_AUTHTOK_ERR
);
880 /* dictionary check */
881 if (pwdef
.do_dictcheck
) {
882 retcode
= check_dictionary(pwbuf
, &pwdef
, pamh
, flags
);
883 if (retcode
!= PAM_SUCCESS
) {
884 free_passwd_defaults(&pwdef
);
889 free_passwd_defaults(&pwdef
);
890 /* password has passed all tests: it's strong enough */
891 return (PAM_SUCCESS
);