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 * Copyright 2018 Joyent, Inc. 26 */ 27 28 #include <strings.h> 29 #include <stdlib.h> 30 #include <syslog.h> 31 #include <errno.h> 32 #include <libintl.h> 33 #include <door.h> 34 #include <sys/types.h> 35 #include <sys/stat.h> 36 #include <fcntl.h> 37 #include <procfs.h> 38 #include <pthread.h> 39 #include "cachemgr.h" 40 41 extern admin_t current_admin; 42 43 #define CLEANUP_WAIT_TIME 60 44 45 typedef enum cleanup_type { 46 CLEANUP_ALL = 1, 47 CLEANUP_BY_PID = 2 48 } cleanup_type_t; 49 50 typedef struct cleanup_op { 51 pid_t pid; 52 cleanup_type_t type; 53 } cleanup_op_t; 54 55 typedef struct main_nscd_struct { 56 pid_t pid; /* main nscd pid */ 57 thread_t tid; /* main nscd tid */ 58 int in_progress; /* A main nscd thread is */ 59 /* waiting for change or */ 60 /* copying data */ 61 int is_waiting_cleanup; /* A main nscd thread is */ 62 /* waiting for another main */ 63 /* nscd thread to be cleaned */ 64 /* up */ 65 } main_nscd_t; 66 67 static chg_info_t chg = { DEFAULTMUTEX, DEFAULTCV, 0, NULL, NULL, NULL, 0 }; 68 69 static main_nscd_t chg_main_nscd = {0, 0, 0, 0}; 70 static mutex_t chg_main_nscd_lock = DEFAULTMUTEX; 71 static cond_t chg_main_nscd_cv = DEFAULTCV; 72 73 /* 74 * The cookie of the configuration and its mutex 75 */ 76 static ldap_get_chg_cookie_t config_cookie = {0, 0}; 77 static mutex_t config_cookie_lock = DEFAULTMUTEX; 78 79 static void cleanup_thread_by_pid(pid_t pid); 80 81 ldap_get_chg_cookie_t 82 chg_config_cookie_get(void) 83 { 84 ldap_get_chg_cookie_t cookie; 85 (void) mutex_lock(&config_cookie_lock); 86 cookie = config_cookie; 87 (void) mutex_unlock(&config_cookie_lock); 88 return (cookie); 89 } 90 91 static void 92 chg_config_cookie_increment_seq_num(void) 93 { 94 (void) mutex_lock(&config_cookie_lock); 95 config_cookie.seq_num++; 96 (void) mutex_unlock(&config_cookie_lock); 97 } 98 99 void 100 chg_config_cookie_set(ldap_get_chg_cookie_t *cookie) 101 { 102 (void) mutex_lock(&config_cookie_lock); 103 config_cookie.mgr_pid = cookie->mgr_pid; 104 config_cookie.seq_num = cookie->seq_num; 105 (void) mutex_unlock(&config_cookie_lock); 106 } 107 static boolean_t 108 chg_cookie_equal(ldap_get_chg_cookie_t *c1, ldap_get_chg_cookie_t *c2) 109 { 110 if (c1->mgr_pid == c2->mgr_pid && c1->seq_num == c2->seq_num) 111 return (B_TRUE); 112 else 113 return (B_FALSE); 114 } 115 /* 116 * Create a node in the list and output the node. The caller can NOT free it. 117 */ 118 static int 119 waiting_list_add(chg_info_t *info, pid_t pid, thread_t tid, 120 waiting_list_t **wlp) 121 { 122 123 waiting_list_t *wl; 124 125 *wlp = NULL; 126 127 if ((wl = (waiting_list_t *)calloc(1, sizeof (waiting_list_t))) 128 == NULL) { 129 logit("waiting_list_add: No memory. pid %ld tid %d\n", 130 pid, tid); 131 return (CHG_NO_MEMORY); 132 } 133 134 wl->pid = pid; 135 wl->tid = tid; 136 137 if (info->chg_w_first == NULL) { 138 info->chg_w_first = wl; 139 info->chg_w_last = wl; 140 } else { 141 info->chg_w_last->next = wl; 142 wl->prev = info->chg_w_last; 143 info->chg_w_last = wl; 144 } 145 *wlp = wl; 146 return (CHG_SUCCESS); 147 } 148 149 /* 150 * Find a node with matching tid in the list and remove it from the list. 151 */ 152 static int 153 waiting_list_delete(chg_info_t *info, thread_t tid) 154 { 155 waiting_list_t *wl; 156 157 for (wl = info->chg_w_first; wl != NULL; wl = wl->next) { 158 if (wl->tid == tid) { 159 if (wl->next == NULL) { 160 if (wl->prev == NULL) { 161 info->chg_w_first = NULL; 162 info->chg_w_last = NULL; 163 } else { 164 wl->prev->next = NULL; 165 info->chg_w_last = wl->prev; 166 } 167 } else { 168 if (wl->prev == NULL) { 169 wl->next->prev = NULL; 170 info->chg_w_first = wl->next; 171 } else { 172 wl->prev->next = wl->next; 173 wl->next->prev = wl->prev; 174 } 175 } 176 free(wl); 177 return (CHG_SUCCESS); 178 } 179 } 180 return (CHG_NOT_FOUND_IN_WAITING_LIST); 181 } 182 183 /* 184 * Delete the thread from the waiting list and remove data when the list 185 * is empty. 186 */ 187 static void 188 waiting_list_cleanup(chg_info_t *chg, thread_t tid) 189 { 190 int rc; 191 192 rc = waiting_list_delete(chg, tid); 193 194 if (rc == CHG_SUCCESS && chg->chg_w_first == NULL) { 195 free(chg->chg_data); 196 chg->chg_data = NULL; 197 chg->chg_wakeup = 0; 198 } 199 } 200 201 /* 202 * Set flag by pid so it can be cleaned up. 203 */ 204 static void 205 waiting_list_set_cleanup(chg_info_t *info, pid_t pid) 206 { 207 waiting_list_t *wl; 208 209 for (wl = info->chg_w_first; wl != NULL; wl = wl->next) { 210 if (wl->pid == pid) { 211 wl->cleanup = 1; 212 break; 213 } 214 } 215 } 216 217 /* 218 * Return: 1 - door client is dead, 0 - door client is alive 219 */ 220 static int 221 door_client_dead(void) 222 { 223 ucred_t *uc = NULL; 224 int rc; 225 226 if (door_ucred(&uc) == -1 && errno == EINVAL) { 227 rc = 1; 228 } else { 229 rc = 0; 230 } 231 if (uc) 232 ucred_free(uc); 233 234 return (rc); 235 } 236 237 /* 238 * This function handles GETSTATUSCHANGE call from main nscd. 239 * The call can be a START op or STOP op. A cookie is sent from main nscd too. 240 * The static global variable main_nscd keeps record of pid, tid and some flags. 241 * If the thread is door_return(), main_nscd.pid, main_nscd.tid are set to 0. 242 * When the call is START op, it checks if main_nscd.pid is 0. If it is, it 243 * proceeds to wait for the change notification. If it's not, which means 244 * another main nscd handling thread is still around. It sends broadcast to 245 * clean up that thread and wait until the cleanup is done then proceeds to 246 * wait for the change notification. If same main nscd sends START op 247 * repeatedly, it'll be rejected. 248 * It also checks the cookie from main nscd. If it's not the same as 249 * ldap_cachemgr's cookie, door returns config change. 250 * If the door call is STOP op, it creates a thread to clean up main nscd START 251 * thread so it won't be blocking. 252 * In waiting for the change notification phase, the thread is waken up by 253 * the notification threads or by the cleanup threads. 254 * If it's a notification, it copies data to the stack then door return. 255 * If it's a cleanup, door_client_dead() is called to verify it then 256 * door return. 257 */ 258 int 259 chg_get_statusChange(LineBuf *info, ldap_call_t *in, pid_t nscd_pid) 260 { 261 int rc = CHG_SUCCESS, another_main_nscd_thread_alive = 0; 262 int len, return_now; 263 thread_t this_tid = thr_self(); 264 waiting_list_t *wl = NULL; 265 ldap_get_change_out_t *cout; 266 ldap_get_chg_cookie_t cookie; 267 268 info->str = NULL; 269 info->len = 0; 270 271 if (in->ldap_u.get_change.op == NS_STATUS_CHANGE_OP_START) { 272 273 (void) mutex_lock(&chg_main_nscd_lock); 274 if (chg_main_nscd.pid != 0) { 275 if (nscd_pid != chg_main_nscd.pid) { 276 /* 277 * This is the case that nscd doesn't shut down 278 * properly(e.g. core) and STOP op is not sent, 279 * the thread handling it is still around and 280 * not cleaned up yet. 281 * Test if the thread is still alive. 282 * If it is, clean it up. 283 * For thr_kill, if sig is 0, a validity check 284 * is done for the existence of the target 285 * thread; no signal is sent. 286 */ 287 if (thr_kill(chg_main_nscd.tid, 0) == 0) { 288 another_main_nscd_thread_alive = 1; 289 cleanup_thread_by_pid( 290 chg_main_nscd.pid); 291 } 292 } else if (chg_main_nscd.in_progress || 293 chg_main_nscd.is_waiting_cleanup) { 294 /* 295 * Same nscd pid can only send door call 296 * one at a time and wait for ldap_cachemgr to 297 * return change data. If it's the same pid 298 * again, it's an nscd error. 299 */ 300 (void) mutex_unlock(&chg_main_nscd_lock); 301 return (CHG_NSCD_REPEATED_CALL); 302 } 303 } 304 /* 305 * Wait for another thread to be cleaned up if it's alive. 306 * After that this cond var is waken up. 307 */ 308 if (another_main_nscd_thread_alive) { 309 while (chg_main_nscd.in_progress) { 310 chg_main_nscd.is_waiting_cleanup = 1; 311 (void) cond_wait(&chg_main_nscd_cv, 312 &chg_main_nscd_lock); 313 } 314 } 315 316 /* 317 * Replace pid and tid and set the flag. 318 */ 319 chg_main_nscd.is_waiting_cleanup = 0; 320 chg_main_nscd.pid = nscd_pid; 321 chg_main_nscd.tid = this_tid; 322 chg_main_nscd.in_progress = 1; 323 (void) mutex_unlock(&chg_main_nscd_lock); 324 325 cookie = chg_config_cookie_get(); 326 327 if (!chg_cookie_equal(&cookie, &in->ldap_u.get_change.cookie)) { 328 /* 329 * different cookie, set new cookie and 330 * return door call right away 331 */ 332 len = sizeof (ldap_get_change_out_t); 333 if ((cout = calloc(1, len)) == NULL) { 334 rc = CHG_NO_MEMORY; 335 } else { 336 cout->type = NS_STATUS_CHANGE_TYPE_CONFIG; 337 cout->cookie = cookie; 338 info->str = (char *)cout; 339 info->len = len; 340 } 341 342 } else { 343 (void) mutex_lock(&chg.chg_lock); 344 345 /* wait for the change notification */ 346 rc = waiting_list_add(&chg, nscd_pid, this_tid, &wl); 347 if (rc == CHG_SUCCESS) { 348 return_now = 0; 349 while (!chg.chg_wakeup) { 350 if (wl->cleanup || 351 door_client_dead()) { 352 return_now = 1; 353 break; 354 } 355 (void) cond_wait(&chg.chg_cv, 356 &chg.chg_lock); 357 } 358 /* Check if door client is still alive again */ 359 if (!return_now && !wl->cleanup && 360 !door_client_dead()) { 361 /* copy data to buffer */ 362 if ((info->str = malloc( 363 chg.chg_data_size)) == NULL) { 364 rc = CHG_NO_MEMORY; 365 } else { 366 (void) memcpy(info->str, 367 chg.chg_data, 368 chg.chg_data_size); 369 info->len = chg.chg_data_size; 370 } 371 } 372 waiting_list_cleanup(&chg, this_tid); 373 } 374 (void) mutex_unlock(&chg.chg_lock); 375 } 376 377 378 /* 379 * Reset pid, tid and flag, send wakeup signal. 380 */ 381 (void) mutex_lock(&chg_main_nscd_lock); 382 chg_main_nscd.pid = 0; 383 chg_main_nscd.tid = 0; 384 chg_main_nscd.in_progress = 0; 385 if (chg_main_nscd.is_waiting_cleanup) 386 (void) cond_broadcast(&chg_main_nscd_cv); 387 388 (void) mutex_unlock(&chg_main_nscd_lock); 389 390 } else if (in->ldap_u.get_change.op == NS_STATUS_CHANGE_OP_STOP) { 391 392 cleanup_thread_by_pid(nscd_pid); 393 rc = CHG_SUCCESS; 394 395 } else { 396 rc = CHG_INVALID_PARAM; 397 } 398 if (rc == CHG_EXCEED_MAX_THREADS) 399 cleanup_thread_by_pid(0); 400 401 return (rc); 402 } 403 404 /* 405 * This function copies the header and data stream to the buffer 406 * then send broadcast to wake up the chg_get_statusChange() threads. 407 */ 408 int 409 chg_notify_statusChange(char *str) 410 { 411 ldap_get_change_out_t *cout = (ldap_get_change_out_t *)str; 412 413 cout->cookie = chg_config_cookie_get(); 414 415 (void) mutex_lock(&chg.chg_lock); 416 if (chg.chg_w_first != NULL && chg.chg_wakeup == 0) { 417 418 if (chg.chg_data) { 419 free(chg.chg_data); 420 chg.chg_data = NULL; 421 } 422 423 chg.chg_data = str; 424 425 if (cout->type == NS_STATUS_CHANGE_TYPE_CONFIG) 426 chg.chg_data_size = sizeof (ldap_get_change_out_t); 427 else 428 /* NS_STATUS_CHANGE_TYPE_SERVER */ 429 chg.chg_data_size = sizeof (ldap_get_change_out_t) - 430 sizeof (int) + cout->data_size; 431 432 chg.chg_wakeup = 1; 433 (void) cond_broadcast(&chg.chg_cv); 434 } 435 (void) mutex_unlock(&chg.chg_lock); 436 437 return (CHG_SUCCESS); 438 } 439 440 /* 441 * This is called when the configuration is refreshed. 442 * The new configuration is different from the current one, a notification 443 * is sent tochg_get_statusChange() threads. 444 */ 445 void 446 chg_test_config_change(ns_config_t *new, int *change_status) 447 { 448 int changed = 0; 449 LineBuf new_cfg, cur_cfg; 450 ns_ldap_error_t *errp = NULL; 451 ldap_config_out_t *new_out, *cur_out; 452 ldap_get_change_out_t *cout; 453 454 (void) memset(&new_cfg, 0, sizeof (LineBuf)); 455 (void) memset(&cur_cfg, 0, sizeof (LineBuf)); 456 /* 457 * Flatten the config data of the newly downloaded config and 458 * current default config and compare both. 459 */ 460 if ((errp = __ns_ldap_LoadDoorInfo(&new_cfg, NULL, new, 0)) != NULL) { 461 __ns_ldap_freeError(&errp); 462 /* error, assume the config is changed */ 463 changed = 1; 464 } else if ((errp = __ns_ldap_LoadDoorInfo(&cur_cfg, NULL, NULL, 0)) 465 != NULL) { 466 __ns_ldap_freeError(&errp); 467 /* error, assume the config is changed */ 468 changed = 1; 469 } 470 if (changed == 0) { 471 new_out = (ldap_config_out_t *)new_cfg.str; 472 cur_out = (ldap_config_out_t *)cur_cfg.str; 473 if (strcmp(new_out->config_str, cur_out->config_str) != 0) { 474 changed = 1; 475 if (current_admin.debug_level >= DBG_PROFILE_REFRESH) { 476 logit("config changed.\n"); 477 } 478 } 479 } 480 if (cur_cfg.str) 481 free(cur_cfg.str); 482 if (new_cfg.str) 483 free(new_cfg.str); 484 485 if (changed) { 486 487 if ((cout = calloc(1, sizeof (ldap_get_change_out_t))) 488 == NULL) { 489 logit("chg_test_config_change: No Memory\n"); 490 } else { 491 /* 492 * Replace the currentdefault config with the new 493 * config 494 */ 495 __s_api_init_config(new); 496 chg_config_cookie_increment_seq_num(); 497 cout->type = NS_STATUS_CHANGE_TYPE_CONFIG; 498 /* 499 * cout->cookie is set by 500 * chg_notify_statusChange 501 */ 502 (void) chg_notify_statusChange((char *)cout); 503 } 504 } else { 505 __s_api_destroy_config(new); 506 } 507 508 *change_status = changed; 509 } 510 511 /* 512 * Wake up chg_get_statusChange() threads to clean up the threads 513 * that main nscd doesn't exist on the other of door anymore or 514 * the thread is marked as cleanup. 515 */ 516 static void 517 cleanup_threads(chg_info_t *chg, pid_t pid, cleanup_type_t type) 518 { 519 (void) mutex_lock(&chg->chg_lock); 520 if (type == CLEANUP_BY_PID) 521 waiting_list_set_cleanup(chg, pid); 522 /* 523 * wake up threads without setting chg.chg_wakeup. 524 * It's for cleanup purpose, not for notifying changes. 525 */ 526 (void) cond_broadcast(&chg->chg_cv); 527 (void) mutex_unlock(&chg->chg_lock); 528 } 529 /* 530 * If arg is NULL, it loops forever, 531 * else it calls cleanup_threads once and exits. 532 */ 533 void * 534 chg_cleanup_waiting_threads(void *arg) 535 { 536 cleanup_op_t *op = (cleanup_op_t *)arg; 537 cleanup_type_t type = 0; 538 pid_t pid; 539 int always = 1, waiting; 540 541 (void) pthread_setname_np(pthread_self(), "chg_cleanup_thr"); 542 543 if (op == NULL) { 544 waiting = 1; 545 type = CLEANUP_ALL; 546 pid = 0; 547 } else { 548 waiting = 0; 549 type = op->type; 550 pid = op->pid; 551 } 552 553 while (always) { 554 if (waiting) 555 (void) sleep(CLEANUP_WAIT_TIME); 556 cleanup_threads(&chg, pid, type); 557 if (!waiting) 558 break; 559 } 560 561 if (op) 562 free(op); 563 564 thr_exit(NULL); 565 return (NULL); 566 } 567 /* 568 * The door server thead which has the door client pid will be marked 569 * as to be clean up. If pid is 0, no marking and just clean up all. 570 */ 571 static void 572 cleanup_thread_by_pid(pid_t pid) 573 { 574 cleanup_op_t *op; 575 576 if ((op = malloc(sizeof (cleanup_op_t))) == NULL) 577 return; 578 579 op->pid = pid; 580 /* clean up all if pid is 0 */ 581 if (pid == 0) 582 op->type = CLEANUP_ALL; 583 else 584 op->type = CLEANUP_BY_PID; 585 586 if (thr_create(NULL, 0, chg_cleanup_waiting_threads, 587 (void *)op, THR_BOUND|THR_DETACHED, NULL) != 0) { 588 free(op); 589 logit("thr_create failed for cleanup_thread_by_pid(%ld)\n", 590 pid); 591 } 592 } 593 594 /* 595 * Output a psinfo of an nscd process with process id pid 596 * Return: 0 - Can't find the process or it's not nscd 597 * 1 - psinfo found 598 * Note: If info is NULL, returns 0 or 1 only and no output from info. 599 */ 600 static int 601 get_nscd_psinfo(pid_t pid, psinfo_t *info) 602 { 603 psinfo_t pinfo; 604 char fname[MAXPATHLEN]; 605 ssize_t ret; 606 int fd; 607 608 if (snprintf(fname, MAXPATHLEN, "/proc/%d/psinfo", pid) > 0) { 609 if ((fd = open(fname, O_RDONLY)) >= 0) { 610 ret = read(fd, &pinfo, sizeof (psinfo_t)); 611 (void) close(fd); 612 if ((ret == sizeof (psinfo_t)) && 613 (strcmp(pinfo.pr_fname, "nscd") == 0)) { 614 if (info) 615 *info = pinfo; 616 return (1); 617 } 618 } 619 } 620 return (0); 621 } 622 /* 623 * If the parent process is nscd and euid is 0, it's a peruser nscd. 624 */ 625 static int 626 is_peruser_nscd(pid_t pid) 627 { 628 pid_t ppid; 629 psinfo_t pinfo; 630 631 if (get_nscd_psinfo(pid, &pinfo)) { 632 ppid = pinfo.pr_ppid; 633 if (get_nscd_psinfo(ppid, &pinfo) && pinfo.pr_euid == 0) 634 /* 635 * get psinfo of parent forker nscd 636 */ 637 return (1); 638 else 639 return (0); 640 } else { 641 return (0); 642 } 643 } 644 /* 645 * Check if the door client making door call is a nscd or peruser nscd and 646 * output door client's pid. 647 */ 648 int 649 chg_is_called_from_nscd_or_peruser_nscd(char *dc_str, pid_t *pidp) 650 { 651 int rc; 652 uid_t euid; 653 pid_t pid; 654 ucred_t *uc = NULL; 655 656 if (door_ucred(&uc) != 0) { 657 rc = errno; 658 logit("door_ucred() call failed %s\n", strerror(rc)); 659 return (0); 660 } 661 euid = ucred_geteuid(uc); 662 pid = *pidp = ucred_getpid(uc); 663 664 if ((euid == 0 && is_called_from_nscd(pid)) || 665 is_peruser_nscd(pid)) { 666 if (current_admin.debug_level >= DBG_ALL) 667 logit("ldap_cachemgr received %s call from pid %ld, " 668 "uid %u, euid %u\n", dc_str, pid, 669 ucred_getruid(uc), euid); 670 rc = 1; 671 } else { 672 if (current_admin.debug_level >= DBG_CANT_FIND) 673 logit("%s call failed(cred): caller pid %ld, uid %u, " 674 "euid %u\n", dc_str, pid, 675 ucred_getruid(uc), euid); 676 677 rc = 0; 678 } 679 680 ucred_free(uc); 681 682 return (rc); 683 } 684