build: remove map.noexstk and map.noexdata (now default)
[unleashed.git] / usr / src / lib / pam_modules / authtok_check / authtok_check.c
blobf58454022e63d20536c21fe016d00727e282ce8f
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.
26 #include <sys/types.h>
27 #include <sys/varargs.h>
28 #include <sys/param.h>
29 #include <sys/sysmacros.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <deflt.h>
33 #include <security/pam_appl.h>
34 #include <security/pam_modules.h>
35 #include <security/pam_impl.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <unistd.h>
39 #include <syslog.h>
40 #include <libintl.h>
41 #include <errno.h>
42 #include <pwd.h>
43 #include "packer.h"
45 #include <passwdutil.h>
47 #define PWADMIN "/etc/default/passwd"
49 #define MINLENGTH 6
50 #define MINDIFF 3
51 #define MINALPHA 2
52 #define MINNONALPHA 1
54 mutex_t dictlock = DEFAULTMUTEX;
57 * We implement:
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.
73 * and
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.
82 * NOTE:
83 * HISTORY is implemented at the repository level (passwdutil).
87 * default password-strength-values, compiled-in or stored in PWADMIN
88 * are kept in here
90 struct pwdefaults {
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 */
110 /*PRINTFLIKE3*/
111 void
112 error(pam_handle_t *pamh, int flags, char *fmt, ...)
114 va_list ap;
115 char msg[1][PAM_MAX_MSG_SIZE];
117 va_start(ap, fmt);
118 (void) vsnprintf(msg[0], sizeof (msg[0]), fmt, ap);
119 va_end(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)
127 char *q;
128 int r = 0;
129 if ((q = defread_r(name, defp)) != NULL) {
130 if (!isdigit(*q)) {
131 syslog(LOG_ERR, "pam_authtok_check: %s contains "
132 "non-integer value for %s: %s. "
133 "Using default instead.", PWADMIN, name, q);
134 } else {
135 *ip = atoi(q);
136 r = 1;
139 return (r);
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)
149 char *q;
150 boolean_t minnonalpha_defined = B_FALSE;
151 pwu_repository_t *pwu_rep;
152 struct pam_repository *pam_rep;
153 attrlist attr[2];
154 int result;
155 char *progname;
156 void *defp;
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;
164 p->dicts = NULL;
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 */
172 p->minspecial = 0;
173 p->mindigit = 0;
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");
188 defclose_r(defp);
189 return (PAM_BUF_ERR);
192 p->do_dictcheck = B_TRUE;
193 } else {
194 p->dicts = NULL;
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.");
202 defclose_r(defp);
203 return (PAM_SYSTEM_ERR);
205 p->do_dictcheck = B_TRUE;
206 } else {
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);
224 defclose_r(defp);
225 return (PAM_SYSTEM_ERR);
227 p->minnonalpha = 0;
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);
235 defclose_r(defp);
236 return (PAM_SYSTEM_ERR);
238 p->minnonalpha = 0;
241 if ((q = defread_r("WHITESPACE=", defp)) != NULL)
242 p->whitespace =
243 (strcasecmp(q, "no") == 0 || strcmp(q, "0") == 0)
244 ? B_FALSE : B_TRUE;
246 defclose_r(defp);
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;
267 } else {
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)
275 free(pwu_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;
283 } else {
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) {
289 char *salt;
291 * We currently need to supply this dummy to
292 * crypt_gensalt(). This will change RSN.
294 struct passwd dummy;
296 dummy.pw_name = user;
298 salt = crypt_gensalt(oldpw, &dummy);
299 if (salt && *salt == '$')
300 p->maxlength = _PASS_MAX;
301 else
302 p->maxlength = _PASS_MAX_XPG;
304 free(salt);
306 p->server_policy = B_FALSE; /* we perform checks */
307 } else {
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 +
317 p->minalpha) {
318 syslog(LOG_ERR, "%s: pam_authtok_check: Defined minimum "
319 "password length (PASSLENGTH=%d) is less then minimum "
320 "characters in the various classes (%d)", progname,
321 p->minlength,
322 p->mindigit + p->minspecial + p->minnonalpha + p->minalpha);
323 p->minlength = p->mindigit + p->minspecial + p->minnonalpha +
324 p->minalpha;
325 syslog(LOG_ERR, "%s: pam_authtok_check: effective "
326 "PASSLENGTH set to %d.", progname, p->minlength);
327 /* this won't lead to failure */
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
347 void
348 free_passwd_defaults(struct pwdefaults *p)
350 if (p)
351 free(p->dicts);
355 * check_circular():
356 * This function return 1 if string "t" is a circular shift of
357 * string "s", else it returns 0. -1 is returned on failure.
358 * We also check to see if string "t" is a reversed-circular shift
359 * of string "s", i.e. "ABCDE" vs. "DCBAE".
361 static int
362 check_circular(s, t)
363 char *s, *t;
365 char c, *p, *o, *r, *buff, *ubuff, *pubuff;
366 unsigned int i, j, k, l, m;
367 size_t len;
368 int ret = 0;
370 i = strlen(s);
371 l = strlen(t);
372 if (i != l)
373 return (0);
374 len = i + 1;
376 buff = malloc(len);
377 ubuff = malloc(len);
378 pubuff = malloc(len);
380 if (buff == NULL || ubuff == NULL || pubuff == NULL) {
381 syslog(LOG_ERR, "pam_authtok_check: out of memory.");
382 return (-1);
385 m = 2;
386 o = &ubuff[0];
387 for (p = s; c = *p++; *o++ = c)
388 if (islower(c))
389 c = toupper(c);
390 *o = '\0';
391 o = &pubuff[0];
392 for (p = t; c = *p++; *o++ = c)
393 if (islower(c))
394 c = toupper(c);
396 *o = '\0';
398 p = &ubuff[0];
399 while (m--) {
400 for (k = 0; k < i; k++) {
401 c = *p++;
402 o = p;
403 l = i;
404 r = &buff[0];
405 while (--l)
406 *r++ = *o++;
407 *r++ = c;
408 *r = '\0';
409 p = &buff[0];
410 if (strcmp(p, pubuff) == 0) {
411 ret = 1;
412 goto out;
415 p = p + i;
416 r = &ubuff[0];
417 j = i;
418 while (j--)
419 *--p = *r++; /* reverse test-string for m==0 pass */
421 out:
422 (void) memset(buff, 0, len);
423 (void) memset(ubuff, 0, len);
424 (void) memset(pubuff, 0, len);
425 free(buff);
426 free(ubuff);
427 free(pubuff);
428 return (ret);
433 * count the different character classes present in the password.
436 check_composition(char *pw, struct pwdefaults *pwdef, pam_handle_t *pamh,
437 int flags)
439 uint_t alpha_cnt = 0;
440 uint_t upper_cnt = 0;
441 uint_t lower_cnt = 0;
442 uint_t special_cnt = 0;
443 uint_t whitespace_cnt = 0;
444 uint_t digit_cnt = 0;
445 uint_t maxrepeat = 0;
446 uint_t repeat = 1;
447 int ret = 0;
448 char *progname;
449 char errmsg[256];
450 char lastc = '\0';
451 uint_t significant = pwdef->maxlength;
452 char *w;
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--) {
458 if (isalpha(*w)) {
459 alpha_cnt++;
460 if (isupper(*w)) {
461 upper_cnt++;
462 } else {
463 lower_cnt++;
465 } else if (isspace(*w))
466 whitespace_cnt++;
467 else if (isdigit(*w))
468 digit_cnt++;
469 else
470 special_cnt++;
471 if (*w == lastc) {
472 if (++repeat > maxrepeat)
473 maxrepeat = repeat;
474 } else {
475 repeat = 1;
477 lastc = *w;
481 * If we only consider part of the password (the first maxlength
482 * characters) we give a modified error message. Otherwise, a
483 * user entering FooBar1234 with PASSLENGTH=6, MINDIGIT=4, while
484 * we're using the default UNIX crypt (8 chars significant),
485 * would not understand what's going on when he's told that
486 * "The password should contain at least 4 digits"...
487 * Instead, we now well him
488 * "The first 8 characters of the password should contain at least
489 * 4 digits."
491 if (pwdef->maxlength < strlen(pw))
493 * TRANSLATION_NOTE
494 * - Make sure the % and %% come over intact
495 * - The last %%s will be replaced by strings like
496 * "alphabetic character(s)"
497 * "numeric or special character(s)"
498 * "special character(s)"
499 * "digit(s)"
500 * "uppercase alpha character(s)"
501 * "lowercase alpha character(s)"
502 * So the final string written to the user might become
503 * "passwd: The first 8 characters of the password must contain
504 * at least 4 uppercase alpha characters(s)"
506 (void) snprintf(errmsg, sizeof (errmsg), dgettext(TEXT_DOMAIN,
507 "%s: The first %d characters of the password must "
508 "contain at least %%d %%s."), progname, pwdef->maxlength);
509 else
511 * TRANSLATION_NOTE
512 * - Make sure the % and %% come over intact
513 * - The last %%s will be replaced by strings like
514 * "alphabetic character(s)"
515 * "numeric or special character(s)"
516 * "special character(s)"
517 * "digit(s)"
518 * "uppercase alpha character(s)"
519 * "lowercase alpha character(s)"
520 * So the final string written to the user might become
521 * "passwd: The password must contain at least 4 uppercase
522 * alpha characters(s)"
524 (void) snprintf(errmsg, sizeof (errmsg), dgettext(TEXT_DOMAIN,
525 "%s: The password must contain at least %%d %%s."),
526 progname);
528 /* Check for whitespace first since it influences special counts */
529 if (whitespace_cnt > 0 && pwdef->whitespace == B_FALSE) {
530 error(pamh, flags, dgettext(TEXT_DOMAIN,
531 "%s: Whitespace characters are not allowed."), progname);
532 ret = 1;
533 goto out;
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)"));
544 ret = 1;
545 goto out;
548 if (pwdef->minnonalpha > 0) {
549 /* specials are defined by MINNONALPHA */
550 /* nonalpha = special+whitespace+digit */
551 if ((special_cnt + whitespace_cnt + digit_cnt) <
552 pwdef->minnonalpha) {
553 error(pamh, flags, errmsg, pwdef->minnonalpha,
554 dgettext(TEXT_DOMAIN,
555 "numeric or special character(s)"));
556 ret = 1;
557 goto out;
559 } else {
560 /* specials are defined by MINSPECIAL and/or MINDIGIT */
561 if ((special_cnt + whitespace_cnt) < pwdef->minspecial) {
562 error(pamh, flags, errmsg, pwdef->minspecial,
563 dgettext(TEXT_DOMAIN, "special character(s)"));
564 ret = 1;
565 goto out;
567 if (digit_cnt < pwdef->mindigit) {
568 error(pamh, flags, errmsg, pwdef->mindigit,
569 dgettext(TEXT_DOMAIN, "digit(s)"));
570 ret = 1;
571 goto out;
575 if (upper_cnt < pwdef->minupper) {
576 error(pamh, flags, errmsg, pwdef->minupper,
577 dgettext(TEXT_DOMAIN, "uppercase alpha character(s)"));
578 ret = 1;
579 goto out;
581 if (lower_cnt < pwdef->minlower) {
582 error(pamh, flags, errmsg, pwdef->minlower,
583 dgettext(TEXT_DOMAIN, "lowercase alpha character(s)"));
584 ret = 1;
585 goto out;
588 if (pwdef->maxrepeat > 0 && maxrepeat > pwdef->maxrepeat) {
589 error(pamh, flags, dgettext(TEXT_DOMAIN,
590 "%s: Too many consecutively repeating characters. "
591 "Maximum allowed is %d."), progname, pwdef->maxrepeat);
592 ret = 1;
594 out:
595 return (ret);
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,
604 int flags)
606 size_t pwlen, opwlen, max;
607 unsigned int diff; /* difference between old and new */
609 if (opw == NULL)
610 opw = "";
612 max = pwdef->maxlength;
613 pwlen = MIN(strlen(pw), max);
614 opwlen = MIN(strlen(opw), max);
616 if (pwlen > opwlen)
617 diff = pwlen - opwlen;
618 else
619 diff = opwlen - pwlen;
621 while (*opw != '\0' && *pw != '\0' && max-- != 0) {
622 if (*opw != *pw)
623 diff++;
624 opw++;
625 pw++;
628 if (diff < pwdef->mindiff) {
629 char *progname;
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);
637 return (1);
640 return (0);
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,
650 int flags)
652 int crack_ret;
653 int ret;
654 char *progname;
656 (void) pam_get_item(pamh, PAM_SERVICE, (void **)&progname);
658 /* dictionary check isn't MT-safe */
659 (void) mutex_lock(&dictlock);
661 if (pwdef->dicts &&
662 make_dict_database(pwdef->dicts, pwdef->db_location) != 0) {
663 (void) mutex_unlock(&dictlock);
664 syslog(LOG_ERR, "pam_authtok_check:pam_sm_chauthtok: "
665 "Dictionary database not present.");
666 error(pamh, flags, dgettext(TEXT_DOMAIN,
667 "%s: password dictionary missing."), progname);
668 return (PAM_SYSTEM_ERR);
671 crack_ret = DictCheck(pw, pwdef->db_location);
673 (void) mutex_unlock(&dictlock);
675 switch (crack_ret) {
676 case DATABASE_OPEN_FAIL:
677 syslog(LOG_ERR, "pam_authtok_check:pam_sm_chauthtok: "
678 "dictionary database open failure: %s", strerror(errno));
679 error(pamh, flags, dgettext(TEXT_DOMAIN,
680 "%s: failed to open dictionary database."), progname);
681 ret = PAM_SYSTEM_ERR;
682 break;
683 case DICTIONARY_WORD:
684 error(pamh, flags, dgettext(TEXT_DOMAIN,
685 "%s: password is based on a dictionary word."), progname);
686 ret = PAM_AUTHTOK_ERR;
687 break;
688 case REVERSE_DICTIONARY_WORD:
689 error(pamh, flags, dgettext(TEXT_DOMAIN,
690 "%s: password is based on a reversed dictionary word."),
691 progname);
692 ret = PAM_AUTHTOK_ERR;
693 break;
694 default:
695 ret = PAM_SUCCESS;
696 break;
698 return (ret);
702 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
704 int debug = 0;
705 int retcode = 0;
706 int force_check = 0;
707 int i;
708 size_t pwlen;
709 char *usrname;
710 char *pwbuf, *opwbuf;
711 pwu_repository_t *pwu_rep = PWU_DEFAULT_REP;
712 pam_repository_t *pwd_rep = NULL;
713 struct pwdefaults pwdef;
714 char *progname;
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)
721 debug = 1;
722 if (strcmp(argv[i], "force_check") == 0)
723 force_check = 1;
724 if (strcmp(argv[i], "server_policy") == 0)
725 pwdef.server_policy = B_TRUE;
728 if (debug)
729 syslog(LOG_AUTH | LOG_DEBUG,
730 "pam_authtok_check: pam_sm_chauthok called(%x) "
731 "force_check = %d", flags, force_check);
733 if ((flags & PAM_PRELIM_CHECK) == 0)
734 return (PAM_IGNORE);
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);
745 if (pwbuf == NULL)
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)
755 return (retcode);
757 if (debug) {
758 syslog(LOG_AUTH | LOG_DEBUG,
759 "pam_authtok_check: MAXLENGTH= %d, server_policy = %s",
760 pwdef.maxlength, pwdef.server_policy ? "true" : "false");
761 syslog(LOG_AUTH | LOG_DEBUG,
762 "pam_authtok_check: PASSLENGTH= %d", pwdef.minlength);
763 syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: NAMECHECK=%s",
764 pwdef.do_namecheck == B_TRUE ? "Yes" : "No");
765 syslog(LOG_AUTH | LOG_DEBUG,
766 "pam_authtok_check: do_dictcheck = %s\n",
767 pwdef.do_dictcheck ? "true" : "false");
768 if (pwdef.do_dictcheck) {
769 syslog(LOG_AUTH | LOG_DEBUG,
770 "pam_authtok_check: DICTIONLIST=%s",
771 (pwdef.dicts != NULL) ? pwdef.dicts : "<not set>");
772 syslog(LOG_AUTH | LOG_DEBUG,
773 "pam_authtok_check: DICTIONDBDIR=%s",
774 pwdef.db_location);
776 syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: MINDIFF=%d",
777 pwdef.mindiff);
778 syslog(LOG_AUTH | LOG_DEBUG,
779 "pam_authtok_check: MINALPHA=%d, MINNONALPHA=%d",
780 pwdef.minalpha, pwdef.minnonalpha);
781 syslog(LOG_AUTH | LOG_DEBUG,
782 "pam_authtok_check: MINSPECIAL=%d, MINDIGIT=%d",
783 pwdef.minspecial, pwdef.mindigit);
784 syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: WHITESPACE=%s",
785 pwdef.whitespace ? "YES" : "NO");
786 syslog(LOG_AUTH | LOG_DEBUG,
787 "pam_authtok_check: MINUPPER=%d, MINLOWER=%d",
788 pwdef.minupper, pwdef.minlower);
789 syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: MAXREPEATS=%d",
790 pwdef.maxrepeat);
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);
800 return (PAM_IGNORE);
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
811 * for now.
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)) {
830 case 1:
831 error(pamh, flags, dgettext(TEXT_DOMAIN,
832 "%s: Password cannot be circular shift of "
833 "logonid."), progname);
834 free_passwd_defaults(&pwdef);
835 return (PAM_AUTHTOK_ERR);
836 case -1:
837 free_passwd_defaults(&pwdef);
838 return (PAM_BUF_ERR);
839 default:
840 break;
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)
859 free(pwu_rep);
860 free_passwd_defaults(&pwdef);
861 return (PAM_AUTHTOK_ERR);
864 if (pwu_rep != PWU_DEFAULT_REP)
865 free(pwu_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);
884 return (retcode);
888 free_passwd_defaults(&pwdef);
889 /* password has passed all tests: it's strong enough */
890 return (PAM_SUCCESS);