dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / pam_modules / authtok_check / authtok_check.c
blob8e0e941eaeba31c24c9d6684929187bb13fa47e3
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <deflt.h>
34 #include <security/pam_appl.h>
35 #include <security/pam_modules.h>
36 #include <security/pam_impl.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <unistd.h>
40 #include <syslog.h>
41 #include <libintl.h>
42 #include <errno.h>
43 #include <pwd.h>
44 #include "packer.h"
46 #include <passwdutil.h>
48 #define PWADMIN "/etc/default/passwd"
50 #define MINLENGTH 6
51 #define MINDIFF 3
52 #define MINALPHA 2
53 #define MINNONALPHA 1
55 mutex_t dictlock = DEFAULTMUTEX;
58 * We implement:
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.
74 * and
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.
83 * NOTE:
84 * HISTORY is implemented at the repository level (passwdutil).
88 * default password-strength-values, compiled-in or stored in PWADMIN
89 * are kept in here
91 struct pwdefaults {
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 */
111 /*PRINTFLIKE3*/
112 void
113 error(pam_handle_t *pamh, int flags, char *fmt, ...)
115 va_list ap;
116 char msg[1][PAM_MAX_MSG_SIZE];
118 va_start(ap, fmt);
119 (void) vsnprintf(msg[0], sizeof (msg[0]), fmt, ap);
120 va_end(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)
128 char *q;
129 int r = 0;
130 if ((q = defread_r(name, defp)) != NULL) {
131 if (!isdigit(*q)) {
132 syslog(LOG_ERR, "pam_authtok_check: %s contains "
133 "non-integer value for %s: %s. "
134 "Using default instead.", PWADMIN, name, q);
135 } else {
136 *ip = atoi(q);
137 r = 1;
140 return (r);
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)
150 char *q;
151 boolean_t minnonalpha_defined = B_FALSE;
152 pwu_repository_t *pwu_rep;
153 struct pam_repository *pam_rep;
154 attrlist attr[2];
155 int result;
156 char *progname;
157 void *defp;
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;
165 p->dicts = NULL;
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 */
173 p->minspecial = 0;
174 p->mindigit = 0;
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");
189 defclose_r(defp);
190 return (PAM_BUF_ERR);
193 p->do_dictcheck = B_TRUE;
194 } else {
195 p->dicts = NULL;
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.");
203 defclose_r(defp);
204 return (PAM_SYSTEM_ERR);
206 p->do_dictcheck = B_TRUE;
207 } else {
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);
225 defclose_r(defp);
226 return (PAM_SYSTEM_ERR);
228 p->minnonalpha = 0;
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);
236 defclose_r(defp);
237 return (PAM_SYSTEM_ERR);
239 p->minnonalpha = 0;
242 if ((q = defread_r("WHITESPACE=", defp)) != NULL)
243 p->whitespace =
244 (strcasecmp(q, "no") == 0 || strcmp(q, "0") == 0)
245 ? B_FALSE : B_TRUE;
247 defclose_r(defp);
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;
268 } else {
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)
276 free(pwu_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;
284 } else {
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) {
290 char *salt;
292 * We currently need to supply this dummy to
293 * crypt_gensalt(). This will change RSN.
295 struct passwd dummy;
297 dummy.pw_name = user;
299 salt = crypt_gensalt(oldpw, &dummy);
300 if (salt && *salt == '$')
301 p->maxlength = _PASS_MAX;
302 else
303 p->maxlength = _PASS_MAX_XPG;
305 free(salt);
307 p->server_policy = B_FALSE; /* we perform checks */
308 } else {
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 +
318 p->minalpha) {
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,
322 p->minlength,
323 p->mindigit + p->minspecial + p->minnonalpha + p->minalpha);
324 p->minlength = p->mindigit + p->minspecial + p->minnonalpha +
325 p->minalpha;
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
348 void
349 free_passwd_defaults(struct pwdefaults *p)
351 if (p)
352 free(p->dicts);
356 * check_circular():
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".
362 static int
363 check_circular(s, t)
364 char *s, *t;
366 char c, *p, *o, *r, *buff, *ubuff, *pubuff;
367 unsigned int i, j, k, l, m;
368 size_t len;
369 int ret = 0;
371 i = strlen(s);
372 l = strlen(t);
373 if (i != l)
374 return (0);
375 len = i + 1;
377 buff = malloc(len);
378 ubuff = malloc(len);
379 pubuff = malloc(len);
381 if (buff == NULL || ubuff == NULL || pubuff == NULL) {
382 syslog(LOG_ERR, "pam_authtok_check: out of memory.");
383 return (-1);
386 m = 2;
387 o = &ubuff[0];
388 for (p = s; c = *p++; *o++ = c)
389 if (islower(c))
390 c = toupper(c);
391 *o = '\0';
392 o = &pubuff[0];
393 for (p = t; c = *p++; *o++ = c)
394 if (islower(c))
395 c = toupper(c);
397 *o = '\0';
399 p = &ubuff[0];
400 while (m--) {
401 for (k = 0; k < i; k++) {
402 c = *p++;
403 o = p;
404 l = i;
405 r = &buff[0];
406 while (--l)
407 *r++ = *o++;
408 *r++ = c;
409 *r = '\0';
410 p = &buff[0];
411 if (strcmp(p, pubuff) == 0) {
412 ret = 1;
413 goto out;
416 p = p + i;
417 r = &ubuff[0];
418 j = i;
419 while (j--)
420 *--p = *r++; /* reverse test-string for m==0 pass */
422 out:
423 (void) memset(buff, 0, len);
424 (void) memset(ubuff, 0, len);
425 (void) memset(pubuff, 0, len);
426 free(buff);
427 free(ubuff);
428 free(pubuff);
429 return (ret);
434 * count the different character classes present in the password.
437 check_composition(char *pw, struct pwdefaults *pwdef, pam_handle_t *pamh,
438 int flags)
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;
447 uint_t repeat = 1;
448 int ret = 0;
449 char *progname;
450 char errmsg[256];
451 char lastc = '\0';
452 uint_t significant = pwdef->maxlength;
453 char *w;
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--) {
459 if (isalpha(*w)) {
460 alpha_cnt++;
461 if (isupper(*w)) {
462 upper_cnt++;
463 } else {
464 lower_cnt++;
466 } else if (isspace(*w))
467 whitespace_cnt++;
468 else if (isdigit(*w))
469 digit_cnt++;
470 else
471 special_cnt++;
472 if (*w == lastc) {
473 if (++repeat > maxrepeat)
474 maxrepeat = repeat;
475 } else {
476 repeat = 1;
478 lastc = *w;
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
490 * 4 digits."
492 if (pwdef->maxlength < strlen(pw))
494 * TRANSLATION_NOTE
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)"
500 * "digit(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);
510 else
512 * TRANSLATION_NOTE
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)"
518 * "digit(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."),
527 progname);
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);
533 ret = 1;
534 goto out;
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)"));
545 ret = 1;
546 goto out;
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)"));
557 ret = 1;
558 goto out;
560 } else {
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)"));
565 ret = 1;
566 goto out;
568 if (digit_cnt < pwdef->mindigit) {
569 error(pamh, flags, errmsg, pwdef->mindigit,
570 dgettext(TEXT_DOMAIN, "digit(s)"));
571 ret = 1;
572 goto out;
576 if (upper_cnt < pwdef->minupper) {
577 error(pamh, flags, errmsg, pwdef->minupper,
578 dgettext(TEXT_DOMAIN, "uppercase alpha character(s)"));
579 ret = 1;
580 goto out;
582 if (lower_cnt < pwdef->minlower) {
583 error(pamh, flags, errmsg, pwdef->minlower,
584 dgettext(TEXT_DOMAIN, "lowercase alpha character(s)"));
585 ret = 1;
586 goto out;
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);
593 ret = 1;
595 out:
596 return (ret);
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,
605 int flags)
607 size_t pwlen, opwlen, max;
608 unsigned int diff; /* difference between old and new */
610 if (opw == NULL)
611 opw = "";
613 max = pwdef->maxlength;
614 pwlen = MIN(strlen(pw), max);
615 opwlen = MIN(strlen(opw), max);
617 if (pwlen > opwlen)
618 diff = pwlen - opwlen;
619 else
620 diff = opwlen - pwlen;
622 while (*opw != '\0' && *pw != '\0' && max-- != 0) {
623 if (*opw != *pw)
624 diff++;
625 opw++;
626 pw++;
629 if (diff < pwdef->mindiff) {
630 char *progname;
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);
638 return (1);
641 return (0);
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,
651 int flags)
653 int crack_ret;
654 int ret;
655 char *progname;
657 (void) pam_get_item(pamh, PAM_SERVICE, (void **)&progname);
659 /* dictionary check isn't MT-safe */
660 (void) mutex_lock(&dictlock);
662 if (pwdef->dicts &&
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);
676 switch (crack_ret) {
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;
683 break;
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;
688 break;
689 case REVERSE_DICTIONARY_WORD:
690 error(pamh, flags, dgettext(TEXT_DOMAIN,
691 "%s: password is based on a reversed dictionary word."),
692 progname);
693 ret = PAM_AUTHTOK_ERR;
694 break;
695 default:
696 ret = PAM_SUCCESS;
697 break;
699 return (ret);
703 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
705 int debug = 0;
706 int retcode = 0;
707 int force_check = 0;
708 int i;
709 size_t pwlen;
710 char *usrname;
711 char *pwbuf, *opwbuf;
712 pwu_repository_t *pwu_rep = PWU_DEFAULT_REP;
713 pam_repository_t *pwd_rep = NULL;
714 struct pwdefaults pwdef;
715 char *progname;
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)
722 debug = 1;
723 if (strcmp(argv[i], "force_check") == 0)
724 force_check = 1;
725 if (strcmp(argv[i], "server_policy") == 0)
726 pwdef.server_policy = B_TRUE;
729 if (debug)
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)
735 return (PAM_IGNORE);
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);
746 if (pwbuf == NULL)
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)
756 return (retcode);
758 if (debug) {
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",
775 pwdef.db_location);
777 syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: MINDIFF=%d",
778 pwdef.mindiff);
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",
791 pwdef.maxrepeat);
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);
801 return (PAM_IGNORE);
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
812 * for now.
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)) {
831 case 1:
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);
837 case -1:
838 free_passwd_defaults(&pwdef);
839 return (PAM_BUF_ERR);
840 default:
841 break;
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)
860 free(pwu_rep);
861 free_passwd_defaults(&pwdef);
862 return (PAM_AUTHTOK_ERR);
865 if (pwu_rep != PWU_DEFAULT_REP)
866 free(pwu_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);
885 return (retcode);
889 free_passwd_defaults(&pwdef);
890 /* password has passed all tests: it's strong enough */
891 return (PAM_SUCCESS);