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