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