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