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