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