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 <stdlib.h> 27 #include <syslog.h> 28 #include <errno.h> 29 #include <string.h> 30 #include <rpc/rpc.h> 31 #include <unistd.h> 32 #include <assert.h> 33 #include <stdarg.h> 34 #include <sys/types.h> 35 #include <sys/wait.h> 36 #include <limits.h> 37 #include <signal.h> 38 #include <pthread.h> 39 #include <synch.h> 40 41 #include <rpcsvc/nis.h> 42 #include <rpcsvc/yppasswd.h> 43 #include <rpcsvc/ypclnt.h> 44 #include <rpc/key_prot.h> 45 #include <rpc/rpc.h> 46 #include <nfs/nfs.h> 47 #include <nfs/nfssys.h> 48 #include <nss_dbdefs.h> 49 #include <nsswitch.h> 50 #include <rpcsvc/nis_dhext.h> 51 52 #include <security/pam_appl.h> 53 #include <security/pam_modules.h> 54 #include <security/pam_impl.h> 55 56 #include <libintl.h> 57 58 #include <sys/mman.h> 59 60 #include <passwdutil.h> 61 62 #include "key_call_uid.h" 63 #include <shadow.h> 64 65 extern int _nfssys(int, void *); 66 67 /* 68 * int msg(pamh, ...) 69 * 70 * display message to the user 71 */ 72 /*PRINTFLIKE2*/ 73 static int 74 msg(pam_handle_t *pamh, char *fmt, ...) 75 { 76 va_list ap; 77 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE]; 78 79 va_start(ap, fmt); 80 (void) vsnprintf(messages[0], sizeof (messages[0]), fmt, ap); 81 va_end(ap); 82 83 return (__pam_display_msg(pamh, PAM_ERROR_MSG, 1, messages, NULL)); 84 } 85 86 87 /* 88 * Get the secret key for the given netname, key length, and algorithm 89 * type and send it to keyserv if the given pw decrypts it. Update the 90 * following counter args as necessary: get_seckey_cnt, good_pw_cnt, and 91 * set_seckey_cnt. 92 * 93 * Returns 0 on malloc failure, else 1. 94 */ 95 static int 96 get_and_set_seckey( 97 pam_handle_t *pamh, /* in */ 98 const char *netname, /* in */ 99 keylen_t keylen, /* in */ 100 algtype_t algtype, /* in */ 101 const char *pw, /* in */ 102 uid_t uid, /* in */ 103 gid_t gid, /* in */ 104 int *get_seckey_cnt, /* out */ 105 int *good_pw_cnt, /* out */ 106 int *set_seckey_cnt, /* out */ 107 int flags, /* in */ 108 int debug) /* in */ 109 { 110 char *skey; 111 int skeylen; 112 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE]; 113 114 skeylen = BITS2NIBBLES(keylen) + 1; 115 116 if ((skey = malloc(skeylen)) == NULL) { 117 return (0); 118 } 119 120 if (getsecretkey_g(netname, keylen, algtype, skey, skeylen, pw)) { 121 (*get_seckey_cnt)++; 122 123 if (skey[0]) { 124 /* password does decrypt secret key */ 125 (*good_pw_cnt)++; 126 if (key_setnet_g_uid(netname, skey, keylen, NULL, 0, 127 algtype, uid, gid) >= 0) { 128 (*set_seckey_cnt)++; 129 } else { 130 if (debug) 131 syslog(LOG_DEBUG, "pam_dhkeys: " 132 "get_and_set_seckey: could not " 133 "set secret key for keytype " 134 "%d-%d", keylen, algtype); 135 } 136 } else { 137 if (pamh && !(flags & PAM_SILENT)) { 138 (void) snprintf(messages[0], 139 sizeof (messages[0]), 140 dgettext(TEXT_DOMAIN, 141 "Password does not " 142 "decrypt secret key (type = %d-%d) " 143 "for '%s'."), keylen, algtype, netname); 144 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, 145 messages, NULL); 146 } 147 } 148 } else { 149 if (debug) 150 syslog(LOG_DEBUG, "pam_dhkeys: get_and_set_seckey: " 151 "could not get secret key for keytype %d-%d", 152 keylen, algtype); 153 } 154 155 free(skey); 156 157 return (1); 158 } 159 160 /* 161 * int establish_key(pamh, flags, debug, netname) 162 * 163 * This routine establishes the Secure RPC Credentials for the 164 * user specified in PAM_USER, using the password in PAM_AUTHTOK. 165 * 166 * Establishing RPC credentials is considered a "helper" function for the PAM 167 * stack so we should only return failures or PAM_IGNORE. Returning PAM_SUCCESS 168 * may short circuit the stack and circumvent later critical checks. 169 * 170 * we are called from pam_sm_setcred: 171 * 1. if we are root (uid == 0), we do nothing and return 172 * PAM_IGNORE. 173 * 2. else, we try to establish credentials. 174 * 175 * We return framework errors as appropriate such as PAM_USER_UNKNOWN, 176 * PAM_BUF_ERR, PAM_PERM_DENIED. 177 * 178 * If we succeed in establishing credentials we return PAM_IGNORE. 179 * 180 * If we fail to establish credentials then we return: 181 * - PAM_SERVICE_ERR (credentials needed) or PAM_SYSTEM_ERR 182 * (credentials not needed) if netname could not be created; 183 * - PAM_AUTH_ERR (credentials needed) or PAM_IGNORE (credentials 184 * not needed) if no credentials were retrieved; 185 * - PAM_AUTH_ERR if the password didn't decrypt the cred; 186 * - PAM_SYSTEM_ERR if the cred's could not be stored. 187 * 188 * This routine returns the user's netname in "netname". 189 * 190 * All tools--but the PAM stack--currently use getpass() to obtain 191 * the user's secure RPC password. We must make sure we don't use more than 192 * the first des_block (eight) characters of whatever is handed down to us. 193 * Therefore, we use a local variable "short_pass" to hold those 8 char's. 194 */ 195 static int 196 establish_key(pam_handle_t *pamh, int flags, int debug, char *netname) 197 { 198 char *user; 199 char *passwd; 200 char short_pass[sizeof (des_block)+1], *short_passp; 201 int result; 202 uid_t uid; 203 gid_t gid; 204 int err; 205 206 struct passwd pw; /* Needed to obtain uid */ 207 char *scratch; 208 int scratchlen; 209 210 mechanism_t **mechs; 211 mechanism_t **mpp; 212 int get_seckey_cnt = 0; 213 int set_seckey_cnt = 0; 214 int good_pw_cnt = 0; 215 int valid_mech_cnt = 0; 216 217 (void) pam_get_item(pamh, PAM_USER, (void **)&user); 218 219 if (user == NULL || *user == '\0') { 220 if (debug) 221 syslog(LOG_DEBUG, "pam_dhkeys: user NULL or empty"); 222 return (PAM_USER_UNKNOWN); 223 } 224 225 (void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&passwd); 226 227 scratchlen = sysconf(_SC_GETPW_R_SIZE_MAX); 228 if ((scratch = malloc(scratchlen)) == NULL) 229 return (PAM_BUF_ERR); 230 231 if (getpwnam_r(user, &pw, scratch, scratchlen) == NULL) { 232 result = PAM_USER_UNKNOWN; 233 goto out; 234 } 235 236 uid = pw.pw_uid; 237 gid = pw.pw_gid; 238 239 /* 240 * We don't set credentials when root logs in. 241 */ 242 if (uid == 0) { 243 result = PAM_IGNORE; 244 goto out; 245 } 246 247 err = user2netname(netname, uid, NULL); 248 249 if (err != 1) { 250 if (debug) 251 syslog(LOG_DEBUG, "pam_dhkeys: user2netname failed"); 252 result = PAM_SYSTEM_ERR; 253 goto out; 254 } 255 256 /* passwd can be NULL (no passwd or su as root) */ 257 if (passwd) { 258 (void) strlcpy(short_pass, passwd, sizeof (short_pass)); 259 short_passp = short_pass; 260 } else { 261 short_passp = NULL; 262 } 263 264 if (mechs = __nis_get_mechanisms(FALSE)) { 265 266 for (mpp = mechs; *mpp; mpp++) { 267 mechanism_t *mp = *mpp; 268 269 if (AUTH_DES_COMPAT_CHK(mp)) 270 break; /* fall through to AUTH_DES below */ 271 272 if (!VALID_MECH_ENTRY(mp)) 273 continue; 274 275 if (debug) 276 syslog(LOG_DEBUG, "pam_dhkeys: trying " 277 "key type = %d-%d", mp->keylen, 278 mp->algtype); 279 valid_mech_cnt++; 280 if (!get_and_set_seckey(pamh, netname, mp->keylen, 281 mp->algtype, short_passp, uid, gid, 282 &get_seckey_cnt, &good_pw_cnt, &set_seckey_cnt, 283 flags, debug)) { 284 result = PAM_BUF_ERR; 285 goto out; 286 } 287 } 288 __nis_release_mechanisms(mechs); 289 /* fall through to AUTH_DES below */ 290 } else { 291 /* 292 * No usable mechs found in security congifuration file thus 293 * fallback to AUTH_DES compat. 294 */ 295 if (debug) 296 syslog(LOG_DEBUG, "pam_dhkeys: no valid mechs " 297 "found. Trying AUTH_DES."); 298 } 299 300 /* 301 * We always perform AUTH_DES for the benefit of services like NFS 302 * that may depend on the classic des 192bit key being set. 303 */ 304 if (!get_and_set_seckey(pamh, netname, AUTH_DES_KEYLEN, 305 AUTH_DES_ALGTYPE, short_passp, uid, gid, &get_seckey_cnt, 306 &good_pw_cnt, &set_seckey_cnt, flags, debug)) { 307 result = PAM_BUF_ERR; 308 goto out; 309 } 310 311 if (debug) { 312 syslog(LOG_DEBUG, "pam_dhkeys: mech key totals:\n"); 313 syslog(LOG_DEBUG, "pam_dhkeys: %d valid mechanism(s)", 314 valid_mech_cnt); 315 syslog(LOG_DEBUG, "pam_dhkeys: %d secret key(s) retrieved", 316 get_seckey_cnt); 317 syslog(LOG_DEBUG, "pam_dhkeys: %d passwd decrypt successes", 318 good_pw_cnt); 319 syslog(LOG_DEBUG, "pam_dhkeys: %d secret key(s) set", 320 set_seckey_cnt); 321 } 322 323 if (get_seckey_cnt == 0) { /* No credentials */ 324 result = PAM_IGNORE; 325 goto out; 326 } 327 328 if (good_pw_cnt == 0) { /* wrong password */ 329 result = PAM_AUTH_ERR; 330 goto out; 331 } 332 333 if (set_seckey_cnt == 0) { 334 result = PAM_SYSTEM_ERR; 335 goto out; 336 } 337 /* Credentials have been successfully established, return PAM_IGNORE */ 338 result = PAM_IGNORE; 339 out: 340 /* 341 * If we are authenticating we attempt to establish credentials 342 * where appropriate. Failure to do so is only an error if we 343 * definitely needed them. Thus always return PAM_IGNORE 344 * if we are authenticating and credentials were not needed. 345 */ 346 free(scratch); 347 348 (void) memset(short_pass, '\0', sizeof (short_pass)); 349 350 return (result); 351 } 352 353 /*ARGSUSED*/ 354 int 355 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) 356 { 357 return (PAM_IGNORE); 358 } 359 360 361 typedef struct argres { 362 uid_t uid; 363 int result; 364 } argres_t; 365 366 /* 367 * Revoke NFS DES credentials. 368 * NFS may not be installed so we need to deal with SIGSYS 369 * when we call _nfssys(); we thus call _nfssys() in a seperate thread that 370 * is created specifically for this call. The thread specific signalmask 371 * is set to ignore SIGSYS. After the call to _nfssys(), the thread 372 * ceases to exist. 373 */ 374 static void * 375 revoke_nfs_cred(void *ap) 376 { 377 struct nfs_revauth_args nra; 378 sigset_t isigset; 379 argres_t *argres = (argres_t *)ap; 380 381 nra.authtype = AUTH_DES; 382 nra.uid = argres->uid; 383 384 (void) sigemptyset(&isigset); 385 (void) sigaddset(&isigset, SIGSYS); 386 387 if (pthread_sigmask(SIG_BLOCK, &isigset, NULL) == 0) { 388 argres->result = _nfssys(NFS_REVAUTH, &nra); 389 if (argres->result < 0 && errno == ENOSYS) { 390 argres->result = 0; 391 } 392 } else { 393 argres->result = -1; 394 } 395 return (NULL); 396 } 397 398 static int 399 remove_key(pam_handle_t *pamh, int flags, int debug) 400 { 401 int result; 402 char *uname; 403 attrlist attr_pw[2]; 404 struct pam_repository *auth_rep = NULL; 405 pwu_repository_t *pwu_rep; 406 uid_t uid; 407 gid_t gid; 408 argres_t argres; 409 pthread_t tid; 410 411 (void) pam_get_item(pamh, PAM_USER, (void **)&uname); 412 if (uname == NULL || *uname == '\0') { 413 if (debug) 414 syslog(LOG_DEBUG, 415 "pam_dhkeys: user NULL or empty in remove_key()"); 416 return (PAM_USER_UNKNOWN); 417 } 418 419 if (strcmp(uname, "root") == 0) { 420 if ((flags & PAM_SILENT) == 0) { 421 char msg[3][PAM_MAX_MSG_SIZE]; 422 (void) snprintf(msg[0], sizeof (msg[0]), 423 dgettext(TEXT_DOMAIN, 424 "removing root credentials would" 425 " break the rpc services that")); 426 (void) snprintf(msg[1], sizeof (msg[1]), 427 dgettext(TEXT_DOMAIN, 428 "use secure rpc on this host!")); 429 (void) snprintf(msg[2], sizeof (msg[2]), 430 dgettext(TEXT_DOMAIN, 431 "root may use keylogout -f to do" 432 " this (at your own risk)!")); 433 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 3, 434 msg, NULL); 435 } 436 return (PAM_PERM_DENIED); 437 } 438 439 (void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&auth_rep); 440 if (auth_rep != NULL) { 441 if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL) 442 return (PAM_BUF_ERR); 443 pwu_rep->type = auth_rep->type; 444 pwu_rep->scope = auth_rep->scope; 445 pwu_rep->scope_len = auth_rep->scope_len; 446 } else { 447 pwu_rep = PWU_DEFAULT_REP; 448 } 449 450 /* Retrieve user's uid/gid from the password repository */ 451 attr_pw[0].type = ATTR_UID; attr_pw[0].next = &attr_pw[1]; 452 attr_pw[1].type = ATTR_GID; attr_pw[1].next = NULL; 453 454 result = __get_authtoken_attr(uname, pwu_rep, attr_pw); 455 456 if (pwu_rep != PWU_DEFAULT_REP) 457 free(pwu_rep); 458 459 if (result == PWU_NOT_FOUND) 460 return (PAM_USER_UNKNOWN); 461 if (result == PWU_DENIED) 462 return (PAM_PERM_DENIED); 463 if (result != PWU_SUCCESS) 464 return (PAM_SYSTEM_ERR); 465 466 uid = (uid_t)attr_pw[0].data.val_i; 467 gid = (gid_t)attr_pw[1].data.val_i; 468 469 (void) key_removesecret_g_uid(uid, gid); 470 471 argres.uid = uid; 472 argres.result = -1; 473 474 if (pthread_create(&tid, NULL, revoke_nfs_cred, (void *)&argres) == 0) 475 (void) pthread_join(tid, NULL); 476 477 if (argres.result < 0) { 478 if ((flags & PAM_SILENT) == 0) { 479 (void) msg(pamh, dgettext(TEXT_DOMAIN, 480 "Warning: NFS credentials not destroyed")); 481 } 482 return (PAM_AUTH_ERR); 483 } 484 485 return (PAM_IGNORE); 486 } 487 488 int 489 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 490 { 491 int i; 492 int debug = 0; 493 int result; 494 char netname[MAXNETNAMELEN + 1]; 495 496 for (i = 0; i < argc; i++) { 497 if (strcmp(argv[i], "debug") == 0) 498 debug = 1; 499 else if (strcmp(argv[i], "nowarn") == 0) 500 flags |= PAM_SILENT; 501 } 502 503 /* Check for invalid flags */ 504 if (flags && (flags & PAM_ESTABLISH_CRED) == 0 && 505 (flags & PAM_REINITIALIZE_CRED) == 0 && 506 (flags & PAM_REFRESH_CRED) == 0 && 507 (flags & PAM_DELETE_CRED) == 0 && 508 (flags & PAM_SILENT) == 0) { 509 syslog(LOG_ERR, "pam_dhkeys: pam_setcred: illegal flags %d", 510 flags); 511 return (PAM_SYSTEM_ERR); 512 } 513 514 515 if ((flags & PAM_REINITIALIZE_CRED) || (flags & PAM_REFRESH_CRED)) { 516 /* doesn't apply to UNIX */ 517 if (debug) 518 syslog(LOG_DEBUG, "pam_dhkeys: cred reinit/refresh " 519 "ignored\n"); 520 return (PAM_IGNORE); 521 } 522 523 if (flags & PAM_DELETE_CRED) { 524 if (debug) 525 syslog(LOG_DEBUG, "pam_dhkeys: removing creds\n"); 526 result = remove_key(pamh, flags, debug); 527 } else { 528 result = establish_key(pamh, flags, debug, netname); 529 /* Some diagnostics */ 530 if ((flags & PAM_SILENT) == 0) { 531 if (result == PAM_AUTH_ERR) 532 (void) msg(pamh, dgettext(TEXT_DOMAIN, 533 "Password does not decrypt any secret " 534 "keys for %s."), netname); 535 else if (result == PAM_SYSTEM_ERR && netname[0]) 536 (void) msg(pamh, dgettext(TEXT_DOMAIN, 537 "Could not set secret key(s) for %s. " 538 "The key server may be down."), netname); 539 } 540 541 /* Not having credentials set is not an error... */ 542 result = PAM_IGNORE; 543 } 544 545 return (result); 546 } 547 548 /*ARGSUSED*/ 549 void 550 rpc_cleanup(pam_handle_t *pamh, void *data, int pam_status) 551 { 552 if (data) { 553 (void) memset(data, 0, strlen(data)); 554 free(data); 555 } 556 } 557 558 /*ARGSUSED*/ 559 int 560 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) 561 { 562 return (PAM_IGNORE); 563 } 564