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