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 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdlib.h> 29 #include <syslog.h> 30 #include <errno.h> 31 #include <string.h> 32 #include <rpc/rpc.h> 33 #include <unistd.h> 34 #include <assert.h> 35 #include <stdarg.h> 36 #include <sys/types.h> 37 #include <sys/wait.h> 38 #include <limits.h> 39 #include <signal.h> 40 #include <pthread.h> 41 #include <synch.h> 42 43 #include <rpcsvc/nis.h> 44 #include <rpcsvc/nispasswd.h> 45 #include <rpcsvc/yppasswd.h> 46 #include <rpcsvc/ypclnt.h> 47 #include <rpc/key_prot.h> 48 #include <rpc/rpc.h> 49 #include <nfs/nfs.h> 50 #include <nfs/nfssys.h> 51 #include <nss_dbdefs.h> 52 #include <nsswitch.h> 53 #include <rpcsvc/nis_dhext.h> 54 55 #include <security/pam_appl.h> 56 #include <security/pam_modules.h> 57 #include <security/pam_impl.h> 58 59 #include <libintl.h> 60 61 #include <sys/mman.h> 62 63 #include <passwdutil.h> 64 65 #include "key_call_uid.h" 66 67 /* to keep track of codepath */ 68 #define CODEPATH_PAM_SM_AUTHENTICATE 0 69 #define CODEPATH_PAM_SM_SETCRED 1 70 71 #define SUNW_OLDRPCPASS "SUNW-OLD-RPC-PASSWORD" 72 73 extern int _nfssys(int, void *); 74 75 /* 76 * int msg(pamh, ...) 77 * 78 * display message to the user 79 */ 80 /*PRINTFLIKE2*/ 81 static int 82 msg(pam_handle_t *pamh, char *fmt, ...) 83 { 84 va_list ap; 85 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE]; 86 87 va_start(ap, fmt); 88 (void) vsnprintf(messages[0], sizeof (messages[0]), fmt, ap); 89 va_end(ap); 90 91 return (__pam_display_msg(pamh, PAM_ERROR_MSG, 1, messages, NULL)); 92 } 93 94 95 /* 96 * Get the secret key for the given netname, key length, and algorithm 97 * type and send it to keyserv if the given pw decrypts it. Update the 98 * following counter args as necessary: get_seckey_cnt, good_pw_cnt, and 99 * set_seckey_cnt. 100 * 101 * Returns 0 on malloc failure, else 1. 102 */ 103 static int 104 get_and_set_seckey( 105 pam_handle_t *pamh, /* in */ 106 const char *netname, /* in */ 107 keylen_t keylen, /* in */ 108 algtype_t algtype, /* in */ 109 const char *pw, /* in */ 110 uid_t uid, /* in */ 111 gid_t gid, /* in */ 112 int *get_seckey_cnt, /* out */ 113 int *good_pw_cnt, /* out */ 114 int *set_seckey_cnt, /* out */ 115 int flags, /* in */ 116 int debug) /* in */ 117 { 118 char *skey; 119 int skeylen; 120 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE]; 121 122 skeylen = BITS2NIBBLES(keylen) + 1; 123 124 if ((skey = malloc(skeylen)) == NULL) { 125 return (0); 126 } 127 128 if (getsecretkey_g(netname, keylen, algtype, skey, skeylen, pw)) { 129 (*get_seckey_cnt)++; 130 131 if (skey[0]) { 132 /* password does decrypt secret key */ 133 (*good_pw_cnt)++; 134 if (key_setnet_g_uid(netname, skey, keylen, NULL, 0, 135 algtype, uid, gid) >= 0) { 136 (*set_seckey_cnt)++; 137 } else { 138 if (debug) 139 syslog(LOG_DEBUG, "pam_dhkeys: " 140 "get_and_set_seckey: could not " 141 "set secret key for keytype " 142 "%d-%d", keylen, algtype); 143 } 144 } else { 145 if (pamh && !(flags & PAM_SILENT)) { 146 (void) snprintf(messages[0], 147 sizeof (messages[0]), 148 dgettext(TEXT_DOMAIN, 149 "Password does not " 150 "decrypt secret key (type = %d-%d) " 151 "for '%s'."), keylen, algtype, netname); 152 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, 153 messages, NULL); 154 } 155 } 156 } else { 157 if (debug) 158 syslog(LOG_DEBUG, "pam_dhkeys: get_and_set_seckey: " 159 "could not get secret key for keytype %d-%d", 160 keylen, algtype); 161 } 162 163 free(skey); 164 165 return (1); 166 } 167 168 /* 169 * int establish_key(pamh, flags, debug, netname) 170 * 171 * This routine established the Secure RPC Credentials for the 172 * user specified in PAM_USER, using the password in PAM_AUTHTOK. 173 * 174 * Because this routine is used for both pam_authenticate *and* 175 * pam_setcred, we have to be somewhat careful: 176 * 177 * - if called from pam_sm_authenticate: 178 * 1. if we don't need creds (no NIS+), we don't set them 179 * and return PAM_IGNORE. 180 * 2. else, we always try to establish credentials; 181 * if (passwd == "*NP*"), not having credentials results 182 * in PAM_AUTH_ERR. 183 * if (passwd != "*NP*"), any failure to set credentials 184 * results in PAM_IGNORE 185 * 186 * - if called from pam_sm_setcred: 187 * If we are root (uid == 0), we do nothing and return PAM_IGNORE. 188 * Otherwise, we try to establish the credentials. 189 * Not having credentials in this case results in PAM_IGNORE. 190 * 191 * For both modi, we return PAM_IGNORE if the creds are established. 192 * If we fail, we return 193 * - PAM_AUTH_ERR if the password didn't decrypt the cred 194 * - PAM_SYSTEM_ERR if the cred's could not be stored. 195 * 196 * This routine returns the user's netname in "netname". 197 * 198 * All tools--but the PAM stack--currently use getpass() to obtain 199 * the user's secure RPC password. We must make sure we don't use more than 200 * the first des_block (eight) characters of whatever is handed down to us. 201 * Therefore, we use a local variable "short_pass" to hold those 8 char's. 202 */ 203 static int 204 establish_key(pam_handle_t *pamh, int flags, int codepath, int debug, 205 char *netname) 206 { 207 char *user; 208 char *passwd; 209 char short_pass[sizeof (des_block)+1], *short_passp; 210 int result; 211 uid_t uid; 212 gid_t gid; 213 int err; 214 215 struct passwd pw; /* Needed to obtain uid */ 216 char *scratch; 217 int scratchlen; 218 219 int need_cred; /* is not having credentials set a failure? */ 220 int auth_cred_flags; 221 /* 222 * no_warn if creds not needed and 223 * authenticating 224 */ 225 int auth_path = (codepath == CODEPATH_PAM_SM_AUTHENTICATE); 226 char *repository_name = NULL; /* which repository are we using */ 227 char *repository_pass = NULL; /* user's password from that rep */ 228 pwu_repository_t *pwu_rep; 229 struct pam_repository *auth_rep; 230 attrlist attr_pw[2]; 231 232 mechanism_t **mechs; 233 mechanism_t **mpp; 234 int get_seckey_cnt = 0; 235 int set_seckey_cnt = 0; 236 int good_pw_cnt = 0; 237 int valid_mech_cnt = 0; 238 239 (void) pam_get_item(pamh, PAM_USER, (void **)&user); 240 241 if (user == NULL || *user == '\0') { 242 if (debug) 243 syslog(LOG_DEBUG, "pam_dhkeys: user NULL or empty"); 244 return (PAM_USER_UNKNOWN); 245 } 246 247 (void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&passwd); 248 249 scratchlen = sysconf(_SC_GETPW_R_SIZE_MAX); 250 if ((scratch = malloc(scratchlen)) == NULL) 251 return (PAM_BUF_ERR); 252 253 if (getpwnam_r(user, &pw, scratch, scratchlen) == NULL) { 254 result = PAM_USER_UNKNOWN; 255 goto out; 256 } 257 258 uid = pw.pw_uid; 259 gid = pw.pw_gid; 260 261 /* 262 * We don't set credentials when root logs in. 263 * We do, however, need to set the credentials if the NIS+ permissions 264 * require so. Thus, we only bail out if we're root and we're 265 * called from pam_setcred. 266 */ 267 if (uid == 0 && codepath == CODEPATH_PAM_SM_SETCRED) { 268 result = PAM_IGNORE; 269 goto out; 270 } 271 272 /* 273 * Check to see if we REALLY need to set the credentials, i.e. 274 * whether not being able to do so is an error or whether we 275 * can ignore it. 276 * We need to get the password from the repository that we're 277 * currently authenticating against. IFF this password equals 278 * "*NP" *AND* we are authenticating against NIS+, we actually 279 * do need to set the credentials. In all other cases, we 280 * can forget about them. 281 */ 282 (void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&auth_rep); 283 if (auth_rep != NULL) { 284 if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL) 285 return (PAM_BUF_ERR); 286 pwu_rep->type = auth_rep->type; 287 pwu_rep->scope = auth_rep->scope; 288 pwu_rep->scope_len = auth_rep->scope_len; 289 } else 290 pwu_rep = PWU_DEFAULT_REP; 291 292 attr_pw[0].type = ATTR_PASSWD; attr_pw[0].next = &attr_pw[1]; 293 attr_pw[1].type = ATTR_REP_NAME; attr_pw[1].next = NULL; 294 result = __get_authtoken_attr(user, pwu_rep, attr_pw); 295 296 if (pwu_rep != PWU_DEFAULT_REP) 297 free(pwu_rep); 298 299 if (result == PWU_NOT_FOUND) { 300 if (debug) 301 syslog(LOG_DEBUG, "pam_dhkeys: user %s not found", 302 user); 303 result = PAM_USER_UNKNOWN; 304 goto out; 305 } else if (result != PWU_SUCCESS) { 306 result = PAM_PERM_DENIED; 307 goto out; 308 } 309 310 repository_name = attr_pw[1].data.val_s; 311 repository_pass = attr_pw[0].data.val_s; 312 313 if (auth_path && (strcmp(repository_name, "nisplus") != 0)) { 314 result = PAM_IGNORE; 315 goto out; 316 } 317 318 need_cred = (strcmp(repository_pass, "*NP*") == 0); 319 if (auth_path) { 320 auth_cred_flags = 321 (need_cred ? flags : flags | PAM_SILENT); 322 } else { 323 auth_cred_flags = flags; 324 } 325 326 if (uid == 0) /* "root", need to create a host-netname */ 327 err = host2netname(netname, NULL, NULL); 328 else 329 err = user2netname(netname, uid, NULL); 330 331 if (err != 1) { 332 if (debug) 333 syslog(LOG_DEBUG, "pam_dhkeys: user2netname failed"); 334 if (need_cred) { 335 syslog(LOG_ALERT, "pam_dhkeys: user %s needs " 336 "Secure RPC Credentials to login.", user); 337 result = PAM_SERVICE_ERR; 338 } else 339 result = PAM_SYSTEM_ERR; 340 goto out; 341 } 342 343 /* passwd can be NULL (no passwd or su as root) */ 344 if (passwd) { 345 (void) strlcpy(short_pass, passwd, sizeof (short_pass)); 346 short_passp = short_pass; 347 } else 348 short_passp = NULL; 349 350 if (mechs = __nis_get_mechanisms(FALSE)) { 351 352 for (mpp = mechs; *mpp; mpp++) { 353 mechanism_t *mp = *mpp; 354 355 if (AUTH_DES_COMPAT_CHK(mp)) 356 break; /* fall through to AUTH_DES below */ 357 358 if (!VALID_MECH_ENTRY(mp)) 359 continue; 360 361 if (debug) 362 syslog(LOG_DEBUG, "pam_dhkeys: trying " 363 "key type = %d-%d", mp->keylen, 364 mp->algtype); 365 valid_mech_cnt++; 366 if (!get_and_set_seckey(pamh, netname, mp->keylen, 367 mp->algtype, short_passp, uid, gid, 368 &get_seckey_cnt, &good_pw_cnt, &set_seckey_cnt, 369 auth_cred_flags, debug)) { 370 result = PAM_BUF_ERR; 371 goto out; 372 } 373 } 374 __nis_release_mechanisms(mechs); 375 /* fall through to AUTH_DES below */ 376 } else { 377 /* 378 * No usable mechs found in NIS+ security cf thus 379 * fallback to AUTH_DES compat. 380 */ 381 if (debug) 382 syslog(LOG_DEBUG, "pam_dhkeys: no valid mechs " 383 "found. Trying AUTH_DES."); 384 } 385 386 /* 387 * We always perform AUTH_DES for the benefit of non-NIS+ 388 * services (e.g. NFS) that may depend on the classic des 389 * 192bit key being set. 390 */ 391 if (!get_and_set_seckey(pamh, netname, AUTH_DES_KEYLEN, 392 AUTH_DES_ALGTYPE, short_passp, uid, gid, &get_seckey_cnt, 393 &good_pw_cnt, &set_seckey_cnt, auth_cred_flags, debug)) { 394 result = PAM_BUF_ERR; 395 goto out; 396 } 397 398 if (debug) { 399 syslog(LOG_DEBUG, "pam_dhkeys: mech key totals:\n"); 400 syslog(LOG_DEBUG, "pam_dhkeys: %d valid mechanism(s)", 401 valid_mech_cnt); 402 syslog(LOG_DEBUG, "pam_dhkeys: %d secret key(s) retrieved", 403 get_seckey_cnt); 404 syslog(LOG_DEBUG, "pam_dhkeys: %d passwd decrypt successes", 405 good_pw_cnt); 406 syslog(LOG_DEBUG, "pam_dhkeys: %d secret key(s) set", 407 set_seckey_cnt); 408 } 409 410 if (get_seckey_cnt == 0) { /* No credentials */ 411 result = need_cred ? PAM_AUTH_ERR : PAM_IGNORE; 412 goto out; 413 } 414 415 if (good_pw_cnt == 0) { /* wrong password */ 416 if (auth_path) { 417 result = need_cred ? PAM_AUTH_ERR : PAM_IGNORE; 418 } else { 419 result = PAM_AUTH_ERR; 420 } 421 goto out; 422 } 423 424 if (set_seckey_cnt == 0) { 425 if (auth_path) { 426 result = need_cred ? PAM_SYSTEM_ERR : PAM_IGNORE; 427 } else { 428 result = PAM_SYSTEM_ERR; 429 } 430 goto out; 431 } 432 433 result = PAM_IGNORE; 434 out: 435 if (repository_name) 436 free(repository_name); 437 if (repository_pass) 438 free(repository_pass); 439 440 free(scratch); 441 442 (void) memset(short_pass, '\0', sizeof (short_pass)); 443 444 return (result); 445 } 446 447 int 448 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) 449 { 450 int i; 451 int debug = 0; 452 int result; 453 char netname[MAXNETNAMELEN + 1]; 454 455 for (i = 0; i < argc; i++) { 456 if (strcmp(argv[i], "debug") == 0) 457 debug = 1; 458 else if (strcmp(argv[i], "nowarn") == 0) 459 flags |= PAM_SILENT; 460 } 461 462 result = establish_key(pamh, flags, CODEPATH_PAM_SM_AUTHENTICATE, debug, 463 netname); 464 465 return (result); 466 } 467 468 469 typedef struct argres { 470 uid_t uid; 471 int result; 472 } argres_t; 473 474 /* 475 * Revoke NFS DES credentials. 476 * NFS may not be installed so we need to deal with SIGSYS 477 * when we call _nfssys(); we thus call _nfssys() in a seperate thread that 478 * is created specifically for this call. The thread specific signalmask 479 * is set to ignore SIGSYS. After the call to _nfssys(), the thread 480 * ceases to exist. 481 */ 482 static void * 483 revoke_nfs_cred(void *ap) 484 { 485 struct nfs_revauth_args nra; 486 sigset_t isigset; 487 argres_t *argres = (argres_t *)ap; 488 489 nra.authtype = AUTH_DES; 490 nra.uid = argres->uid; 491 492 (void) sigemptyset(&isigset); 493 (void) sigaddset(&isigset, SIGSYS); 494 495 if (pthread_sigmask(SIG_BLOCK, &isigset, NULL) == 0) { 496 argres->result = _nfssys(NFS_REVAUTH, &nra); 497 if (argres->result < 0 && errno == ENOSYS) { 498 argres->result = 0; 499 } 500 } else { 501 argres->result = -1; 502 } 503 return (NULL); 504 } 505 506 static int 507 remove_key(pam_handle_t *pamh, int flags, int debug) 508 { 509 int result; 510 char *uname; 511 attrlist attr_pw[2]; 512 struct pam_repository *auth_rep = NULL; 513 pwu_repository_t *pwu_rep; 514 uid_t uid; 515 gid_t gid; 516 argres_t argres; 517 thread_t tid; 518 519 (void) pam_get_item(pamh, PAM_USER, (void **)&uname); 520 if (uname == NULL || *uname == NULL) { 521 if (debug) 522 syslog(LOG_DEBUG, 523 "pam_dhkeys: user NULL or empty in remove_key()"); 524 return (PAM_USER_UNKNOWN); 525 } 526 527 if (strcmp(uname, "root") == 0) { 528 if ((flags & PAM_SILENT) == 0) { 529 char msg[3][PAM_MAX_MSG_SIZE]; 530 (void) snprintf(msg[0], sizeof (msg[0]), 531 dgettext(TEXT_DOMAIN, 532 "removing root credentials would" 533 " break the rpc services that")); 534 (void) snprintf(msg[1], sizeof (msg[1]), 535 dgettext(TEXT_DOMAIN, 536 "use secure rpc on this host!")); 537 (void) snprintf(msg[2], sizeof (msg[2]), 538 dgettext(TEXT_DOMAIN, 539 "root may use keylogout -f to do" 540 " this (at your own risk)!")); 541 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 3, 542 msg, NULL); 543 } 544 return (PAM_PERM_DENIED); 545 } 546 547 (void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&auth_rep); 548 if (auth_rep != NULL) { 549 if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL) 550 return (PAM_BUF_ERR); 551 pwu_rep->type = auth_rep->type; 552 pwu_rep->scope = auth_rep->scope; 553 pwu_rep->scope_len = auth_rep->scope_len; 554 } else 555 pwu_rep = PWU_DEFAULT_REP; 556 557 /* Retrieve user's uid/gid from the password repository */ 558 attr_pw[0].type = ATTR_UID; attr_pw[0].next = &attr_pw[1]; 559 attr_pw[1].type = ATTR_GID; attr_pw[1].next = NULL; 560 561 result = __get_authtoken_attr(uname, pwu_rep, attr_pw); 562 563 if (pwu_rep != PWU_DEFAULT_REP) 564 free(pwu_rep); 565 566 if (result == PWU_NOT_FOUND) 567 return (PAM_USER_UNKNOWN); 568 if (result == PWU_DENIED) 569 return (PAM_PERM_DENIED); 570 if (result != PWU_SUCCESS) 571 return (PAM_SYSTEM_ERR); 572 573 uid = (uid_t)attr_pw[0].data.val_i; 574 gid = (gid_t)attr_pw[1].data.val_i; 575 576 (void) key_removesecret_g_uid(uid, gid); 577 578 argres.uid = uid; 579 argres.result = -1; 580 581 if (pthread_create(&tid, NULL, revoke_nfs_cred, (void *)&argres) == 0) 582 (void) pthread_join(tid, NULL); 583 584 if (argres.result < 0) { 585 if ((flags & PAM_SILENT) == 0) { 586 (void) msg(pamh, dgettext(TEXT_DOMAIN, 587 "Warning: NFS credentials not destroyed")); 588 } 589 return (PAM_AUTH_ERR); 590 } 591 592 return (PAM_IGNORE); 593 } 594 595 int 596 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 597 { 598 int i; 599 int debug = 0; 600 int result; 601 char netname[MAXNETNAMELEN + 1]; 602 603 for (i = 0; i < argc; i++) { 604 if (strcmp(argv[i], "debug") == 0) 605 debug = 1; 606 else if (strcmp(argv[i], "nowarn") == 0) 607 flags |= PAM_SILENT; 608 } 609 610 /* Check for invalid flags */ 611 if (flags && (flags & PAM_ESTABLISH_CRED) == 0 && 612 (flags & PAM_REINITIALIZE_CRED) == 0 && 613 (flags & PAM_REFRESH_CRED) == 0 && 614 (flags & PAM_DELETE_CRED) == 0 && 615 (flags & PAM_SILENT) == 0) { 616 syslog(LOG_ERR, "pam_dhkeys: pam_setcred: illegal flags %d", 617 flags); 618 return (PAM_SYSTEM_ERR); 619 } 620 621 622 if ((flags & PAM_REINITIALIZE_CRED) || (flags & PAM_REFRESH_CRED)) { 623 /* doesn't apply to UNIX */ 624 if (debug) 625 syslog(LOG_DEBUG, "pam_dhkeys: cred reinit/refresh " 626 "ignored\n"); 627 return (PAM_IGNORE); 628 } 629 630 if (flags & PAM_DELETE_CRED) { 631 if (debug) 632 syslog(LOG_DEBUG, "pam_dhkeys: removing creds\n"); 633 result = remove_key(pamh, flags, debug); 634 } else { 635 result = establish_key(pamh, flags, CODEPATH_PAM_SM_SETCRED, 636 debug, netname); 637 /* Some diagnostics */ 638 if ((flags & PAM_SILENT) == 0) { 639 if (result == PAM_AUTH_ERR) 640 (void) msg(pamh, dgettext(TEXT_DOMAIN, 641 "Password does not decrypt any secret " 642 "keys for %s."), netname); 643 else if (result == PAM_SYSTEM_ERR && netname[0]) 644 (void) msg(pamh, dgettext(TEXT_DOMAIN, 645 "Could not set secret key(s) for %s. " 646 "The key server may be down."), netname); 647 } 648 649 /* Not having credentials set is not an error... */ 650 result = PAM_IGNORE; 651 } 652 653 return (result); 654 } 655 656 /*ARGSUSED*/ 657 void 658 rpc_cleanup(pam_handle_t *pamh, void *data, int pam_status) 659 { 660 if (data) { 661 (void) memset(data, 0, strlen(data)); 662 free(data); 663 } 664 } 665 666 int 667 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) 668 { 669 int i; 670 int debug = 0; 671 int res; 672 pam_repository_t *pam_rep; 673 pwu_repository_t *pwu_rep; 674 char *oldpw; 675 char *user; 676 int tries; 677 int oldpw_ok; 678 char *oldrpcpw; 679 char *oldrpcpass; 680 char *data; 681 /* password truncated at 8 chars, see comment at establish_key() */ 682 char short_pass[sizeof (des_block)+1], *short_passp; 683 684 for (i = 0; i < argc; i++) 685 if (strcmp(argv[i], "debug") == 0) 686 debug = 1; 687 688 if (debug) 689 syslog(LOG_DEBUG, "pam_dhkeys: entered pam_sm_chauthtok()"); 690 691 if ((flags & PAM_PRELIM_CHECK) == 0) 692 return (PAM_IGNORE); 693 694 /* 695 * See if the old secure-rpc password has already been set 696 */ 697 res = pam_get_data(pamh, SUNW_OLDRPCPASS, (const void **)&oldrpcpass); 698 if (res == PAM_SUCCESS) { 699 if (debug) 700 syslog(LOG_DEBUG, 701 "pam_dhkeys: OLDRPCPASS already set"); 702 return (PAM_IGNORE); 703 } 704 705 (void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&pam_rep); 706 707 (void) pam_get_item(pamh, PAM_USER, (void **)&user); 708 709 (void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&oldpw); 710 711 if (user == NULL || *user == '\0') { 712 if (debug) 713 syslog(LOG_DEBUG, "pam_dhkeys: user NULL or empty"); 714 return (PAM_USER_UNKNOWN); 715 } 716 717 /* oldpw can be NULL (eg. root changing someone's passwd) */ 718 if (oldpw) { 719 (void) strlcpy(short_pass, oldpw, sizeof (short_pass)); 720 short_passp = short_pass; 721 } else 722 short_passp = NULL; 723 724 /* 725 * For NIS+ we need to check whether the old password equals 726 * the RPC password. If it doesn't, we won't be able to update 727 * the secure RPC credentials later on in the process. 728 */ 729 730 if (pam_rep == NULL) 731 pwu_rep = PWU_DEFAULT_REP; 732 else { 733 if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL) 734 return (PAM_BUF_ERR); 735 pwu_rep->type = pam_rep->type; 736 pwu_rep->scope = pam_rep->scope; 737 pwu_rep->scope_len = pam_rep->scope_len; 738 } 739 740 switch (__verify_rpc_passwd(user, short_passp, pwu_rep)) { 741 case PWU_SUCCESS: 742 /* oldpw matches RPC password, or no RPC password needed */ 743 744 if (pwu_rep != PWU_DEFAULT_REP) 745 free(pwu_rep); 746 747 if (short_passp) { 748 if ((data = strdup(short_pass)) == NULL) { 749 (void) memset(short_pass, '\0', 750 sizeof (short_pass)); 751 return (PAM_BUF_ERR); 752 } 753 } else 754 data = NULL; 755 756 (void) pam_set_data(pamh, SUNW_OLDRPCPASS, data, rpc_cleanup); 757 return (PAM_IGNORE); 758 759 case PWU_NOT_FOUND: 760 if (pwu_rep != PWU_DEFAULT_REP) 761 free(pwu_rep); 762 (void) memset(short_pass, '\0', sizeof (short_pass)); 763 return (PAM_USER_UNKNOWN); 764 case PWU_BAD_CREDPASS: 765 /* The old password does not decrypt any credentials */ 766 break; 767 case PWU_CRED_ERROR: 768 /* 769 * Indicates that the user's credentials could not be 770 * retrieved or removed. This could occur when a NIS+ 771 * user is in transition to another account authority. 772 */ 773 if (pwu_rep != PWU_DEFAULT_REP) 774 free(pwu_rep); 775 (void) memset(short_pass, '\0', sizeof (short_pass)); 776 return (PAM_AUTHTOK_ERR); 777 default: 778 if (pwu_rep != PWU_DEFAULT_REP) 779 free(pwu_rep); 780 (void) memset(short_pass, '\0', sizeof (short_pass)); 781 return (PAM_SYSTEM_ERR); 782 } 783 784 /* 785 * We got here because the OLDAUTHTOK doesn't match the Secure RPC 786 * password. In compliance with the old behavior, we give the 787 * user two chances to get the password right. If that succeeds 788 * all is well; if it doesn't, we'll return an error. 789 */ 790 791 (void) msg(pamh, dgettext(TEXT_DOMAIN, 792 "This password differs from your secure RPC password.")); 793 794 tries = 0; 795 oldpw_ok = 0; 796 797 while (oldpw_ok == 0 && ++tries < 3) { 798 if (tries > 1) 799 (void) msg(pamh, dgettext(TEXT_DOMAIN, 800 "This password does not decrypt your " 801 "secure RPC password.")); 802 res = __pam_get_authtok(pamh, PAM_PROMPT, 0, 803 dgettext(TEXT_DOMAIN, 804 "Please enter your old Secure RPC password: "), &oldpw); 805 if (res != PAM_SUCCESS) { 806 if (pwu_rep != PWU_DEFAULT_REP) 807 free(pwu_rep); 808 return (res); 809 } 810 (void) strlcpy(short_pass, oldpw, sizeof (short_pass)); 811 (void) memset(oldpw, 0, strlen(oldpw)); 812 free(oldpw); 813 oldpw = NULL; 814 if (__verify_rpc_passwd(user, short_pass, pwu_rep) == 815 PWU_SUCCESS) 816 oldpw_ok = 1; 817 } 818 819 if (pwu_rep != PWU_DEFAULT_REP) 820 free(pwu_rep); 821 822 if (oldpw_ok == 0) { 823 (void) memset(short_pass, '\0', sizeof (short_pass)); 824 return (PAM_AUTHTOK_ERR); 825 } 826 827 /* 828 * Since the PAM framework only provides space for two different 829 * password (one old and one current), there is officially no 830 * place to put additional passwords (like our old rpc password). 831 * We have no choice but to stuff it in a data item, and hope it 832 * will be picked up by the password-update routines. 833 */ 834 835 oldrpcpw = strdup(short_pass); 836 (void) memset(short_pass, '\0', sizeof (short_pass)); 837 838 if (oldrpcpw == NULL) 839 return (PAM_BUF_ERR); 840 841 res = pam_set_data(pamh, SUNW_OLDRPCPASS, oldrpcpw, rpc_cleanup); 842 843 return (res); 844 } 845