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