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  * Copyright 2012 Milan Jurik. All rights reserved.
  25  * Copyright 2018 Joyent, Inc.
  26  */
  27 
  28 #include <stdlib.h>
  29 #include <alloca.h>
  30 #include <signal.h>
  31 #include <sys/stat.h>
  32 #include <unistd.h>
  33 #include <pthread.h>
  34 #include <time.h>
  35 #include <errno.h>
  36 #include <door.h>
  37 #include <zone.h>
  38 #include <resolv.h>
  39 #include <sys/socket.h>
  40 #include <net/route.h>
  41 #include <string.h>
  42 #include <net/if.h>
  43 #include <sys/stat.h>
  44 #include <fcntl.h>
  45 #include "nscd_common.h"
  46 #include "nscd_door.h"
  47 #include "nscd_config.h"
  48 #include "nscd_switch.h"
  49 #include "nscd_log.h"
  50 #include "nscd_selfcred.h"
  51 #include "nscd_frontend.h"
  52 #include "nscd_admin.h"
  53 
  54 static void rts_mon(void);
  55 static void keep_open_dns_socket(void);
  56 
  57 extern nsc_ctx_t *cache_ctx_p[];
  58 
  59 /*
  60  * Current active Configuration data for the frontend component
  61  */
  62 static nscd_cfg_global_frontend_t       frontend_cfg_g;
  63 static nscd_cfg_frontend_t              *frontend_cfg;
  64 
  65 static int      max_servers = 0;
  66 static int      max_servers_set = 0;
  67 static int      per_user_is_on = 1;
  68 
  69 static char     *main_execname;
  70 static char     **main_argv;
  71 extern int      _whoami;
  72 extern long     activity;
  73 extern mutex_t  activity_lock;
  74 
  75 static sema_t   common_sema;
  76 
  77 static thread_key_t     lookup_state_key;
  78 static mutex_t          create_lock = DEFAULTMUTEX;
  79 static int              num_servers = 0;
  80 static thread_key_t     server_key;
  81 
  82 /*
  83  * Bind a TSD value to a server thread. This enables the destructor to
  84  * be called if/when this thread exits.  This would be a programming
  85  * error, but better safe than sorry.
  86  */
  87 /*ARGSUSED*/
  88 static void *
  89 server_tsd_bind(void *arg)
  90 {
  91         static void *value = 0;
  92 
  93         (void) thr_setname(thr_self(), "server_tsd_bind");
  94 
  95         /* disable cancellation to avoid hangs if server threads disappear */
  96         (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
  97         (void) thr_setspecific(server_key, value);
  98         (void) door_return(NULL, 0, NULL, 0);
  99 
 100         /* make lint happy */
 101         return (NULL);
 102 }
 103 
 104 /*
 105  * Server threads are created here.
 106  */
 107 /*ARGSUSED*/
 108 static void
 109 server_create(door_info_t *dip)
 110 {
 111         (void) mutex_lock(&create_lock);
 112         if (++num_servers > max_servers) {
 113                 num_servers--;
 114                 (void) mutex_unlock(&create_lock);
 115                 return;
 116         }
 117         (void) mutex_unlock(&create_lock);
 118         (void) thr_create(NULL, 0, server_tsd_bind, NULL,
 119             THR_BOUND|THR_DETACHED, NULL);
 120 }
 121 
 122 /*
 123  * Server thread are destroyed here
 124  */
 125 /*ARGSUSED*/
 126 static void
 127 server_destroy(void *arg)
 128 {
 129         (void) mutex_lock(&create_lock);
 130         num_servers--;
 131         (void) mutex_unlock(&create_lock);
 132 }
 133 
 134 /*
 135  * get clearance
 136  */
 137 int
 138 _nscd_get_clearance(sema_t *sema)
 139 {
 140         if (sema_trywait(&common_sema) == 0) {
 141                 (void) thr_setspecific(lookup_state_key, NULL);
 142                 return (0);
 143         }
 144 
 145         if (sema_trywait(sema) == 0) {
 146                 (void) thr_setspecific(lookup_state_key, (void*)1);
 147                 return (0);
 148         }
 149 
 150         return (1);
 151 }
 152 
 153 
 154 /*
 155  * release clearance
 156  */
 157 int
 158 _nscd_release_clearance(sema_t *sema)
 159 {
 160         int     which;
 161 
 162         (void) thr_getspecific(lookup_state_key, (void**)&which);
 163         if (which == 0) /* from common pool */ {
 164                 (void) sema_post(&common_sema);
 165                 return (0);
 166         }
 167 
 168         (void) sema_post(sema);
 169         return (1);
 170 }
 171 
 172 static void
 173 dozip(void)
 174 {
 175         /* not much here */
 176 }
 177 
 178 /*
 179  * _nscd_restart_if_cfgfile_changed()
 180  * Restart if modification times of nsswitch.conf or resolv.conf have changed.
 181  *
 182  * If nsswitch.conf has changed then it is possible that sources for
 183  * various backends have changed and therefore the current cached
 184  * data may not be consistent with the new data sources.  By
 185  * restarting the cache will be cleared and the new configuration will
 186  * be used.
 187  *
 188  * The check for resolv.conf is made as only the first call to
 189  * res_gethostbyname() or res_getaddrbyname() causes a call to
 190  * res_ninit() to occur which in turn parses resolv.conf.  Therefore
 191  * to benefit from changes to resolv.conf nscd must be restarted when
 192  * resolv.conf is updated, removed or created.  If res_getXbyY calls
 193  * are removed from NSS then this check could be removed.
 194  *
 195  */
 196 void
 197 _nscd_restart_if_cfgfile_changed()
 198 {
 199 
 200         static mutex_t          nsswitch_lock = DEFAULTMUTEX;
 201         static timestruc_t      last_nsswitch_check = { 0 };
 202         static timestruc_t      last_nsswitch_modified = { 0 };
 203         static timestruc_t      last_resolv_modified = { -1, 0 };
 204         static mutex_t          restarting_lock = DEFAULTMUTEX;
 205         static int              restarting = 0;
 206         int                     restart = 0;
 207         time_t                  now = time(NULL);
 208         char                    *me = "_nscd_restart_if_cfgfile_changed";
 209 
 210 #define FLAG_RESTART_REQUIRED   if (restarting == 0) {\
 211                                         (void) mutex_lock(&restarting_lock);\
 212                                         if (restarting == 0) {\
 213                                                 restarting = 1;\
 214                                                 restart = 1;\
 215                                         }\
 216                                         (void) mutex_unlock(&restarting_lock);\
 217                                 }
 218 
 219         if (restarting == 1)
 220                 return;
 221 
 222         if (now - last_nsswitch_check.tv_sec < _NSC_FILE_CHECK_TIME)
 223                 return;
 224 
 225         (void) mutex_lock(&nsswitch_lock);
 226 
 227         if (now - last_nsswitch_check.tv_sec >= _NSC_FILE_CHECK_TIME) {
 228                 struct stat nss_buf;
 229                 struct stat res_buf;
 230 
 231                 last_nsswitch_check.tv_sec = now;
 232                 last_nsswitch_check.tv_nsec = 0;
 233 
 234                 (void) mutex_unlock(&nsswitch_lock); /* let others continue */
 235 
 236                 if (stat("/etc/nsswitch.conf", &nss_buf) < 0) {
 237                         return;
 238                 } else if (last_nsswitch_modified.tv_sec == 0) {
 239                         last_nsswitch_modified = nss_buf.st_mtim;
 240                 }
 241 
 242                 if (last_nsswitch_modified.tv_sec < nss_buf.st_mtim.tv_sec ||
 243                     (last_nsswitch_modified.tv_sec == nss_buf.st_mtim.tv_sec &&
 244                     last_nsswitch_modified.tv_nsec < nss_buf.st_mtim.tv_nsec)) {
 245                         FLAG_RESTART_REQUIRED;
 246                 }
 247 
 248                 if (restart == 0) {
 249                         if (stat("/etc/resolv.conf", &res_buf) < 0) {
 250                                 /* Unable to stat file, were we previously? */
 251                                 if (last_resolv_modified.tv_sec > 0) {
 252                                         /* Yes, it must have been removed. */
 253                                         FLAG_RESTART_REQUIRED;
 254                                 } else if (last_resolv_modified.tv_sec == -1) {
 255                                         /* No, then we've never seen it. */
 256                                         last_resolv_modified.tv_sec = 0;
 257                                 }
 258                         } else if (last_resolv_modified.tv_sec == -1) {
 259                                 /* We've just started and file is present. */
 260                                 last_resolv_modified = res_buf.st_mtim;
 261                         } else if (last_resolv_modified.tv_sec == 0) {
 262                                 /* Wasn't there at start-up. */
 263                                 FLAG_RESTART_REQUIRED;
 264                         } else if (last_resolv_modified.tv_sec <
 265                             res_buf.st_mtim.tv_sec ||
 266                             (last_resolv_modified.tv_sec ==
 267                             res_buf.st_mtim.tv_sec &&
 268                             last_resolv_modified.tv_nsec <
 269                             res_buf.st_mtim.tv_nsec)) {
 270                                 FLAG_RESTART_REQUIRED;
 271                         }
 272                 }
 273 
 274                 if (restart == 1) {
 275                         char *fmri;
 276 
 277                         /*
 278                          * if in self cred mode, kill the forker and
 279                          * child nscds
 280                          */
 281                         if (_nscd_is_self_cred_on(0, NULL)) {
 282                                 _nscd_kill_forker();
 283                                 _nscd_kill_all_children();
 284                         }
 285 
 286                         /*
 287                          * time for restart
 288                          */
 289                         _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_INFO)
 290                         (me, "nscd restart due to %s or %s change\n",
 291                             "/etc/nsswitch.conf", "resolv.conf");
 292                         /*
 293                          * try to restart under smf
 294                          */
 295                         if ((fmri = getenv("SMF_FMRI")) == NULL) {
 296                                 /* not running under smf - reexec */
 297                                 (void) execv(main_execname, main_argv);
 298                                 exit(1); /* just in case */
 299                         }
 300 
 301                         if (smf_restart_instance(fmri) == 0)
 302                                 (void) sleep(10); /* wait a bit */
 303                         exit(1); /* give up waiting for resurrection */
 304                 }
 305 
 306         } else
 307                 (void) mutex_unlock(&nsswitch_lock);
 308 }
 309 
 310 uid_t
 311 _nscd_get_client_euid()
 312 {
 313         ucred_t *uc = NULL;
 314         uid_t   id;
 315         char    *me = "get_client_euid";
 316 
 317         if (door_ucred(&uc) != 0) {
 318                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
 319                 (me, "door_ucred: %s\n", strerror(errno));
 320                 return ((uid_t)-1);
 321         }
 322 
 323         id = ucred_geteuid(uc);
 324         ucred_free(uc);
 325         return (id);
 326 }
 327 
 328 /*
 329  * Check to see if the door client's euid is 0 or if it has required_priv
 330  * privilege. Return 0 if yes, -1 otherwise.
 331  * Supported values for required_priv are:
 332  *    - NSCD_ALL_PRIV: for all zones privileges
 333  *    - NSCD_READ_PRIV: for PRIV_FILE_DAC_READ privilege
 334  */
 335 int
 336 _nscd_check_client_priv(int required_priv)
 337 {
 338         int                     rc = 0;
 339         ucred_t                 *uc = NULL;
 340         const priv_set_t        *eset;
 341         char                    *me = "_nscd_check_client_read_priv";
 342         priv_set_t              *zs;    /* zone */
 343 
 344         if (door_ucred(&uc) != 0) {
 345                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
 346                 (me, "door_ucred: %s\n", strerror(errno));
 347                 return (-1);
 348         }
 349 
 350         if (ucred_geteuid(uc) == 0) {
 351                 ucred_free(uc);
 352                 return (0);
 353         }
 354 
 355         eset = ucred_getprivset(uc, PRIV_EFFECTIVE);
 356         switch (required_priv) {
 357                 case NSCD_ALL_PRIV:
 358                         zs = priv_str_to_set("zone", ",", NULL);
 359                         if (!priv_isequalset(eset, zs)) {
 360                                 _NSCD_LOG(NSCD_LOG_FRONT_END,
 361                                     NSCD_LOG_LEVEL_ERROR)
 362                                 (me, "missing all zones privileges\n");
 363                                 rc = -1;
 364                         }
 365                         priv_freeset(zs);
 366                         break;
 367                 case NSCD_READ_PRIV:
 368                         if (!priv_ismember(eset, PRIV_FILE_DAC_READ))
 369                                 rc = -1;
 370                         break;
 371                 default:
 372                         _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
 373                         (me, "unknown required_priv: %d\n", required_priv);
 374                         rc = -1;
 375                         break;
 376         }
 377         ucred_free(uc);
 378         return (rc);
 379 }
 380 
 381 static void
 382 N2N_check_priv(
 383         void                    *buf,
 384         char                    *dc_str)
 385 {
 386         nss_pheader_t           *phdr = (nss_pheader_t *)buf;
 387         ucred_t                 *uc = NULL;
 388         const priv_set_t        *eset;
 389         zoneid_t                zoneid;
 390         int                     errnum;
 391         char                    *me = "N2N_check_priv";
 392 
 393         if (door_ucred(&uc) != 0) {
 394                 errnum = errno;
 395                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
 396                 (me, "door_ucred: %s\n", strerror(errno));
 397 
 398                 NSCD_SET_STATUS(phdr, NSS_ERROR, errnum);
 399                 return;
 400         }
 401 
 402         eset = ucred_getprivset(uc, PRIV_EFFECTIVE);
 403         zoneid = ucred_getzoneid(uc);
 404 
 405         if ((zoneid != GLOBAL_ZONEID && zoneid != getzoneid()) ||
 406             eset != NULL ? !priv_ismember(eset, PRIV_SYS_ADMIN) :
 407             ucred_geteuid(uc) != 0) {
 408 
 409                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT)
 410                 (me, "%s call failed(cred): caller pid %d, uid %d, "
 411                     "euid %d, zoneid %d\n", dc_str, ucred_getpid(uc),
 412                     ucred_getruid(uc), ucred_geteuid(uc), zoneid);
 413                 ucred_free(uc);
 414 
 415                 NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES);
 416                 return;
 417         }
 418 
 419         _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
 420         (me, "nscd received %s cmd from pid %d, uid %d, "
 421             "euid %d, zoneid %d\n", dc_str, ucred_getpid(uc),
 422             ucred_getruid(uc), ucred_geteuid(uc), zoneid);
 423 
 424         ucred_free(uc);
 425 
 426         NSCD_SET_STATUS_SUCCESS(phdr);
 427 }
 428 
 429 void
 430 _nscd_APP_check_cred(
 431         void            *buf,
 432         pid_t           *pidp,
 433         char            *dc_str,
 434         int             log_comp,
 435         int             log_level)
 436 {
 437         nss_pheader_t   *phdr = (nss_pheader_t *)buf;
 438         ucred_t         *uc = NULL;
 439         uid_t           ruid;
 440         uid_t           euid;
 441         pid_t           pid;
 442         int             errnum;
 443         char            *me = "_nscd_APP_check_cred";
 444 
 445         if (door_ucred(&uc) != 0) {
 446                 errnum = errno;
 447                 _NSCD_LOG(log_comp, NSCD_LOG_LEVEL_ERROR)
 448                 (me, "door_ucred: %s\n", strerror(errno));
 449 
 450                 NSCD_SET_STATUS(phdr, NSS_ERROR, errnum);
 451                 return;
 452         }
 453 
 454         NSCD_SET_STATUS_SUCCESS(phdr);
 455         pid = ucred_getpid(uc);
 456         if (NSS_PACKED_CRED_CHECK(buf, ruid = ucred_getruid(uc),
 457             euid = ucred_geteuid(uc))) {
 458                 if (pidp != NULL) {
 459                         if (*pidp == (pid_t)-1)
 460                                 *pidp = pid;
 461                         else if (*pidp != pid) {
 462                                 NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES);
 463                         }
 464                 }
 465         } else {
 466                 NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES);
 467         }
 468 
 469         ucred_free(uc);
 470 
 471         if (NSCD_STATUS_IS_NOT_OK(phdr)) {
 472                 _NSCD_LOG(log_comp, log_level)
 473                 (me, "%s call failed: caller pid %d (input pid = %d), ruid %d, "
 474                     "euid %d, header ruid %d, header euid %d\n", dc_str,
 475                     pid, (pidp != NULL) ? *pidp : -1, ruid, euid,
 476                     ((nss_pheader_t *)(buf))->p_ruid,
 477                     ((nss_pheader_t *)(buf))->p_euid);
 478         }
 479 }
 480 
 481 /* log error and return -1 when an invalid packed buffer header is found */
 482 static int
 483 pheader_error(nss_pheader_t *phdr, uint32_t call_number)
 484 {
 485         char *call_num_str;
 486 
 487         switch (call_number) {
 488         case NSCD_SEARCH:
 489                 call_num_str = "NSCD_SEARCH";
 490                 break;
 491         case NSCD_SETENT:
 492                 call_num_str = "NSCD_SETENT";
 493                 break;
 494         case NSCD_GETENT:
 495                 call_num_str = "NSCD_GETENT";
 496                 break;
 497         case NSCD_ENDENT:
 498                 call_num_str = "NSCD_ENDENT";
 499                 break;
 500         case NSCD_PUT:
 501                 call_num_str = "NSCD_PUT";
 502                 break;
 503         case NSCD_GETHINTS:
 504                 call_num_str = "NSCD_GETHINTS";
 505                 break;
 506         default:
 507                 call_num_str = "UNKNOWN";
 508                 break;
 509         }
 510 
 511         _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT)
 512         ("pheader_error", "call number %s: invalid packed buffer header\n",
 513             call_num_str);
 514 
 515         NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL);
 516         return (-1);
 517 }
 518 
 519 /*
 520  * Validate the header of a getXbyY or setent/getent/endent request.
 521  * Return 0 if good, -1 otherwise.
 522  *
 523  * A valid header looks like the following (size is arg_size, does
 524  * not include the output area):
 525  * +----------------------------------+ --
 526  * | nss_pheader_t (header fixed part)| ^
 527  * |                                  | |
 528  * | pbufsiz, dbd,off, key_off,       | len = sizeof(nss_pheader_t)
 529  * | data_off ....                    | |
 530  * |                                  | v
 531  * +----------------------------------+ <----- dbd_off
 532  * | dbd (database description)       | ^
 533  * | nss_dbd_t + up to 3 strings      | |
 534  * | length = sizeof(nss_dbd_t) +     | len = key_off - dbd_off
 535  * |          length of 3 strings +   | |
 536  * |          length of padding       | |
 537  * | (total length in multiple of 4)  | v
 538  * +----------------------------------+ <----- key_off
 539  * | lookup key                       | ^
 540  * | nss_XbyY_key_t, content varies,  | |
 541  * | based on database and lookup op  | len = data_off - key_off
 542  * | length = data_off - key_off      | |
 543  * | including padding, multiple of 4 | v
 544  * +----------------------------------+ <----- data_off (= arg_size)
 545  * |                                  | ^
 546  * | area to hold results             | |
 547  * |                                  | len = data_len (= pbufsiz -
 548  * |                                  | |                 data_off)
 549  * |                                  | v
 550  * +----------------------------------+ <----- pbufsiz
 551  */
 552 static int
 553 validate_pheader(
 554         void            *argp,
 555         size_t          arg_size,
 556         uint32_t        call_number)
 557 {
 558         nss_pheader_t   *phdr = (nss_pheader_t *)(void *)argp;
 559         nssuint_t       l1, l2;
 560 
 561         /*
 562          * current version is NSCD_HEADER_REV, length of the fixed part
 563          * of the header must match the size of nss_pheader_t
 564          */
 565         if (phdr->p_version != NSCD_HEADER_REV ||
 566             phdr->dbd_off != sizeof (nss_pheader_t))
 567                 return (pheader_error(phdr, call_number));
 568 
 569         /*
 570          * buffer size and offsets must be in multiple of 4
 571          */
 572         if ((arg_size & 3) || (phdr->dbd_off & 3) || (phdr->key_off & 3) ||
 573             (phdr->data_off & 3))
 574                 return (pheader_error(phdr, call_number));
 575 
 576         /*
 577          * the input arg_size is the length of the request header
 578          * and should be less than NSCD_PHDR_MAXLEN
 579          */
 580         if (phdr->data_off != arg_size || arg_size > NSCD_PHDR_MAXLEN)
 581                 return (pheader_error(phdr, call_number));
 582 
 583         /* get length of the dbd area */
 584         l1 = phdr->key_off - phdr-> dbd_off;
 585 
 586         /*
 587          * dbd area may contain padding, so length of dbd should
 588          * not be less than the length of the actual data
 589          */
 590         if (l1 < phdr->dbd_len)
 591                 return (pheader_error(phdr, call_number));
 592 
 593         /* get length of the key area */
 594         l2 = phdr->data_off - phdr->key_off;
 595 
 596         /*
 597          * key area may contain padding, so length of key area should
 598          * not be less than the length of the actual data
 599          */
 600         if (l2 < phdr->key_len)
 601                 return (pheader_error(phdr, call_number));
 602 
 603         /*
 604          * length of fixed part + lengths of dbd and key area = length of
 605          * the request header
 606          */
 607         if (sizeof (nss_pheader_t) + l1 + l2 != phdr->data_off)
 608                 return (pheader_error(phdr, call_number));
 609 
 610         /* header length + data length = buffer length */
 611         if (phdr->data_off + phdr->data_len != phdr->pbufsiz)
 612                 return (pheader_error(phdr, call_number));
 613 
 614         return (0);
 615 }
 616 
 617 /* log error and return -1 when an invalid nscd to nscd buffer is found */
 618 static int
 619 N2Nbuf_error(nss_pheader_t *phdr, uint32_t call_number)
 620 {
 621         char *call_num_str;
 622 
 623         switch (call_number) {
 624         case NSCD_PING:
 625                 call_num_str = "NSCD_PING";
 626                 break;
 627 
 628         case NSCD_IMHERE:
 629                 call_num_str = "NSCD_IMHERE";
 630                 break;
 631 
 632         case NSCD_PULSE:
 633                 call_num_str = "NSCD_PULSE";
 634                 break;
 635 
 636         case NSCD_FORK:
 637                 call_num_str = "NSCD_FORK";
 638                 break;
 639 
 640         case NSCD_KILL:
 641                 call_num_str = "NSCD_KILL";
 642                 break;
 643 
 644         case NSCD_REFRESH:
 645                 call_num_str = "NSCD_REFRESH";
 646                 break;
 647 
 648         case NSCD_GETPUADMIN:
 649                 call_num_str = "NSCD_GETPUADMIN";
 650                 break;
 651 
 652         case NSCD_GETADMIN:
 653                 call_num_str = "NSCD_GETADMIN";
 654                 break;
 655 
 656         case NSCD_SETADMIN:
 657                 call_num_str = "NSCD_SETADMIN";
 658                 break;
 659 
 660         case NSCD_KILLSERVER:
 661                 call_num_str = "NSCD_KILLSERVER";
 662                 break;
 663         default:
 664                 call_num_str = "UNKNOWN";
 665                 break;
 666         }
 667 
 668         _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT)
 669         ("N2Nbuf_error", "call number %s: invalid N2N buffer\n", call_num_str);
 670 
 671         NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0,
 672             NSCD_DOOR_BUFFER_CHECK_FAILED);
 673 
 674         return (-1);
 675 }
 676 
 677 /*
 678  * Validate the buffer of an nscd to nscd request.
 679  * Return 0 if good, -1 otherwise.
 680  *
 681  * A valid buffer looks like the following (size is arg_size):
 682  * +----------------------------------+ --
 683  * | nss_pheader_t (header fixed part)| ^
 684  * |                                  | |
 685  * | pbufsiz, dbd,off, key_off,       | len = sizeof(nss_pheader_t)
 686  * | data_off ....                    | |
 687  * |                                  | v
 688  * +----------------------------------+ <---dbd_off = key_off = data_off
 689  * |                                  | ^
 690  * | input data/output data           | |
 691  * | OR no data                       | len = data_len (= pbufsiz -
 692  * |                                  | |                 data_off)
 693  * |                                  | | len could be zero
 694  * |                                  | v
 695  * +----------------------------------+ <--- pbufsiz
 696  */
 697 static int
 698 validate_N2Nbuf(
 699         void            *argp,
 700         size_t          arg_size,
 701         uint32_t        call_number)
 702 {
 703         nss_pheader_t   *phdr = (nss_pheader_t *)(void *)argp;
 704 
 705         /*
 706          * current version is NSCD_HEADER_REV, length of the fixed part
 707          * of the header must match the size of nss_pheader_t
 708          */
 709         if (phdr->p_version != NSCD_HEADER_REV ||
 710             phdr->dbd_off != sizeof (nss_pheader_t))
 711                 return (N2Nbuf_error(phdr, call_number));
 712 
 713         /*
 714          * There are no dbd and key data, so the dbd, key, data
 715          * offsets should be equal
 716          */
 717         if (phdr->dbd_off != phdr->key_off ||
 718             phdr->dbd_off != phdr->data_off)
 719                 return (N2Nbuf_error(phdr, call_number));
 720 
 721         /*
 722          * the input arg_size is the buffer length and should
 723          * be less or equal than NSCD_N2NBUF_MAXLEN
 724          */
 725         if (phdr->pbufsiz != arg_size || arg_size > NSCD_N2NBUF_MAXLEN)
 726                 return (N2Nbuf_error(phdr, call_number));
 727 
 728         /* header length + data length = buffer length */
 729         if (phdr->data_off + phdr->data_len != phdr->pbufsiz)
 730                 return (N2Nbuf_error(phdr, call_number));
 731 
 732         return (0);
 733 }
 734 
 735 static void
 736 lookup(char *argp, size_t arg_size)
 737 {
 738         nsc_lookup_args_t       largs;
 739         char                    space[NSCD_LOOKUP_BUFSIZE];
 740         nss_pheader_t           *phdr = (nss_pheader_t *)(void *)argp;
 741 
 742         NSCD_ALLOC_LOOKUP_BUFFER(argp, arg_size, phdr, space,
 743             sizeof (space));
 744 
 745         /*
 746          * make sure the first couple bytes of the data area is null,
 747          * so that bad strings in the packed header stop here
 748          */
 749         (void) memset((char *)phdr + phdr->data_off, 0, 16);
 750 
 751         (void) memset(&largs, 0, sizeof (largs));
 752         largs.buffer = argp;
 753         largs.bufsize = arg_size;
 754         nsc_lookup(&largs, 0);
 755 
 756         /*
 757          * only the PUN needs to keep track of the
 758          * activity count to determine when to
 759          * terminate itself
 760          */
 761         if (_whoami == NSCD_CHILD) {
 762                 (void) mutex_lock(&activity_lock);
 763                 ++activity;
 764                 (void) mutex_unlock(&activity_lock);
 765         }
 766 
 767         NSCD_SET_RETURN_ARG(phdr, arg_size);
 768         (void) door_return(argp, arg_size, NULL, 0);
 769 }
 770 
 771 static void
 772 getent(char *argp, size_t arg_size)
 773 {
 774         char                    space[NSCD_LOOKUP_BUFSIZE];
 775         nss_pheader_t           *phdr = (nss_pheader_t *)(void *)argp;
 776 
 777         NSCD_ALLOC_LOOKUP_BUFFER(argp, arg_size, phdr, space, sizeof (space));
 778 
 779         nss_pgetent(argp, arg_size);
 780 
 781         NSCD_SET_RETURN_ARG(phdr, arg_size);
 782         (void) door_return(argp, arg_size, NULL, 0);
 783 }
 784 
 785 static int
 786 is_db_per_user(void *buf, char *dblist)
 787 {
 788         nss_pheader_t   *phdr = (nss_pheader_t *)buf;
 789         nss_dbd_t       *pdbd;
 790         char            *dbname, *dbn;
 791         int             len;
 792 
 793         /* copy db name into a temp buffer */
 794         pdbd = (nss_dbd_t *)((void *)((char *)buf + phdr->dbd_off));
 795         dbname = (char *)pdbd + pdbd->o_name;
 796         len = strlen(dbname);
 797         dbn = alloca(len + 2);
 798         (void) memcpy(dbn, dbname, len);
 799 
 800         /* check if <dbname> + ',' can be found in the dblist string */
 801         dbn[len] = ',';
 802         dbn[len + 1] = '\0';
 803         if (strstr(dblist, dbn) != NULL)
 804                 return (1);
 805 
 806         /*
 807          * check if <dbname> can be found in the last part
 808          * of the dblist string
 809          */
 810         dbn[len] = '\0';
 811         if (strstr(dblist, dbn) != NULL)
 812                 return (1);
 813 
 814         return (0);
 815 }
 816 
 817 /*
 818  * Check to see if all conditions are met for processing per-user
 819  * requests. Returns 1 if yes, -1 if backend is not configured,
 820  * 0 otherwise.
 821  */
 822 static int
 823 need_per_user_door(void *buf, int whoami, uid_t uid, char **dblist)
 824 {
 825         nss_pheader_t   *phdr = (nss_pheader_t *)buf;
 826 
 827         NSCD_SET_STATUS_SUCCESS(phdr);
 828 
 829         /* if already a per-user nscd, no need to get per-user door */
 830         if (whoami == NSCD_CHILD)
 831                 return (0);
 832 
 833         /* forker shouldn't be asked */
 834         if (whoami == NSCD_FORKER) {
 835                 NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
 836                 return (0);
 837         }
 838 
 839         /* if door client is root, no need for a per-user door */
 840         if (uid == 0)
 841                 return (0);
 842 
 843         /*
 844          * if per-user lookup is not configured, no per-user
 845          * door available
 846          */
 847         if (_nscd_is_self_cred_on(0, dblist) == 0)
 848                 return (-1);
 849 
 850         /*
 851          * if per-user lookup is not configured for the db,
 852          * don't bother
 853          */
 854         if (is_db_per_user(phdr, *dblist) == 0)
 855                 return (0);
 856 
 857         return (1);
 858 }
 859 
 860 static void
 861 if_selfcred_return_per_user_door(char *argp, size_t arg_size,
 862         door_desc_t *dp, int whoami)
 863 {
 864         nss_pheader_t   *phdr = (nss_pheader_t *)((void *)argp);
 865         char            *dblist;
 866         int             door = -1;
 867         int             rc = 0;
 868         door_desc_t     desc;
 869         char            *space;
 870         int             len;
 871 
 872         /*
 873          * check to see if self-cred is configured and
 874          * need to return an alternate PUN door
 875          */
 876         if (per_user_is_on == 1) {
 877                 rc = need_per_user_door(argp, whoami,
 878                     _nscd_get_client_euid(), &dblist);
 879                 if (rc == -1)
 880                         per_user_is_on = 0;
 881         }
 882         if (rc <= 0) {
 883                 /*
 884                  * self-cred not configured, and no error detected,
 885                  * return to continue the door call processing
 886                  */
 887                 if (NSCD_STATUS_IS_OK(phdr))
 888                         return;
 889                 else
 890                         /*
 891                          * configured but error detected,
 892                          * stop the door call processing
 893                          */
 894                         (void) door_return(argp, phdr->data_off, NULL, 0);
 895         }
 896 
 897         /* get the alternate PUN door */
 898         _nscd_proc_alt_get(argp, &door);
 899         if (NSCD_GET_STATUS(phdr) != NSS_ALTRETRY) {
 900                 (void) door_return(argp, phdr->data_off, NULL, 0);
 901         }
 902 
 903         /* return the alternate door descriptor */
 904         len = strlen(dblist) + 1;
 905         space = alloca(arg_size + len);
 906         phdr->data_len = len;
 907         (void) memcpy(space, phdr, arg_size);
 908         (void) strncpy((char *)space + arg_size, dblist, len);
 909         dp = &desc;
 910         dp->d_attributes = DOOR_DESCRIPTOR;
 911         dp->d_data.d_desc.d_descriptor = door;
 912         arg_size += len;
 913         (void) door_return(space, arg_size, dp, 1);
 914 }
 915 
 916 /*ARGSUSED*/
 917 static void
 918 switcher(void *cookie, char *argp, size_t arg_size,
 919     door_desc_t *dp, uint_t n_desc)
 920 {
 921         int                     iam;
 922         pid_t                   ent_pid = -1;
 923         nss_pheader_t           *phdr = (nss_pheader_t *)((void *)argp);
 924         void                    *uptr;
 925         int                     len;
 926         size_t                  buflen;
 927         int                     callnum;
 928         char                    *me = "switcher";
 929 
 930         _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
 931         (me, "switcher ...\n");
 932 
 933         if (argp == DOOR_UNREF_DATA) {
 934                 (void) printf("Door Slam... exiting\n");
 935                 exit(0);
 936         }
 937 
 938         if (argp == NULL) { /* empty door call */
 939                 (void) door_return(NULL, 0, 0, 0); /* return the favor */
 940         }
 941 
 942         /*
 943          *  need to restart if main nscd and config file(s) changed
 944          */
 945         if (_whoami == NSCD_MAIN)
 946                 _nscd_restart_if_cfgfile_changed();
 947 
 948         if ((phdr->nsc_callnumber & NSCDV2CATMASK) == NSCD_CALLCAT_APP) {
 949 
 950                 /* make sure the packed buffer header is good */
 951                 if (validate_pheader(argp, arg_size,
 952                     phdr->nsc_callnumber) == -1)
 953                         (void) door_return(argp, arg_size, NULL, 0);
 954 
 955                 switch (phdr->nsc_callnumber) {
 956 
 957                 case NSCD_SEARCH:
 958 
 959                 /* if a fallback to main nscd, skip per-user setup */
 960                 if (phdr->p_status != NSS_ALTRETRY)
 961                         if_selfcred_return_per_user_door(argp, arg_size,
 962                             dp, _whoami);
 963                 lookup(argp, arg_size);
 964 
 965                 break;
 966 
 967                 case NSCD_SETENT:
 968 
 969                 _nscd_APP_check_cred(argp, &ent_pid, "NSCD_SETENT",
 970                     NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT);
 971                 if (NSCD_STATUS_IS_OK(phdr)) {
 972                         if_selfcred_return_per_user_door(argp, arg_size,
 973                             dp, _whoami);
 974                         nss_psetent(argp, arg_size, ent_pid);
 975                 }
 976                 break;
 977 
 978                 case NSCD_GETENT:
 979 
 980                 getent(argp, arg_size);
 981                 break;
 982 
 983                 case NSCD_ENDENT:
 984 
 985                 nss_pendent(argp, arg_size);
 986                 break;
 987 
 988                 case NSCD_PUT:
 989 
 990                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
 991                 (me, "door call NSCD_PUT not supported yet\n");
 992 
 993                 NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
 994                 break;
 995 
 996                 case NSCD_GETHINTS:
 997 
 998                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
 999                 (me, "door call NSCD_GETHINTS not supported yet\n");
1000 
1001                 NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
1002                 break;
1003 
1004                 default:
1005 
1006                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1007                 (me, "Unknown name service door call op %x\n",
1008                     phdr->nsc_callnumber);
1009 
1010                 NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL);
1011                 break;
1012                 }
1013 
1014                 (void) door_return(argp, arg_size, NULL, 0);
1015         }
1016 
1017         iam = NSCD_MAIN;
1018         callnum = phdr->nsc_callnumber & ~NSCD_WHOAMI;
1019         if (callnum == NSCD_IMHERE ||
1020             callnum == NSCD_PULSE || callnum == NSCD_FORK)
1021                 iam = phdr->nsc_callnumber & NSCD_WHOAMI;
1022         else
1023                 callnum = phdr->nsc_callnumber;
1024 
1025         /* nscd -> nscd v2 calls */
1026 
1027         /* make sure the buffer is good */
1028         if (validate_N2Nbuf(argp, arg_size, callnum) == -1)
1029                 (void) door_return(argp, arg_size, NULL, 0);
1030 
1031         switch (callnum) {
1032 
1033         case NSCD_PING:
1034                 NSCD_SET_STATUS_SUCCESS(phdr);
1035                 break;
1036 
1037         case NSCD_IMHERE:
1038                 _nscd_proc_iamhere(argp, dp, n_desc, iam);
1039                 break;
1040 
1041         case NSCD_PULSE:
1042                 N2N_check_priv(argp, "NSCD_PULSE");
1043                 if (NSCD_STATUS_IS_OK(phdr))
1044                         _nscd_proc_pulse(argp, iam);
1045                 break;
1046 
1047         case NSCD_FORK:
1048                 N2N_check_priv(argp, "NSCD_FORK");
1049                 if (NSCD_STATUS_IS_OK(phdr))
1050                         _nscd_proc_fork(argp, iam);
1051                 break;
1052 
1053         case NSCD_KILL:
1054                 N2N_check_priv(argp, "NSCD_KILL");
1055                 if (NSCD_STATUS_IS_OK(phdr))
1056                         exit(0);
1057                 break;
1058 
1059         case NSCD_REFRESH:
1060                 N2N_check_priv(argp, "NSCD_REFRESH");
1061                 if (NSCD_STATUS_IS_OK(phdr)) {
1062                         if (_nscd_refresh() != NSCD_SUCCESS)
1063                                 exit(1);
1064                         NSCD_SET_STATUS_SUCCESS(phdr);
1065                 }
1066                 break;
1067 
1068         case NSCD_GETPUADMIN:
1069 
1070                 if (_nscd_is_self_cred_on(0, NULL)) {
1071                         _nscd_peruser_getadmin(argp, sizeof (nscd_admin_t));
1072                 } else {
1073                         NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0,
1074                             NSCD_SELF_CRED_NOT_CONFIGURED);
1075                 }
1076                 break;
1077 
1078         case NSCD_GETADMIN:
1079 
1080                 len = _nscd_door_getadmin((void *)argp);
1081                 if (len == 0)
1082                         break;
1083 
1084                 /* size of door buffer not big enough, allocate one */
1085                 NSCD_ALLOC_DOORBUF(NSCD_GETADMIN, len, uptr, buflen);
1086 
1087                 /* copy packed header */
1088                 *(nss_pheader_t *)uptr = *(nss_pheader_t *)((void *)argp);
1089 
1090                 /* set new buffer size */
1091                 ((nss_pheader_t *)uptr)->pbufsiz = buflen;
1092 
1093                 /* try one more time */
1094                 (void) _nscd_door_getadmin((void *)uptr);
1095                 (void) door_return(uptr, buflen, NULL, 0);
1096                 break;
1097 
1098         case NSCD_SETADMIN:
1099                 N2N_check_priv(argp, "NSCD_SETADMIN");
1100                 if (NSCD_STATUS_IS_OK(phdr))
1101                         _nscd_door_setadmin(argp);
1102                 break;
1103 
1104         case NSCD_KILLSERVER:
1105                 N2N_check_priv(argp, "NSCD_KILLSERVER");
1106                 if (NSCD_STATUS_IS_OK(phdr)) {
1107                         /* also kill the forker nscd if one is running */
1108                         _nscd_kill_forker();
1109                         exit(0);
1110                 }
1111                 break;
1112 
1113         default:
1114                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1115                 (me, "Unknown name service door call op %d\n",
1116                     phdr->nsc_callnumber);
1117 
1118                 NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL);
1119 
1120                 (void) door_return(argp, arg_size, NULL, 0);
1121                 break;
1122 
1123         }
1124         (void) door_return(argp, arg_size, NULL, 0);
1125 }
1126 
1127 int
1128 _nscd_setup_server(char *execname, char **argv)
1129 {
1130 
1131         int             fd;
1132         int             errnum;
1133         int             bind_failed = 0;
1134         mode_t          old_mask;
1135         struct stat     buf;
1136         sigset_t        myset;
1137         struct sigaction action;
1138         char            *me = "_nscd_setup_server";
1139 
1140         main_execname = execname;
1141         main_argv = argv;
1142 
1143         /* Any nscd process is to ignore SIGPIPE */
1144         if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
1145                 errnum = errno;
1146                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1147                 (me, "signal (SIGPIPE): %s\n", strerror(errnum));
1148                 return (-1);
1149         }
1150 
1151         keep_open_dns_socket();
1152 
1153         /*
1154          * the max number of server threads should be fixed now, so
1155          * set flag to indicate that no in-flight change is allowed
1156          */
1157         max_servers_set = 1;
1158 
1159         (void) thr_keycreate(&lookup_state_key, NULL);
1160         (void) sema_init(&common_sema, frontend_cfg_g.common_worker_threads,
1161             USYNC_THREAD, 0);
1162 
1163         /* Establish server thread pool */
1164         (void) door_server_create(server_create);
1165         if (thr_keycreate(&server_key, server_destroy) != 0) {
1166                 errnum = errno;
1167                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1168                 (me, "thr_keycreate (server thread): %s\n",
1169                     strerror(errnum));
1170                 return (-1);
1171         }
1172 
1173         /* Create a door */
1174         if ((fd = door_create(switcher, NAME_SERVICE_DOOR_COOKIE,
1175             DOOR_UNREF | DOOR_NO_CANCEL)) < 0) {
1176                 errnum = errno;
1177                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1178                 (me, "door_create: %s\n", strerror(errnum));
1179                 return (-1);
1180         }
1181 
1182         /* if not main nscd, no more setup to do */
1183         if (_whoami != NSCD_MAIN)
1184                 return (fd);
1185 
1186         /* bind to file system */
1187         if (is_system_labeled() && (getzoneid() == GLOBAL_ZONEID)) {
1188                 if (stat(TSOL_NAME_SERVICE_DOOR, &buf) < 0) {
1189                         int     newfd;
1190 
1191                         /* make sure the door will be readable by all */
1192                         old_mask = umask(0);
1193                         if ((newfd = creat(TSOL_NAME_SERVICE_DOOR, 0444)) < 0) {
1194                                 errnum = errno;
1195                                 _NSCD_LOG(NSCD_LOG_FRONT_END,
1196                                     NSCD_LOG_LEVEL_ERROR)
1197                                 (me, "Cannot create %s: %s\n",
1198                                     TSOL_NAME_SERVICE_DOOR,
1199                                     strerror(errnum));
1200                                 bind_failed = 1;
1201                         }
1202                         /* rstore the old file mode creation mask */
1203                         (void) umask(old_mask);
1204                         (void) close(newfd);
1205                 }
1206                 if (symlink(TSOL_NAME_SERVICE_DOOR, NAME_SERVICE_DOOR) != 0) {
1207                         if (errno != EEXIST) {
1208                                 errnum = errno;
1209                                 _NSCD_LOG(NSCD_LOG_FRONT_END,
1210                                     NSCD_LOG_LEVEL_ERROR)
1211                                 (me, "Cannot symlink %s: %s\n",
1212                                     NAME_SERVICE_DOOR, strerror(errnum));
1213                                 bind_failed = 1;
1214                         }
1215                 }
1216         } else if (stat(NAME_SERVICE_DOOR, &buf) < 0) {
1217                 int     newfd;
1218 
1219                 /* make sure the door will be readable by all */
1220                 old_mask = umask(0);
1221                 if ((newfd = creat(NAME_SERVICE_DOOR, 0444)) < 0) {
1222                         errnum = errno;
1223                         _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1224                         (me, "Cannot create %s: %s\n", NAME_SERVICE_DOOR,
1225                             strerror(errnum));
1226                         bind_failed = 1;
1227                 }
1228                 /* rstore the old file mode creation mask */
1229                 (void) umask(old_mask);
1230                 (void) close(newfd);
1231         }
1232 
1233         if (bind_failed == 1) {
1234                 (void) door_revoke(fd);
1235                 return (-1);
1236         }
1237 
1238         if (fattach(fd, NAME_SERVICE_DOOR) < 0) {
1239                 if ((errno != EBUSY) ||
1240                     (fdetach(NAME_SERVICE_DOOR) <  0) ||
1241                     (fattach(fd, NAME_SERVICE_DOOR) < 0)) {
1242                         errnum = errno;
1243                         _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1244                         (me, "fattach: %s\n", strerror(errnum));
1245                         (void) door_revoke(fd);
1246                         return (-1);
1247                 }
1248         }
1249 
1250         /*
1251          * kick off routing socket monitor thread
1252          */
1253         if (thr_create(NULL, NULL,
1254             (void *(*)(void *))rts_mon, 0, 0, NULL) != 0) {
1255                 errnum = errno;
1256                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1257                 (me, "thr_create (routing socket monitor): %s\n",
1258                     strerror(errnum));
1259 
1260                 (void) door_revoke(fd);
1261                 return (-1);
1262         }
1263 
1264         /*
1265          * set up signal handler for SIGHUP
1266          */
1267         action.sa_handler = dozip;
1268         action.sa_flags = 0;
1269         (void) sigemptyset(&action.sa_mask);
1270         (void) sigemptyset(&myset);
1271         (void) sigaddset(&myset, SIGHUP);
1272 
1273         if (sigaction(SIGHUP, &action, NULL) < 0) {
1274                 errnum = errno;
1275                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1276                 (me, "sigaction (SIGHUP): %s\n", strerror(errnum));
1277 
1278                 (void) door_revoke(fd);
1279                 return (-1);
1280         }
1281 
1282         return (fd);
1283 }
1284 
1285 int
1286 _nscd_setup_child_server(int did)
1287 {
1288 
1289         int             errnum;
1290         int             fd;
1291         nscd_rc_t       rc;
1292         char            *me = "_nscd_setup_child_server";
1293 
1294         /* Re-establish our own server thread pool */
1295         (void) door_server_create(server_create);
1296         if (thr_keycreate(&server_key, server_destroy) != 0) {
1297                 errnum = errno;
1298                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
1299                 (me, "thr_keycreate failed: %s", strerror(errnum));
1300                 return (-1);
1301         }
1302 
1303         /*
1304          * Create a new door.
1305          * Keep DOOR_REFUSE_DESC (self-cred nscds don't fork)
1306          */
1307         (void) close(did);
1308         if ((fd = door_create(switcher, NAME_SERVICE_DOOR_COOKIE,
1309             DOOR_REFUSE_DESC|DOOR_UNREF|DOOR_NO_CANCEL)) < 0) {
1310                 errnum = errno;
1311                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
1312                 (me, "door_create failed: %s", strerror(errnum));
1313                 return (-1);
1314         }
1315 
1316         /*
1317          * kick off routing socket monitor thread
1318          */
1319         if (thr_create(NULL, NULL,
1320             (void *(*)(void *))rts_mon, 0, 0, NULL) != 0) {
1321                 errnum = errno;
1322                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1323                 (me, "thr_create (routing socket monitor): %s\n",
1324                     strerror(errnum));
1325                 (void) door_revoke(fd);
1326                 return (-1);
1327         }
1328 
1329         /*
1330          * start monitoring the states of the name service clients
1331          */
1332         rc = _nscd_init_smf_monitor();
1333         if (rc != NSCD_SUCCESS) {
1334                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1335         (me, "unable to start the SMF monitor (rc = %d)\n", rc);
1336 
1337                 (void) door_revoke(fd);
1338                 return (-1);
1339         }
1340 
1341         return (fd);
1342 }
1343 
1344 nscd_rc_t
1345 _nscd_alloc_frontend_cfg()
1346 {
1347         frontend_cfg  = calloc(NSCD_NUM_DB, sizeof (nscd_cfg_frontend_t));
1348         if (frontend_cfg == NULL)
1349                 return (NSCD_NO_MEMORY);
1350 
1351         return (NSCD_SUCCESS);
1352 }
1353 
1354 
1355 /* ARGSUSED */
1356 nscd_rc_t
1357 _nscd_cfg_frontend_notify(
1358         void                            *data,
1359         struct nscd_cfg_param_desc      *pdesc,
1360         nscd_cfg_id_t                   *nswdb,
1361         nscd_cfg_flag_t                 dflag,
1362         nscd_cfg_error_t                **errorp,
1363         void                            *cookie)
1364 {
1365         void                            *dp;
1366 
1367         /*
1368          * At init time, the whole group of config params are received.
1369          * At update time, group or individual parameter value could
1370          * be received.
1371          */
1372 
1373         if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_INIT) ||
1374             _nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) {
1375                 /*
1376                  * group data is received, copy in the
1377                  * entire strcture
1378                  */
1379                 if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL))
1380                         frontend_cfg_g = *(nscd_cfg_global_frontend_t *)data;
1381                 else
1382                         frontend_cfg[nswdb->index] =
1383                             *(nscd_cfg_frontend_t *)data;
1384 
1385         } else {
1386                 /*
1387                  * individual paramater is received: copy in the
1388                  * parameter value.
1389                  */
1390                 if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL))
1391                         dp = (char *)&frontend_cfg_g + pdesc->p_offset;
1392                 else
1393                         dp = (char *)&frontend_cfg[nswdb->index] +
1394                             pdesc->p_offset;
1395                 (void) memcpy(dp, data, pdesc->p_size);
1396         }
1397 
1398         return (NSCD_SUCCESS);
1399 }
1400 
1401 /* ARGSUSED */
1402 nscd_rc_t
1403 _nscd_cfg_frontend_verify(
1404         void                            *data,
1405         struct  nscd_cfg_param_desc     *pdesc,
1406         nscd_cfg_id_t                   *nswdb,
1407         nscd_cfg_flag_t                 dflag,
1408         nscd_cfg_error_t                **errorp,
1409         void                            **cookie)
1410 {
1411 
1412         char                            *me = "_nscd_cfg_frontend_verify";
1413 
1414         /*
1415          * if max. number of server threads is set and in effect,
1416          * don't allow changing of the frontend configuration
1417          */
1418         if (max_servers_set) {
1419                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_INFO)
1420         (me, "changing of the frontend configuration not allowed now");
1421 
1422                 return (NSCD_CFG_CHANGE_NOT_ALLOWED);
1423         }
1424 
1425         return (NSCD_SUCCESS);
1426 }
1427 
1428 /* ARGSUSED */
1429 nscd_rc_t
1430 _nscd_cfg_frontend_get_stat(
1431         void                            **stat,
1432         struct nscd_cfg_stat_desc       *sdesc,
1433         nscd_cfg_id_t                   *nswdb,
1434         nscd_cfg_flag_t                 *dflag,
1435         void                            (**free_stat)(void *stat),
1436         nscd_cfg_error_t                **errorp)
1437 {
1438         return (NSCD_SUCCESS);
1439 }
1440 
1441 void
1442 _nscd_init_cache_sema(sema_t *sema, char *cache_name)
1443 {
1444         int     i, j;
1445         char    *dbn;
1446 
1447         if (max_servers == 0)
1448                 max_servers = frontend_cfg_g.common_worker_threads +
1449                     frontend_cfg_g.cache_hit_threads;
1450 
1451         for (i = 0; i < NSCD_NUM_DB; i++) {
1452 
1453                 dbn = NSCD_NSW_DB_NAME(i);
1454                 if (strcasecmp(dbn, cache_name) == 0) {
1455                         j = frontend_cfg[i].worker_thread_per_nsw_db;
1456                         (void) sema_init(sema, j, USYNC_THREAD, 0);
1457                         max_servers += j;
1458                         break;
1459                 }
1460         }
1461 }
1462 
1463 /*
1464  * Monitor the routing socket.  Address lists stored in the ipnodes
1465  * cache are sorted based on destination address selection rules,
1466  * so when things change that could affect that sorting (interfaces
1467  * go up or down, flags change, etc.), we clear that cache so the
1468  * list will be re-ordered the next time the hostname is resolved.
1469  */
1470 static void
1471 rts_mon(void)
1472 {
1473         int     rt_sock, rdlen, idx;
1474         union {
1475                 struct {
1476                         struct rt_msghdr rtm;
1477                         struct sockaddr_storage addrs[RTA_NUMBITS];
1478                 } r;
1479                 struct if_msghdr ifm;
1480                 struct ifa_msghdr ifam;
1481         } mbuf;
1482         struct ifa_msghdr *ifam = &mbuf.ifam;
1483         char    *me = "rts_mon";
1484 
1485         (void) thr_setname(thr_self(), me);
1486 
1487         rt_sock = socket(PF_ROUTE, SOCK_RAW, 0);
1488         if (rt_sock < 0) {
1489                 _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1490                 (me, "Failed to open routing socket: %s\n", strerror(errno));
1491                 thr_exit(0);
1492         }
1493 
1494         for (;;) {
1495                 rdlen = read(rt_sock, &mbuf, sizeof (mbuf));
1496                 if (rdlen <= 0) {
1497                         if (rdlen == 0 || (errno != EINTR && errno != EAGAIN)) {
1498                                 _NSCD_LOG(NSCD_LOG_FRONT_END,
1499                                     NSCD_LOG_LEVEL_ERROR)
1500                                 (me, "routing socket read: %s\n",
1501                                     strerror(errno));
1502                                 thr_exit(0);
1503                         }
1504                         continue;
1505                 }
1506                 if (ifam->ifam_version != RTM_VERSION) {
1507                                 _NSCD_LOG(NSCD_LOG_FRONT_END,
1508                                     NSCD_LOG_LEVEL_ERROR)
1509                                 (me, "rx unknown version (%d) on "
1510                                     "routing socket.\n",
1511                                     ifam->ifam_version);
1512                         continue;
1513                 }
1514                 switch (ifam->ifam_type) {
1515                 case RTM_NEWADDR:
1516                 case RTM_DELADDR:
1517                         /* if no ipnodes cache, then nothing to do */
1518                         idx = get_cache_idx("ipnodes");
1519                         if (cache_ctx_p[idx] == NULL ||
1520                             cache_ctx_p[idx]->reaper_on != nscd_true)
1521                                 break;
1522                         nsc_invalidate(cache_ctx_p[idx], NULL, NULL);
1523                         break;
1524                 case RTM_ADD:
1525                 case RTM_DELETE:
1526                 case RTM_CHANGE:
1527                 case RTM_GET:
1528                 case RTM_LOSING:
1529                 case RTM_REDIRECT:
1530                 case RTM_MISS:
1531                 case RTM_LOCK:
1532                 case RTM_OLDADD:
1533                 case RTM_OLDDEL:
1534                 case RTM_RESOLVE:
1535                 case RTM_IFINFO:
1536                 case RTM_CHGADDR:
1537                 case RTM_FREEADDR:
1538                         break;
1539                 default:
1540                         _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1541                         (me, "rx unknown msg type (%d) on routing socket.\n",
1542                             ifam->ifam_type);
1543                         break;
1544                 }
1545         }
1546 }
1547 
1548 static void
1549 keep_open_dns_socket(void)
1550 {
1551         _res.options |= RES_STAYOPEN; /* just keep this udp socket open */
1552 }