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