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 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 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 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 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 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 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 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 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 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