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