1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
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.
25 * Copyright 2023 OmniOS Community Edition (OmniOSce) Association.
26 * Copyright 2025 RackTop Systems, Inc.
27 */
28
29 #include <sys/types.h>
30 #include <sys/varargs.h>
31 #include <sys/param.h>
32 #include <sys/sysmacros.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <deflt.h>
36 #include <security/pam_appl.h>
37 #include <security/pam_modules.h>
38 #include <security/pam_impl.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <unistd.h>
42 #include <syslog.h>
43 #include <libintl.h>
44 #include <errno.h>
45 #include <pwd.h>
46 #include "packer.h"
47
48 #include <passwdutil.h>
49
50 #define PWADMIN "/etc/default/passwd"
51
52 #define MINLENGTH 6
53 #define MINDIFF 3
54 #define MINALPHA 2
55 #define MINNONALPHA 1
56
57 mutex_t dictlock = DEFAULTMUTEX;
58
59 /*
60 * We implement:
61 * PASSLENGTH (int) minimum password length
62 * NAMECHECK (yes/no) perform comparison of password and loginname
63 * MINDIFF (int) minimum number of character-positions in which
64 * the old and the new password should differ.
65 * MINALPHA (int) minimum number of Alpha characters
66 * MINUPPER (int) minimum number of upper-case characters
67 * MINLOWER (int) minimum number of lower-case characters
68 * MAXREPEATS (int) maximum number of consecutively repeating chars
69 * WHITESPACE (yes/no) Are whitespaces allowed?
70 *
71 * Furthermore, these two mutualy exclusive groups of options are allowed:
72 *
73 * MINNONALPHA (int) minimum number of characters from the
74 * character classes [ punct, space, digit ]
75 * if WHITESPACE == NO, whitespaces don't count.
76 * and
77 * MINSPECIAL (int) minimum number of punctuation characters.
78 * if WHITESPACE != NO, whitespace is seen as
79 * a "special" character.
80 * MINDIGIT (int) minimum number of digits
81 *
82 * specifying options from both groups results in an error to syslog and
83 * failure to change the password.
84 *
85 * NOTE:
86 * HISTORY is implemented at the repository level (passwdutil).
87 */
88
89 /*
90 * default password-strength-values, compiled-in or stored in PWADMIN
91 * are kept in here
92 */
93 struct pwdefaults {
94 boolean_t server_policy; /* server policy flag from pam.conf */
95 uint_t minlength; /* minimum password length */
96 uint_t maxlength; /* maximum (significant) length */
97 boolean_t do_namecheck; /* check password against user's gecos */
98 char db_location[MAXPATHLEN]; /* location of the generated database */
99 boolean_t do_dictcheck; /* perform dictionary lookup */
100 char *dicts; /* list of dictionaries configured */
101 uint_t mindiff; /* old and new should differ by this much */
102 uint_t minalpha; /* minimum alpha characters required */
103 uint_t minupper; /* minimum uppercase characters required */
104 uint_t minlower; /* minimum lowercase characters required */
105 uint_t minnonalpha; /* minimum special (non alpha) required */
106 uint_t maxrepeat; /* maximum number of repeating chars allowed */
107 uint_t minspecial; /* punctuation characters */
108 uint_t mindigit; /* minimum number of digits required */
109 boolean_t whitespace; /* is whitespace allowed in a password */
110 };
111
112
113 /*PRINTFLIKE3*/
114 void
error(pam_handle_t * pamh,int flags,char * fmt,...)115 error(pam_handle_t *pamh, int flags, char *fmt, ...)
116 {
117 va_list ap;
118 char msg[1][PAM_MAX_MSG_SIZE];
119
120 va_start(ap, fmt);
121 (void) vsnprintf(msg[0], sizeof (msg[0]), fmt, ap);
122 va_end(ap);
123 if ((flags & PAM_SILENT) == 0)
124 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, msg, NULL);
125 }
126
127 int
defread_int(char * name,uint_t * ip,void * defp)128 defread_int(char *name, uint_t *ip, void *defp)
129 {
130 char *q;
131 int r = 0;
132 if ((q = defread_r(name, defp)) != NULL) {
133 if (!isdigit(*q)) {
134 syslog(LOG_ERR, "pam_authtok_check: %s contains "
135 "non-integer value for %s: %s. "
136 "Using default instead.", PWADMIN, name, q);
137 } else {
138 *ip = atoi(q);
139 r = 1;
140 }
141 }
142 return (r);
143 }
144
145 /*
146 * fill in static defaults, and augment with settings from PWADMIN
147 * get system defaults with regard to maximum password length
148 */
149 int
get_passwd_defaults(pam_handle_t * pamh,const char * user,struct pwdefaults * p)150 get_passwd_defaults(pam_handle_t *pamh, const char *user, struct pwdefaults *p)
151 {
152 char *q;
153 boolean_t minnonalpha_defined = B_FALSE;
154 pwu_repository_t *pwu_rep;
155 const struct pam_repository *pam_rep;
156 attrlist attr[2];
157 int result;
158 const char *progname;
159 void *defp;
160
161 (void) pam_get_item(pamh, PAM_SERVICE, (const void **)&progname);
162
163 /* Module defaults */
164 p->minlength = MINLENGTH;
165 p->do_namecheck = B_TRUE;
166 p->do_dictcheck = B_FALSE;
167 p->dicts = NULL;
168 p->mindiff = MINDIFF;
169 p->minalpha = MINALPHA;
170 p->minnonalpha = MINNONALPHA;
171 p->minupper = 0; /* not configured by default */
172 p->minlower = 0; /* not configured by default */
173 p->maxrepeat = 0; /* not configured by default */
174
175 p->minspecial = 0;
176 p->mindigit = 0;
177 p->whitespace = B_TRUE;
178
179 /*
180 * Determine the number of significant characters in a password
181 *
182 * we find out where the user information came from (which repository),
183 * and which password-crypt-algorithm is to be used (based on the
184 * old password, or the system default).
185 *
186 * If the user comes from a repository other than FILES/NIS
187 * the module-flag "server_policy" means that we don't perform
188 * any checks on the user, but let the repository decide instead.
189 */
190
191 (void) pam_get_item(pamh, PAM_REPOSITORY, (const void **)&pam_rep);
192 if (pam_rep != NULL) {
193 if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL)
194 return (PAM_BUF_ERR);
195 pwu_rep->type = pam_rep->type;
196 pwu_rep->scope = pam_rep->scope;
197 pwu_rep->scope_len = pam_rep->scope_len;
198 } else {
199 pwu_rep = PWU_DEFAULT_REP;
200 }
201
202 attr[0].type = ATTR_PASSWD; attr[0].next = &attr[1];
203 attr[1].type = ATTR_REP_NAME; attr[1].next = NULL;
204 result = __get_authtoken_attr(user, pwu_rep, attr);
205 if (pwu_rep != PWU_DEFAULT_REP)
206 free(pwu_rep);
207
208 if (result != PWU_SUCCESS) {
209 /*
210 * In the unlikely event that we can't obtain any info about
211 * the users password, we assume the most strict scenario.
212 */
213 p->maxlength = _PASS_MAX_XPG;
214 } else {
215 char *oldpw = attr[0].data.val_s;
216 char *repository = attr[1].data.val_s;
217 if ((strcmp(repository, "files") == 0 ||
218 strcmp(repository, "nis") == 0) ||
219 p->server_policy == B_FALSE) {
220 char *salt;
221 /*
222 * We currently need to supply this dummy to
223 * crypt_gensalt(). This will change RSN.
224 */
225 struct passwd dummy;
226
227 dummy.pw_name = strdup(user);
228 if (dummy.pw_name == NULL) {
229 free(attr[0].data.val_s);
230 free(attr[1].data.val_s);
231 return (PAM_BUF_ERR);
232 }
233
234 salt = crypt_gensalt(oldpw, &dummy);
235 free(dummy.pw_name);
236 if (salt && *salt == '$')
237 p->maxlength = _PASS_MAX;
238 else
239 p->maxlength = _PASS_MAX_XPG;
240
241 free(salt);
242
243 p->server_policy = B_FALSE; /* we perform checks */
244 } else {
245 /* not files or nis AND server_policy is set */
246 p->maxlength = _PASS_MAX;
247 }
248 free(attr[0].data.val_s);
249 free(attr[1].data.val_s);
250 }
251
252 if ((defp = defopen_r(PWADMIN)) == NULL)
253 return (PAM_SUCCESS);
254
255 (void) defread_int("PASSLENGTH=", &p->minlength, defp);
256
257 if ((q = defread_r("NAMECHECK=", defp)) != NULL &&
258 strcasecmp(q, "NO") == 0)
259 p->do_namecheck = B_FALSE;
260
261 if ((q = defread_r("DICTIONLIST=", defp)) != NULL) {
262 if ((p->dicts = strdup(q)) == NULL) {
263 syslog(LOG_ERR, "pam_authtok_check: out of memory");
264 defclose_r(defp);
265 return (PAM_BUF_ERR);
266
267 }
268 p->do_dictcheck = B_TRUE;
269 } else {
270 p->dicts = NULL;
271 }
272
273 if ((q = defread_r("DICTIONDBDIR=", defp)) != NULL) {
274 if (strlcpy(p->db_location, q, sizeof (p->db_location)) >=
275 sizeof (p->db_location)) {
276 syslog(LOG_ERR, "pam_authtok_check: value for "
277 "DICTIONDBDIR too large.");
278 defclose_r(defp);
279 return (PAM_SYSTEM_ERR);
280 }
281 p->do_dictcheck = B_TRUE;
282 } else {
283 (void) strlcpy(p->db_location, CRACK_DIR,
284 sizeof (p->db_location));
285 }
286
287 (void) defread_int("MINDIFF=", &p->mindiff, defp);
288 (void) defread_int("MINALPHA=", &p->minalpha, defp);
289 (void) defread_int("MINUPPER=", &p->minupper, defp);
290 (void) defread_int("MINLOWER=", &p->minlower, defp);
291 if (defread_int("MINNONALPHA=", &p->minnonalpha, defp))
292 minnonalpha_defined = B_TRUE;
293 (void) defread_int("MAXREPEATS=", &p->maxrepeat, defp);
294
295 if (defread_int("MINSPECIAL=", &p->minspecial, defp)) {
296 if (minnonalpha_defined) {
297 syslog(LOG_ERR, "pam_authtok_check: %s contains "
298 "definition for MINNONALPHA and for MINSPECIAL. "
299 "These options are mutually exclusive.", PWADMIN);
300 defclose_r(defp);
301 return (PAM_SYSTEM_ERR);
302 }
303 p->minnonalpha = 0;
304 }
305
306 if (defread_int("MINDIGIT=", &p->mindigit, defp)) {
307 if (minnonalpha_defined) {
308 syslog(LOG_ERR, "pam_authtok_check: %s contains "
309 "definition for MINNONALPHA and for MINDIGIT. "
310 "These options are mutually exclusive.", PWADMIN);
311 defclose_r(defp);
312 return (PAM_SYSTEM_ERR);
313 }
314 p->minnonalpha = 0;
315 }
316
317 if ((q = defread_r("WHITESPACE=", defp)) != NULL)
318 p->whitespace =
319 (strcasecmp(q, "no") == 0 || strcmp(q, "0") == 0)
320 ? B_FALSE : B_TRUE;
321
322 defclose_r(defp);
323
324 /* sanity check of the configured parameters */
325 if (p->minlength < p->mindigit + p->minspecial + p->minnonalpha +
326 p->minalpha) {
327 syslog(LOG_ERR, "%s: pam_authtok_check: Defined minimum "
328 "password length (PASSLENGTH=%d) is less then minimum "
329 "characters in the various classes (%d)", progname,
330 p->minlength,
331 p->mindigit + p->minspecial + p->minnonalpha + p->minalpha);
332 p->minlength = p->mindigit + p->minspecial + p->minnonalpha +
333 p->minalpha;
334 syslog(LOG_ERR, "%s: pam_authtok_check: effective "
335 "PASSLENGTH set to %d.", progname, p->minlength);
336 /* this won't lead to failure */
337 }
338
339 if (p->maxlength < p->minlength) {
340 syslog(LOG_ERR, "%s: pam_authtok_check: The configured "
341 "minimum password length (PASSLENGTH=%d) is larger than "
342 "the number of significant characters the current "
343 "encryption algorithm uses (%d). See policy.conf(5) for "
344 "alternative password encryption algorithms.", progname);
345 /* this won't lead to failure */
346 }
347
348 return (PAM_SUCCESS);
349 }
350
351 /*
352 * free_passwd_defaults(struct pwdefaults *p)
353 *
354 * free space occupied by the defaults read from PWADMIN
355 */
356 void
free_passwd_defaults(struct pwdefaults * p)357 free_passwd_defaults(struct pwdefaults *p)
358 {
359 if (p && p->dicts)
360 free(p->dicts);
361 }
362
363 /*
364 * check_circular():
365 * This function return 1 if string "t" is a circular shift of
366 * string "s", else it returns 0. -1 is returned on failure.
367 * We also check to see if string "t" is a reversed-circular shift
368 * of string "s", i.e. "ABCDE" vs. "DCBAE".
369 */
370 static int
check_circular(const char * s,const char * t)371 check_circular(const char *s, const char *t)
372 {
373 const char *p;
374 char c, *u, *o, *r, *buff, *ubuff, *pubuff;
375 unsigned int i, j, k, l, m;
376 size_t len;
377 int ret = 0;
378
379 i = strlen(s);
380 l = strlen(t);
381 if (i != l)
382 return (0);
383 len = i + 1;
384
385 buff = malloc(len);
386 ubuff = malloc(len);
387 pubuff = malloc(len);
388
389 if (buff == NULL || ubuff == NULL || pubuff == NULL) {
390 syslog(LOG_ERR, "pam_authtok_check: out of memory.");
391 free(buff);
392 free(ubuff);
393 free(pubuff);
394 return (-1);
395 }
396
397 m = 2;
398 o = &ubuff[0];
399 for (p = s; c = *p++; *o++ = c) {
400 if (islower(c))
401 c = toupper(c);
402 }
403 *o = '\0';
404 o = &pubuff[0];
405 for (p = t; c = *p++; *o++ = c) {
406 if (islower(c))
407 c = toupper(c);
408 }
409
410 *o = '\0';
411
412 u = &ubuff[0];
413 while (m--) {
414 for (k = 0; k < i; k++) {
415 c = *u++;
416 o = u;
417 l = i;
418 r = &buff[0];
419 while (--l)
420 *r++ = *o++;
421 *r++ = c;
422 *r = '\0';
423 u = &buff[0];
424 if (strcmp(u, pubuff) == 0) {
425 ret = 1;
426 goto out;
427 }
428 }
429 u = u + i;
430 r = &ubuff[0];
431 j = i;
432 while (j--)
433 *--u = *r++; /* reverse test-string for m==0 pass */
434 }
435 out:
436 (void) memset(buff, 0, len);
437 (void) memset(ubuff, 0, len);
438 (void) memset(pubuff, 0, len);
439 free(buff);
440 free(ubuff);
441 free(pubuff);
442 return (ret);
443 }
444
445
446 /*
447 * count the different character classes present in the password.
448 */
449 int
check_composition(const char * pw,struct pwdefaults * pwdef,pam_handle_t * pamh,int flags)450 check_composition(const char *pw, struct pwdefaults *pwdef, pam_handle_t *pamh,
451 int flags)
452 {
453 uint_t alpha_cnt = 0;
454 uint_t upper_cnt = 0;
455 uint_t lower_cnt = 0;
456 uint_t special_cnt = 0;
457 uint_t whitespace_cnt = 0;
458 uint_t digit_cnt = 0;
459 uint_t maxrepeat = 0;
460 uint_t repeat = 1;
461 int ret = 0;
462 const char *progname;
463 char errmsg[256];
464 char lastc = '\0';
465 uint_t significant = pwdef->maxlength;
466 const char *w;
467
468 (void) pam_get_item(pamh, PAM_SERVICE, (const void **)&progname);
469
470 /* go over the password gathering statistics */
471 for (w = pw; significant != 0 && *w != '\0'; w++, significant--) {
472 if (isalpha(*w)) {
473 alpha_cnt++;
474 if (isupper(*w)) {
475 upper_cnt++;
476 } else {
477 lower_cnt++;
478 }
479 } else if (isspace(*w))
480 whitespace_cnt++;
481 else if (isdigit(*w))
482 digit_cnt++;
483 else
484 special_cnt++;
485 if (*w == lastc) {
486 if (++repeat > maxrepeat)
487 maxrepeat = repeat;
488 } else {
489 repeat = 1;
490 }
491 lastc = *w;
492 }
493
494 /*
495 * If we only consider part of the password (the first maxlength
496 * characters) we give a modified error message. Otherwise, a
497 * user entering FooBar1234 with PASSLENGTH=6, MINDIGIT=4, while
498 * we're using the default UNIX crypt (8 chars significant),
499 * would not understand what's going on when they're told that
500 * "The password should contain at least 4 digits"...
501 * Instead, we now tell them
502 * "The first 8 characters of the password should contain at least
503 * 4 digits."
504 */
505 if (pwdef->maxlength < strlen(pw))
506 /*
507 * TRANSLATION_NOTE
508 * - Make sure the % and %% come over intact
509 * - The last %%s will be replaced by strings like
510 * "alphabetic character(s)"
511 * "numeric or special character(s)"
512 * "special character(s)"
513 * "digit(s)"
514 * "uppercase alpha character(s)"
515 * "lowercase alpha character(s)"
516 * So the final string written to the user might become
517 * "passwd: The first 8 characters of the password must contain
518 * at least 4 uppercase alpha characters(s)"
519 */
520 (void) snprintf(errmsg, sizeof (errmsg), dgettext(TEXT_DOMAIN,
521 "%s: The first %d characters of the password must "
522 "contain at least %%d %%s."), progname, pwdef->maxlength);
523 else
524 /*
525 * TRANSLATION_NOTE
526 * - Make sure the % and %% come over intact
527 * - The last %%s will be replaced by strings like
528 * "alphabetic character(s)"
529 * "numeric or special character(s)"
530 * "special character(s)"
531 * "digit(s)"
532 * "uppercase alpha character(s)"
533 * "lowercase alpha character(s)"
534 * So the final string written to the user might become
535 * "passwd: The password must contain at least 4 uppercase
536 * alpha characters(s)"
537 */
538 (void) snprintf(errmsg, sizeof (errmsg), dgettext(TEXT_DOMAIN,
539 "%s: The password must contain at least %%d %%s."),
540 progname);
541
542 /* Check for whitespace first since it influences special counts */
543 if (whitespace_cnt > 0 && pwdef->whitespace == B_FALSE) {
544 error(pamh, flags, dgettext(TEXT_DOMAIN,
545 "%s: Whitespace characters are not allowed."), progname);
546 ret = 1;
547 goto out;
548 }
549
550 /*
551 * Once we get here, whitespace_cnt is either 0, or whitespaces are
552 * to be treated a special characters.
553 */
554
555 if (alpha_cnt < pwdef->minalpha) {
556 error(pamh, flags, errmsg, pwdef->minalpha,
557 dgettext(TEXT_DOMAIN, "alphabetic character(s)"));
558 ret = 1;
559 goto out;
560 }
561
562 if (pwdef->minnonalpha > 0) {
563 /* specials are defined by MINNONALPHA */
564 /* nonalpha = special+whitespace+digit */
565 if ((special_cnt + whitespace_cnt + digit_cnt) <
566 pwdef->minnonalpha) {
567 error(pamh, flags, errmsg, pwdef->minnonalpha,
568 dgettext(TEXT_DOMAIN,
569 "numeric or special character(s)"));
570 ret = 1;
571 goto out;
572 }
573 } else {
574 /* specials are defined by MINSPECIAL and/or MINDIGIT */
575 if ((special_cnt + whitespace_cnt) < pwdef->minspecial) {
576 error(pamh, flags, errmsg, pwdef->minspecial,
577 dgettext(TEXT_DOMAIN, "special character(s)"));
578 ret = 1;
579 goto out;
580 }
581 if (digit_cnt < pwdef->mindigit) {
582 error(pamh, flags, errmsg, pwdef->mindigit,
583 dgettext(TEXT_DOMAIN, "digit(s)"));
584 ret = 1;
585 goto out;
586 }
587 }
588
589 if (upper_cnt < pwdef->minupper) {
590 error(pamh, flags, errmsg, pwdef->minupper,
591 dgettext(TEXT_DOMAIN, "uppercase alpha character(s)"));
592 ret = 1;
593 goto out;
594 }
595 if (lower_cnt < pwdef->minlower) {
596 error(pamh, flags, errmsg, pwdef->minlower,
597 dgettext(TEXT_DOMAIN, "lowercase alpha character(s)"));
598 ret = 1;
599 goto out;
600 }
601
602 if (pwdef->maxrepeat > 0 && maxrepeat > pwdef->maxrepeat) {
603 error(pamh, flags, dgettext(TEXT_DOMAIN,
604 "%s: Too many consecutively repeating characters. "
605 "Maximum allowed is %d."), progname, pwdef->maxrepeat);
606 ret = 1;
607 }
608 out:
609 return (ret);
610 }
611
612 /*
613 * make sure that old and new password differ by at least 'mindiff'
614 * positions. Return 0 if OK, 1 otherwise
615 */
616 int
check_diff(const char * pw,const char * opw,struct pwdefaults * pwdef,pam_handle_t * pamh,int flags)617 check_diff(const char *pw, const char *opw, struct pwdefaults *pwdef,
618 pam_handle_t *pamh, int flags)
619 {
620 size_t pwlen, opwlen, max;
621 unsigned int diff; /* difference between old and new */
622
623 if (opw == NULL)
624 opw = "";
625
626 max = pwdef->maxlength;
627 pwlen = MIN(strlen(pw), max);
628 opwlen = MIN(strlen(opw), max);
629
630 if (pwlen > opwlen)
631 diff = pwlen - opwlen;
632 else
633 diff = opwlen - pwlen;
634
635 while (*opw != '\0' && *pw != '\0' && max-- != 0) {
636 if (*opw != *pw)
637 diff++;
638 opw++;
639 pw++;
640 }
641
642 if (diff < pwdef->mindiff) {
643 const char *progname;
644
645 (void) pam_get_item(pamh, PAM_SERVICE,
646 (const void **)&progname);
647
648 error(pamh, flags, dgettext(TEXT_DOMAIN,
649 "%s: The first %d characters of the old and new passwords "
650 "must differ by at least %d positions."), progname,
651 pwdef->maxlength, pwdef->mindiff);
652 return (1);
653 }
654
655 return (0);
656 }
657
658 /*
659 * check to see if password is in one way or another based on a
660 * dictionary word. Returns 0 if password is OK, 1 if it is based
661 * on a dictionary word and hence should be rejected.
662 */
663 int
check_dictionary(const char * pw,struct pwdefaults * pwdef,pam_handle_t * pamh,int flags)664 check_dictionary(const char *pw, struct pwdefaults *pwdef, pam_handle_t *pamh,
665 int flags)
666 {
667 int crack_ret;
668 int ret;
669 const char *progname;
670
671 (void) pam_get_item(pamh, PAM_SERVICE, (const void **)&progname);
672
673 /* dictionary check isn't MT-safe */
674 (void) mutex_lock(&dictlock);
675
676 if (pwdef->dicts &&
677 make_dict_database(pwdef->dicts, pwdef->db_location) != 0) {
678 (void) mutex_unlock(&dictlock);
679 syslog(LOG_ERR, "pam_authtok_check:pam_sm_chauthtok: "
680 "Dictionary database not present.");
681 error(pamh, flags, dgettext(TEXT_DOMAIN,
682 "%s: password dictionary missing."), progname);
683 return (PAM_SYSTEM_ERR);
684 }
685
686 crack_ret = DictCheck(pw, pwdef->db_location);
687
688 (void) mutex_unlock(&dictlock);
689
690 switch (crack_ret) {
691 case DATABASE_OPEN_FAIL:
692 syslog(LOG_ERR, "pam_authtok_check:pam_sm_chauthtok: "
693 "dictionary database open failure: %s", strerror(errno));
694 error(pamh, flags, dgettext(TEXT_DOMAIN,
695 "%s: failed to open dictionary database."), progname);
696 ret = PAM_SYSTEM_ERR;
697 break;
698 case DICTIONARY_WORD:
699 error(pamh, flags, dgettext(TEXT_DOMAIN,
700 "%s: password is based on a dictionary word."), progname);
701 ret = PAM_AUTHTOK_ERR;
702 break;
703 case REVERSE_DICTIONARY_WORD:
704 error(pamh, flags, dgettext(TEXT_DOMAIN,
705 "%s: password is based on a reversed dictionary word."),
706 progname);
707 ret = PAM_AUTHTOK_ERR;
708 break;
709 default:
710 ret = PAM_SUCCESS;
711 break;
712 }
713 return (ret);
714 }
715
716 int
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc,const char ** argv)717 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
718 {
719 int debug = 0;
720 int retcode = 0;
721 int force_check = 0;
722 int i;
723 size_t pwlen;
724 const char *usrname;
725 const char *pwbuf, *opwbuf;
726 pwu_repository_t *pwu_rep = PWU_DEFAULT_REP;
727 const pam_repository_t *pwd_rep = NULL;
728 struct pwdefaults pwdef;
729 const char *progname;
730
731 /* needs to be set before option processing */
732 pwdef.server_policy = B_FALSE;
733
734 for (i = 0; i < argc; i++) {
735 if (strcmp(argv[i], "debug") == 0)
736 debug = 1;
737 if (strcmp(argv[i], "force_check") == 0)
738 force_check = 1;
739 if (strcmp(argv[i], "server_policy") == 0)
740 pwdef.server_policy = B_TRUE;
741 }
742
743 if (debug)
744 syslog(LOG_AUTH | LOG_DEBUG,
745 "pam_authtok_check: pam_sm_chauthok called(%x) "
746 "force_check = %d", flags, force_check);
747
748 if ((flags & PAM_PRELIM_CHECK) == 0)
749 return (PAM_IGNORE);
750
751 (void) pam_get_item(pamh, PAM_SERVICE, (const void **)&progname);
752 (void) pam_get_item(pamh, PAM_USER, (const void **)&usrname);
753 if (usrname == NULL || *usrname == '\0') {
754 syslog(LOG_ERR, "pam_authtok_check: username name is empty");
755 return (PAM_USER_UNKNOWN);
756 }
757
758 (void) pam_get_item(pamh, PAM_AUTHTOK, (const void **)&pwbuf);
759 (void) pam_get_item(pamh, PAM_OLDAUTHTOK, (const void **)&opwbuf);
760 if (pwbuf == NULL)
761 return (PAM_AUTHTOK_ERR);
762
763 /* none of these checks holds if caller say so */
764 if ((flags & PAM_NO_AUTHTOK_CHECK) != 0 && force_check == 0)
765 return (PAM_SUCCESS);
766
767 /* read system-defaults */
768 retcode = get_passwd_defaults(pamh, usrname, &pwdef);
769 if (retcode != PAM_SUCCESS)
770 return (retcode);
771
772 if (debug) {
773 syslog(LOG_AUTH | LOG_DEBUG,
774 "pam_authtok_check: MAXLENGTH= %d, server_policy = %s",
775 pwdef.maxlength, pwdef.server_policy ? "true" : "false");
776 syslog(LOG_AUTH | LOG_DEBUG,
777 "pam_authtok_check: PASSLENGTH= %d", pwdef.minlength);
778 syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: NAMECHECK=%s",
779 pwdef.do_namecheck == B_TRUE ? "Yes" : "No");
780 syslog(LOG_AUTH | LOG_DEBUG,
781 "pam_authtok_check: do_dictcheck = %s\n",
782 pwdef.do_dictcheck ? "true" : "false");
783 if (pwdef.do_dictcheck) {
784 syslog(LOG_AUTH | LOG_DEBUG,
785 "pam_authtok_check: DICTIONLIST=%s",
786 (pwdef.dicts != NULL) ? pwdef.dicts : "<not set>");
787 syslog(LOG_AUTH | LOG_DEBUG,
788 "pam_authtok_check: DICTIONDBDIR=%s",
789 pwdef.db_location);
790 }
791 syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: MINDIFF=%d",
792 pwdef.mindiff);
793 syslog(LOG_AUTH | LOG_DEBUG,
794 "pam_authtok_check: MINALPHA=%d, MINNONALPHA=%d",
795 pwdef.minalpha, pwdef.minnonalpha);
796 syslog(LOG_AUTH | LOG_DEBUG,
797 "pam_authtok_check: MINSPECIAL=%d, MINDIGIT=%d",
798 pwdef.minspecial, pwdef.mindigit);
799 syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: WHITESPACE=%s",
800 pwdef.whitespace ? "YES" : "NO");
801 syslog(LOG_AUTH | LOG_DEBUG,
802 "pam_authtok_check: MINUPPER=%d, MINLOWER=%d",
803 pwdef.minupper, pwdef.minlower);
804 syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: MAXREPEATS=%d",
805 pwdef.maxrepeat);
806 }
807
808 /*
809 * If server policy is still true (might be changed from the
810 * value specified in /etc/pam.conf by get_passwd_defaults()),
811 * we return ignore and let the server do all the checks.
812 */
813 if (pwdef.server_policy == B_TRUE) {
814 free_passwd_defaults(&pwdef);
815 return (PAM_IGNORE);
816 }
817
818 /*
819 * XXX: JV: we can't really make any assumption on the length of
820 * the password that will be used by the crypto algorithm.
821 * for UNIX-style encryption, minalpha=5,minnonalpha=5 might
822 * be impossible, but not for MD5 style hashes... what to do?
823 *
824 * since we don't know what alg. will be used, we operate on
825 * the password as entered, so we don't sanity check anything
826 * for now.
827 */
828
829 /*
830 * Make sure new password is long enough
831 */
832 pwlen = strlen(pwbuf);
833
834 if (pwlen < pwdef.minlength) {
835 error(pamh, flags, dgettext(TEXT_DOMAIN,
836 "%s: Password too short - must be at least %d "
837 "characters."), progname, pwdef.minlength);
838 free_passwd_defaults(&pwdef);
839 return (PAM_AUTHTOK_ERR);
840 }
841
842 /* Make sure the password doesn't equal--a shift of--the username */
843 if (pwdef.do_namecheck) {
844 switch (check_circular(usrname, pwbuf)) {
845 case 1:
846 error(pamh, flags, dgettext(TEXT_DOMAIN,
847 "%s: Password cannot be circular shift of "
848 "logonid."), progname);
849 free_passwd_defaults(&pwdef);
850 return (PAM_AUTHTOK_ERR);
851 case -1:
852 free_passwd_defaults(&pwdef);
853 return (PAM_BUF_ERR);
854 default:
855 break;
856 }
857 }
858
859 /* Check if new password is in history list. */
860 (void) pam_get_item(pamh, PAM_REPOSITORY, (const void **)&pwd_rep);
861 if (pwd_rep != NULL) {
862 if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL)
863 return (PAM_BUF_ERR);
864 pwu_rep->type = pwd_rep->type;
865 pwu_rep->scope = pwd_rep->scope;
866 pwu_rep->scope_len = pwd_rep->scope_len;
867 }
868
869 if (__check_history(usrname, pwbuf, pwu_rep) == PWU_SUCCESS) {
870 /* password found in history */
871 error(pamh, flags, dgettext(TEXT_DOMAIN,
872 "%s: Password in history list."), progname);
873 if (pwu_rep != PWU_DEFAULT_REP)
874 free(pwu_rep);
875 free_passwd_defaults(&pwdef);
876 return (PAM_AUTHTOK_ERR);
877 }
878
879 if (pwu_rep != PWU_DEFAULT_REP)
880 free(pwu_rep);
881
882 /* check MINALPHA, MINLOWER, etc. */
883 if (check_composition(pwbuf, &pwdef, pamh, flags) != 0) {
884 free_passwd_defaults(&pwdef);
885 return (PAM_AUTHTOK_ERR);
886 }
887
888 /* make sure the old and new password are not too much alike */
889 if (check_diff(pwbuf, opwbuf, &pwdef, pamh, flags) != 0) {
890 free_passwd_defaults(&pwdef);
891 return (PAM_AUTHTOK_ERR);
892 }
893
894 /* dictionary check */
895 if (pwdef.do_dictcheck) {
896 retcode = check_dictionary(pwbuf, &pwdef, pamh, flags);
897 if (retcode != PAM_SUCCESS) {
898 free_passwd_defaults(&pwdef);
899 return (retcode);
900 }
901 }
902
903 free_passwd_defaults(&pwdef);
904 /* password has passed all tests: it's strong enough */
905 return (PAM_SUCCESS);
906 }
907