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