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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Routines to handle getpw* calls in nscd 31 */ 32 33 #include <assert.h> 34 #include <errno.h> 35 #include <memory.h> 36 #include <signal.h> 37 #include <stdlib.h> 38 #include <stdio.h> 39 #include <string.h> 40 #include <sys/door.h> 41 #include <sys/stat.h> 42 #include <sys/time.h> 43 #include <sys/types.h> 44 #include <sys/wait.h> 45 #include <thread.h> 46 #include <unistd.h> 47 #include <nss_common.h> 48 #include <ucred.h> 49 50 #include "getxby_door.h" 51 #include "server_door.h" 52 53 #include "nscd.h" 54 55 static hash_t *uid_hash; 56 static hash_t *nam_hash; 57 static mutex_t passwd_lock = DEFAULTMUTEX; 58 static waiter_t passwd_wait; 59 60 static void getpw_invalidate_unlocked(void); 61 static void getpw_namekeepalive(int keep, int interval); 62 static void getpw_uidkeepalive(int keep, int interval); 63 static void update_pw_bucket(nsc_bucket_t **old, nsc_bucket_t *new, 64 int callnumber); 65 static nsc_bucket_t *fixbuffer(nsc_return_t *in, int maxlen); 66 static void do_findnams(nsc_bucket_t *ptr, int *table, char *name); 67 static void do_finduids(nsc_bucket_t *ptr, int *table, int uid); 68 static void do_invalidate(nsc_bucket_t **ptr, int callnumber); 69 70 void 71 getpw_init(void) 72 { 73 uid_hash = make_ihash(current_admin.passwd.nsc_suggestedsize); 74 nam_hash = make_hash(current_admin.passwd.nsc_suggestedsize); 75 } 76 77 static void 78 do_invalidate(nsc_bucket_t ** ptr, int callnumber) 79 { 80 if (*ptr != NULL && *ptr != (nsc_bucket_t *)-1) { 81 /* leave pending calls alone */ 82 update_pw_bucket(ptr, NULL, callnumber); 83 } 84 } 85 86 static void 87 do_finduids(nsc_bucket_t *ptr, int *table, int uid) 88 { 89 90 /* 91 * be careful with ptr - it may be -1 or NULL. 92 */ 93 if (ptr != NULL && ptr != (nsc_bucket_t *)-1) { 94 insertn(table, ptr->nsc_hits, uid); 95 } 96 } 97 98 static void 99 do_findnams(nsc_bucket_t *ptr, int *table, char *name) 100 { 101 102 /* 103 * be careful with ptr - it may be -1 or NULL. 104 */ 105 if (ptr != NULL && ptr != (nsc_bucket_t *)-1) { 106 char *tmp = (char *)insertn(table, ptr->nsc_hits, 107 (int)strdup(name)); 108 if (tmp != (char *)-1) 109 free(tmp); 110 } 111 } 112 113 114 115 void 116 getpw_revalidate(void) 117 { 118 for (;;) { 119 int slp; 120 int interval; 121 int count; 122 123 slp = current_admin.passwd.nsc_pos_ttl; 124 125 if (slp < 60) { 126 slp = 60; 127 } 128 129 if ((count = current_admin.passwd.nsc_keephot) != 0) { 130 interval = (slp / 2)/count; 131 if (interval == 0) interval = 1; 132 sleep(slp * 2 / 3); 133 getpw_uidkeepalive(count, interval); 134 getpw_namekeepalive(count, interval); 135 } else { 136 sleep(slp); 137 } 138 } 139 } 140 141 static void 142 getpw_uidkeepalive(int keep, int interval) 143 { 144 int *table; 145 nsc_data_t ping; 146 int i; 147 148 if (!keep) 149 return; 150 151 table = maken(keep); 152 mutex_lock(&passwd_lock); 153 operate_hash(uid_hash, do_finduids, (char *)table); 154 mutex_unlock(&passwd_lock); 155 156 for (i = 1; i <= keep; i++) { 157 ping.nsc_call.nsc_callnumber = GETPWUID; 158 if ((ping.nsc_call.nsc_u.uid = table[keep + 1 + i]) == -1) 159 continue; /* unused slot in table */ 160 launch_update(&ping.nsc_call); 161 sleep(interval); 162 } 163 free(table); 164 } 165 166 167 static void 168 getpw_namekeepalive(int keep, int interval) 169 { 170 int *table; 171 union { 172 nsc_data_t ping; 173 char space[sizeof (nsc_data_t) + NSCDMAXNAMELEN]; 174 } u; 175 176 int i; 177 178 if (!keep) 179 return; 180 181 table = maken(keep); 182 mutex_lock(&passwd_lock); 183 operate_hash(nam_hash, do_findnams, (char *)table); 184 mutex_unlock(&passwd_lock); 185 186 for (i = 1; i <= keep; i++) { 187 char *tmp; 188 u.ping.nsc_call.nsc_callnumber = GETPWNAM; 189 190 if ((tmp = (char *)table[keep + 1 + i]) == (char *)-1) 191 continue; /* unused slot in table */ 192 193 strcpy(u.ping.nsc_call.nsc_u.name, tmp); 194 195 launch_update(&u.ping.nsc_call); 196 sleep(interval); 197 } 198 199 for (i = 1; i <= keep; i++) { 200 char *tmp; 201 if ((tmp = (char *)table[keep + 1 + i]) != (char *)-1) 202 free(tmp); 203 } 204 205 free(table); 206 } 207 208 209 210 211 /* 212 * This routine marks all entries as invalid 213 * 214 */ 215 void 216 getpw_invalidate(void) 217 { 218 mutex_lock(&passwd_lock); 219 getpw_invalidate_unlocked(); 220 mutex_unlock(&passwd_lock); 221 } 222 223 static void 224 getpw_invalidate_unlocked(void) 225 { 226 operate_hash_addr(nam_hash, do_invalidate, (char *)GETPWNAM); 227 operate_hash_addr(uid_hash, do_invalidate, (char *)GETPWUID); 228 current_admin.passwd.nsc_invalidate_count++; 229 } 230 231 void 232 getpw_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in, time_t now) 233 { 234 int out_of_date; 235 nsc_bucket_t *retb; 236 char **bucket; 237 238 static time_t lastmod; 239 240 int bufferspace = maxsize - sizeof (nsc_return_t); 241 242 if (current_admin.passwd.nsc_enabled == 0) { 243 out->nsc_return_code = NOSERVER; 244 out->nsc_bufferbytesused = sizeof (*out); 245 return; 246 } 247 248 mutex_lock(&passwd_lock); 249 250 if (current_admin.passwd.nsc_check_files) { 251 struct stat buf; 252 253 if (stat("/etc/passwd", &buf) < 0) { 254 /*EMPTY*/; 255 } else if (lastmod == 0) { 256 lastmod = buf.st_mtime; 257 } else if (lastmod < buf.st_mtime) { 258 getpw_invalidate_unlocked(); 259 lastmod = buf.st_mtime; 260 } 261 } 262 263 if (current_admin.debug_level >= DBG_ALL) { 264 if (MASKUPDATEBIT(in->nsc_callnumber) == GETPWUID) { 265 logit("getpw_lookup: looking for uid %d\n", 266 in->nsc_u.uid); 267 } else { 268 logit("getpw_lookup: looking for name %s\n", 269 in->nsc_u.name); 270 } 271 } 272 273 for (;;) { 274 if (MASKUPDATEBIT(in->nsc_callnumber) == GETPWUID) { 275 bucket = get_hash(uid_hash, (char *)in->nsc_u.uid); 276 } else { /* make reasonableness check here */ 277 if (strlen(in->nsc_u.name) > NSCDMAXNAMELEN) { 278 ucred_t *uc = NULL; 279 280 if (door_ucred(&uc) != 0) { 281 logit("getpw_lookup: Name too long, " 282 "but no user credential: %s\n", 283 strerror(errno)); 284 } else { 285 286 logit("getpw_lookup: Name too long " 287 "from pid %d uid %d\n", 288 ucred_getpid(uc), 289 ucred_getruid(uc)); 290 ucred_free(uc); 291 } 292 293 294 out->nsc_errno = NSS_NOTFOUND; 295 out->nsc_return_code = NOTFOUND; 296 out->nsc_bufferbytesused = sizeof (*out); 297 goto getout; 298 } 299 bucket = get_hash(nam_hash, in->nsc_u.name); 300 } 301 302 if (*bucket == (char *)-1) { /* pending lookup */ 303 if (get_clearance(in->nsc_callnumber) != 0) { 304 /* no threads available */ 305 out->nsc_return_code = NOSERVER; 306 /* cannot process now */ 307 out->nsc_bufferbytesused = sizeof (*out); 308 current_admin.passwd.nsc_throttle_count++; 309 goto getout; 310 } 311 nscd_wait(&passwd_wait, &passwd_lock, bucket); 312 release_clearance(in->nsc_callnumber); 313 continue; /* go back and relookup hash bucket */ 314 } 315 break; 316 } 317 318 /* 319 * check for no name_service mode 320 */ 321 322 if (*bucket == NULL && current_admin.avoid_nameservice) { 323 out->nsc_return_code = NOTFOUND; 324 out->nsc_bufferbytesused = sizeof (*out); 325 } else if (*bucket == NULL || 326 (in->nsc_callnumber & UPDATEBIT) || 327 (out_of_date = (!current_admin.avoid_nameservice && 328 (current_admin.passwd.nsc_old_data_ok == 0) && 329 (((nsc_bucket_t *)*bucket)->nsc_timestamp < now)))) { 330 /* 331 * time has expired 332 */ 333 int saved_errno; 334 int saved_hits = 0; 335 struct passwd *p; 336 337 if (get_clearance(in->nsc_callnumber) != 0) { 338 /* no threads available */ 339 out->nsc_return_code = NOSERVER; 340 /* cannot process now */ 341 out->nsc_bufferbytesused = sizeof (*out); 342 current_admin.passwd.nsc_throttle_count++; 343 goto getout; 344 } 345 if (*bucket != NULL) { 346 saved_hits = ((nsc_bucket_t *)*bucket)->nsc_hits; 347 } 348 349 /* 350 * block any threads accessing this bucket if data 351 * is non-existent or out of date 352 */ 353 354 if (*bucket == NULL || out_of_date) { 355 update_pw_bucket((nsc_bucket_t **)bucket, 356 (nsc_bucket_t *)-1, 357 in->nsc_callnumber); 358 } else { 359 /* 360 * if still not -1 bucket we are doing 361 * update... mark to prevent pileups of threads if 362 * the name service is hanging.. 363 */ 364 ((nsc_bucket_t *)(*bucket))->nsc_status |= 365 ST_UPDATE_PENDING; 366 /* cleared by deletion of old data */ 367 } 368 mutex_unlock(&passwd_lock); 369 370 if (MASKUPDATEBIT(in->nsc_callnumber) == GETPWUID) { 371 p = _uncached_getpwuid_r(in->nsc_u.uid, &out->nsc_u.pwd, 372 out->nsc_u.buff+sizeof (struct passwd), 373 bufferspace); 374 saved_errno = errno; 375 } else { 376 p = _uncached_getpwnam_r(in->nsc_u.name, 377 &out->nsc_u.pwd, 378 out->nsc_u.buff+sizeof (struct passwd), 379 bufferspace); 380 saved_errno = errno; 381 } 382 383 mutex_lock(&passwd_lock); 384 385 release_clearance(in->nsc_callnumber); 386 387 if (p == NULL) { /* data not found */ 388 if (current_admin.debug_level >= DBG_CANT_FIND) { 389 if (MASKUPDATEBIT(in->nsc_callnumber) == 390 GETPWUID) { 391 logit("getpw_lookup: nscd COULDN'T FIND uid %d\n", 392 in->nsc_u.uid); 393 } else { 394 logit("getpw_lookup: nscd COULDN'T FIND passwd name %s\n", 395 in->nsc_u.name); 396 } 397 } 398 399 if (!(UPDATEBIT & in->nsc_callnumber)) 400 current_admin.passwd.nsc_neg_cache_misses++; 401 402 retb = (nsc_bucket_t *)malloc(sizeof (nsc_bucket_t)); 403 404 retb->nsc_refcount = 1; 405 retb->nsc_data.nsc_bufferbytesused = 406 sizeof (nsc_return_t); 407 retb->nsc_data.nsc_return_code = NOTFOUND; 408 retb->nsc_data.nsc_errno = saved_errno; 409 memcpy(out, &retb->nsc_data, 410 retb->nsc_data.nsc_bufferbytesused); 411 update_pw_bucket((nsc_bucket_t **)bucket, retb, 412 in->nsc_callnumber); 413 goto getout; 414 } else { 415 if (current_admin.debug_level >= DBG_ALL) { 416 if (MASKUPDATEBIT(in->nsc_callnumber) == 417 GETPWUID) { 418 logit("getpw_lookup: nscd FOUND uid %d\n", 419 in->nsc_u.uid); 420 } else { 421 logit("getpw_lookup: nscd FOUND passwd name %s\n", 422 in->nsc_u.name); 423 } 424 } 425 if (!(UPDATEBIT & in->nsc_callnumber)) 426 current_admin.passwd.nsc_pos_cache_misses++; 427 428 retb = fixbuffer(out, bufferspace); 429 update_pw_bucket((nsc_bucket_t **)bucket, 430 retb, in->nsc_callnumber); 431 if (saved_hits) 432 retb->nsc_hits = saved_hits; 433 } 434 } else { /* found entry in cache */ 435 retb = (nsc_bucket_t *)*bucket; 436 437 retb->nsc_hits++; 438 439 memcpy(out, &(retb->nsc_data), 440 retb->nsc_data.nsc_bufferbytesused); 441 442 if (out->nsc_return_code == SUCCESS) { 443 if (!(UPDATEBIT & in->nsc_callnumber)) 444 current_admin.passwd.nsc_pos_cache_hits++; 445 if (current_admin.debug_level >= DBG_ALL) { 446 if (MASKUPDATEBIT(in->nsc_callnumber) == 447 GETPWUID) { 448 logit("getpw_lookup: found uid %d in cache\n", 449 in->nsc_u.uid); 450 } else { 451 logit("getpw_lookup: found name %s in cache\n", 452 in->nsc_u.name); 453 } 454 } 455 } else { 456 if (!(UPDATEBIT & in->nsc_callnumber)) 457 current_admin.passwd.nsc_neg_cache_hits++; 458 if (current_admin.debug_level >= DBG_ALL) { 459 if (MASKUPDATEBIT(in->nsc_callnumber) == 460 GETPWUID) { 461 logit("getpw_lookup: %d marked as NOT FOUND in cache.\n", 462 in->nsc_u.uid); 463 } else { 464 logit("getpw_lookup: %s marked as NOT FOUND in cache.\n", 465 in->nsc_u.name); 466 } 467 } 468 } 469 470 if ((retb->nsc_timestamp < now) && 471 !(in->nsc_callnumber & UPDATEBIT) && 472 !(retb->nsc_status & ST_UPDATE_PENDING)) { 473 logit("launch update since time = %d\n", 474 retb->nsc_timestamp); 475 retb->nsc_status |= ST_UPDATE_PENDING; 476 /* cleared by deletion of old data */ 477 launch_update(in); 478 } 479 } 480 481 getout: 482 483 mutex_unlock(&passwd_lock); 484 485 /* 486 * secure mode check - blank out passwd if call sucessfull 487 * and caller != effective id 488 */ 489 if ((current_admin.passwd.nsc_secure_mode != 0) && 490 (out->nsc_return_code == SUCCESS) && 491 !(UPDATEBIT & in->nsc_callnumber)) { 492 493 ucred_t *uc = NULL; 494 495 if (door_ucred(&uc) != 0) { 496 perror("door_ucred"); 497 } else { 498 if (ucred_geteuid(uc) != out->nsc_u.pwd.pw_uid) { 499 /* 500 * write *NP* into passwd field if 501 * not already that way... we fixed 502 * the buffer code so there's always room. 503 */ 504 int len; 505 506 char *foo = out->nsc_u.buff 507 + sizeof (struct passwd) 508 + (int)out->nsc_u.pwd.pw_passwd; 509 510 len = strlen(foo); 511 if (len > 0 && 512 strcmp(foo, "*NP*") != 0 && 513 strcmp(foo, "x") != 0) { 514 if (len < 5) 515 len = 5; 516 strncpy(foo, "*NP*", len); 517 /* 518 * strncpy will 519 * blank all 520 */ 521 } 522 } 523 ucred_free(uc); 524 } 525 } 526 } 527 528 /*ARGSUSED*/ 529 static void 530 update_pw_bucket(nsc_bucket_t **old, nsc_bucket_t *new, int callnumber) 531 { 532 if (*old != NULL && *old != (nsc_bucket_t *)-1) { 533 /* old data exists */ 534 free(*old); 535 current_admin.passwd.nsc_entries--; 536 } 537 538 /* 539 * we can do this before reseting *old since we're holding the lock 540 */ 541 542 else if (*old == (nsc_bucket_t *)-1) { 543 nscd_signal(&passwd_wait, (char **)old); 544 } 545 546 547 548 *old = new; 549 550 if ((new != NULL) && 551 (new != (nsc_bucket_t *)-1)) { 552 /* real data, not just update pending or invalidate */ 553 554 new->nsc_hits = 1; 555 new->nsc_status = 0; 556 new->nsc_refcount = 1; 557 current_admin.passwd.nsc_entries++; 558 559 if (new->nsc_data.nsc_return_code == SUCCESS) { 560 new->nsc_timestamp = time(NULL) + 561 current_admin.passwd.nsc_pos_ttl; 562 } else { 563 new->nsc_timestamp = time(NULL) + 564 current_admin.passwd.nsc_neg_ttl; 565 } 566 } 567 } 568 569 570 /*ARGSUSED*/ 571 static nsc_bucket_t * 572 fixbuffer(nsc_return_t *in, int maxlen) 573 { 574 nsc_bucket_t *retb; 575 char *dest; 576 577 nsc_return_t *out; 578 int offset; 579 int strs; 580 int pwlen; 581 582 /* 583 * find out the size of the data block we're going to need 584 */ 585 586 strs = 0; 587 strs += 1 + strlen(in->nsc_u.pwd.pw_name); 588 pwlen = strlen(in->nsc_u.pwd.pw_passwd); 589 if (pwlen < 4) 590 pwlen = 4; 591 strs += 1 + pwlen; 592 strs += 1 + strlen(in->nsc_u.pwd.pw_age); 593 strs += 1 + strlen(in->nsc_u.pwd.pw_comment); 594 strs += 1 + strlen(in->nsc_u.pwd.pw_gecos); 595 strs += 1 + strlen(in->nsc_u.pwd.pw_dir); 596 strs += 1 + strlen(in->nsc_u.pwd.pw_shell); 597 598 599 /* 600 * allocate it and copy it in 601 * code doesn't assume packing order in original buffer 602 */ 603 604 if ((retb = (nsc_bucket_t *)malloc(sizeof (*retb) + strs)) == NULL) { 605 return (NULL); 606 } 607 608 out = &(retb->nsc_data); 609 610 611 612 out->nsc_bufferbytesused = sizeof (*in) + strs; 613 out->nsc_return_code = SUCCESS; 614 out->nsc_errno = 0; 615 616 out->nsc_u.pwd.pw_uid = in->nsc_u.pwd.pw_uid; 617 out->nsc_u.pwd.pw_gid = in->nsc_u.pwd.pw_gid; 618 619 dest = retb->nsc_data.nsc_u.buff + sizeof (struct passwd); 620 621 offset = (int)dest; 622 623 strcpy(dest, in->nsc_u.pwd.pw_name); 624 strs = 1 + strlen(in->nsc_u.pwd.pw_name); 625 out->nsc_u.pwd.pw_name = dest - offset; 626 dest += strs; 627 628 strcpy(dest, in->nsc_u.pwd.pw_passwd); 629 strs = 1 + pwlen; 630 out->nsc_u.pwd.pw_passwd = dest - offset; 631 dest += strs; 632 633 strcpy(dest, in->nsc_u.pwd.pw_age); 634 strs = 1 + strlen(in->nsc_u.pwd.pw_age); 635 out->nsc_u.pwd.pw_age = dest - offset; 636 dest += strs; 637 638 strcpy(dest, in->nsc_u.pwd.pw_comment); 639 strs = 1 + strlen(in->nsc_u.pwd.pw_comment); 640 out->nsc_u.pwd.pw_comment = dest - offset; 641 dest += strs; 642 643 strcpy(dest, in->nsc_u.pwd.pw_gecos); 644 strs = 1 + strlen(in->nsc_u.pwd.pw_gecos); 645 out->nsc_u.pwd.pw_gecos = dest - offset; 646 dest += strs; 647 648 strcpy(dest, in->nsc_u.pwd.pw_dir); 649 strs = 1 + strlen(in->nsc_u.pwd.pw_dir); 650 out->nsc_u.pwd.pw_dir = dest - offset; 651 dest += strs; 652 653 strcpy(dest, in->nsc_u.pwd.pw_shell); 654 out->nsc_u.pwd.pw_shell = dest - offset; 655 656 memcpy(in, out, retb->nsc_data.nsc_bufferbytesused); 657 658 659 return (retb); 660 661 } 662 663 void 664 getpw_uid_reaper() 665 { 666 nsc_reaper("getpw_uid", uid_hash, ¤t_admin.passwd, &passwd_lock); 667 } 668 669 void 670 getpw_nam_reaper() 671 { 672 nsc_reaper("getpw_nam", nam_hash, ¤t_admin.passwd, &passwd_lock); 673 } 674