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/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 strcmp(repository, "nisplus") == 0) || 289 p->server_policy == B_FALSE) { 290 char *salt; 291 /* 292 * We currently need to supply this dummy to 293 * crypt_gensalt(). This will change RSN. 294 */ 295 struct passwd dummy; 296 297 dummy.pw_name = user; 298 299 salt = crypt_gensalt(oldpw, &dummy); 300 if (salt && *salt == '$') 301 p->maxlength = _PASS_MAX; 302 else 303 p->maxlength = _PASS_MAX_XPG; 304 305 free(salt); 306 307 p->server_policy = B_FALSE; /* we perform checks */ 308 } else { 309 /* not files, nis or nisplus AND server_policy is set */ 310 p->maxlength = _PASS_MAX; 311 } 312 free(attr[0].data.val_s); 313 free(attr[1].data.val_s); 314 } 315 316 /* sanity check of the configured parameters */ 317 if (p->minlength < p->mindigit + p->minspecial + p->minnonalpha + 318 p->minalpha) { 319 syslog(LOG_ERR, "%s: pam_authtok_check: Defined minimum " 320 "password length (PASSLENGTH=%d) is less then minimum " 321 "characters in the various classes (%d)", progname, 322 p->minlength, 323 p->mindigit + p->minspecial + p->minnonalpha + p->minalpha); 324 p->minlength = p->mindigit + p->minspecial + p->minnonalpha + 325 p->minalpha; 326 syslog(LOG_ERR, "%s: pam_authtok_check: effective " 327 "PASSLENGTH set to %d.", progname, p->minlength); 328 /* this won't lead to failure */ 329 } 330 331 if (p->maxlength < p->minlength) { 332 syslog(LOG_ERR, "%s: pam_authtok_check: The configured " 333 "minimum password length (PASSLENGTH=%d) is larger than " 334 "the number of significant characters the current " 335 "encryption algorithm uses (%d). See policy.conf(4) for " 336 "alternative password encryption algorithms.", progname); 337 /* this won't lead to failure */ 338 } 339 340 return (PAM_SUCCESS); 341 } 342 343 /* 344 * free_passwd_defaults(struct pwdefaults *p) 345 * 346 * free space occupied by the defaults read from PWADMIN 347 */ 348 void 349 free_passwd_defaults(struct pwdefaults *p) 350 { 351 if (p && p->dicts) 352 free(p->dicts); 353 } 354 355 /* 356 * check_circular(): 357 * This function return 1 if string "t" is a circular shift of 358 * string "s", else it returns 0. -1 is returned on failure. 359 * We also check to see if string "t" is a reversed-circular shift 360 * of string "s", i.e. "ABCDE" vs. "DCBAE". 361 */ 362 static int 363 check_circular(s, t) 364 char *s, *t; 365 { 366 char c, *p, *o, *r, *buff, *ubuff, *pubuff; 367 unsigned int i, j, k, l, m; 368 size_t len; 369 int ret = 0; 370 371 i = strlen(s); 372 l = strlen(t); 373 if (i != l) 374 return (0); 375 len = i + 1; 376 377 buff = malloc(len); 378 ubuff = malloc(len); 379 pubuff = malloc(len); 380 381 if (buff == NULL || ubuff == NULL || pubuff == NULL) { 382 syslog(LOG_ERR, "pam_authtok_check: out of memory."); 383 return (-1); 384 } 385 386 m = 2; 387 o = &ubuff[0]; 388 for (p = s; c = *p++; *o++ = c) 389 if (islower(c)) 390 c = toupper(c); 391 *o = '\0'; 392 o = &pubuff[0]; 393 for (p = t; c = *p++; *o++ = c) 394 if (islower(c)) 395 c = toupper(c); 396 397 *o = '\0'; 398 399 p = &ubuff[0]; 400 while (m--) { 401 for (k = 0; k < i; k++) { 402 c = *p++; 403 o = p; 404 l = i; 405 r = &buff[0]; 406 while (--l) 407 *r++ = *o++; 408 *r++ = c; 409 *r = '\0'; 410 p = &buff[0]; 411 if (strcmp(p, pubuff) == 0) { 412 ret = 1; 413 goto out; 414 } 415 } 416 p = p + i; 417 r = &ubuff[0]; 418 j = i; 419 while (j--) 420 *--p = *r++; /* reverse test-string for m==0 pass */ 421 } 422 out: 423 (void) memset(buff, 0, len); 424 (void) memset(ubuff, 0, len); 425 (void) memset(pubuff, 0, len); 426 free(buff); 427 free(ubuff); 428 free(pubuff); 429 return (ret); 430 } 431 432 433 /* 434 * count the different character classes present in the password. 435 */ 436 int 437 check_composition(char *pw, struct pwdefaults *pwdef, pam_handle_t *pamh, 438 int flags) 439 { 440 uint_t alpha_cnt = 0; 441 uint_t upper_cnt = 0; 442 uint_t lower_cnt = 0; 443 uint_t special_cnt = 0; 444 uint_t whitespace_cnt = 0; 445 uint_t digit_cnt = 0; 446 uint_t maxrepeat = 0; 447 uint_t repeat = 1; 448 int ret = 0; 449 char *progname; 450 char errmsg[256]; 451 char lastc = '\0'; 452 uint_t significant = pwdef->maxlength; 453 char *w; 454 455 (void) pam_get_item(pamh, PAM_SERVICE, (void **)&progname); 456 457 /* go over the password gathering statistics */ 458 for (w = pw; significant != 0 && *w != '\0'; w++, significant--) { 459 if (isalpha(*w)) { 460 alpha_cnt++; 461 if (isupper(*w)) { 462 upper_cnt++; 463 } else { 464 lower_cnt++; 465 } 466 } else if (isspace(*w)) 467 whitespace_cnt++; 468 else if (isdigit(*w)) 469 digit_cnt++; 470 else 471 special_cnt++; 472 if (*w == lastc) { 473 if (++repeat > maxrepeat) 474 maxrepeat = repeat; 475 } else { 476 repeat = 1; 477 } 478 lastc = *w; 479 } 480 481 /* 482 * If we only consider part of the password (the first maxlength 483 * characters) we give a modified error message. Otherwise, a 484 * user entering FooBar1234 with PASSLENGTH=6, MINDIGIT=4, while 485 * we're using the default UNIX crypt (8 chars significant), 486 * would not understand what's going on when he's told that 487 * "The password should contain at least 4 digits"... 488 * Instead, we now well him 489 * "The first 8 characters of the password should contain at least 490 * 4 digits." 491 */ 492 if (pwdef->maxlength < strlen(pw)) 493 /* 494 * TRANSLATION_NOTE 495 * - Make sure the % and %% come over intact 496 * - The last %%s will be replaced by strings like 497 * "alphabetic character(s)" 498 * "numeric or special character(s)" 499 * "special character(s)" 500 * "digit(s)" 501 * "uppercase alpha character(s)" 502 * "lowercase alpha character(s)" 503 * So the final string written to the user might become 504 * "passwd: The first 8 characters of the password must contain 505 * at least 4 uppercase alpha characters(s)" 506 */ 507 (void) snprintf(errmsg, sizeof (errmsg), dgettext(TEXT_DOMAIN, 508 "%s: The first %d characters of the password must " 509 "contain at least %%d %%s."), progname, pwdef->maxlength); 510 else 511 /* 512 * TRANSLATION_NOTE 513 * - Make sure the % and %% come over intact 514 * - The last %%s will be replaced by strings like 515 * "alphabetic character(s)" 516 * "numeric or special character(s)" 517 * "special character(s)" 518 * "digit(s)" 519 * "uppercase alpha character(s)" 520 * "lowercase alpha character(s)" 521 * So the final string written to the user might become 522 * "passwd: The password must contain at least 4 uppercase 523 * alpha characters(s)" 524 */ 525 (void) snprintf(errmsg, sizeof (errmsg), dgettext(TEXT_DOMAIN, 526 "%s: The password must contain at least %%d %%s."), 527 progname); 528 529 /* Check for whitespace first since it influences special counts */ 530 if (whitespace_cnt > 0 && pwdef->whitespace == B_FALSE) { 531 error(pamh, flags, dgettext(TEXT_DOMAIN, 532 "%s: Whitespace characters are not allowed."), progname); 533 ret = 1; 534 goto out; 535 } 536 537 /* 538 * Once we get here, whitespace_cnt is either 0, or whitespaces are 539 * to be treated a special characters. 540 */ 541 542 if (alpha_cnt < pwdef->minalpha) { 543 error(pamh, flags, errmsg, pwdef->minalpha, 544 dgettext(TEXT_DOMAIN, "alphabetic character(s)")); 545 ret = 1; 546 goto out; 547 } 548 549 if (pwdef->minnonalpha > 0) { 550 /* specials are defined by MINNONALPHA */ 551 /* nonalpha = special+whitespace+digit */ 552 if ((special_cnt + whitespace_cnt + digit_cnt) < 553 pwdef->minnonalpha) { 554 error(pamh, flags, errmsg, pwdef->minnonalpha, 555 dgettext(TEXT_DOMAIN, 556 "numeric or special character(s)")); 557 ret = 1; 558 goto out; 559 } 560 } else { 561 /* specials are defined by MINSPECIAL and/or MINDIGIT */ 562 if ((special_cnt + whitespace_cnt) < pwdef->minspecial) { 563 error(pamh, flags, errmsg, pwdef->minspecial, 564 dgettext(TEXT_DOMAIN, "special character(s)")); 565 ret = 1; 566 goto out; 567 } 568 if (digit_cnt < pwdef->mindigit) { 569 error(pamh, flags, errmsg, pwdef->mindigit, 570 dgettext(TEXT_DOMAIN, "digit(s)")); 571 ret = 1; 572 goto out; 573 } 574 } 575 576 if (upper_cnt < pwdef->minupper) { 577 error(pamh, flags, errmsg, pwdef->minupper, 578 dgettext(TEXT_DOMAIN, "uppercase alpha character(s)")); 579 ret = 1; 580 goto out; 581 } 582 if (lower_cnt < pwdef->minlower) { 583 error(pamh, flags, errmsg, pwdef->minlower, 584 dgettext(TEXT_DOMAIN, "lowercase alpha character(s)")); 585 ret = 1; 586 goto out; 587 } 588 589 if (pwdef->maxrepeat > 0 && maxrepeat > pwdef->maxrepeat) { 590 error(pamh, flags, dgettext(TEXT_DOMAIN, 591 "%s: Too many consecutively repeating characters. " 592 "Maximum allowed is %d."), progname, pwdef->maxrepeat); 593 ret = 1; 594 } 595 out: 596 return (ret); 597 } 598 599 /* 600 * make sure that old and new password differ by at least 'mindiff' 601 * positions. Return 0 if OK, 1 otherwise 602 */ 603 int 604 check_diff(char *pw, char *opw, struct pwdefaults *pwdef, pam_handle_t *pamh, 605 int flags) 606 { 607 size_t pwlen, opwlen, max; 608 unsigned int diff; /* difference between old and new */ 609 610 if (opw == NULL) 611 opw = ""; 612 613 max = pwdef->maxlength; 614 pwlen = MIN(strlen(pw), max); 615 opwlen = MIN(strlen(opw), max); 616 617 if (pwlen > opwlen) 618 diff = pwlen - opwlen; 619 else 620 diff = opwlen - pwlen; 621 622 while (*opw != '\0' && *pw != '\0' && max-- != 0) { 623 if (*opw != *pw) 624 diff++; 625 opw++; 626 pw++; 627 } 628 629 if (diff < pwdef->mindiff) { 630 char *progname; 631 632 (void) pam_get_item(pamh, PAM_SERVICE, (void **)&progname); 633 634 error(pamh, flags, dgettext(TEXT_DOMAIN, 635 "%s: The first %d characters of the old and new passwords " 636 "must differ by at least %d positions."), progname, 637 pwdef->maxlength, pwdef->mindiff); 638 return (1); 639 } 640 641 return (0); 642 } 643 644 /* 645 * check to see if password is in one way or another based on a 646 * dictionary word. Returns 0 if password is OK, 1 if it is based 647 * on a dictionary word and hence should be rejected. 648 */ 649 int 650 check_dictionary(char *pw, struct pwdefaults *pwdef, pam_handle_t *pamh, 651 int flags) 652 { 653 int crack_ret; 654 int ret; 655 char *progname; 656 657 (void) pam_get_item(pamh, PAM_SERVICE, (void **)&progname); 658 659 /* dictionary check isn't MT-safe */ 660 (void) mutex_lock(&dictlock); 661 662 if (pwdef->dicts && 663 make_dict_database(pwdef->dicts, pwdef->db_location) != 0) { 664 (void) mutex_unlock(&dictlock); 665 syslog(LOG_ERR, "pam_authtok_check:pam_sm_chauthtok: " 666 "Dictionary database not present."); 667 error(pamh, flags, dgettext(TEXT_DOMAIN, 668 "%s: password dictionary missing."), progname); 669 return (PAM_SYSTEM_ERR); 670 } 671 672 crack_ret = DictCheck(pw, pwdef->db_location); 673 674 (void) mutex_unlock(&dictlock); 675 676 switch (crack_ret) { 677 case DATABASE_OPEN_FAIL: 678 syslog(LOG_ERR, "pam_authtok_check:pam_sm_chauthtok: " 679 "dictionary database open failure: %s", strerror(errno)); 680 error(pamh, flags, dgettext(TEXT_DOMAIN, 681 "%s: failed to open dictionary database."), progname); 682 ret = PAM_SYSTEM_ERR; 683 break; 684 case DICTIONARY_WORD: 685 error(pamh, flags, dgettext(TEXT_DOMAIN, 686 "%s: password is based on a dictionary word."), progname); 687 ret = PAM_AUTHTOK_ERR; 688 break; 689 case REVERSE_DICTIONARY_WORD: 690 error(pamh, flags, dgettext(TEXT_DOMAIN, 691 "%s: password is based on a reversed dictionary word."), 692 progname); 693 ret = PAM_AUTHTOK_ERR; 694 break; 695 default: 696 ret = PAM_SUCCESS; 697 break; 698 } 699 return (ret); 700 } 701 702 int 703 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) 704 { 705 int debug = 0; 706 int retcode = 0; 707 int force_check = 0; 708 int i; 709 size_t pwlen; 710 char *usrname; 711 char *pwbuf, *opwbuf; 712 pwu_repository_t *pwu_rep = PWU_DEFAULT_REP; 713 pam_repository_t *pwd_rep = NULL; 714 struct pwdefaults pwdef; 715 char *progname; 716 717 /* needs to be set before option processing */ 718 pwdef.server_policy = B_FALSE; 719 720 for (i = 0; i < argc; i++) { 721 if (strcmp(argv[i], "debug") == 0) 722 debug = 1; 723 if (strcmp(argv[i], "force_check") == 0) 724 force_check = 1; 725 if (strcmp(argv[i], "server_policy") == 0) 726 pwdef.server_policy = B_TRUE; 727 } 728 729 if (debug) 730 syslog(LOG_AUTH | LOG_DEBUG, 731 "pam_authtok_check: pam_sm_chauthok called(%x) " 732 "force_check = %d", flags, force_check); 733 734 if ((flags & PAM_PRELIM_CHECK) == 0) 735 return (PAM_IGNORE); 736 737 (void) pam_get_item(pamh, PAM_SERVICE, (void **)&progname); 738 (void) pam_get_item(pamh, PAM_USER, (void **)&usrname); 739 if (usrname == NULL || *usrname == '\0') { 740 syslog(LOG_ERR, "pam_authtok_check: username name is empty"); 741 return (PAM_USER_UNKNOWN); 742 } 743 744 (void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&pwbuf); 745 (void) pam_get_item(pamh, PAM_OLDAUTHTOK, (void **)&opwbuf); 746 if (pwbuf == NULL) 747 return (PAM_AUTHTOK_ERR); 748 749 /* none of these checks holds if caller say so */ 750 if ((flags & PAM_NO_AUTHTOK_CHECK) != 0 && force_check == 0) 751 return (PAM_SUCCESS); 752 753 /* read system-defaults */ 754 retcode = get_passwd_defaults(pamh, usrname, &pwdef); 755 if (retcode != PAM_SUCCESS) 756 return (retcode); 757 758 if (debug) { 759 syslog(LOG_AUTH | LOG_DEBUG, 760 "pam_authtok_check: MAXLENGTH= %d, server_policy = %s", 761 pwdef.maxlength, pwdef.server_policy ? "true" : "false"); 762 syslog(LOG_AUTH | LOG_DEBUG, 763 "pam_authtok_check: PASSLENGTH= %d", pwdef.minlength); 764 syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: NAMECHECK=%s", 765 pwdef.do_namecheck == B_TRUE ? "Yes" : "No"); 766 syslog(LOG_AUTH | LOG_DEBUG, 767 "pam_authtok_check: do_dictcheck = %s\n", 768 pwdef.do_dictcheck ? "true" : "false"); 769 if (pwdef.do_dictcheck) { 770 syslog(LOG_AUTH | LOG_DEBUG, 771 "pam_authtok_check: DICTIONLIST=%s", 772 (pwdef.dicts != NULL) ? pwdef.dicts : "<not set>"); 773 syslog(LOG_AUTH | LOG_DEBUG, 774 "pam_authtok_check: DICTIONDBDIR=%s", 775 pwdef.db_location); 776 } 777 syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: MINDIFF=%d", 778 pwdef.mindiff); 779 syslog(LOG_AUTH | LOG_DEBUG, 780 "pam_authtok_check: MINALPHA=%d, MINNONALPHA=%d", 781 pwdef.minalpha, pwdef.minnonalpha); 782 syslog(LOG_AUTH | LOG_DEBUG, 783 "pam_authtok_check: MINSPECIAL=%d, MINDIGIT=%d", 784 pwdef.minspecial, pwdef.mindigit); 785 syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: WHITESPACE=%s", 786 pwdef.whitespace ? "YES" : "NO"); 787 syslog(LOG_AUTH | LOG_DEBUG, 788 "pam_authtok_check: MINUPPER=%d, MINLOWER=%d", 789 pwdef.minupper, pwdef.minlower); 790 syslog(LOG_AUTH | LOG_DEBUG, "pam_authtok_check: MAXREPEATS=%d", 791 pwdef.maxrepeat); 792 } 793 794 /* 795 * If server policy is still true (might be changed from the 796 * value specified in /etc/pam.conf by get_passwd_defaults()), 797 * we return ignore and let the server do all the checks. 798 */ 799 if (pwdef.server_policy == B_TRUE) { 800 free_passwd_defaults(&pwdef); 801 return (PAM_IGNORE); 802 } 803 804 /* 805 * XXX: JV: we can't really make any assumption on the length of 806 * the password that will be used by the crypto algorithm. 807 * for UNIX-style encryption, minalpha=5,minnonalpha=5 might 808 * be impossible, but not for MD5 style hashes... what to do? 809 * 810 * since we don't know what alg. will be used, we operate on 811 * the password as entered, so we don't sanity check anything 812 * for now. 813 */ 814 815 /* 816 * Make sure new password is long enough 817 */ 818 pwlen = strlen(pwbuf); 819 820 if (pwlen < pwdef.minlength) { 821 error(pamh, flags, dgettext(TEXT_DOMAIN, 822 "%s: Password too short - must be at least %d " 823 "characters."), progname, pwdef.minlength); 824 free_passwd_defaults(&pwdef); 825 return (PAM_AUTHTOK_ERR); 826 } 827 828 /* Make sure the password doesn't equal--a shift of--the username */ 829 if (pwdef.do_namecheck) { 830 switch (check_circular(usrname, pwbuf)) { 831 case 1: 832 error(pamh, flags, dgettext(TEXT_DOMAIN, 833 "%s: Password cannot be circular shift of " 834 "logonid."), progname); 835 free_passwd_defaults(&pwdef); 836 return (PAM_AUTHTOK_ERR); 837 case -1: 838 free_passwd_defaults(&pwdef); 839 return (PAM_BUF_ERR); 840 default: 841 break; 842 } 843 } 844 845 /* Check if new password is in history list. */ 846 (void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&pwd_rep); 847 if (pwd_rep != NULL) { 848 if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL) 849 return (PAM_BUF_ERR); 850 pwu_rep->type = pwd_rep->type; 851 pwu_rep->scope = pwd_rep->scope; 852 pwu_rep->scope_len = pwd_rep->scope_len; 853 } 854 855 if (__check_history(usrname, pwbuf, pwu_rep) == PWU_SUCCESS) { 856 /* password found in history */ 857 error(pamh, flags, dgettext(TEXT_DOMAIN, 858 "%s: Password in history list."), progname); 859 if (pwu_rep != PWU_DEFAULT_REP) 860 free(pwu_rep); 861 free_passwd_defaults(&pwdef); 862 return (PAM_AUTHTOK_ERR); 863 } 864 865 if (pwu_rep != PWU_DEFAULT_REP) 866 free(pwu_rep); 867 868 /* check MINALPHA, MINLOWER, etc. */ 869 if (check_composition(pwbuf, &pwdef, pamh, flags) != 0) { 870 free_passwd_defaults(&pwdef); 871 return (PAM_AUTHTOK_ERR); 872 } 873 874 /* make sure the old and new password are not too much alike */ 875 if (check_diff(pwbuf, opwbuf, &pwdef, pamh, flags) != 0) { 876 free_passwd_defaults(&pwdef); 877 return (PAM_AUTHTOK_ERR); 878 } 879 880 /* dictionary check */ 881 if (pwdef.do_dictcheck) { 882 retcode = check_dictionary(pwbuf, &pwdef, pamh, flags); 883 if (retcode != PAM_SUCCESS) { 884 free_passwd_defaults(&pwdef); 885 return (retcode); 886 } 887 } 888 889 free_passwd_defaults(&pwdef); 890 /* password has passed all tests: it's strong enough */ 891 return (PAM_SUCCESS); 892 } 893