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