xref: /illumos-gate/usr/src/cmd/nscd/nscd_frontend.c (revision cb6207858a9fcc2feaee22e626912fba281ac969)
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 2006 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 static void
175 restart_if_cfgfile_changed()
176 {
177 
178 	static mutex_t	nsswitch_lock = DEFAULTMUTEX;
179 	static time_t	last_nsswitch_check = 0;
180 	static time_t	last_nsswitch_modified = 0;
181 	static time_t	last_resolv_modified = 0;
182 	time_t		now = time(NULL);
183 	char		*me = "restart_if_cfgfile_changed";
184 
185 	if (now - last_nsswitch_check <= _NSC_FILE_CHECK_TIME)
186 		return;
187 
188 	(void) mutex_lock(&nsswitch_lock);
189 
190 	if (now - last_nsswitch_check > _NSC_FILE_CHECK_TIME) {
191 		struct stat nss_buf;
192 		struct stat res_buf;
193 
194 		last_nsswitch_check = now;
195 
196 		(void) mutex_unlock(&nsswitch_lock); /* let others continue */
197 
198 		/*
199 		 *  This code keeps us from statting resolv.conf
200 		 *  if it doesn't exist, yet prevents us from ignoring
201 		 *  it if it happens to disappear later on for a bit.
202 		 */
203 
204 		if (last_resolv_modified >= 0) {
205 			if (stat("/etc/resolv.conf", &res_buf) < 0) {
206 				if (last_resolv_modified == 0)
207 				    last_resolv_modified = -1;
208 				else
209 				    res_buf.st_mtime = last_resolv_modified;
210 			} else if (last_resolv_modified == 0) {
211 			    last_resolv_modified = res_buf.st_mtime;
212 			}
213 		}
214 
215 		if (stat("/etc/nsswitch.conf", &nss_buf) < 0) {
216 
217 			/*EMPTY*/;
218 
219 		} else if (last_nsswitch_modified == 0) {
220 
221 			last_nsswitch_modified = nss_buf.st_mtime;
222 
223 		} else if ((last_nsswitch_modified < nss_buf.st_mtime) ||
224 		    ((last_resolv_modified > 0) &&
225 		    (last_resolv_modified < res_buf.st_mtime))) {
226 			static mutex_t exit_lock = DEFAULTMUTEX;
227 			char *fmri;
228 
229 			/*
230 			 * if in self cred mode, kill the forker and
231 			 * child nscds
232 			 */
233 			if (_nscd_is_self_cred_on(0, NULL)) {
234 				_nscd_kill_forker();
235 				_nscd_kill_all_children();
236 			}
237 
238 			/*
239 			 * time for restart
240 			 */
241 			_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_INFO)
242 			(me, "nscd restart due to %s or %s change\n",
243 				"/etc/nsswitch.conf", "resolv.conf");
244 			/*
245 			 * try to restart under smf
246 			 */
247 			if ((fmri = getenv("SMF_FMRI")) == NULL) {
248 				/* not running under smf - reexec */
249 				(void) execv(main_execname, main_argv);
250 				exit(1); /* just in case */
251 			}
252 
253 			/* prevent multiple restarts */
254 			(void) mutex_lock(&exit_lock);
255 
256 			if (smf_restart_instance(fmri) == 0)
257 				(void) sleep(10); /* wait a bit */
258 			exit(1); /* give up waiting for resurrection */
259 		}
260 
261 	} else
262 	    (void) mutex_unlock(&nsswitch_lock);
263 }
264 
265 uid_t
266 _nscd_get_client_euid()
267 {
268 	ucred_t	*uc = NULL;
269 	uid_t	id;
270 	char	*me = "get_client_euid";
271 
272 	if (door_ucred(&uc) != 0) {
273 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
274 		(me, "door_ucred: %s\n", strerror(errno));
275 		return ((uid_t)-1);
276 	}
277 
278 	id = ucred_geteuid(uc);
279 	ucred_free(uc);
280 	return (id);
281 }
282 
283 static void
284 N2N_check_priv(
285 	void			*buf,
286 	char			*dc_str)
287 {
288 	nss_pheader_t		*phdr = (nss_pheader_t *)buf;
289 	ucred_t			*uc = NULL;
290 	const priv_set_t	*eset;
291 	zoneid_t		zoneid;
292 	int			errnum;
293 	char			*me = "N2N_check_priv";
294 
295 	if (door_ucred(&uc) != 0) {
296 		errnum = errno;
297 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
298 		(me, "door_ucred: %s\n", strerror(errno));
299 
300 		NSCD_RETURN_STATUS(phdr, NSS_ERROR, errnum);
301 	}
302 
303 	eset = ucred_getprivset(uc, PRIV_EFFECTIVE);
304 	zoneid = ucred_getzoneid(uc);
305 
306 	if ((zoneid != GLOBAL_ZONEID && zoneid != getzoneid()) ||
307 		eset != NULL ? !priv_ismember(eset, PRIV_SYS_ADMIN) :
308 		ucred_geteuid(uc) != 0) {
309 
310 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT)
311 		(me, "%s call failed(cred): caller pid %d, uid %d, "
312 		"euid %d, zoneid %d\n", dc_str, ucred_getpid(uc),
313 		ucred_getruid(uc), ucred_geteuid(uc), zoneid);
314 		ucred_free(uc);
315 
316 		NSCD_RETURN_STATUS(phdr, NSS_ERROR, EACCES);
317 	}
318 
319 	_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
320 	(me, "nscd received %s cmd from pid %d, uid %d, "
321 	"euid %d, zoneid %d\n", dc_str, ucred_getpid(uc),
322 	ucred_getruid(uc), ucred_geteuid(uc), zoneid);
323 
324 	ucred_free(uc);
325 
326 	NSCD_RETURN_STATUS_SUCCESS(phdr);
327 }
328 
329 void
330 _nscd_APP_check_cred(
331 	void		*buf,
332 	pid_t		*pidp,
333 	char		*dc_str,
334 	int		log_comp,
335 	int		log_level)
336 {
337 	nss_pheader_t	*phdr = (nss_pheader_t *)buf;
338 	ucred_t		*uc = NULL;
339 	uid_t		ruid;
340 	uid_t		euid;
341 	pid_t		pid;
342 	int		errnum;
343 	char		*me = "_nscd_APP_check_cred";
344 
345 	if (door_ucred(&uc) != 0) {
346 		errnum = errno;
347 		_NSCD_LOG(log_comp, NSCD_LOG_LEVEL_ERROR)
348 		(me, "door_ucred: %s\n", strerror(errno));
349 
350 		NSCD_RETURN_STATUS(phdr, NSS_ERROR, errnum);
351 	}
352 
353 	NSCD_SET_STATUS_SUCCESS(phdr);
354 	pid = ucred_getpid(uc);
355 	if (NSS_PACKED_CRED_CHECK(buf, ruid = ucred_getruid(uc),
356 		euid = ucred_geteuid(uc))) {
357 		if (pidp != NULL) {
358 			if (*pidp == (pid_t)-1)
359 				*pidp = pid;
360 			else if (*pidp != pid) {
361 				NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES);
362 			}
363 		}
364 	} else {
365 		NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES);
366 	}
367 
368 	ucred_free(uc);
369 
370 	if (NSCD_STATUS_IS_NOT_OK(phdr)) {
371 		_NSCD_LOG(log_comp, log_level)
372 		(me, "%s call failed: caller pid %d (input pid = %d), ruid %d, "
373 		"euid %d, header ruid %d, header euid %d\n", dc_str,
374 		pid, (pidp != NULL) ? *pidp : -1, ruid, euid,
375 		((nss_pheader_t *)(buf))->p_ruid,
376 		((nss_pheader_t *)(buf))->p_euid);
377 	}
378 }
379 
380 static void
381 lookup(char *argp, size_t arg_size)
382 {
383 	nsc_lookup_args_t	largs;
384 	char			space[NSCD_LOOKUP_BUFSIZE];
385 	nss_pheader_t		*phdr = (nss_pheader_t *)(void *)argp;
386 
387 	NSCD_ALLOC_LOOKUP_BUFFER(argp, arg_size, phdr, space,
388 		sizeof (space));
389 
390 	(void) memset(&largs, 0, sizeof (largs));
391 	largs.buffer = argp;
392 	largs.bufsize = arg_size;
393 	nsc_lookup(&largs, 0);
394 
395 	/*
396 	 * only the PUN needs to keep track of the
397 	 * activity count to determine when to
398 	 * terminate itself
399 	 */
400 	if (_whoami == NSCD_CHILD) {
401 		(void) mutex_lock(&activity_lock);
402 		++activity;
403 		(void) mutex_unlock(&activity_lock);
404 	}
405 
406 	NSCD_SET_RETURN_ARG(phdr, arg_size);
407 	(void) door_return(argp, arg_size, NULL, 0);
408 }
409 
410 static void
411 getent(char *argp, size_t arg_size)
412 {
413 	char			space[NSCD_LOOKUP_BUFSIZE];
414 	nss_pheader_t		*phdr = (nss_pheader_t *)(void *)argp;
415 
416 	NSCD_ALLOC_LOOKUP_BUFFER(argp, arg_size, phdr,
417 		space, sizeof (space));
418 
419 	nss_pgetent(argp, arg_size);
420 
421 	NSCD_SET_RETURN_ARG(phdr, arg_size);
422 	(void) door_return(argp, arg_size, NULL, 0);
423 }
424 
425 static int
426 is_db_per_user(void *buf, char *dblist)
427 {
428 	nss_pheader_t	*phdr = (nss_pheader_t *)buf;
429 	nss_dbd_t	*pdbd;
430 	char		*dbname, *dbn;
431 	int		len;
432 
433 	/* copy db name into a temp buffer */
434 	pdbd = (nss_dbd_t *)((void *)((char *)buf + phdr->dbd_off));
435 	dbname = (char *)pdbd + pdbd->o_name;
436 	len = strlen(dbname);
437 	dbn = alloca(len + 2);
438 	(void) memcpy(dbn, dbname, len);
439 
440 	/* check if <dbname> + ',' can be found in the dblist string */
441 	dbn[len] = ',';
442 	dbn[len + 1] = '\0';
443 	if (strstr(dblist, dbn) != NULL)
444 		return (1);
445 
446 	/*
447 	 * check if <dbname> can be found in the last part
448 	 * of the dblist string
449 	 */
450 	dbn[len] = '\0';
451 	if (strstr(dblist, dbn) != NULL)
452 		return (1);
453 
454 	return (0);
455 }
456 
457 /*
458  * Check to see if all conditions are met for processing per-user
459  * requests. Returns 1 if yes, -1 if backend is not configured,
460  * 0 otherwise.
461  */
462 static int
463 need_per_user_door(void *buf, int whoami, uid_t uid, char **dblist)
464 {
465 	nss_pheader_t	*phdr = (nss_pheader_t *)buf;
466 
467 	NSCD_SET_STATUS_SUCCESS(phdr);
468 
469 	/* if already a per-user nscd, no need to get per-user door */
470 	if (whoami == NSCD_CHILD)
471 		return (0);
472 
473 	/* forker shouldn't be asked */
474 	if (whoami == NSCD_FORKER) {
475 		NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
476 		return (0);
477 	}
478 
479 	/* if door client is root, no need for a per-user door */
480 	if (uid == 0)
481 		return (0);
482 
483 	/*
484 	 * if per-user lookup is not configured, no per-user
485 	 * door available
486 	 */
487 	if (_nscd_is_self_cred_on(0, dblist) == 0)
488 		return (-1);
489 
490 	/*
491 	 * if per-user lookup is not configured for the db,
492 	 * don't bother
493 	 */
494 	if (is_db_per_user(phdr, *dblist) == 0)
495 		return (0);
496 
497 	return (1);
498 }
499 
500 static void
501 if_selfcred_return_per_user_door(char *argp, size_t arg_size,
502 	door_desc_t *dp, int whoami)
503 {
504 	nss_pheader_t	*phdr = (nss_pheader_t *)((void *)argp);
505 	char		*dblist;
506 	int		door = -1;
507 	int		rc = 0;
508 	door_desc_t	desc;
509 	char		space[1024*4];
510 
511 	/*
512 	 * check to see if self-cred is configured and
513 	 * need to return an alternate PUN door
514 	 */
515 	if (per_user_is_on == 1) {
516 		rc = need_per_user_door(argp, whoami,
517 			_nscd_get_client_euid(), &dblist);
518 		if (rc == -1)
519 			per_user_is_on = 0;
520 	}
521 	if (rc <= 0) {
522 		/*
523 		 * self-cred not configured, and no error detected,
524 		 * return to continue the door call processing
525 		 */
526 		if (NSCD_STATUS_IS_OK(phdr))
527 			return;
528 		else
529 			/*
530 			 * configured but error detected,
531 			 * stop the door call processing
532 			 */
533 			(void) door_return(argp, phdr->data_off, NULL, 0);
534 	}
535 
536 	/* get the alternate PUN door */
537 	_nscd_proc_alt_get(argp, &door);
538 	if (NSCD_GET_STATUS(phdr) != NSS_ALTRETRY) {
539 		(void) door_return(argp, phdr->data_off, NULL, 0);
540 	}
541 
542 	/* return the alternate door descriptor */
543 	(void) memcpy(space, phdr, NSCD_PHDR_LEN(phdr));
544 	argp = space;
545 	phdr = (nss_pheader_t *)(void *)space;
546 	dp = &desc;
547 	dp->d_attributes = DOOR_DESCRIPTOR;
548 	dp->d_data.d_desc.d_descriptor = door;
549 	phdr->data_len = strlen(dblist) + 1;
550 	(void) strcpy(((char *)phdr) + NSCD_PHDR_LEN(phdr), dblist);
551 
552 	arg_size = NSCD_PHDR_LEN(phdr) + NSCD_DATA_LEN(phdr);
553 	(void) door_return(argp, arg_size, dp, 1);
554 }
555 
556 /*ARGSUSED*/
557 static void
558 switcher(void *cookie, char *argp, size_t arg_size,
559     door_desc_t *dp, uint_t n_desc)
560 {
561 	int			iam;
562 	pid_t			ent_pid = -1;
563 	nss_pheader_t		*phdr = (nss_pheader_t *)((void *)argp);
564 	void			*uptr;
565 	int			buflen, len;
566 	int			callnum;
567 	char			*me = "switcher";
568 
569 	_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
570 	(me, "switcher ...\n");
571 
572 	if (argp == DOOR_UNREF_DATA) {
573 		(void) printf("Door Slam... exiting\n");
574 		exit(0);
575 	}
576 
577 	if (argp == NULL) { /* empty door call */
578 		(void) door_return(NULL, 0, 0, 0); /* return the favor */
579 	}
580 
581 	/*
582 	 *  need to restart if main nscd and config file(s) changed
583 	 */
584 	if (_whoami == NSCD_MAIN)
585 		restart_if_cfgfile_changed();
586 
587 	if ((phdr->nsc_callnumber & NSCDV2CATMASK) == NSCD_CALLCAT_APP) {
588 		switch (phdr->nsc_callnumber) {
589 		case NSCD_SEARCH:
590 
591 		/* if a fallback to main nscd, skip per-user setup */
592 		if (phdr->p_status != NSS_ALTRETRY)
593 			if_selfcred_return_per_user_door(argp, arg_size,
594 				dp, _whoami);
595 		lookup(argp, arg_size);
596 
597 		break;
598 
599 		case NSCD_SETENT:
600 
601 		_nscd_APP_check_cred(argp, &ent_pid, "NSCD_SETENT",
602 			NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT);
603 		if (NSCD_STATUS_IS_OK(phdr)) {
604 			if_selfcred_return_per_user_door(argp, arg_size,
605 				dp, _whoami);
606 			nss_psetent(argp, arg_size, ent_pid);
607 		}
608 		break;
609 
610 		case NSCD_GETENT:
611 
612 		getent(argp, arg_size);
613 		break;
614 
615 		case NSCD_ENDENT:
616 
617 		nss_pendent(argp, arg_size);
618 		break;
619 
620 		case NSCD_PUT:
621 
622 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
623 		(me, "door call NSCD_PUT not supported yet\n");
624 
625 		NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
626 		break;
627 
628 		case NSCD_GETHINTS:
629 
630 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
631 		(me, "door call NSCD_GETHINTS not supported yet\n");
632 
633 		NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
634 		break;
635 
636 		default:
637 
638 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
639 		(me, "Unknown name service door call op %x\n",
640 		phdr->nsc_callnumber);
641 
642 		NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL);
643 		break;
644 		}
645 
646 		(void) door_return(argp, arg_size, NULL, 0);
647 	}
648 
649 	iam = NSCD_MAIN;
650 	callnum = phdr->nsc_callnumber & ~NSCD_WHOAMI;
651 	if (callnum == NSCD_IMHERE ||
652 		callnum == NSCD_PULSE || callnum == NSCD_FORK)
653 		iam = phdr->nsc_callnumber & NSCD_WHOAMI;
654 	else
655 		callnum = phdr->nsc_callnumber;
656 
657 	/* nscd -> nscd v2 calls */
658 	switch (callnum) {
659 
660 	case NSCD_PING:
661 		NSCD_SET_STATUS_SUCCESS(phdr);
662 		break;
663 
664 	case NSCD_IMHERE:
665 		_nscd_proc_iamhere(argp, dp, n_desc, iam);
666 		break;
667 
668 	case NSCD_PULSE:
669 		N2N_check_priv(argp, "NSCD_PULSE");
670 		if (NSCD_STATUS_IS_OK(phdr))
671 			_nscd_proc_pulse(argp, iam);
672 		break;
673 
674 	case NSCD_FORK:
675 		N2N_check_priv(argp, "NSCD_FORK");
676 		if (NSCD_STATUS_IS_OK(phdr))
677 			_nscd_proc_fork(argp, iam);
678 		break;
679 
680 	case NSCD_KILL:
681 		N2N_check_priv(argp, "NSCD_KILL");
682 		if (NSCD_STATUS_IS_OK(phdr))
683 			exit(0);
684 		break;
685 
686 	case NSCD_REFRESH:
687 		if (_nscd_refresh() != NSCD_SUCCESS)
688 			exit(1);
689 		NSCD_SET_STATUS_SUCCESS(phdr);
690 		break;
691 
692 	case NSCD_GETPUADMIN:
693 
694 		if (_nscd_is_self_cred_on(0, NULL)) {
695 			_nscd_peruser_getadmin(argp, sizeof (nscd_admin_t));
696 		} else {
697 			NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0,
698 				NSCD_SELF_CRED_NOT_CONFIGURED);
699 		}
700 		break;
701 
702 	case NSCD_GETADMIN:
703 
704 		len = _nscd_door_getadmin((void *)argp);
705 		if (len == 0)
706 			break;
707 
708 		/* size of door buffer not big enough, allocate one */
709 		NSCD_ALLOC_DOORBUF(NSCD_GETADMIN, len, uptr, buflen);
710 
711 		/* copy packed header */
712 		*(nss_pheader_t *)uptr = *(nss_pheader_t *)((void *)argp);
713 
714 		/* set new buffer size */
715 		((nss_pheader_t *)uptr)->pbufsiz = buflen;
716 
717 		/* try one more time */
718 		(void) _nscd_door_getadmin((void *)uptr);
719 		(void) door_return(uptr, buflen, NULL, 0);
720 		break;
721 
722 	case NSCD_SETADMIN:
723 		N2N_check_priv(argp, "NSCD_SETADMIN");
724 		if (NSCD_STATUS_IS_OK(phdr))
725 			_nscd_door_setadmin(argp);
726 		break;
727 
728 	case NSCD_KILLSERVER:
729 		N2N_check_priv(argp, "NSCD_KILLSERVER");
730 		if (NSCD_STATUS_IS_OK(phdr)) {
731 			/* also kill the forker nscd if one is running */
732 			_nscd_kill_forker();
733 			exit(0);
734 		}
735 		break;
736 
737 	default:
738 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
739 		(me, "Unknown name service door call op %d\n",
740 			phdr->nsc_callnumber);
741 
742 		NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL);
743 
744 		(void) door_return(argp, arg_size, NULL, 0);
745 		break;
746 
747 	}
748 	(void) door_return(argp, arg_size, NULL, 0);
749 }
750 
751 int
752 _nscd_setup_server(char *execname, char **argv)
753 {
754 
755 	int		fd;
756 	int		errnum;
757 	int		bind_failed = 0;
758 	struct stat	buf;
759 	sigset_t	myset;
760 	struct sigaction action;
761 	char		*me = "_nscd_setup_server";
762 
763 	main_execname = execname;
764 	main_argv = argv;
765 
766 	keep_open_dns_socket();
767 
768 	/*
769 	 * the max number of server threads should be fixed now, so
770 	 * set flag to indicate that no in-flight change is allowed
771 	 */
772 	max_servers_set = 1;
773 
774 	(void) thr_keycreate(&lookup_state_key, NULL);
775 	(void) sema_init(&common_sema,
776 			frontend_cfg_g.common_worker_threads,
777 			USYNC_THREAD, 0);
778 
779 	/* Establish server thread pool */
780 	(void) door_server_create(server_create);
781 	if (thr_keycreate(&server_key, server_destroy) != 0) {
782 		errnum = errno;
783 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
784 		(me, "thr_keycreate (server thread): %s\n",
785 			strerror(errnum));
786 		return (-1);
787 	}
788 
789 	/* Create a door */
790 	if ((fd = door_create(switcher, NAME_SERVICE_DOOR_COOKIE,
791 	    DOOR_UNREF | DOOR_NO_CANCEL)) < 0) {
792 		errnum = errno;
793 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
794 		(me, "door_create: %s\n", strerror(errnum));
795 		return (-1);
796 	}
797 
798 	/* if not main nscd, no more setup to do */
799 	if (_whoami != NSCD_MAIN)
800 		return (fd);
801 
802 	/* bind to file system */
803 	if (is_system_labeled()) {
804 		if (stat(TSOL_NAME_SERVICE_DOOR, &buf) < 0) {
805 			int newfd;
806 			if ((newfd = creat(TSOL_NAME_SERVICE_DOOR, 0444)) < 0) {
807 				errnum = errno;
808 				_NSCD_LOG(NSCD_LOG_FRONT_END,
809 					NSCD_LOG_LEVEL_ERROR)
810 				(me, "Cannot create %s: %s\n",
811 					TSOL_NAME_SERVICE_DOOR,
812 					strerror(errnum));
813 				bind_failed = 1;
814 			}
815 			(void) close(newfd);
816 		}
817 		if (symlink(TSOL_NAME_SERVICE_DOOR, NAME_SERVICE_DOOR) != 0) {
818 			if (errno != EEXIST) {
819 				errnum = errno;
820 				_NSCD_LOG(NSCD_LOG_FRONT_END,
821 					NSCD_LOG_LEVEL_ERROR)
822 				(me, "Cannot symlink %s: %s\n",
823 					NAME_SERVICE_DOOR,
824 					strerror(errnum));
825 				bind_failed = 1;
826 			}
827 		}
828 	} else if (stat(NAME_SERVICE_DOOR, &buf) < 0) {
829 		int newfd;
830 		if ((newfd = creat(NAME_SERVICE_DOOR, 0444)) < 0) {
831 			errnum = errno;
832 			_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
833 			(me, "Cannot create %s: %s\n", NAME_SERVICE_DOOR,
834 				strerror(errnum));
835 			bind_failed = 1;
836 		}
837 		(void) close(newfd);
838 	}
839 
840 	if (bind_failed == 1) {
841 		(void) door_revoke(fd);
842 		return (-1);
843 	}
844 
845 	if (fattach(fd, NAME_SERVICE_DOOR) < 0) {
846 		if ((errno != EBUSY) ||
847 		(fdetach(NAME_SERVICE_DOOR) <  0) ||
848 		(fattach(fd, NAME_SERVICE_DOOR) < 0)) {
849 			errnum = errno;
850 			_NSCD_LOG(NSCD_LOG_FRONT_END,
851 				NSCD_LOG_LEVEL_ERROR)
852 			(me, "fattach: %s\n", strerror(errnum));
853 			(void) door_revoke(fd);
854 			return (-1);
855 		}
856 	}
857 
858 	/*
859 	 * kick off routing socket monitor thread
860 	 */
861 	if (thr_create(NULL, NULL,
862 		(void *(*)(void *))rts_mon, 0, 0, NULL) != 0) {
863 		errnum = errno;
864 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
865 		(me, "thr_create (routing socket monitor): %s\n",
866 			strerror(errnum));
867 
868 		(void) door_revoke(fd);
869 		return (-1);
870 	}
871 
872 	/*
873 	 * set up signal handler for SIGHUP
874 	 */
875 	action.sa_handler = dozip;
876 	action.sa_flags = 0;
877 	(void) sigemptyset(&action.sa_mask);
878 	(void) sigemptyset(&myset);
879 	(void) sigaddset(&myset, SIGHUP);
880 
881 	if (sigaction(SIGHUP, &action, NULL) < 0) {
882 		errnum = errno;
883 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
884 		(me, "sigaction (SIGHUP): %s\n", strerror(errnum));
885 
886 		(void) door_revoke(fd);
887 		return (-1);
888 	}
889 
890 	return (fd);
891 }
892 
893 int
894 _nscd_setup_child_server(int did)
895 {
896 
897 	int		errnum;
898 	int		fd;
899 	nscd_rc_t	rc;
900 	char		*me = "_nscd_setup_child_server";
901 
902 	/* Re-establish our own server thread pool */
903 	(void) door_server_create(server_create);
904 	if (thr_keycreate(&server_key, server_destroy) != 0) {
905 		errnum = errno;
906 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
907 		(me, "thr_keycreate failed: %s", strerror(errnum));
908 		return (-1);
909 	}
910 
911 	/*
912 	 * Create a new door.
913 	 * Keep DOOR_REFUSE_DESC (self-cred nscds don't fork)
914 	 */
915 	(void) close(did);
916 	if ((fd = door_create(switcher,
917 		NAME_SERVICE_DOOR_COOKIE,
918 		DOOR_REFUSE_DESC|DOOR_UNREF|DOOR_NO_CANCEL)) < 0) {
919 		errnum = errno;
920 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
921 		(me, "door_create failed: %s", strerror(errnum));
922 		return (-1);
923 	}
924 
925 	/*
926 	 * kick off routing socket monitor thread
927 	 */
928 	if (thr_create(NULL, NULL,
929 		(void *(*)(void *))rts_mon, 0, 0, NULL) != 0) {
930 		errnum = errno;
931 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
932 		(me, "thr_create (routing socket monitor): %s\n",
933 			strerror(errnum));
934 		(void) door_revoke(fd);
935 		return (-1);
936 	}
937 
938 	/*
939 	 * start monitoring the states of the name service clients
940 	 */
941 	rc = _nscd_init_smf_monitor();
942 	if (rc != NSCD_SUCCESS) {
943 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
944 	(me, "unable to start the SMF monitor (rc = %d)\n", rc);
945 
946 		(void) door_revoke(fd);
947 		return (-1);
948 	}
949 
950 	return (fd);
951 }
952 
953 nscd_rc_t
954 _nscd_alloc_frontend_cfg()
955 {
956 	frontend_cfg  = calloc(NSCD_NUM_DB, sizeof (nscd_cfg_frontend_t));
957 	if (frontend_cfg == NULL)
958 		return (NSCD_NO_MEMORY);
959 
960 	return (NSCD_SUCCESS);
961 }
962 
963 
964 /* ARGSUSED */
965 nscd_rc_t
966 _nscd_cfg_frontend_notify(
967 	void				*data,
968 	struct nscd_cfg_param_desc	*pdesc,
969 	nscd_cfg_id_t			*nswdb,
970 	nscd_cfg_flag_t			dflag,
971 	nscd_cfg_error_t		**errorp,
972 	void				*cookie)
973 {
974 	void				*dp;
975 
976 	/*
977 	 * At init time, the whole group of config params are received.
978 	 * At update time, group or individual parameter value could
979 	 * be received.
980 	 */
981 
982 	if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_INIT) ||
983 		_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) {
984 		/*
985 		 * group data is received, copy in the
986 		 * entire strcture
987 		 */
988 		if (_nscd_cfg_flag_is_set(pdesc->pflag,
989 			NSCD_CFG_PFLAG_GLOBAL))
990 			frontend_cfg_g =
991 				*(nscd_cfg_global_frontend_t *)data;
992 		else
993 			frontend_cfg[nswdb->index] =
994 				*(nscd_cfg_frontend_t *)data;
995 
996 	} else {
997 		/*
998 		 * individual paramater is received: copy in the
999 		 * parameter value.
1000 		 */
1001 		if (_nscd_cfg_flag_is_set(pdesc->pflag,
1002 			NSCD_CFG_PFLAG_GLOBAL))
1003 			dp = (char *)&frontend_cfg_g + pdesc->p_offset;
1004 		else
1005 			dp = (char *)&frontend_cfg[nswdb->index] +
1006 				pdesc->p_offset;
1007 		(void) memcpy(dp, data, pdesc->p_size);
1008 	}
1009 
1010 	return (NSCD_SUCCESS);
1011 }
1012 
1013 /* ARGSUSED */
1014 nscd_rc_t
1015 _nscd_cfg_frontend_verify(
1016 	void				*data,
1017 	struct	nscd_cfg_param_desc	*pdesc,
1018 	nscd_cfg_id_t			*nswdb,
1019 	nscd_cfg_flag_t			dflag,
1020 	nscd_cfg_error_t		**errorp,
1021 	void				**cookie)
1022 {
1023 
1024 	char				*me = "_nscd_cfg_frontend_verify";
1025 
1026 	/*
1027 	 * if max. number of server threads is set and in effect,
1028 	 * don't allow changing of the frontend configuration
1029 	 */
1030 	if (max_servers_set) {
1031 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_INFO)
1032 	(me, "changing of the frontend configuration not allowed now");
1033 
1034 		return (NSCD_CFG_CHANGE_NOT_ALLOWED);
1035 	}
1036 
1037 	return (NSCD_SUCCESS);
1038 }
1039 
1040 /* ARGSUSED */
1041 nscd_rc_t
1042 _nscd_cfg_frontend_get_stat(
1043 	void				**stat,
1044 	struct nscd_cfg_stat_desc	*sdesc,
1045 	nscd_cfg_id_t			*nswdb,
1046 	nscd_cfg_flag_t			*dflag,
1047 	void				(**free_stat)(void *stat),
1048 	nscd_cfg_error_t		**errorp)
1049 {
1050 	return (NSCD_SUCCESS);
1051 }
1052 
1053 void
1054 _nscd_init_cache_sema(sema_t *sema, char *cache_name)
1055 {
1056 	int	i, j;
1057 	char	*dbn;
1058 
1059 	if (max_servers == 0)
1060 		max_servers = frontend_cfg_g.common_worker_threads +
1061 		frontend_cfg_g.cache_hit_threads;
1062 
1063 	for (i = 0; i < NSCD_NUM_DB; i++) {
1064 
1065 		dbn = NSCD_NSW_DB_NAME(i);
1066 		if (strcasecmp(dbn, cache_name) == 0) {
1067 			j = frontend_cfg[i].worker_thread_per_nsw_db;
1068 			(void) sema_init(sema, j, USYNC_THREAD, 0);
1069 			max_servers += j;
1070 			break;
1071 		}
1072 	}
1073 }
1074 
1075 /*
1076  * Monitor the routing socket.  Address lists stored in the ipnodes
1077  * cache are sorted based on destination address selection rules,
1078  * so when things change that could affect that sorting (interfaces
1079  * go up or down, flags change, etc.), we clear that cache so the
1080  * list will be re-ordered the next time the hostname is resolved.
1081  */
1082 static void
1083 rts_mon(void)
1084 {
1085 	int	rt_sock, rdlen, idx;
1086 	union {
1087 		struct {
1088 			struct rt_msghdr rtm;
1089 			struct sockaddr_storage addrs[RTA_NUMBITS];
1090 		} r;
1091 		struct if_msghdr ifm;
1092 		struct ifa_msghdr ifam;
1093 	} mbuf;
1094 	struct ifa_msghdr *ifam = &mbuf.ifam;
1095 	char	*me = "rts_mon";
1096 
1097 	rt_sock = socket(PF_ROUTE, SOCK_RAW, 0);
1098 	if (rt_sock < 0) {
1099 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1100 		(me, "Failed to open routing socket: %s\n", strerror(errno));
1101 		thr_exit(0);
1102 	}
1103 
1104 	for (;;) {
1105 		rdlen = read(rt_sock, &mbuf, sizeof (mbuf));
1106 		if (rdlen <= 0) {
1107 			if (rdlen == 0 || (errno != EINTR && errno != EAGAIN)) {
1108 				_NSCD_LOG(NSCD_LOG_FRONT_END,
1109 					NSCD_LOG_LEVEL_ERROR)
1110 				(me, "routing socket read: %s\n",
1111 					strerror(errno));
1112 				thr_exit(0);
1113 			}
1114 			continue;
1115 		}
1116 		if (ifam->ifam_version != RTM_VERSION) {
1117 				_NSCD_LOG(NSCD_LOG_FRONT_END,
1118 					NSCD_LOG_LEVEL_ERROR)
1119 				(me, "rx unknown version (%d) on "
1120 					"routing socket.\n",
1121 					ifam->ifam_version);
1122 			continue;
1123 		}
1124 		switch (ifam->ifam_type) {
1125 		case RTM_NEWADDR:
1126 		case RTM_DELADDR:
1127 			/* if no ipnodes cache, then nothing to do */
1128 			idx = get_cache_idx("ipnodes");
1129 			if (cache_ctx_p[idx] == NULL ||
1130 				cache_ctx_p[idx]->reaper_on != nscd_true)
1131 				break;
1132 			nsc_invalidate(cache_ctx_p[idx], NULL, NULL);
1133 			break;
1134 		case RTM_ADD:
1135 		case RTM_DELETE:
1136 		case RTM_CHANGE:
1137 		case RTM_GET:
1138 		case RTM_LOSING:
1139 		case RTM_REDIRECT:
1140 		case RTM_MISS:
1141 		case RTM_LOCK:
1142 		case RTM_OLDADD:
1143 		case RTM_OLDDEL:
1144 		case RTM_RESOLVE:
1145 		case RTM_IFINFO:
1146 			break;
1147 		default:
1148 			_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1149 			(me, "rx unknown msg type (%d) on routing socket.\n",
1150 			    ifam->ifam_type);
1151 			break;
1152 		}
1153 	}
1154 }
1155 
1156 static void
1157 keep_open_dns_socket(void)
1158 {
1159 	_res.options |= RES_STAYOPEN; /* just keep this udp socket open */
1160 }
1161