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