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 /* 29 * Simple doors ldap cache daemon 30 */ 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <signal.h> 35 #include <door.h> 36 #include <time.h> 37 #include <string.h> 38 #include <strings.h> 39 #include <libintl.h> 40 #include <sys/stat.h> 41 #include <sys/time.h> 42 #include <sys/wait.h> 43 #include <stdlib.h> 44 #include <errno.h> 45 #include <pthread.h> 46 #include <thread.h> 47 #include <stdarg.h> 48 #include <fcntl.h> 49 #include <assert.h> 50 #include <unistd.h> 51 #include <memory.h> 52 #include <sys/types.h> 53 #include <syslog.h> 54 #include <locale.h> /* LC_ALL */ 55 56 #include <alloca.h> 57 #include <ucontext.h> 58 59 #include "cachemgr.h" 60 61 static void detachfromtty(); 62 admin_t current_admin; 63 static int will_become_server; 64 65 static void switcher(void *cookie, char *argp, size_t arg_size, 66 door_desc_t *dp, uint_t n_desc); 67 static void usage(char *s); 68 static int cachemgr_set_lf(admin_t *ptr, char *logfile); 69 static int client_getadmin(admin_t *ptr); 70 static int setadmin(ldap_call_t *ptr); 71 static int client_setadmin(admin_t *ptr); 72 static int client_showstats(admin_t *ptr); 73 74 #ifdef SLP 75 int use_slp = 0; 76 static unsigned int refresh = 10800; /* dynamic discovery interval */ 77 #endif /* SLP */ 78 79 static ldap_stat_t * 80 getcacheptr(char *s) 81 { 82 static const char *caches[1] = {"ldap"}; 83 84 if (strncmp(caches[0], s, strlen(caches[0])) == 0) 85 return (¤t_admin.ldap_stat); 86 87 return (NULL); 88 } 89 90 char * 91 getcacheopt(char *s) 92 { 93 while (*s && *s != ',') 94 s++; 95 return ((*s == ',') ? (s + 1) : NULL); 96 } 97 98 /* 99 * This is here to prevent the ldap_cachemgr becomes 100 * daemonlized to early to soon during boot time. 101 * This causes problems during boot when automounter 102 * and others try to use libsldap before ldap_cachemgr 103 * finishes walking the server list. 104 */ 105 static void 106 sig_ok_to_exit(int signo) 107 { 108 if (signo == SIGUSR1) { 109 logit("sig_ok_to_exit(): parent exiting...\n"); 110 exit(0); 111 } else { 112 logit("sig_ok_to_exit(): invalid signal(%d) received.\n", 113 signo); 114 syslog(LOG_ERR, gettext("ldap_cachemgr: " 115 "invalid signal(%d) received."), signo); 116 exit(1); 117 } 118 } 119 #define LDAP_TABLES 1 /* ldap */ 120 #define TABLE_THREADS 10 121 #define COMMON_THREADS 20 122 #define CACHE_MISS_THREADS (COMMON_THREADS + LDAP_TABLES * TABLE_THREADS) 123 #define CACHE_HIT_THREADS 20 124 #define MAX_SERVER_THREADS (CACHE_HIT_THREADS + CACHE_MISS_THREADS) 125 126 static sema_t common_sema; 127 static sema_t ldap_sema; 128 static thread_key_t lookup_state_key; 129 130 static void 131 initialize_lookup_clearance() 132 { 133 (void) thr_keycreate(&lookup_state_key, NULL); 134 (void) sema_init(&common_sema, COMMON_THREADS, USYNC_THREAD, 0); 135 (void) sema_init(&ldap_sema, TABLE_THREADS, USYNC_THREAD, 0); 136 } 137 138 int 139 get_clearance(int callnumber) 140 { 141 sema_t *table_sema = NULL; 142 char *tab; 143 144 if (sema_trywait(&common_sema) == 0) { 145 (void) thr_setspecific(lookup_state_key, NULL); 146 return (0); 147 } 148 149 switch (callnumber) { 150 case GETLDAPCONFIG: 151 tab = "ldap"; 152 table_sema = &ldap_sema; 153 break; 154 default: 155 logit("Internal Error: get_clearance\n"); 156 break; 157 } 158 159 if (sema_trywait(table_sema) == 0) { 160 (void) thr_setspecific(lookup_state_key, (void*)1); 161 return (0); 162 } 163 164 if (current_admin.debug_level >= DBG_CANT_FIND) { 165 logit("get_clearance: throttling load for %s table\n", tab); 166 } 167 168 return (-1); 169 } 170 171 int 172 release_clearance(int callnumber) 173 { 174 int which; 175 sema_t *table_sema = NULL; 176 177 (void) thr_getspecific(lookup_state_key, (void**)&which); 178 if (which == 0) /* from common pool */ { 179 (void) sema_post(&common_sema); 180 return (0); 181 } 182 183 switch (callnumber) { 184 case GETLDAPCONFIG: 185 table_sema = &ldap_sema; 186 break; 187 default: 188 logit("Internal Error: release_clearance\n"); 189 break; 190 } 191 (void) sema_post(table_sema); 192 193 return (0); 194 } 195 196 197 static mutex_t create_lock; 198 static int num_servers = 0; 199 static thread_key_t server_key; 200 201 202 /* 203 * Bind a TSD value to a server thread. This enables the destructor to 204 * be called if/when this thread exits. This would be a programming error, 205 * but better safe than sorry. 206 */ 207 208 /*ARGSUSED*/ 209 static void * 210 server_tsd_bind(void *arg) 211 { 212 static void *value = 0; 213 214 /* 215 * disable cancellation to prevent hangs when server 216 * threads disappear 217 */ 218 219 (void) thr_setspecific(server_key, value); 220 (void) door_return(NULL, 0, NULL, 0); 221 222 return (value); 223 } 224 225 /* 226 * Server threads are created here. 227 */ 228 229 /*ARGSUSED*/ 230 static void 231 server_create(door_info_t *dip) 232 { 233 (void) mutex_lock(&create_lock); 234 if (++num_servers > MAX_SERVER_THREADS) { 235 num_servers--; 236 (void) mutex_unlock(&create_lock); 237 return; 238 } 239 (void) mutex_unlock(&create_lock); 240 (void) thr_create(NULL, 0, server_tsd_bind, NULL, 241 THR_BOUND|THR_DETACHED, NULL); 242 } 243 244 /* 245 * Server thread are destroyed here 246 */ 247 248 /*ARGSUSED*/ 249 static void 250 server_destroy(void *arg) 251 { 252 (void) mutex_lock(&create_lock); 253 num_servers--; 254 (void) mutex_unlock(&create_lock); 255 } 256 257 int 258 main(int argc, char ** argv) 259 { 260 int did; 261 int opt; 262 int errflg = 0; 263 int showstats = 0; 264 int doset = 0; 265 int dofg = 0; 266 struct stat buf; 267 sigset_t myset; 268 struct sigaction sighupaction; 269 static void client_killserver(); 270 int debug_level = 0; 271 272 /* setup for localization */ 273 (void) setlocale(LC_ALL, ""); 274 (void) textdomain(TEXT_DOMAIN); 275 276 openlog("ldap_cachemgr", LOG_PID, LOG_DAEMON); 277 278 if (chdir(NSLDAPDIRECTORY) < 0) { 279 (void) fprintf(stderr, gettext("chdir(\"%s\") failed: %s\n"), 280 NSLDAPDIRECTORY, strerror(errno)); 281 exit(1); 282 } 283 284 /* 285 * Correctly set file mode creation mask, so to make the new files 286 * created for door calls being readable by all. 287 */ 288 (void) umask(0); 289 290 /* 291 * Special case non-root user here - he/she/they/it can just print 292 * stats 293 */ 294 295 if (geteuid()) { 296 if (argc != 2 || strcmp(argv[1], "-g")) { 297 (void) fprintf(stderr, 298 gettext("Must be root to use any option " 299 "other than -g.\n\n")); 300 usage(argv[0]); 301 } 302 303 if ((__ns_ldap_cache_ping() != SUCCESS) || 304 (client_getadmin(¤t_admin) != 0)) { 305 (void) fprintf(stderr, 306 gettext("%s doesn't appear to be running.\n"), 307 argv[0]); 308 exit(1); 309 } 310 (void) client_showstats(¤t_admin); 311 exit(0); 312 } 313 314 315 316 /* 317 * Determine if there is already a daemon running 318 */ 319 320 will_become_server = (__ns_ldap_cache_ping() != SUCCESS); 321 322 /* 323 * load normal config file 324 */ 325 326 if (will_become_server) { 327 static const ldap_stat_t defaults = { 328 0, /* stat */ 329 DEFAULTTTL}; /* ttl */ 330 331 current_admin.ldap_stat = defaults; 332 (void) strcpy(current_admin.logfile, LOGFILE); 333 } else { 334 if (client_getadmin(¤t_admin)) { 335 (void) fprintf(stderr, gettext("Cannot contact %s " 336 "properly(?)\n"), argv[0]); 337 exit(1); 338 } 339 } 340 341 #ifndef SLP 342 while ((opt = getopt(argc, argv, "fKgl:r:d:")) != EOF) { 343 #else 344 while ((opt = getopt(argc, argv, "fKgs:l:r:d:")) != EOF) { 345 #endif /* SLP */ 346 ldap_stat_t *cache; 347 348 switch (opt) { 349 case 'K': 350 client_killserver(); 351 exit(0); 352 break; 353 case 'g': 354 showstats++; 355 break; 356 case 'f': 357 dofg++; 358 break; 359 case 'r': 360 doset++; 361 cache = getcacheptr("ldap"); 362 if (!optarg) { 363 errflg++; 364 break; 365 } 366 cache->ldap_ttl = atoi(optarg); 367 break; 368 case 'l': 369 doset++; 370 (void) strlcpy(current_admin.logfile, 371 optarg, sizeof (current_admin.logfile)); 372 break; 373 case 'd': 374 doset++; 375 debug_level = atoi(optarg); 376 break; 377 #ifdef SLP 378 case 's': /* undocumented: use dynamic (SLP) config */ 379 use_slp = 1; 380 break; 381 #endif /* SLP */ 382 default: 383 errflg++; 384 break; 385 } 386 } 387 388 if (errflg) 389 usage(argv[0]); 390 391 /* 392 * will not show statistics if no daemon running 393 */ 394 if (will_become_server && showstats) { 395 (void) fprintf(stderr, 396 gettext("%s doesn't appear to be running.\n"), 397 argv[0]); 398 exit(1); 399 } 400 401 if (!will_become_server) { 402 if (showstats) { 403 (void) client_showstats(¤t_admin); 404 } 405 if (doset) { 406 current_admin.debug_level = debug_level; 407 if (client_setadmin(¤t_admin) < 0) { 408 (void) fprintf(stderr, 409 gettext("Error during admin call\n")); 410 exit(1); 411 } 412 } 413 if (!showstats && !doset) { 414 (void) fprintf(stderr, 415 gettext("%s already running....use '%s " 416 "-K' to stop\n"), argv[0], argv[0]); 417 } 418 exit(0); 419 } 420 421 /* 422 * daemon from here on 423 */ 424 425 if (debug_level) { 426 /* 427 * we're debugging... 428 */ 429 if (strlen(current_admin.logfile) == 0) 430 /* 431 * no specified log file 432 */ 433 (void) strcpy(current_admin.logfile, LOGFILE); 434 else 435 (void) cachemgr_set_lf(¤t_admin, 436 current_admin.logfile); 437 /* 438 * validate the range of debug level number 439 * and set the number to current_admin.debug_level 440 */ 441 if (cachemgr_set_dl(¤t_admin, debug_level) < 0) { 442 /* 443 * print error messages to the screen 444 * cachemgr_set_dl prints msgs to cachemgr.log 445 * only 446 */ 447 (void) fprintf(stderr, 448 gettext("Incorrect Debug Level: %d\n" 449 "It should be between %d and %d\n"), 450 debug_level, DBG_OFF, MAXDEBUG); 451 exit(-1); 452 } 453 } else { 454 if (strlen(current_admin.logfile) == 0) 455 (void) strcpy(current_admin.logfile, "/dev/null"); 456 (void) cachemgr_set_lf(¤t_admin, 457 current_admin.logfile); 458 } 459 460 if (dofg == 0) 461 detachfromtty(argv[0]); 462 463 /* 464 * perform some initialization 465 */ 466 467 initialize_lookup_clearance(); 468 469 if (getldap_init() != 0) 470 exit(-1); 471 472 /* 473 * Establish our own server thread pool 474 */ 475 476 (void) door_server_create(server_create); 477 if (thr_keycreate(&server_key, server_destroy) != 0) { 478 logit("thr_keycreate() call failed\n"); 479 syslog(LOG_ERR, 480 gettext("ldap_cachemgr: thr_keycreate() call failed")); 481 perror("thr_keycreate"); 482 exit(-1); 483 } 484 485 /* 486 * Create a door 487 */ 488 489 if ((did = door_create(switcher, LDAP_CACHE_DOOR_COOKIE, 490 DOOR_UNREF | DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) < 0) { 491 logit("door_create() call failed\n"); 492 syslog(LOG_ERR, gettext( 493 "ldap_cachemgr: door_create() call failed")); 494 perror("door_create"); 495 exit(-1); 496 } 497 498 /* 499 * bind to file system 500 */ 501 502 if (stat(LDAP_CACHE_DOOR, &buf) < 0) { 503 int newfd; 504 505 if ((newfd = creat(LDAP_CACHE_DOOR, 0444)) < 0) { 506 logit("Cannot create %s:%s\n", 507 LDAP_CACHE_DOOR, 508 strerror(errno)); 509 exit(1); 510 } 511 (void) close(newfd); 512 } 513 514 if (fattach(did, LDAP_CACHE_DOOR) < 0) { 515 if ((errno != EBUSY) || 516 (fdetach(LDAP_CACHE_DOOR) < 0) || 517 (fattach(did, LDAP_CACHE_DOOR) < 0)) { 518 logit("fattach() call failed\n"); 519 syslog(LOG_ERR, gettext( 520 "ldap_cachemgr: fattach() call failed")); 521 perror("fattach"); 522 exit(2); 523 } 524 } 525 526 /* catch SIGHUP revalid signals */ 527 sighupaction.sa_handler = getldap_revalidate; 528 sighupaction.sa_flags = 0; 529 (void) sigemptyset(&sighupaction.sa_mask); 530 (void) sigemptyset(&myset); 531 (void) sigaddset(&myset, SIGHUP); 532 533 if (sigaction(SIGHUP, &sighupaction, NULL) < 0) { 534 logit("sigaction() call failed\n"); 535 syslog(LOG_ERR, 536 gettext("ldap_cachemgr: sigaction() call failed")); 537 perror("sigaction"); 538 exit(1); 539 } 540 541 if (thr_sigsetmask(SIG_BLOCK, &myset, NULL) < 0) { 542 logit("thr_sigsetmask() call failed\n"); 543 syslog(LOG_ERR, 544 gettext("ldap_cachemgr: thr_sigsetmask() call failed")); 545 perror("thr_sigsetmask"); 546 exit(1); 547 } 548 549 /* 550 * kick off revalidate threads only if ttl != 0 551 */ 552 553 if (thr_create(NULL, NULL, (void *(*)(void*))getldap_refresh, 554 0, 0, NULL) != 0) { 555 logit("thr_create() call failed\n"); 556 syslog(LOG_ERR, 557 gettext("ldap_cachemgr: thr_create() call failed")); 558 perror("thr_create"); 559 exit(1); 560 } 561 562 /* 563 * kick off the thread which refreshes the server info 564 */ 565 566 if (thr_create(NULL, NULL, (void *(*)(void*))getldap_serverInfo_refresh, 567 0, 0, NULL) != 0) { 568 logit("thr_create() call failed\n"); 569 syslog(LOG_ERR, 570 gettext("ldap_cachemgr: thr_create() call failed")); 571 perror("thr_create"); 572 exit(1); 573 } 574 575 #ifdef SLP 576 if (use_slp) { 577 /* kick off SLP discovery thread */ 578 if (thr_create(NULL, NULL, (void *(*)(void *))discover, 579 (void *)&refresh, 0, NULL) != 0) { 580 logit("thr_create() call failed\n"); 581 syslog(LOG_ERR, gettext("ldap_cachemgr: thr_create() " 582 "call failed")); 583 perror("thr_create"); 584 exit(1); 585 } 586 } 587 #endif /* SLP */ 588 589 if (thr_sigsetmask(SIG_UNBLOCK, &myset, NULL) < 0) { 590 logit("thr_sigsetmask() call failed\n"); 591 syslog(LOG_ERR, 592 gettext("ldap_cachemgr: the_sigsetmask() call failed")); 593 perror("thr_sigsetmask"); 594 exit(1); 595 } 596 597 /*CONSTCOND*/ 598 while (1) { 599 (void) pause(); 600 } 601 /* NOTREACHED */ 602 /*LINTED E_FUNC_HAS_NO_RETURN_STMT*/ 603 } 604 605 606 /* 607 * Before calling the alloca() function we have to be sure that we won't get 608 * beyond the stack. Since we don't know the precise layout of the stack, 609 * the address of an automatic of the function gives us a rough idea, plus/minus 610 * a bit. We also need a bit more of stackspace after the call to be able 611 * to call further functions. Even something as simple as making a system call 612 * from within this function can take ~100 Bytes of stackspace. 613 */ 614 #define SAFETY_BUFFER 32 * 1024 /* 32KB */ 615 616 static 617 size_t 618 get_data_size(LineBuf *config_info, int *err_code) 619 { 620 size_t configSize = sizeof (ldap_return_t); 621 dataunion *buf = NULL; /* For the 'sizeof' purpose */ 622 623 if (config_info->str != NULL && 624 config_info->len >= sizeof (buf->data.ldap_ret.ldap_u.config)) { 625 configSize = sizeof (buf->space) + 626 config_info->len - 627 sizeof (buf->data.ldap_ret.ldap_u.config); 628 629 if (!stack_inbounds((char *)&buf - 630 (configSize + SAFETY_BUFFER))) { 631 /* 632 * We do not have enough space on the stack 633 * to accomodate the whole DUAProfile 634 */ 635 logit("The DUAProfile is too big. There is not enough " 636 "space to process it. Ignoring it.\n"); 637 syslog(LOG_ERR, gettext("ldap_cachemgr: The DUAProfile " 638 "is too big. There is not enough space " 639 "to process it. Ignoring it.")); 640 641 *err_code = SERVERERROR; 642 643 free(config_info->str); 644 config_info->str = NULL; 645 config_info->len = 0; 646 configSize = sizeof (ldap_return_t); 647 } 648 } 649 650 return (configSize); 651 } 652 653 /*ARGSUSED*/ 654 static void 655 switcher(void *cookie, char *argp, size_t arg_size, 656 door_desc_t *dp, uint_t n_desc) 657 { 658 #define GETSIZE 1000 659 #define ALLOCATE 1001 660 #define PREPARE 1002 661 662 ldap_call_t *ptr = (ldap_call_t *)argp; 663 door_cred_t dc; 664 665 LineBuf configInfo; 666 dataunion *buf = NULL; 667 /* 668 * By default the size of a buffer to be passed down to a client 669 * is equal to the size of the ldap_return_t structure. We need 670 * a bigger buffer in a few cases. 671 */ 672 size_t configSize = sizeof (ldap_return_t); 673 int ldapErrno = 0, state, leave = 0; 674 struct { 675 void *begin; 676 size_t size; 677 uint8_t destroy; 678 } dataSource; 679 680 if (argp == DOOR_UNREF_DATA) { 681 logit("Door Slam... invalid door param\n"); 682 syslog(LOG_ERR, gettext("ldap_cachemgr: Door Slam... " 683 "invalid door param")); 684 (void) printf(gettext("Door Slam... invalid door param\n")); 685 exit(0); 686 } 687 688 if (ptr == NULL) { /* empty door call */ 689 (void) door_return(NULL, 0, 0, 0); /* return the favor */ 690 } 691 692 bzero(&dataSource, sizeof (dataSource)); 693 694 /* 695 * We presume that sizeof (ldap_return_t) bytes are always available 696 * on the stack 697 */ 698 state = ptr->ldap_callnumber; 699 700 /* 701 * The common behavior of the state machine below is as follows: 702 * 703 * Each incoming request is processed in several steps. 704 * 705 * First stage is specific for a particular request. It can be 706 * an error check or gathering data or empty. See the actual comments 707 * for the requests. For the GETLDAPCONFIG, GETLDAPSERVER, GETCACHESTAT, 708 * and GETCACHE there is an additional substage calculating the size of 709 * the data being passed to a door client. 710 * The next step is obligatory. It allocates a buffer which will be 711 * passed down to the door_return() routine. 712 * The last (also obligatory) step sets the return code and, if a data 713 * is available for the transfer and no errors have occurred, copies 714 * the data to the buffer. 715 * 716 * After the state machine has finished, the door_return() function 717 * is called unconditionally 718 */ 719 while (!leave) { 720 switch (state) { 721 case NULLCALL: 722 /* 723 * Just a 'ping'. Use the default size 724 * of the buffer and set the 725 * 'OK' error code. 726 */ 727 state = ALLOCATE; 728 break; 729 case GETLDAPCONFIG: 730 /* 731 * Get the current LDAP configuration. 732 * Since this is dynamic data and its size can exceed 733 * the size of ldap_return_t, the next step will 734 * calculate who much space exactly is required. 735 */ 736 getldap_lookup(&configInfo, ptr); 737 738 state = GETSIZE; 739 break; 740 case GETLDAPSERVER: 741 /* 742 * Get the root DSE for a next server in the list. 743 * Since this is dynamic data and its size can exceed 744 * the size of ldap_return_t, the next step will 745 * calculate who much space exactly is required. 746 */ 747 getldap_getserver(&configInfo, ptr); 748 749 state = GETSIZE; 750 break; 751 case GETCACHESTAT: 752 /* 753 * Get the cache stattistics. 754 * Since this is dynamic data and its size can exceed 755 * the size of ldap_return_t, the next step will 756 * calculate how much space exactly is required. 757 */ 758 getldap_get_cacheStat(&configInfo); 759 760 state = GETSIZE; 761 break; 762 case GETADMIN: 763 /* 764 * Get current configuration and statistics. 765 * The size of the statistics structure is less then 766 * sizeof (ldap_return_t). So specify the source 767 * where to take the info and proceed with the memory 768 * allocation. 769 */ 770 state = ALLOCATE; 771 772 if (ldapErrno == 0) { 773 dataSource.begin = ¤t_admin; 774 dataSource.size = sizeof (current_admin); 775 dataSource.destroy = 0; 776 } 777 778 break; 779 case SETADMIN: 780 case KILLSERVER: 781 /* 782 * Process the request and proceed with the default 783 * buffer allocation. 784 */ 785 if (door_cred(&dc) == 0) { 786 switch (ptr->ldap_callnumber) { 787 case KILLSERVER: 788 logit("ldap_cachemgr received " 789 "KILLSERVER cmd from pid %ld, " 790 "uid %ld, euid %ld\n", 791 dc.dc_pid, dc.dc_ruid, dc.dc_euid); 792 exit(0); 793 break; 794 case SETADMIN: 795 if (dc.dc_euid != 0) { 796 logit("SETADMIN call failed " 797 "(cred): " 798 "caller pid %ld, uid %ld, " 799 "euid %ld\n", 800 dc.dc_pid, 801 dc.dc_ruid, 802 dc.dc_euid); 803 ldapErrno = -1; 804 break; 805 } 806 /* Yes, if a client's effective uid */ 807 /* is noty defined, continue */ 808 /* with setadmin() */ 809 default: 810 ldapErrno = setadmin(ptr); 811 break; 812 } 813 } else { 814 logit("door_cred() call failed\n"); 815 syslog(LOG_ERR, gettext("ldap_cachemgr: " 816 "door_cred() call failed")); 817 perror("door_cred"); 818 ldapErrno = -1; 819 } 820 821 state = ALLOCATE; 822 break; 823 case GETCACHE: 824 /* 825 * Get the cache stattistics. 826 * Since this is dynamic data and its size can exceed 827 * the size of ldap_return_t, the next step will 828 * calculate how much space exactly is required. 829 */ 830 getldap_get_cacheData(&configInfo, ptr); 831 832 state = GETSIZE; 833 break; 834 case SETCACHE: 835 /* 836 * Process the request and proceed with the default 837 * buffer allocation. 838 */ 839 ldapErrno = getldap_set_cacheData(ptr); 840 841 current_admin.ldap_stat.ldap_numbercalls++; 842 843 state = ALLOCATE; 844 break; 845 default: 846 /* 847 * This means an unknown request type. Proceed with 848 * the default buffer allocation. 849 */ 850 logit("Unknown ldap service door call op %d\n", 851 ptr->ldap_callnumber); 852 ldapErrno = -99; 853 854 state = ALLOCATE; 855 break; 856 case GETSIZE: 857 /* 858 * This stage calculates how much data will be 859 * passed down to the client, checks if there is 860 * enough space on the stack to accommodate the data, 861 * increases the value of the configSize variable 862 * if necessary and specifies the data source. 863 * In case of any error occurred ldapErrno will be set 864 * appropriately. 865 */ 866 if (configInfo.str == NULL) { 867 ldapErrno = -1; 868 } 869 870 configSize = get_data_size(&configInfo, &ldapErrno); 871 872 if (ldapErrno == 0) { 873 dataSource.begin = configInfo.str; 874 dataSource.size = configInfo.len; 875 dataSource.destroy = 1; 876 } 877 878 current_admin.ldap_stat.ldap_numbercalls++; 879 880 state = ALLOCATE; 881 break; 882 case ALLOCATE: 883 /* 884 * Allocate a buffer of the calculated (or default) size 885 * and proceed with populating it with data. 886 */ 887 buf = (dataunion *) alloca(configSize); 888 889 state = PREPARE; 890 break; 891 case PREPARE: 892 /* 893 * Set a return code and, if a data source is specified, 894 * copy data from the source to the buffer. 895 */ 896 buf->data.ldap_ret.ldap_errno = ldapErrno; 897 buf->data.ldap_ret.ldap_return_code = ldapErrno; 898 buf->data.ldap_ret.ldap_bufferbytesused = configSize; 899 900 if (dataSource.begin != NULL) { 901 (void) memcpy(buf->data.ldap_ret.ldap_u.config, 902 dataSource.begin, 903 dataSource.size); 904 if (dataSource.destroy) { 905 free(dataSource.begin); 906 } 907 } 908 909 /* 910 * Leave the state machine and send the data 911 * to the client. 912 */ 913 leave = 1; 914 break; 915 } 916 } 917 918 (void) door_return((char *)&buf->data, 919 buf->data.ldap_ret.ldap_bufferbytesused, 920 NULL, 921 0); 922 #undef GETSIZE 923 #undef ALLOCATE 924 #undef PREPARE 925 } 926 927 static void 928 usage(char *s) 929 { 930 (void) fprintf(stderr, 931 gettext("Usage: %s [-d debug_level] [-l logfilename]\n"), s); 932 (void) fprintf(stderr, gettext(" [-K] " 933 "[-r revalidate_interval] ")); 934 #ifndef SLP 935 (void) fprintf(stderr, gettext(" [-g]\n")); 936 #else 937 (void) fprintf(stderr, gettext(" [-g] [-s]\n")); 938 #endif /* SLP */ 939 exit(1); 940 } 941 942 943 static int logfd = -1; 944 945 static int 946 cachemgr_set_lf(admin_t *ptr, char *logfile) 947 { 948 int newlogfd; 949 950 /* 951 * we don't really want to try and open the log file 952 * /dev/null since that will fail w/ our security fixes 953 */ 954 955 if (logfile == NULL || *logfile == 0) { 956 /*EMPTY*/; 957 } else if (strcmp(logfile, "/dev/null") == 0) { 958 (void) strcpy(current_admin.logfile, "/dev/null"); 959 (void) close(logfd); 960 logfd = -1; 961 } else { 962 if ((newlogfd = 963 open(logfile, O_EXCL|O_WRONLY|O_CREAT, 0644)) < 0) { 964 /* 965 * File already exists... now we need to get cute 966 * since opening a file in a world-writeable directory 967 * safely is hard = it could be a hard link or a 968 * symbolic link to a system file. 969 * 970 */ 971 struct stat before; 972 973 if (lstat(logfile, &before) < 0) { 974 logit("Cannot open new logfile \"%s\": %sn", 975 logfile, strerror(errno)); 976 return (-1); 977 } 978 if (S_ISREG(before.st_mode) && /* no symbolic links */ 979 (before.st_nlink == 1) && /* no hard links */ 980 (before.st_uid == 0)) { /* owned by root */ 981 if ((newlogfd = 982 open(logfile, 983 O_APPEND|O_WRONLY, 0644)) < 0) { 984 logit("Cannot open new logfile " 985 "\"%s\": %s\n", 986 logfile, strerror(errno)); 987 return (-1); 988 } 989 } else { 990 logit("Cannot use specified logfile " 991 "\"%s\": file is/has links or isn't " 992 "owned by root\n", logfile); 993 return (-1); 994 } 995 } 996 (void) strlcpy(ptr->logfile, logfile, sizeof (ptr->logfile)); 997 (void) close(logfd); 998 logfd = newlogfd; 999 logit("Starting ldap_cachemgr, logfile %s\n", logfile); 1000 } 1001 return (0); 1002 } 1003 1004 /*PRINTFLIKE1*/ 1005 void 1006 logit(char *format, ...) 1007 { 1008 static mutex_t loglock; 1009 struct timeval tv; 1010 char buffer[BUFSIZ]; 1011 va_list ap; 1012 1013 va_start(ap, format); 1014 1015 if (logfd >= 0) { 1016 int safechars; 1017 1018 (void) gettimeofday(&tv, NULL); 1019 (void) ctime_r(&tv.tv_sec, buffer, BUFSIZ); 1020 (void) snprintf(buffer+19, BUFSIZE, ".%.4ld ", 1021 tv.tv_usec/100); 1022 safechars = sizeof (buffer) - 30; 1023 if (vsnprintf(buffer+25, safechars, format, ap) > safechars) 1024 (void) strcat(buffer, "...\n"); 1025 (void) mutex_lock(&loglock); 1026 (void) write(logfd, buffer, strlen(buffer)); 1027 (void) mutex_unlock(&loglock); 1028 } 1029 va_end(ap); 1030 } 1031 1032 1033 static int 1034 client_getadmin(admin_t *ptr) 1035 { 1036 dataunion u; 1037 ldap_data_t *dptr; 1038 int ndata; 1039 int adata; 1040 1041 u.data.ldap_call.ldap_callnumber = GETADMIN; 1042 ndata = sizeof (u); 1043 adata = sizeof (u.data); 1044 dptr = &u.data; 1045 1046 if (__ns_ldap_trydoorcall(&dptr, &ndata, &adata) != SUCCESS) { 1047 return (-1); 1048 } 1049 (void) memcpy(ptr, dptr->ldap_ret.ldap_u.buff, sizeof (*ptr)); 1050 1051 return (0); 1052 } 1053 1054 1055 static int 1056 setadmin(ldap_call_t *ptr) 1057 { 1058 admin_t *new; 1059 1060 new = (admin_t *)ptr->ldap_u.domainname; 1061 1062 /* 1063 * global admin stuff 1064 */ 1065 1066 if ((cachemgr_set_lf(¤t_admin, new->logfile) < 0) || 1067 cachemgr_set_dl(¤t_admin, new->debug_level) < 0) { 1068 return (-1); 1069 } 1070 1071 if (cachemgr_set_ttl(¤t_admin.ldap_stat, 1072 "ldap", 1073 new->ldap_stat.ldap_ttl) < 0) { 1074 return (-1); 1075 } 1076 1077 return (0); 1078 } 1079 1080 1081 static void 1082 client_killserver() 1083 { 1084 dataunion u; 1085 ldap_data_t *dptr; 1086 int ndata; 1087 int adata; 1088 1089 u.data.ldap_call.ldap_callnumber = KILLSERVER; 1090 ndata = sizeof (u); 1091 adata = sizeof (ldap_call_t); 1092 dptr = &u.data; 1093 1094 __ns_ldap_trydoorcall(&dptr, &ndata, &adata); 1095 } 1096 1097 1098 static int 1099 client_setadmin(admin_t *ptr) 1100 { 1101 dataunion u; 1102 ldap_data_t *dptr; 1103 int ndata; 1104 int adata; 1105 1106 u.data.ldap_call.ldap_callnumber = SETADMIN; 1107 (void) memcpy(u.data.ldap_call.ldap_u.domainname, ptr, sizeof (*ptr)); 1108 ndata = sizeof (u); 1109 adata = sizeof (*ptr); 1110 dptr = &u.data; 1111 1112 if (__ns_ldap_trydoorcall(&dptr, &ndata, &adata) != SUCCESS) { 1113 return (-1); 1114 } 1115 1116 return (0); 1117 } 1118 1119 static int 1120 client_showstats(admin_t *ptr) 1121 { 1122 dataunion u; 1123 ldap_data_t *dptr; 1124 int ndata; 1125 int adata; 1126 char *rbuf, *sptr, *rest; 1127 1128 /* 1129 * print admin data 1130 */ 1131 (void) printf(gettext("\ncachemgr configuration:\n")); 1132 (void) printf(gettext("server debug level %10d\n"), ptr->debug_level); 1133 (void) printf(gettext("server log file\t\"%s\"\n"), ptr->logfile); 1134 (void) printf(gettext("number of calls to ldapcachemgr %10d\n"), 1135 ptr->ldap_stat.ldap_numbercalls); 1136 1137 /* 1138 * get cache data statistics 1139 */ 1140 u.data.ldap_call.ldap_callnumber = GETCACHESTAT; 1141 ndata = sizeof (u); 1142 adata = sizeof (ldap_call_t); 1143 dptr = &u.data; 1144 1145 if (__ns_ldap_trydoorcall(&dptr, &ndata, &adata) != SUCCESS) { 1146 (void) printf( 1147 gettext("\nCache data statistics not available!\n")); 1148 return (0); 1149 } 1150 1151 /* 1152 * print cache data statistics line by line 1153 */ 1154 (void) printf(gettext("\ncachemgr cache data statistics:\n")); 1155 rbuf = dptr->ldap_ret.ldap_u.buff; 1156 sptr = strtok_r(rbuf, DOORLINESEP, &rest); 1157 for (;;) { 1158 (void) printf("%s\n", sptr); 1159 sptr = strtok_r(NULL, DOORLINESEP, &rest); 1160 if (sptr == NULL) 1161 break; 1162 } 1163 return (0); 1164 } 1165 1166 1167 /* 1168 * detach from tty 1169 */ 1170 static void 1171 detachfromtty(char *pgm) 1172 { 1173 int status; 1174 pid_t pid, wret; 1175 1176 (void) close(0); 1177 (void) close(1); 1178 /* 1179 * Block the SIGUSR1 signal 1180 * just in case that the child 1181 * process may run faster than 1182 * the parent process and 1183 * send this signal before 1184 * the signal handler is ready 1185 * in the parent process. 1186 * This error will cause the parent 1187 * to exit with the User Signal 1 1188 * exit code (144). 1189 */ 1190 (void) sighold(SIGUSR1); 1191 pid = fork1(); 1192 switch (pid) { 1193 case (pid_t)-1: 1194 logit("detachfromtty(): fork1() call failed\n"); 1195 (void) fprintf(stderr, 1196 gettext("%s: fork1() call failed.\n"), 1197 pgm); 1198 syslog(LOG_ERR, 1199 gettext("ldap_cachemgr: fork1() call failed.")); 1200 exit(1); 1201 break; 1202 case 0: 1203 /* 1204 * child process does not 1205 * need to worry about 1206 * the SIGUSR1 signal 1207 */ 1208 (void) sigrelse(SIGUSR1); 1209 (void) close(2); 1210 break; 1211 default: 1212 /* 1213 * Wait forever until the child process 1214 * has exited, or has signalled that at 1215 * least one server in the server list 1216 * is up. 1217 */ 1218 if (signal(SIGUSR1, sig_ok_to_exit) == SIG_ERR) { 1219 logit("detachfromtty(): " 1220 "can't set up signal handler to " 1221 " catch SIGUSR1.\n"); 1222 (void) fprintf(stderr, 1223 gettext("%s: signal() call failed.\n"), 1224 pgm); 1225 syslog(LOG_ERR, gettext("ldap_cachemgr: " 1226 "can't set up signal handler to " 1227 " catch SIGUSR1.")); 1228 exit(1); 1229 } 1230 1231 /* 1232 * now unblock the SIGUSR1 signal 1233 * to handle the pending or 1234 * soon to arrive SIGUSR1 signal 1235 */ 1236 (void) sigrelse(SIGUSR1); 1237 wret = waitpid(pid, &status, 0); 1238 1239 if (wret == -1) { 1240 logit("detachfromtty(): " 1241 "waitpid() call failed\n"); 1242 (void) fprintf(stderr, 1243 gettext("%s: waitpid() call failed.\n"), 1244 pgm); 1245 syslog(LOG_ERR, 1246 gettext("ldap_cachemgr: waitpid() " 1247 "call failed.")); 1248 exit(1); 1249 } 1250 if (wret != pid) { 1251 logit("detachfromtty(): " 1252 "waitpid() returned %ld when " 1253 "child pid was %ld\n", 1254 wret, pid); 1255 (void) fprintf(stderr, 1256 gettext( 1257 "%s: waitpid() returned %ld when " 1258 "child pid was %ld.\n"), 1259 pgm, wret, pid); 1260 syslog(LOG_ERR, 1261 gettext("ldap_cachemgr: waitpid() " 1262 "returned different " 1263 "child pid.")); 1264 exit(1); 1265 } 1266 1267 /* evaluate return status */ 1268 if (WIFEXITED(status)) { 1269 if (WEXITSTATUS(status) == 0) { 1270 exit(0); 1271 } 1272 logit("detachfromtty(): " 1273 "child failed (rc = %d).\n", 1274 WEXITSTATUS(status)); 1275 (void) fprintf(stderr, 1276 gettext("%s: failed. Please see " 1277 "syslog for details.\n"), 1278 pgm); 1279 syslog(LOG_ERR, 1280 gettext("ldap_cachemgr: failed " 1281 "(rc = %d)."), 1282 WEXITSTATUS(status)); 1283 } else if (WIFSIGNALED(status)) { 1284 logit("detachfromtty(): " 1285 "child terminated by signal %d.\n", 1286 WTERMSIG(status)); 1287 (void) fprintf(stderr, 1288 gettext("%s: terminated by signal %d.\n"), 1289 pgm, WTERMSIG(status)); 1290 syslog(LOG_ERR, 1291 gettext("ldap_cachemgr: terminated by " 1292 "signal %d.\n"), 1293 WTERMSIG(status)); 1294 } else if (WCOREDUMP(status)) { 1295 logit("detachfromtty(): child core dumped.\n"), 1296 (void) fprintf(stderr, 1297 gettext("%s: core dumped.\n"), 1298 pgm); 1299 syslog(LOG_ERR, 1300 gettext("ldap_cachemgr: " 1301 "core dumped.\n")); 1302 } 1303 1304 exit(1); 1305 } 1306 (void) setsid(); 1307 if (open("/dev/null", O_RDWR, 0) != -1) { 1308 (void) dup(0); 1309 (void) dup(0); 1310 } 1311 } 1312