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 if (mechs = __nis_get_mechanisms(FALSE)) { 264 265 for (mpp = mechs; *mpp; mpp++) { 266 mechanism_t *mp = *mpp; 267 268 if (AUTH_DES_COMPAT_CHK(mp)) 269 break; /* fall through to AUTH_DES below */ 270 271 if (!VALID_MECH_ENTRY(mp)) 272 continue; 273 274 if (debug) 275 syslog(LOG_DEBUG, "pam_dhkeys: trying " 276 "key type = %d-%d", mp->keylen, 277 mp->algtype); 278 valid_mech_cnt++; 279 if (!get_and_set_seckey(pamh, netname, mp->keylen, 280 mp->algtype, short_passp, uid, gid, 281 &get_seckey_cnt, &good_pw_cnt, &set_seckey_cnt, 282 flags, debug)) { 283 result = PAM_BUF_ERR; 284 goto out; 285 } 286 } 287 __nis_release_mechanisms(mechs); 288 /* fall through to AUTH_DES below */ 289 } else { 290 /* 291 * No usable mechs found in security congifuration file thus 292 * fallback to AUTH_DES compat. 293 */ 294 if (debug) 295 syslog(LOG_DEBUG, "pam_dhkeys: no valid mechs " 296 "found. Trying AUTH_DES."); 297 } 298 299 /* 300 * We always perform AUTH_DES for the benefit of services like NFS 301 * that may depend on the classic des 192bit key being set. 302 */ 303 if (!get_and_set_seckey(pamh, netname, AUTH_DES_KEYLEN, 304 AUTH_DES_ALGTYPE, short_passp, uid, gid, &get_seckey_cnt, 305 &good_pw_cnt, &set_seckey_cnt, flags, debug)) { 306 result = PAM_BUF_ERR; 307 goto out; 308 } 309 310 if (debug) { 311 syslog(LOG_DEBUG, "pam_dhkeys: mech key totals:\n"); 312 syslog(LOG_DEBUG, "pam_dhkeys: %d valid mechanism(s)", 313 valid_mech_cnt); 314 syslog(LOG_DEBUG, "pam_dhkeys: %d secret key(s) retrieved", 315 get_seckey_cnt); 316 syslog(LOG_DEBUG, "pam_dhkeys: %d passwd decrypt successes", 317 good_pw_cnt); 318 syslog(LOG_DEBUG, "pam_dhkeys: %d secret key(s) set", 319 set_seckey_cnt); 320 } 321 322 if (get_seckey_cnt == 0) { /* No credentials */ 323 result = PAM_IGNORE; 324 goto out; 325 } 326 327 if (good_pw_cnt == 0) { /* wrong password */ 328 result = PAM_AUTH_ERR; 329 goto out; 330 } 331 332 if (set_seckey_cnt == 0) { 333 result = PAM_SYSTEM_ERR; 334 goto out; 335 } 336 /* Credentials have been successfully established, return PAM_IGNORE */ 337 result = PAM_IGNORE; 338 out: 339 /* 340 * If we are authenticating we attempt to establish credentials 341 * where appropriate. Failure to do so is only an error if we 342 * definitely needed them. Thus always return PAM_IGNORE 343 * if we are authenticating and credentials were not needed. 344 */ 345 free(scratch); 346 347 (void) memset(short_pass, '\0', sizeof (short_pass)); 348 349 return (result); 350 } 351 352 /*ARGSUSED*/ 353 int 354 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) 355 { 356 return (PAM_IGNORE); 357 } 358 359 360 typedef struct argres { 361 uid_t uid; 362 int result; 363 } argres_t; 364 365 /* 366 * Revoke NFS DES credentials. 367 * NFS may not be installed so we need to deal with SIGSYS 368 * when we call _nfssys(); we thus call _nfssys() in a seperate thread that 369 * is created specifically for this call. The thread specific signalmask 370 * is set to ignore SIGSYS. After the call to _nfssys(), the thread 371 * ceases to exist. 372 */ 373 static void * 374 revoke_nfs_cred(void *ap) 375 { 376 struct nfs_revauth_args nra; 377 sigset_t isigset; 378 argres_t *argres = (argres_t *)ap; 379 380 nra.authtype = AUTH_DES; 381 nra.uid = argres->uid; 382 383 (void) sigemptyset(&isigset); 384 (void) sigaddset(&isigset, SIGSYS); 385 386 if (pthread_sigmask(SIG_BLOCK, &isigset, NULL) == 0) { 387 argres->result = _nfssys(NFS_REVAUTH, &nra); 388 if (argres->result < 0 && errno == ENOSYS) { 389 argres->result = 0; 390 } 391 } else { 392 argres->result = -1; 393 } 394 return (NULL); 395 } 396 397 static int 398 remove_key(pam_handle_t *pamh, int flags, int debug) 399 { 400 int result; 401 char *uname; 402 attrlist attr_pw[2]; 403 struct pam_repository *auth_rep = NULL; 404 pwu_repository_t *pwu_rep; 405 uid_t uid; 406 gid_t gid; 407 argres_t argres; 408 pthread_t tid; 409 410 (void) pam_get_item(pamh, PAM_USER, (void **)&uname); 411 if (uname == NULL || *uname == NULL) { 412 if (debug) 413 syslog(LOG_DEBUG, 414 "pam_dhkeys: user NULL or empty in remove_key()"); 415 return (PAM_USER_UNKNOWN); 416 } 417 418 if (strcmp(uname, "root") == 0) { 419 if ((flags & PAM_SILENT) == 0) { 420 char msg[3][PAM_MAX_MSG_SIZE]; 421 (void) snprintf(msg[0], sizeof (msg[0]), 422 dgettext(TEXT_DOMAIN, 423 "removing root credentials would" 424 " break the rpc services that")); 425 (void) snprintf(msg[1], sizeof (msg[1]), 426 dgettext(TEXT_DOMAIN, 427 "use secure rpc on this host!")); 428 (void) snprintf(msg[2], sizeof (msg[2]), 429 dgettext(TEXT_DOMAIN, 430 "root may use keylogout -f to do" 431 " this (at your own risk)!")); 432 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 3, 433 msg, NULL); 434 } 435 return (PAM_PERM_DENIED); 436 } 437 438 (void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&auth_rep); 439 if (auth_rep != NULL) { 440 if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL) 441 return (PAM_BUF_ERR); 442 pwu_rep->type = auth_rep->type; 443 pwu_rep->scope = auth_rep->scope; 444 pwu_rep->scope_len = auth_rep->scope_len; 445 } else 446 pwu_rep = PWU_DEFAULT_REP; 447 448 /* Retrieve user's uid/gid from the password repository */ 449 attr_pw[0].type = ATTR_UID; attr_pw[0].next = &attr_pw[1]; 450 attr_pw[1].type = ATTR_GID; attr_pw[1].next = NULL; 451 452 result = __get_authtoken_attr(uname, pwu_rep, attr_pw); 453 454 if (pwu_rep != PWU_DEFAULT_REP) 455 free(pwu_rep); 456 457 if (result == PWU_NOT_FOUND) 458 return (PAM_USER_UNKNOWN); 459 if (result == PWU_DENIED) 460 return (PAM_PERM_DENIED); 461 if (result != PWU_SUCCESS) 462 return (PAM_SYSTEM_ERR); 463 464 uid = (uid_t)attr_pw[0].data.val_i; 465 gid = (gid_t)attr_pw[1].data.val_i; 466 467 (void) key_removesecret_g_uid(uid, gid); 468 469 argres.uid = uid; 470 argres.result = -1; 471 472 if (pthread_create(&tid, NULL, revoke_nfs_cred, (void *)&argres) == 0) 473 (void) pthread_join(tid, NULL); 474 475 if (argres.result < 0) { 476 if ((flags & PAM_SILENT) == 0) { 477 (void) msg(pamh, dgettext(TEXT_DOMAIN, 478 "Warning: NFS credentials not destroyed")); 479 } 480 return (PAM_AUTH_ERR); 481 } 482 483 return (PAM_IGNORE); 484 } 485 486 int 487 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 488 { 489 int i; 490 int debug = 0; 491 int result; 492 char netname[MAXNETNAMELEN + 1]; 493 494 for (i = 0; i < argc; i++) { 495 if (strcmp(argv[i], "debug") == 0) 496 debug = 1; 497 else if (strcmp(argv[i], "nowarn") == 0) 498 flags |= PAM_SILENT; 499 } 500 501 /* Check for invalid flags */ 502 if (flags && (flags & PAM_ESTABLISH_CRED) == 0 && 503 (flags & PAM_REINITIALIZE_CRED) == 0 && 504 (flags & PAM_REFRESH_CRED) == 0 && 505 (flags & PAM_DELETE_CRED) == 0 && 506 (flags & PAM_SILENT) == 0) { 507 syslog(LOG_ERR, "pam_dhkeys: pam_setcred: illegal flags %d", 508 flags); 509 return (PAM_SYSTEM_ERR); 510 } 511 512 513 if ((flags & PAM_REINITIALIZE_CRED) || (flags & PAM_REFRESH_CRED)) { 514 /* doesn't apply to UNIX */ 515 if (debug) 516 syslog(LOG_DEBUG, "pam_dhkeys: cred reinit/refresh " 517 "ignored\n"); 518 return (PAM_IGNORE); 519 } 520 521 if (flags & PAM_DELETE_CRED) { 522 if (debug) 523 syslog(LOG_DEBUG, "pam_dhkeys: removing creds\n"); 524 result = remove_key(pamh, flags, debug); 525 } else { 526 result = establish_key(pamh, flags, debug, netname); 527 /* Some diagnostics */ 528 if ((flags & PAM_SILENT) == 0) { 529 if (result == PAM_AUTH_ERR) 530 (void) msg(pamh, dgettext(TEXT_DOMAIN, 531 "Password does not decrypt any secret " 532 "keys for %s."), netname); 533 else if (result == PAM_SYSTEM_ERR && netname[0]) 534 (void) msg(pamh, dgettext(TEXT_DOMAIN, 535 "Could not set secret key(s) for %s. " 536 "The key server may be down."), netname); 537 } 538 539 /* Not having credentials set is not an error... */ 540 result = PAM_IGNORE; 541 } 542 543 return (result); 544 } 545 546 /*ARGSUSED*/ 547 void 548 rpc_cleanup(pam_handle_t *pamh, void *data, int pam_status) 549 { 550 if (data) { 551 (void) memset(data, 0, strlen(data)); 552 free(data); 553 } 554 } 555 556 /*ARGSUSED*/ 557 int 558 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) 559 { 560 return (PAM_IGNORE); 561 } 562