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