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