xref: /illumos-gate/usr/src/cmd/nscd/nscd_switch.c (revision 24fe0b3bf671e123467ce1df0b67cadd3614c8e4)
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 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdlib.h>	/* getenv() */
28 #include <assert.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <pthread.h>
32 #include <dlfcn.h>
33 #include <nss_dbdefs.h>
34 #include <exec_attr.h>
35 #include <gssapi/gssapi.h>
36 #include "nscd_door.h"
37 #include "nscd_switch.h"
38 #include "nscd_log.h"
39 #include "nscd_frontend.h"
40 
41 #pragma weak nss_search = _nss_search
42 #define	nss_search	_nss_search
43 
44 extern rwlock_t nscd_smf_service_state_lock;
45 
46 /* nscd id: main, forker, or child */
47 extern int _whoami;
48 
49 static int
50 retry_test(nss_status_t res, int n, struct __nsw_lookup_v1 *lkp)
51 {
52 	if (res != NSS_TRYAGAIN && res !=  NSS_NISSERVDNS_TRYAGAIN) {
53 		if (res == NSS_SUCCESS) {
54 			__NSW_UNPAUSE_ACTION(lkp->actions[__NSW_TRYAGAIN]);
55 			__NSW_UNPAUSE_ACTION(
56 			    lkp->actions[__NSW_NISSERVDNS_TRYAGAIN]);
57 		}
58 		return (0);
59 	}
60 
61 	if ((res == NSS_TRYAGAIN &&
62 	    lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER) ||
63 	    (res == NSS_NISSERVDNS_TRYAGAIN &&
64 	    lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER))
65 		return (1);
66 
67 	if (res == NSS_TRYAGAIN &&
68 	    lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
69 		if (n <= lkp->max_retries)
70 			return (1);
71 		else {
72 			lkp->actions[__NSW_TRYAGAIN] = __NSW_TRYAGAIN_PAUSED;
73 			return (0);
74 		}
75 
76 	if (res == NSS_NISSERVDNS_TRYAGAIN &&
77 	    lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
78 		if (n <= lkp->max_retries)
79 			return (1);
80 		else {
81 			lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] =
82 			    __NSW_TRYAGAIN_PAUSED;
83 			return (0);
84 		}
85 
86 	return (0);
87 }
88 
89 static thread_key_t loopback_key = THR_ONCE_KEY;
90 typedef struct lb_key {
91 	int		srci;
92 	int		dbi;
93 	int		fnum;
94 	int		*lb_flagp;
95 } lb_key_t;
96 
97 static int
98 set_loopback_key(lb_key_t *key) {
99 
100 	int		rc;
101 
102 	rc = thr_keycreate_once(&loopback_key, NULL);
103 	/* set key if not already set */
104 	if (rc == 0 && pthread_getspecific(loopback_key) == NULL)
105 		rc = thr_setspecific(loopback_key, key);
106 
107 	return (rc);
108 }
109 
110 static lb_key_t *
111 get_loopback_key(void) {
112 
113 	char		*me = "get_loopback_key";
114 	lb_key_t	*k = NULL;
115 
116 	k = pthread_getspecific(loopback_key);
117 
118 	_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
119 	(me, "get loopback key, key = %p\n", k);
120 
121 	return (k);
122 }
123 
124 static void
125 clear_loopback_key(lb_key_t *key) {
126 
127 	char		*me = "clear_loopback_key";
128 
129 	if (loopback_key != THR_ONCE_KEY && key != NULL) {
130 		/*
131 		 * key->lb_flagp points to the location of the
132 		 * flag, check_flag, in the stack where it was
133 		 * first set; clearing the flag tells that
134 		 * stack the loopback error has been resolved
135 		 */
136 		*key->lb_flagp = 0;
137 		(void) thr_setspecific(loopback_key, NULL);
138 	}
139 
140 	_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
141 	(me, "key %p cleared\n", key);
142 }
143 
144 static thread_key_t initf_key = THR_ONCE_KEY;
145 
146 static int
147 set_initf_key(void *pbuf) {
148 
149 	int		rc;
150 
151 	rc = thr_keycreate_once(&initf_key, NULL);
152 	if (rc == 0)
153 		rc = thr_setspecific(initf_key, pbuf);
154 
155 	return (rc);
156 }
157 
158 static void *
159 get_initf_key(void) {
160 
161 	char		*me = "get_initf_key";
162 	void		*pbuf;
163 
164 	pbuf = pthread_getspecific(initf_key);
165 
166 	_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
167 	(me, "got initf pbuf, key = %p\n", pbuf);
168 
169 	return (pbuf);
170 }
171 
172 static void
173 clear_initf_key(void) {
174 
175 	char		*me = "clear_initf_key";
176 
177 	(void) thr_setspecific(initf_key, NULL);
178 
179 	_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
180 	(me, "initf pbuf cleared\n");
181 }
182 
183 /*
184  * Call the input initf function to extract the
185  * NSS front end parameters and examine them to
186  * determine if an NSS lookup is to be performed
187  * on a regular or a pseudo (called from compat
188  * backend) database. Then set the necessary
189  * parameters for later data structures creation
190  * and processing.
191  */
192 static nscd_rc_t
193 getparams(
194 	int			search_fnum,
195 	nss_db_initf_t		initf,
196 	nscd_nsw_params_t	*params)
197 {
198 
199 	nscd_rc_t	rc = NSCD_SUCCESS;
200 	nss_db_params_t	*p;
201 	int		j;
202 	char		*dbn;
203 	const char	*n;
204 	char		*me = "getparams";
205 
206 	p = &params->p;
207 	(void) memset(p, 0, sizeof (*p));
208 	(*initf)(p);
209 	params->dbi = -1;
210 	params->cfgdbi = -1;
211 	params->compati = -1;
212 	params->dnsi = -1;
213 
214 	/* map database name to index */
215 	n = p->name;
216 	for (j = 0; j < NSCD_NUM_DB; j++) {
217 		dbn = NSCD_NSW_DB_NAME(j);
218 		if (*n != *dbn)
219 			continue;
220 		if (strcmp(n, dbn) == 0) {
221 			params->dbi = j;
222 			if (*n != 'h' && *n != 'i' && *n != 's' && *n != 'a')
223 				break;
224 			if (strcmp(n, NSS_DBNAM_HOSTS) == 0 &&
225 			    search_fnum == NSS_DBOP_HOSTS_BYNAME)
226 				params->dnsi = 0;
227 			else if (strcmp(n, NSS_DBNAM_IPNODES) == 0 &&
228 			    search_fnum == NSS_DBOP_IPNODES_BYNAME)
229 				params->dnsi = 1;
230 			else if (strcmp(n, NSS_DBNAM_SHADOW) == 0)
231 				params->privdb = 1;
232 			break;
233 		}
234 	}
235 
236 	/*
237 	 * use the switch policy for passwd_compat or
238 	 * group_compat?
239 	 */
240 	if (p->config_name != NULL) {
241 
242 		n = p->config_name;
243 		for (j = 0; j < NSCD_NUM_DB; j++) {
244 			dbn = NSCD_NSW_DB_NAME(j);
245 			if (*n == *dbn) {
246 				if (strcmp(n, dbn) == 0) {
247 					params->cfgdbi = j;
248 					break;
249 				}
250 			}
251 		}
252 	}
253 
254 	/* map the database name to the pseudo database index */
255 	if (params->cfgdbi != -1) {
256 		if (strstr(p->config_name, "_compat") != NULL) {
257 			n = p->name;
258 			for (j = params->cfgdbi; j < NSCD_NUM_DB; j++) {
259 				dbn = NSCD_NSW_DB_NAME(j);
260 				if (*n == *dbn) {
261 					if (strcmp(n, dbn) == 0) {
262 						params->compati = j;
263 						break;
264 					}
265 				}
266 			}
267 		}
268 	}
269 
270 	/*
271 	 * if unsupported database, let caller determine what to do next
272 	 */
273 	if (params->dbi == -1) {
274 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
275 		(me, "unsupported database: %s\n", p->name);
276 		return (NSCD_CFG_UNSUPPORTED_SWITCH_DB);
277 	}
278 
279 	return (rc);
280 }
281 
282 static void
283 nscd_initf(nss_db_params_t	*p)
284 {
285 	nss_pheader_t		*pbuf;
286 	nssuint_t		off;
287 	nss_dbd_t		*pdbd;
288 	char			*me = "nscd_initf";
289 
290 	pbuf = (nss_pheader_t *)get_initf_key();
291 	if (pbuf == NULL) {
292 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
293 		(me, "ERROR: initf key not set\n");
294 		return;
295 	}
296 
297 	if (pbuf->dbd_len <= sizeof (nss_dbd_t)) {
298 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
299 		(me, "invalid db front params data ? dbd_len = %d\n",
300 		    pbuf->dbd_len);
301 		return;
302 	}
303 
304 	off = pbuf->dbd_off;
305 	pdbd = (nss_dbd_t *)((void *)((char *)pbuf + off));
306 
307 	p->name = (char *)pdbd + pdbd->o_name;
308 	p->config_name = (char *)pdbd + pdbd->o_config_name;
309 	p->default_config = (char *)pdbd + pdbd->o_default_config;
310 	p->flags = (enum nss_dbp_flags)pdbd->flags;
311 	(void) memcpy(&p->private, &pbuf->nscdpriv, sizeof (p->private));
312 
313 	_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
314 	(me, "db frontend params: name =%s, config_name = %s, "
315 	"default_config = %s, flags = %x\n", p->name,
316 	    (p->config_name && *p->config_name != '\0' ?
317 	    p->config_name : "<NOT SPECIFIED>"),
318 	    (p->default_config && *p->default_config != '\0' ?
319 	    p->default_config : "<NOT SPECIFIED>"),
320 	    p->flags);
321 }
322 
323 
324 static void
325 trace_result(
326 	int		dbi,
327 	int		srci,
328 	int		op,
329 	nss_status_t	res,
330 	nss_XbyY_args_t	*arg)
331 {
332 	char	*res_str;
333 	char	*src = "?";
334 	char	*db = "?";
335 	char	*data_str = "<NOT STRING FORMAT>";
336 	int	data_len = 0;
337 	char	*me = "trace_result";
338 
339 	switch (res) {
340 	case NSS_SUCCESS:
341 		res_str = "NSS_SUCCESS";
342 		break;
343 	case NSS_NOTFOUND:
344 		res_str = "NSS_NOTFOUND";
345 		break;
346 	case NSS_UNAVAIL:
347 		res_str = "NSS_UNAVAIL";
348 		break;
349 	case NSS_TRYAGAIN:
350 		res_str = "NSS_TRYAGAIN";
351 		break;
352 	case NSS_NISSERVDNS_TRYAGAIN:
353 		res_str = "NSS_NISSERVDNS_TRYAGAIN";
354 		break;
355 	default:
356 		res_str = "UNKNOWN STATUS";
357 		break;
358 	}
359 
360 	if (dbi != -1)
361 		db = NSCD_NSW_DB_NAME(dbi);
362 	if (srci != -1)
363 		src = NSCD_NSW_SRC_NAME(srci);
364 
365 	if (arg->buf.result == NULL) {
366 		data_str = arg->buf.buffer;
367 		data_len = arg->returnlen;
368 	}
369 
370 	if (res == NSS_SUCCESS) {
371 		_nscd_logit(me, "%s: database: %s, operation: %d, "
372 		    "source: %s returned >>%s<<, length = %d\n",
373 		    res_str, db, op, src, data_str, data_len);
374 		return;
375 	}
376 
377 	_nscd_logit(me, "%s: database: %s, operation: %d, source: %s, "
378 	    "erange= %d, herrno: %s (%d)\n",
379 	    res_str, db, op, src, arg->erange, hstrerror(arg->h_errno),
380 	    arg->h_errno);
381 }
382 
383 /*
384  * Determine if a request should be done locally in the getXbyY caller's
385  * process. Return none zero if yes, 0 otherwise. This should be called
386  * before the switch engine steps through the backends/sources.
387  * This function returns 1 if:
388  *   -- the database is exec_attr and the search_flag is GET_ALL
389  */
390 static int
391 try_local(
392 	int			dbi,
393 	void			*arg)
394 {
395 	struct nss_XbyY_args	*ap = (struct nss_XbyY_args *)arg;
396 	_priv_execattr		*ep;
397 	int			rc = 0;
398 	char			*me = "try_local";
399 
400 	if (strcmp(NSCD_NSW_DB_NAME(dbi), NSS_DBNAM_EXECATTR) == 0) {
401 		if ((ep = ap->key.attrp) != NULL && IS_GET_ALL(ep->search_flag))
402 			rc = 1;
403 	}
404 
405 	if (rc != 0) {
406 
407 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
408 		(me, "TRYLOCAL: exec_attr:GET_ALL\n");
409 	}
410 
411 	return (rc);
412 }
413 
414 /*
415  * Determine if a request should be done locally in the getXbyY caller's
416  * process. Return none zero if yes, 0 otherwise. This should be called
417  * before the switch engine invokes any backend.
418  * This function returns 1 if:
419  *   -- the database is shadow and the source is nisplus OR
420  *   -- the database is shadow and the source is compat
421  */
422 static int
423 try_local2(
424 	int	dbi,
425 	int	srci)
426 {
427 	int	rc = 0;
428 	char	*me = "try_local2";
429 
430 	if (*NSCD_NSW_DB_NAME(dbi) == 's' &&
431 	    strcmp(NSCD_NSW_DB_NAME(dbi), NSS_DBNAM_SHADOW) == 0) {
432 		if (strcmp(NSCD_NSW_SRC_NAME(srci), "nisplus") == 0 ||
433 		    strcmp(NSCD_NSW_SRC_NAME(srci), "compat") == 0)
434 			rc = 1;
435 	}
436 
437 	if (rc != 0) {
438 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
439 		(me, "TRYLOCAL: database: shadow, source: %s\n",
440 		    NSCD_NSW_SRC_NAME(srci));
441 	}
442 
443 	return (rc);
444 }
445 
446 static nscd_rc_t
447 get_lib_func(void **handle, void **func, mutex_t *lock,
448 	char *lib, char *name, void **func_p)
449 {
450 	char	*me = "get_lib_func";
451 	void	*sym;
452 
453 	if (func_p != NULL && *handle != NULL && *func != NULL) {
454 		*func_p = *func;
455 		return (NSCD_SUCCESS);
456 	}
457 
458 	(void) mutex_lock(lock);
459 
460 	/* close the handle if requested */
461 	if (func_p == NULL) {
462 		if (*handle != NULL) {
463 			(void) dlclose(*handle);
464 			*handle = NULL;
465 			*func = NULL;
466 		}
467 		(void) mutex_unlock(lock);
468 		return (NSCD_SUCCESS);
469 	}
470 
471 	if (*handle != NULL && *func != NULL) {
472 		*func_p = *func;
473 		(void) mutex_unlock(lock);
474 		return (NSCD_SUCCESS);
475 	}
476 
477 	if (*handle == NULL) {
478 		*handle = dlopen(lib, RTLD_LAZY);
479 		if (*handle == NULL) {
480 			_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_ERROR)
481 			(me, "unable to dlopen %s\n", lib);
482 			(void) mutex_unlock(lock);
483 			return (NSCD_CFG_DLOPEN_ERROR);
484 		}
485 	}
486 
487 	if ((sym = dlsym(*handle, name)) == NULL) {
488 
489 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_ERROR)
490 		(me, "unable to find symbol %s:%s\n", lib, name);
491 		(void) mutex_unlock(lock);
492 		return (NSCD_CFG_DLSYM_ERROR);
493 	} else {
494 		*func_p = sym;
495 		*func = sym;
496 	}
497 
498 	(void) mutex_unlock(lock);
499 	return (NSCD_SUCCESS);
500 }
501 
502 static nscd_rc_t
503 get_libc_nss_search(void **func_p)
504 {
505 	static void	*handle = NULL;
506 	static void	*func = NULL;
507 	static mutex_t	lock = DEFAULTMUTEX;
508 
509 	return (get_lib_func(&handle, &func, &lock,
510 	    "libc.so", "nss_search", func_p));
511 }
512 
513 static nscd_rc_t
514 get_gss_func(void **func_p)
515 {
516 	static void	*handle = NULL;
517 	static void	*func = NULL;
518 	static mutex_t	lock = DEFAULTMUTEX;
519 
520 	return (get_lib_func(&handle, &func, &lock,
521 	    "libgss.so", "gss_inquire_cred", func_p));
522 }
523 
524 static nscd_rc_t
525 get_sldap_shadow_func(void **func_p)
526 {
527 	static void	*handle = NULL;
528 	static void	*func = NULL;
529 	static mutex_t	lock = DEFAULTMUTEX;
530 
531 	return (get_lib_func(&handle, &func, &lock,
532 	    "libsldap.so", "__ns_ldap_is_shadow_update_enabled",
533 	    func_p));
534 }
535 
536 /*
537  * get_dns_funcs returns pointers to gethostbyname functions in the
538  * dynamically loaded nss_dns & nss_mdns modules that return host
539  * lookup results along with the TTL value in the DNS resource
540  * records. The dnsi parameter indicates whether the lookup database
541  * is hosts(0) or ipnodes(1). The srcname parameter identifies the DNS
542  * module: dns/mdns and the function returns the address of the specific
543  * gethostbyname function in func_p variable.
544  */
545 static nscd_rc_t
546 get_dns_funcs(int dnsi, nss_status_t (**func_p)(), const char *srcname)
547 {
548 	int		si;
549 	void		**funcpp;
550 	static void	*handle[2] = { NULL, NULL };
551 	static mutex_t	func_lock[2] = { DEFAULTMUTEX, DEFAULTMUTEX };
552 	static void 	*func[2][2] = {{NULL, NULL}, {NULL, NULL}};
553 	static const char	*lib[2] = { "nss_dns.so.1", "nss_mdns.so.1" };
554 	static const char 	*func_name[2][2] =
555 		{{ "_nss_get_dns_hosts_name", "_nss_get_dns_ipnodes_name" },
556 		{ "_nss_get_mdns_hosts_name", "_nss_get_mdns_ipnodes_name" }};
557 
558 	/* source index: 0 = dns, 1 = mdns */
559 	if (strcmp(srcname, "dns") == 0)
560 		si = 0;
561 	else
562 		si = 1;
563 
564 	/*
565 	 * function index (func[si][dnsi]):
566 	 * [0,0] = dns/hosts, [0,1] = dns/ipnodes,
567 	 * [1,0] = mdns/hosts, [1,1] = mdns/ipnodes
568 	 */
569 
570 	if (dnsi < 0) { /* close handle */
571 		funcpp = NULL;
572 		(void) mutex_lock(&func_lock[si]);
573 		func[si][0] = NULL;
574 		func[si][1] = NULL;
575 		(void) mutex_unlock(&func_lock[si]);
576 	} else
577 		funcpp = (void **)func_p;
578 
579 	return (get_lib_func(&handle[si], &func[si][dnsi], &func_lock[si],
580 	    (char *)lib[si], (char *)func_name[si][dnsi], funcpp));
581 }
582 
583 static nss_status_t
584 search_dns_withttl(nscd_sw_return_t *swret, const char *srcname, int dnsi)
585 {
586 	nss_status_t	(*func)();
587 	nss_status_t	res = NSS_UNAVAIL;
588 	nscd_rc_t	rc;
589 
590 	swret->noarg = 0;
591 	if (strcmp(srcname, "dns") != 0 && strcmp(srcname, "mdns") != 0)
592 		return (NSS_ERROR);
593 
594 	rc = get_dns_funcs(dnsi, &func, srcname);
595 	if (rc == NSCD_SUCCESS) {
596 		/*
597 		 * data_len in the packed buf header may be changed
598 		 * by the dns or mdns backend, reset it just in
599 		 * case
600 		 */
601 		((nss_pheader_t *)swret->pbuf)->data_len =
602 		    swret->datalen;
603 		res = (func)(NULL, &swret->pbuf, &swret->pbufsiz);
604 	}
605 	return (res);
606 }
607 
608 /*
609  * Returns a flag to indicate if needs to fall back to the
610  * main nscd when a per-user lookup failed with rc NSS_NOTFOUND.
611  */
612 static int
613 set_fallback_flag(char *srcname, nss_status_t rc)
614 {
615 	char	*me = "set_fallback_flag";
616 	if (strcmp(srcname, "ldap") == 0 && rc == NSS_NOTFOUND) {
617 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
618 		(me, "NSS_NOTFOUND (ldap): fallback to main nscd "
619 		"may be needed\n");
620 		return (1);
621 	}
622 	return (0);
623 }
624 
625 nss_status_t
626 nss_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum,
627 	void *search_args)
628 {
629 	char			*me = "nss_search";
630 	nss_status_t		res = NSS_UNAVAIL;
631 	nscd_nsw_state_t	*s = NULL;
632 	int			n_src;
633 	unsigned int		status_vec = 0;
634 	int			dbi, srci = -1;
635 	int			check_loopback = 0;
636 	int			state_thr = 0;
637 	lb_key_t		key, *k = NULL;
638 	nss_db_root_t		root_db;
639 	nscd_nsw_params_t	params;
640 	nscd_sw_return_t	*swret;
641 
642 	_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
643 	(me, "rootp = %p, initf = %p, search_fnum = %d, "
644 	    "search_args = %p\n", rootp, initf,
645 	    search_fnum, search_args);
646 
647 	NSCD_SW_STATS_G.lookup_request_received_g++;
648 	NSCD_SW_STATS_G.lookup_request_in_progress_g++;
649 	NSCD_SW_STATS_G.lookup_request_queued_g++;
650 
651 	/* determine db index, cfg db index, etc */
652 	if (getparams(search_fnum, initf, &params) ==
653 	    NSCD_CFG_UNSUPPORTED_SWITCH_DB) {
654 		/*
655 		 * if unsupported database and the request is from the
656 		 * the door, tell the door client to try it locally
657 		 */
658 		if (initf == nscd_initf) {
659 			res = NSS_TRYLOCAL;
660 			goto error_exit;
661 		} else { /* otherwise, let libc:nss_search() handle it */
662 			nss_status_t	(*func)();
663 
664 			if (get_libc_nss_search((void **)&func) ==
665 			    NSCD_SUCCESS)
666 				return ((func)(rootp, initf, search_fnum,
667 				    search_args));
668 			else
669 				goto error_exit;
670 		}
671 	}
672 	dbi = params.dbi;
673 
674 	/* get address of the switch engine return data area */
675 	if (initf == nscd_initf) {
676 		swret = (nscd_sw_return_t *)params.p.private;
677 		swret->srci = -1;
678 	} else {
679 		swret = NULL;
680 		params.dnsi = -1;
681 	}
682 
683 	/*
684 	 * for door request that should be processed by the client,
685 	 * send it back with status NSS_TRYLOCAL
686 	 */
687 	if (initf == nscd_initf && try_local(dbi, search_args) == 1) {
688 		res = NSS_TRYLOCAL;
689 		goto error_exit;
690 	}
691 
692 	NSCD_SW_STATS(dbi).lookup_request_received++;
693 	NSCD_SW_STATS(dbi).lookup_request_in_progress++;
694 	NSCD_SW_STATS(dbi).lookup_request_queued++;
695 
696 	/* if lookup not enabled, return NSS_UNAVAIL  */
697 	if (!(NSCD_SW_CFG_G.enable_lookup_g == nscd_true &&
698 	    NSCD_SW_CFG(dbi).enable_lookup == nscd_true)) {
699 
700 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
701 		(me, "lookup not enabled for %s\n", NSCD_NSW_DB_NAME(dbi));
702 
703 		goto error_exit;
704 	}
705 
706 	/* determine if loopback checking is configured */
707 	if (NSCD_SW_CFG_G.enable_loopback_checking_g == nscd_true &&
708 	    NSCD_SW_CFG(dbi).enable_loopback_checking == nscd_true) {
709 		check_loopback = 1;
710 
711 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
712 		(me, "loopback checking enabled for %s\n",
713 		    NSCD_NSW_DB_NAME(dbi));
714 	}
715 
716 	if (check_loopback) {
717 		k = get_loopback_key();
718 		if (k != NULL) {
719 			if (k->dbi != dbi || k->fnum != search_fnum) {
720 				clear_loopback_key(k);
721 				k = NULL;
722 			}
723 		}
724 	}
725 
726 	if (s == 0) {
727 		nscd_rc_t	rc;
728 
729 		if (check_loopback) {
730 			rc = _nscd_get_nsw_state_thread(&root_db, &params);
731 			state_thr = 1;
732 		} else
733 			rc = _nscd_get_nsw_state(&root_db, &params);
734 
735 		NSCD_SW_STATS_G.lookup_request_queued_g--;
736 		NSCD_SW_STATS(dbi).lookup_request_queued--;
737 
738 		if (rc != NSCD_SUCCESS)
739 				goto error_exit;
740 
741 		s = (nscd_nsw_state_t *)root_db.s;
742 	}
743 
744 	_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
745 	(me, "database = %s, config = >>%s<<\n", NSCD_NSW_DB_NAME(dbi),
746 	    (*s->nsw_cfg_p)->nsw_cfg_str);
747 
748 	for (n_src = 0;  n_src < s->max_src;  n_src++) {
749 		nss_backend_t		*be = NULL;
750 		nss_backend_op_t	funcp = NULL;
751 		struct __nsw_lookup_v1	*lkp;
752 		int			smf_state;
753 		int			n_loop = 0;
754 		int			max_retry = 10;
755 
756 		res = NSS_UNAVAIL;
757 
758 		if (n_src == 0)
759 			lkp = s->config->lookups;
760 		else
761 			lkp = lkp->next;
762 
763 		/* set the number of max. retries */
764 		if (lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
765 			max_retry = lkp->max_retries;
766 
767 		srci = (*s->nsw_cfg_p)->src_idx[n_src];
768 		if (swret != NULL)
769 			swret->srci = srci;
770 
771 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
772 		(me, "nsw source = %s\n", NSCD_NSW_SRC_NAME(srci));
773 
774 		/*
775 		 * If no privilege to look up, skip.
776 		 * 'files' requires PRIV_FILE_DAC_READ to read shadow(4) data,
777 		 * 'ldap' requires all zones privilege.
778 		 */
779 		if (params.privdb == 1 && swret != NULL) {
780 			boolean_t	(*is_shadow_update_enabled)();
781 			boolean_t	check_ldap_priv = B_FALSE;
782 
783 			if (strcmp(NSCD_NSW_SRC_NAME(srci), "ldap") == 0) {
784 				if (get_sldap_shadow_func(
785 				    (void **)&is_shadow_update_enabled) ==
786 				    NSCD_SUCCESS &&
787 				    is_shadow_update_enabled()) {
788 					check_ldap_priv = B_TRUE;
789 
790 					/*
791 					 * A peruser nscd doesn't have
792 					 * the privileges to lookup a
793 					 * private database, such as shadow,
794 					 * returns NSS_ALTRETRY to have the
795 					 * main nscd do the job.
796 					 */
797 					if (_whoami == NSCD_CHILD) {
798 						res = NSS_ALTRETRY;
799 						goto free_nsw_state;
800 					}
801 				}
802 			}
803 
804 			if ((strcmp(NSCD_NSW_SRC_NAME(srci), "files") == 0 &&
805 			    _nscd_check_client_priv(NSCD_READ_PRIV) != 0) ||
806 			    (check_ldap_priv &&
807 			    _nscd_check_client_priv(NSCD_ALL_PRIV) != 0)) {
808 				_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
809 				    NSCD_LOG_LEVEL_DEBUG)
810 				(me, "no privilege to look up, skip source\n");
811 
812 				goto next_src;
813 			}
814 		}
815 
816 		/* get state of the (backend) client service */
817 		smf_state = _nscd_get_smf_state(srci, dbi, 0);
818 
819 		/* stop if the source is one that should be TRYLOCAL */
820 		if (initf == nscd_initf &&	/* request is from the door */
821 		    (smf_state == NSCD_SVC_STATE_UNSUPPORTED_SRC ||
822 		    (smf_state == NSCD_SVC_STATE_FOREIGN_SRC &&
823 		    s->be_version_p[n_src] == NULL) ||
824 		    (params.privdb && try_local2(dbi, srci) == 1))) {
825 			_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
826 			(me, "returning TRYLOCAL ... \n");
827 			res = NSS_TRYLOCAL;
828 			goto free_nsw_state;
829 		}
830 
831 		if (check_loopback && k != NULL) {
832 
833 			if (k->srci == srci && k->dbi == dbi)
834 				if (k->fnum == search_fnum) {
835 
836 					_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
837 					    NSCD_LOG_LEVEL_DEBUG)
838 					(me, "loopback detected: "
839 					    "source = %s, database = %s "
840 					    "search fnum = %d\n",
841 					    NSCD_NSW_SRC_NAME(srci),
842 					    NSCD_NSW_DB_NAME(dbi), search_fnum);
843 
844 				NSCD_SW_STATS_G.loopback_nsw_db_skipped_g++;
845 				NSCD_SW_STATS(dbi).loopback_nsw_db_skipped++;
846 					continue;
847 				}
848 		}
849 
850 		be = s->be[n_src];
851 		if (be != NULL)
852 			funcp = NSS_LOOKUP_DBOP(be, search_fnum);
853 
854 		/* request could be from within nscd so check states again */
855 		if (be == NULL || (params.dnsi < 0 && (funcp == NULL ||
856 		    (smf_state != NSCD_SVC_STATE_UNINITED &&
857 		    smf_state != NSCD_SVC_STATE_UNSUPPORTED_SRC &&
858 		    smf_state != NSCD_SVC_STATE_FOREIGN_SRC &&
859 		    smf_state < SCF_STATE_ONLINE)))) {
860 
861 			_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
862 			    NSCD_LOG_LEVEL_DEBUG)
863 			(me, "unable to look up source %s: be = %p, "
864 			"smf state = %d, funcp = %p\n",
865 			    NSCD_NSW_SRC_NAME(srci), be, smf_state, funcp);
866 
867 			goto next_src;
868 		}
869 
870 		do {
871 			/*
872 			 * we can only retry max_retry times,
873 			 * otherwise threads may get stuck in this
874 			 * do-while loop forever
875 			 */
876 			if (n_loop > max_retry) {
877 				if (swret != NULL)
878 					res = NSS_TRYLOCAL;
879 				goto free_nsw_state;
880 			}
881 
882 			/*
883 			 * set up to prevent loopback
884 			 */
885 			if (check_loopback && k == NULL) {
886 				key.srci = srci;
887 				key.dbi = dbi;
888 				key.fnum = search_fnum;
889 				key.lb_flagp = &check_loopback;
890 				(void) set_loopback_key(&key);
891 				k = &key;
892 			}
893 
894 			_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
895 			    NSCD_LOG_LEVEL_DEBUG)
896 			(me, "looking up source = %s, loop# = %d \n",
897 			    NSCD_NSW_SRC_NAME(srci), n_loop);
898 
899 			/*
900 			 * search the backend, if hosts lookups,
901 			 * try to get the hosts data with ttl first
902 			 */
903 			if (params.dnsi >= 0) {
904 				res = search_dns_withttl(swret,
905 				    NSCD_NSW_SRC_NAME(srci), params.dnsi);
906 				/*
907 				 * if not able to get ttl, fall back
908 				 * to the regular backend call
909 				 */
910 				if (res == NSS_ERROR)
911 					res = (*funcp)(be, search_args);
912 				else {
913 					/*
914 					 * status/result are in the
915 					 * packed buffer, not
916 					 * search_args
917 					 */
918 					swret->noarg = 1;
919 				}
920 			} else
921 				res = (*funcp)(be, search_args);
922 			if (swret != NULL)
923 				swret->errnum = errno;
924 
925 			/*
926 			 * backend is not up, check and update the
927 			 * smf state table
928 			 */
929 			if (res == NSS_UNAVAIL)
930 				(void) _nscd_get_smf_state(srci, dbi, 1);
931 
932 			/*
933 			 * may need to fall back to use the main nscd
934 			 * if per-user lookup
935 			 */
936 			if (_whoami == NSCD_CHILD && swret != NULL)
937 				swret->fallback = set_fallback_flag(
938 				    NSCD_NSW_SRC_NAME(srci), res);
939 
940 			_NSCD_LOG_IF(NSCD_LOG_SWITCH_ENGINE,
941 			    NSCD_LOG_LEVEL_DEBUG) {
942 
943 				/*
944 				 * set up to trace the result/status
945 				 * of the dns/ttl lookup
946 				 */
947 				if (swret != NULL && swret->noarg == 1) {
948 					nss_pheader_t *phdr;
949 					struct nss_XbyY_args *arg;
950 					arg = (struct nss_XbyY_args *)
951 					    search_args;
952 					phdr = (nss_pheader_t *)swret->pbuf;
953 					arg->buf.buffer = (char *)phdr +
954 					    phdr->data_off;
955 					arg->returnlen = phdr->data_len;
956 					if (phdr->p_errno == ERANGE)
957 						arg->erange = 1;
958 					arg->h_errno = phdr->p_herrno;
959 				}
960 
961 				trace_result(dbi, srci, search_fnum, res,
962 				    (nss_XbyY_args_t *)search_args);
963 			}
964 
965 			n_loop++;
966 		} while (retry_test(res, n_loop, lkp));
967 
968 		next_src:
969 
970 		status_vec |= (1 << res);
971 
972 		if (__NSW_ACTION_V1(lkp, res) == __NSW_RETURN) {
973 			break;
974 		}
975 	}
976 
977 	free_nsw_state:
978 
979 	if (state_thr == 1)
980 		_nscd_put_nsw_state_thread(s);
981 	else
982 		_nscd_put_nsw_state(s);
983 	if (check_loopback && k != NULL)
984 		clear_loopback_key(k);
985 
986 	if (res != NSS_SUCCESS)
987 		goto error_exit;
988 
989 	NSCD_SW_STATS_G.lookup_request_succeeded_g++;
990 	NSCD_SW_STATS(dbi).lookup_request_succeeded++;
991 	NSCD_SW_STATS_G.lookup_request_in_progress_g--;
992 	NSCD_SW_STATS(dbi).lookup_request_in_progress--;
993 
994 	return (NSS_SUCCESS);
995 
996 	error_exit:
997 
998 	NSCD_SW_STATS_G.lookup_request_failed_g++;
999 	NSCD_SW_STATS_G.lookup_request_in_progress_g--;
1000 	NSCD_SW_STATS(dbi).lookup_request_failed++;
1001 	NSCD_SW_STATS(dbi).lookup_request_in_progress--;
1002 
1003 	return (res);
1004 }
1005 
1006 
1007 /* ===> get/set/endent */
1008 
1009 static void		nss_setent_u(nss_db_root_t *,
1010 				    nss_db_initf_t,
1011 				    nss_getent_t *);
1012 static nss_status_t	nss_getent_u(nss_db_root_t *,
1013 				    nss_db_initf_t,
1014 				    nss_getent_t *,
1015 				    void *);
1016 static void		nss_endent_u(nss_db_root_t *,
1017 				    nss_db_initf_t,
1018 				    nss_getent_t *);
1019 
1020 void
1021 nss_setent(nss_db_root_t *rootp, nss_db_initf_t initf,
1022 	nss_getent_t *contextpp)
1023 {
1024 	if (contextpp == 0)
1025 		return;
1026 	nss_setent_u(rootp, initf, contextpp);
1027 }
1028 
1029 nss_status_t
1030 nss_getent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp,
1031 	void *args)
1032 {
1033 	nss_status_t		status;
1034 
1035 	if (contextpp == 0) {
1036 		return (NSS_UNAVAIL);
1037 	}
1038 	status = nss_getent_u(rootp, initf, contextpp, args);
1039 	return (status);
1040 }
1041 
1042 void
1043 nss_endent(nss_db_root_t *rootp, nss_db_initf_t initf,
1044 	nss_getent_t *contextpp)
1045 {
1046 	if (contextpp == 0)
1047 		return;
1048 	nss_endent_u(rootp, initf, contextpp);
1049 }
1050 
1051 /*ARGSUSED*/
1052 static void
1053 end_iter_u(nss_db_root_t *rootp, struct nss_getent_context *contextp)
1054 {
1055 	nscd_getent_context_t	*ctx;
1056 	nscd_nsw_state_t	*s;
1057 	nss_backend_t		*be;
1058 	int			n_src;
1059 
1060 	ctx = (nscd_getent_context_t *)contextp;
1061 	s = ctx->nsw_state;
1062 	n_src = ctx->n_src;
1063 	be = ctx->be;
1064 
1065 	if (s != 0) {
1066 		if (n_src < s->max_src && be != 0) {
1067 			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1068 			ctx->be = 0;  /* Should be unnecessary, but hey */
1069 		}
1070 	}
1071 	ctx->n_src = 0;
1072 }
1073 
1074 static void
1075 nss_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1076 	nss_getent_t *contextpp)
1077 {
1078 	char			*me = "nss_setent_u";
1079 	nscd_nsw_state_t	*s;
1080 	nscd_getent_context_t	*contextp;
1081 	nscd_nsw_params_t	params;
1082 	nss_db_root_t		root;
1083 	nss_backend_t		*be;
1084 	int			n_src, i;
1085 	nscd_sw_return_t	*swret = NULL;
1086 
1087 	_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1088 	(me, "rootp = %p, initf = %p, contextpp = %p \n",
1089 	    rootp, initf, contextpp);
1090 
1091 	/*
1092 	 * Get the nsw db index via the initf function. If unsupported
1093 	 * database, no need to continue
1094 	 */
1095 	if (getparams(-1, initf, &params) == NSCD_CFG_UNSUPPORTED_SWITCH_DB)
1096 		return;
1097 
1098 	/* get address of the switch engine return data area */
1099 	if (initf == nscd_initf)
1100 		swret = (nscd_sw_return_t *)params.p.private;
1101 
1102 	/* if no privilege to look up, return */
1103 	if (params.privdb == 1 && swret != NULL &&
1104 	    _nscd_check_client_priv(NSCD_READ_PRIV) != 0) {
1105 
1106 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1107 		(me, "no privilege \n");
1108 		return;
1109 	}
1110 
1111 	if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
1112 		if ((_nscd_get_getent_ctx(contextpp, &params)) !=
1113 		    NSCD_SUCCESS) {
1114 			return;
1115 		}
1116 		contextp = (nscd_getent_context_t *)contextpp->ctx;
1117 	}
1118 	s = contextp->nsw_state;
1119 
1120 	if (s == 0) {
1121 		if (_nscd_get_nsw_state(&root, &params) !=
1122 		    NSCD_SUCCESS) {
1123 			return;
1124 		}
1125 		s = (nscd_nsw_state_t *)root.s;
1126 		contextp->nsw_state = s;
1127 
1128 	} else {
1129 		s	= contextp->nsw_state;
1130 		n_src	= contextp->n_src;
1131 		be	= contextp->be;
1132 		if (n_src == 0 && be != 0) {
1133 			/*
1134 			 * Optimization:  don't do endent, don't change
1135 			 *   backends, just do the setent.  Look Ma, no locks
1136 			 *   (nor any context that needs updating).
1137 			 */
1138 			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1139 			return;
1140 		}
1141 		if (n_src < s->max_src && be != 0) {
1142 			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1143 			contextp->be = 0;	/* Play it safe */
1144 		}
1145 	}
1146 	for (n_src = 0, be = 0; n_src < s->max_src &&
1147 	    (be = s->be[n_src]) == 0; n_src++) {
1148 		;
1149 	}
1150 
1151 	contextp->n_src	= n_src;
1152 	contextp->be	= be;
1153 
1154 	if (be == 0) {
1155 		/* Things are broken enough that we can't do setent/getent */
1156 		nss_endent_u(rootp, initf, contextpp);
1157 		return;
1158 	}
1159 
1160 	/*
1161 	 * make sure all the backends are supported
1162 	 */
1163 	for (i = 0; i < s->max_src; i++) {
1164 		int	st, srci;
1165 
1166 		if (s->be[i] == NULL)
1167 			continue;
1168 
1169 		srci = (*s->nsw_cfg_p)->src_idx[i];
1170 		st = _nscd_get_smf_state(srci, params.dbi, 1);
1171 		if (st == NSCD_SVC_STATE_UNSUPPORTED_SRC ||
1172 		    (st == NSCD_SVC_STATE_FOREIGN_SRC &&
1173 		    s->be_version_p[i] == NULL && initf == nscd_initf) ||
1174 		    st == NSCD_SVC_STATE_UNINITED ||
1175 		    (params.privdb &&
1176 		    try_local2(params.dbi, srci) == 1)) {
1177 			nss_endent_u(rootp, initf, contextpp);
1178 
1179 			_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
1180 			    NSCD_LOG_LEVEL_DEBUG)
1181 			(me, "backend (%s) not available (state = %d)\n",
1182 			    NSCD_NSW_SRC_NAME(srci), st);
1183 
1184 			return;
1185 		}
1186 	}
1187 
1188 	(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1189 }
1190 
1191 nss_status_t
1192 nss_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1193 	nss_getent_t *contextpp, void *args)
1194 {
1195 	char			*me = "nss_getent_u";
1196 	nscd_nsw_state_t	*s;
1197 	nscd_getent_context_t	*contextp;
1198 	int			n_src;
1199 	nss_backend_t		*be;
1200 
1201 	_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1202 	(me, "rootp = %p, initf = %p, contextpp = %p, args = %p\n",
1203 	    rootp, initf, contextpp, args);
1204 
1205 	if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
1206 		nss_setent_u(rootp, initf, contextpp);
1207 		if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
1208 			/* Give up */
1209 			_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
1210 			    NSCD_LOG_LEVEL_ERROR)
1211 			(me, "not able to obtain getent context ... give up\n");
1212 
1213 			return (NSS_UNAVAIL);
1214 		}
1215 	}
1216 
1217 	s	= contextp->nsw_state;
1218 	n_src	= contextp->n_src;
1219 	be	= contextp->be;
1220 
1221 	if (s == 0) {
1222 		/*
1223 		 * We've done an end_iter() and haven't done nss_setent()
1224 		 * or nss_endent() since;  we should stick in this state
1225 		 * until the caller invokes one of those two routines.
1226 		 */
1227 		return (NSS_SUCCESS);
1228 	}
1229 
1230 	while (n_src < s->max_src) {
1231 		nss_status_t		res;
1232 		struct __nsw_lookup_v1	*lkp = NULL;
1233 		int			n;
1234 
1235 		/* get the nsw config for the current source */
1236 		lkp = s->config->lookups;
1237 		for (n = 0; n < n_src; n++)
1238 			lkp = lkp->next;
1239 
1240 		if (be == 0) {
1241 			/* If it's null it's a bug, but let's play safe */
1242 			res = NSS_UNAVAIL;
1243 		} else {
1244 			_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
1245 			    NSCD_LOG_LEVEL_DEBUG)
1246 			(me, "database: %s, backend: %s, nsswitch config: %s\n",
1247 			    NSCD_NSW_DB_NAME(s->dbi),
1248 			    lkp->service_name,
1249 			    (*s->nsw_cfg_p)->nsw_cfg_str);
1250 
1251 			res = NSS_INVOKE_DBOP(be, NSS_DBOP_GETENT, args);
1252 		}
1253 
1254 		if (__NSW_ACTION_V1(lkp, res) == __NSW_RETURN) {
1255 			if (res != __NSW_SUCCESS) {
1256 				end_iter_u(rootp,
1257 				    (struct nss_getent_context *)contextp);
1258 			}
1259 			return (res);
1260 		}
1261 		(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1262 		do {
1263 			n_src++;
1264 		} while (n_src < s->max_src &&
1265 		    (be = s->be[n_src]) == 0);
1266 		if (be == 0) {
1267 			/*
1268 			 * This is the case where we failed to get the backend
1269 			 * for the last source. We exhausted all sources.
1270 			 */
1271 			nss_endent_u(rootp, initf, contextpp);
1272 			return (NSS_NOTFOUND);
1273 		}
1274 		contextp->n_src	= n_src;
1275 		contextp->be	= be;
1276 		(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1277 	}
1278 	/* Got to the end of the sources without finding another entry */
1279 	end_iter_u(rootp, (struct nss_getent_context *)contextp);
1280 	return (NSS_SUCCESS);
1281 	/* success is either a successful entry or end of the sources */
1282 }
1283 
1284 /*ARGSUSED*/
1285 void
1286 nss_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1287 	nss_getent_t *contextpp)
1288 {
1289 	char			*me = "nss_endent_u";
1290 	nscd_getent_context_t	*contextp;
1291 
1292 	_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1293 	(me, "rootp = %p, initf = %p, contextpp = %p \n",
1294 	    rootp, initf, contextpp);
1295 
1296 	if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
1297 		/* nss_endent() on an unused context is a no-op */
1298 		return;
1299 	}
1300 
1301 	if (_nscd_is_getent_ctx_in_use(contextp) == 0) {
1302 		end_iter_u(rootp, (struct nss_getent_context *)contextp);
1303 		_nscd_put_getent_ctx(contextp);
1304 		contextpp->ctx = NULL;
1305 	}
1306 }
1307 
1308 /*
1309  * _nss_db_state_destr() and nss_delete() do nothing in nscd
1310  * but is needed to make the caller (below nscd) happy
1311  */
1312 /*ARGSUSED*/
1313 void
1314 _nss_db_state_destr(struct nss_db_state *s)
1315 {
1316 	/* nsw state in nscd is always reused, so do nothing here */
1317 }
1318 
1319 /*ARGSUSED*/
1320 void
1321 nss_delete(nss_db_root_t *rootp)
1322 {
1323 	/*
1324 	 * the only resource kept tracked by the nss_db_root_t
1325 	 * is the nsw state which is always reused and no need
1326 	 * to be freed. So just return.
1327 	 */
1328 }
1329 
1330 /*
1331  * Start of nss_psearch/nss_psetent()/nss_pgetent()/nss_pendent()
1332  * buffers switch entry points
1333  */
1334 
1335 /*
1336  * nss_psearch opens a packed structure header, assembles a local
1337  * nss_XbyY_args_t structure and calls the local copy of nss_search.
1338  * The return data is assembled in "files native format" in the
1339  * return buffer location.  Status if packed back up with the buffer
1340  * and the whole wad is returned to the cache or the client.
1341  */
1342 
1343 void
1344 nss_psearch(void *buffer, size_t length)
1345 {
1346 	/* inputs */
1347 	nss_db_initf_t		initf;
1348 	int			dbop;
1349 	int			rc;
1350 	nss_XbyY_args_t		arg;
1351 	nss_status_t		status;
1352 	nscd_sw_return_t	swret = { 0 }, *swrp = &swret;
1353 	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1354 	char			*me = "nss_psearch";
1355 
1356 	if (buffer == NULL || length == 0) {
1357 		NSCD_RETURN_STATUS(pbuf, NSS_ERROR, EFAULT);
1358 	}
1359 
1360 	status = nss_packed_arg_init(buffer, length,
1361 	    NULL, &initf, &dbop, &arg);
1362 	if (status != NSS_SUCCESS) {
1363 		NSCD_RETURN_STATUS(pbuf, status, -1);
1364 	}
1365 
1366 	/*
1367 	 * pass the address of the return data area
1368 	 * for the switch engine to return its own data
1369 	 */
1370 	(void) memcpy(&pbuf->nscdpriv, &swrp, sizeof (swrp));
1371 	swret.pbuf = buffer;
1372 	swret.pbufsiz = length;
1373 	swret.datalen = pbuf->data_len;
1374 
1375 	/*
1376 	 * use the generic nscd_initf for all database lookups
1377 	 * (the TSD key is the pointer to the packed header)
1378 	 */
1379 	rc = set_initf_key(pbuf);
1380 	if (rc != 0) {
1381 		NSCD_RETURN_STATUS(pbuf, NSS_UNAVAIL, EINVAL);
1382 	}
1383 	initf = nscd_initf;
1384 
1385 	/* Perform local search and pack results into return buffer */
1386 	/* nscd's search ignores db_root */
1387 	status = nss_search(NULL, initf, dbop, &arg);
1388 
1389 	/*
1390 	 * If status is NSS_NOTFOUND and ldap also returned
1391 	 * NSS_NOTFOUND, it is possible that the user does
1392 	 * not have a credential, so check and see if
1393 	 * needs to return NSS_ALTRETRY to let the main
1394 	 * nscd get a chance to process the lookup
1395 	 */
1396 	if (swret.fallback == 1 && status == NSS_NOTFOUND) {
1397 		OM_uint32	(*func)();
1398 		OM_uint32	stat;
1399 		nscd_rc_t	rc;
1400 
1401 		rc = get_gss_func((void **)&func);
1402 		if (rc == NSCD_SUCCESS) {
1403 			if (func(&stat, GSS_C_NO_CREDENTIAL,
1404 			    NULL, NULL, NULL, NULL) != GSS_S_COMPLETE) {
1405 
1406 				_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
1407 				    NSCD_LOG_LEVEL_DEBUG)
1408 			(me, "NSS_ALTRETRY: fallback to main nscd needed\n");
1409 
1410 				status = NSS_ALTRETRY;
1411 			}
1412 		}
1413 	}
1414 
1415 	NSCD_SET_STATUS(pbuf, status, -1);
1416 	errno = swret.errnum;
1417 
1418 	/*
1419 	 * Move result/status from args to packed buffer only if
1420 	 * arg was being used and rc from the switch engine is not
1421 	 * NSS_TRYLOCAL.
1422 	 */
1423 	if (!swret.noarg && status != NSS_TRYLOCAL)
1424 		nss_packed_set_status(buffer, length, status,  &arg);
1425 
1426 	_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1427 	(me, "switch engine result: source is %s, status %d, "
1428 	"herrno is %d, errno is %s\n",
1429 	    (swret.srci != -1) ? NSCD_NSW_SRC_NAME(swret.srci) : "<NOTSET>",
1430 	    pbuf->p_status, pbuf->p_herrno, strerror(pbuf->p_errno));
1431 
1432 	/* clear the TSD key used by the generic initf */
1433 	clear_initf_key();
1434 	pbuf->nscdpriv = 0;
1435 }
1436 
1437 static void
1438 nscd_map_contextp(void *buffer, nss_getent_t *contextp,
1439     nssuint_t **cookie_num_p, nssuint_t **seqnum_p, int setent)
1440 {
1441 	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1442 	nssuint_t		off;
1443 	nscd_getent_context_t	*ctx;
1444 	char			*me = "nscd_map_contextp";
1445 	nscd_getent_p1_cookie_t	*cookie;
1446 
1447 	if (buffer == NULL) {
1448 		NSCD_RETURN_STATUS(pbuf, NSS_ERROR, EFAULT);
1449 	}
1450 
1451 	off = pbuf->key_off;
1452 	cookie = (nscd_getent_p1_cookie_t *)((void *)((char *)buffer + off));
1453 	if (seqnum_p != NULL)
1454 		*seqnum_p = &cookie->p1_seqnum;
1455 
1456 	/*
1457 	 * if called by nss_psetent, and the passed in cookie number
1458 	 * is NSCD_NEW_COOKIE, then there is no cookie yet, return a
1459 	 * pointer pointing to where the cookie number will be stored.
1460 	 * Also because there is no cookie to validate, just return
1461 	 * success.
1462 	 *
1463 	 * On the other hand, if a cookie number is passed in, we need
1464 	 * to validate the cookie number before returning.
1465 	 */
1466 	if (cookie_num_p != NULL)
1467 		*cookie_num_p = &cookie->p1_cookie_num;
1468 	if (setent == 1 && cookie->p1_cookie_num == NSCD_NEW_COOKIE) {
1469 			NSCD_RETURN_STATUS_SUCCESS(pbuf);
1470 	}
1471 
1472 	/*
1473 	 * If the sequence number and start time match nscd's p0 cookie,
1474 	 * then either setent was done twice in a row or this is the
1475 	 * first getent after the setent, return success as well.
1476 	 */
1477 	if (cookie->p1_seqnum == NSCD_P0_COOKIE_SEQNUM) {
1478 		nscd_getent_p0_cookie_t *p0c =
1479 		    (nscd_getent_p0_cookie_t *)cookie;
1480 		if (p0c->p0_time == _nscd_get_start_time())
1481 			NSCD_RETURN_STATUS_SUCCESS(pbuf);
1482 	}
1483 
1484 	_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1485 	(me, "cookie # = %lld,  sequence # = %lld\n",
1486 	    cookie->p1_cookie_num, cookie->p1_seqnum);
1487 
1488 	ctx = _nscd_is_getent_ctx(cookie->p1_cookie_num);
1489 
1490 	if (ctx == NULL) {
1491 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1492 		(me, "No matching context found (cookie number: %lld)\n",
1493 		    cookie->p1_cookie_num);
1494 
1495 		NSCD_RETURN_STATUS(pbuf, NSS_ERROR, EFAULT);
1496 	}
1497 
1498 	/* if not called by nss_psetent, verify sequence number */
1499 	if (setent != 1 && ctx->seq_num !=
1500 	    (nscd_seq_num_t)cookie->p1_seqnum) {
1501 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1502 		(me, "invalid sequence # (%lld)\n", cookie->p1_seqnum);
1503 
1504 		_nscd_free_ctx_if_aborted(ctx);
1505 		NSCD_RETURN_STATUS(pbuf, NSS_ERROR, EFAULT);
1506 	}
1507 
1508 	contextp->ctx = (struct nss_getent_context *)ctx;
1509 
1510 	NSCD_RETURN_STATUS_SUCCESS(pbuf);
1511 }
1512 
1513 void
1514 nss_psetent(void *buffer, size_t length, pid_t pid)
1515 {
1516 	nss_getent_t		context = { 0 };
1517 	nss_getent_t		*contextp = &context;
1518 	nssuint_t		*cookie_num_p;
1519 	nssuint_t		*seqnum_p;
1520 	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1521 	nscd_getent_p0_cookie_t *p0c;
1522 	char			*me = "nss_psetent";
1523 
1524 	if (buffer == NULL || length == 0) {
1525 		NSCD_RETURN_STATUS(pbuf, NSS_ERROR, EFAULT);
1526 	}
1527 
1528 	/*
1529 	 * If this is a per-user nscd, and the user does not have
1530 	 * the necessary credential, return NSS_TRYLOCAL, so the
1531 	 * setent/getent can be done locally in the process of the
1532 	 * setent call
1533 	 */
1534 	if (_whoami == NSCD_CHILD) {
1535 		OM_uint32	(*func)();
1536 		OM_uint32	stat;
1537 		nscd_rc_t	rc;
1538 
1539 		rc = get_gss_func((void **)&func);
1540 		if (rc == NSCD_SUCCESS) {
1541 			if (func(&stat, GSS_C_NO_CREDENTIAL,
1542 			    NULL, NULL, NULL, NULL) != GSS_S_COMPLETE) {
1543 
1544 				_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
1545 				    NSCD_LOG_LEVEL_DEBUG)
1546 			(me, "NSS_TRYLOCAL: fallback to caller process\n");
1547 				NSCD_RETURN_STATUS(pbuf, NSS_TRYLOCAL, 0);
1548 			}
1549 		}
1550 	}
1551 
1552 	/* check cookie number */
1553 	nscd_map_contextp(buffer, contextp, &cookie_num_p, &seqnum_p, 1);
1554 	if (NSCD_STATUS_IS_NOT_OK(pbuf))
1555 		return;
1556 
1557 	/* set cookie number and sequence number */
1558 	p0c = (nscd_getent_p0_cookie_t *)cookie_num_p;
1559 	if (contextp->ctx ==  NULL) {
1560 		/*
1561 		 * first setent (no existing getent context),
1562 		 * return a p0 cookie
1563 		 */
1564 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1565 		(me, "first setent, no getent context yet\n");
1566 	} else {
1567 		/*
1568 		 * doing setent on an existing getent context,
1569 		 * release resources allocated and return a
1570 		 * p0 cookie
1571 		 */
1572 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1573 		(me, "setent resetting sequence number = %lld\n",  *seqnum_p);
1574 
1575 		if (_nscd_is_getent_ctx_in_use((nscd_getent_context_t *)
1576 		    contextp->ctx) == 0) {
1577 			/*
1578 			 * context not in use, release the backend and
1579 			 * return the context to the pool
1580 			 */
1581 			end_iter_u(NULL, contextp->ctx);
1582 			_nscd_put_getent_ctx(
1583 			    (nscd_getent_context_t *)contextp->ctx);
1584 			contextp->ctx = NULL;
1585 		}
1586 	}
1587 
1588 	p0c->p0_pid = pid;
1589 	p0c->p0_time = _nscd_get_start_time();
1590 	p0c->p0_seqnum = NSCD_P0_COOKIE_SEQNUM;
1591 	_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1592 	(me, "returning a p0 cookie: pid = %ld, time = %ld, seq #= %llx\n",
1593 	    p0c->p0_pid, p0c->p0_time, p0c->p0_seqnum);
1594 
1595 	NSCD_RETURN_STATUS(pbuf, NSS_SUCCESS, 0);
1596 }
1597 
1598 static void
1599 delayed_setent(nss_pheader_t *pbuf, nss_db_initf_t initf,
1600 	nss_getent_t *contextp, nssuint_t *cookie_num_p,
1601 	nssuint_t *seqnum_p, pid_t pid)
1602 {
1603 	nscd_getent_context_t	*ctx;
1604 	nscd_sw_return_t	swret = { 0 }, *swrp = &swret;
1605 	char			*me = "delayed_setent";
1606 
1607 	/*
1608 	 * check credential
1609 	 */
1610 	_nscd_APP_check_cred(pbuf, &pid, "NSCD_DELAYED_SETENT",
1611 	    NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_ERROR);
1612 	if (NSCD_STATUS_IS_NOT_OK(pbuf)) {
1613 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1614 		(me, "invalid credential\n");
1615 		return;
1616 	}
1617 
1618 	/*
1619 	 * pass the packed header buffer pointer to nss_setent
1620 	 */
1621 	(void) memcpy(&pbuf->nscdpriv, &swrp, sizeof (swrp));
1622 	swret.pbuf = pbuf;
1623 
1624 	/* Perform local setent and set context */
1625 	nss_setent(NULL, initf, contextp);
1626 
1627 	/* insert cookie info into packed buffer header */
1628 	ctx = (nscd_getent_context_t *)contextp->ctx;
1629 	if (ctx != NULL) {
1630 		*cookie_num_p = ctx->cookie_num;
1631 		*seqnum_p = ctx->seq_num;
1632 		ctx->pid = pid;
1633 	} else {
1634 		/*
1635 		 * not able to allocate a getent context, the
1636 		 * client should try the enumeration locally
1637 		 */
1638 		*cookie_num_p = NSCD_LOCAL_COOKIE;
1639 		*seqnum_p = 0;
1640 
1641 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1642 		(me, "NSS_TRYLOCAL: cookie # = %lld,  sequence # = %lld\n",
1643 		    *cookie_num_p, *seqnum_p);
1644 		NSCD_RETURN_STATUS(pbuf, NSS_TRYLOCAL, 0);
1645 	}
1646 
1647 	_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1648 	(me, "NSS_SUCCESS: cookie # = %lld,  sequence # = %lld\n",
1649 	    ctx->cookie_num, ctx->seq_num);
1650 
1651 	NSCD_RETURN_STATUS(pbuf, NSS_SUCCESS, 0);
1652 }
1653 
1654 void
1655 nss_pgetent(void *buffer, size_t length)
1656 {
1657 	/* inputs */
1658 	nss_db_initf_t		initf;
1659 	nss_getent_t		context = { 0 };
1660 	nss_getent_t		*contextp = &context;
1661 	nss_XbyY_args_t		arg = { 0};
1662 	nss_status_t		status;
1663 	nssuint_t		*cookie_num_p;
1664 	nssuint_t		*seqnum_p;
1665 	nscd_getent_context_t	*ctx;
1666 	int			rc;
1667 	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1668 	char			*me = "nss_pgetent";
1669 
1670 	if (buffer == NULL || length == 0) {
1671 		NSCD_RETURN_STATUS(pbuf, NSS_ERROR, EFAULT);
1672 	}
1673 
1674 	/* verify the cookie passed in */
1675 	nscd_map_contextp(buffer, contextp, &cookie_num_p, &seqnum_p, 0);
1676 	if (NSCD_STATUS_IS_NOT_OK(pbuf))
1677 		return;
1678 
1679 	/*
1680 	 * use the generic nscd_initf for all the getent requests
1681 	 * (the TSD key is the pointer to the packed header)
1682 	 */
1683 	rc = set_initf_key(pbuf);
1684 	if (rc != 0) {
1685 		NSCD_RETURN_STATUS(pbuf, NSS_UNAVAIL, EINVAL);
1686 	}
1687 	initf = nscd_initf;
1688 
1689 	/* if no context yet, get one */
1690 	if (contextp->ctx ==  NULL) {
1691 		nscd_getent_p0_cookie_t *p0c =
1692 		    (nscd_getent_p0_cookie_t *)cookie_num_p;
1693 
1694 		delayed_setent(pbuf, initf, contextp, cookie_num_p,
1695 		    seqnum_p, p0c->p0_pid);
1696 		if (NSCD_STATUS_IS_NOT_OK(pbuf)) {
1697 			clear_initf_key();
1698 			return;
1699 		}
1700 	}
1701 
1702 	status = nss_packed_context_init(buffer, length,
1703 	    NULL, &initf, &contextp, &arg);
1704 	if (status != NSS_SUCCESS) {
1705 		clear_initf_key();
1706 		_nscd_free_ctx_if_aborted(
1707 		    (nscd_getent_context_t *)contextp->ctx);
1708 		NSCD_RETURN_STATUS(pbuf, status, -1);
1709 	}
1710 
1711 	/* Perform local search and pack results into return buffer */
1712 	status = nss_getent(NULL, initf, contextp, &arg);
1713 	NSCD_SET_STATUS(pbuf, status, -1);
1714 	nss_packed_set_status(buffer, length, status,  &arg);
1715 
1716 	/* increment sequence number in the buffer and nscd context */
1717 	if (status == NSS_SUCCESS) {
1718 		ctx = (nscd_getent_context_t *)contextp->ctx;
1719 		ctx->seq_num++;
1720 		*seqnum_p = ctx->seq_num;
1721 		*cookie_num_p = ctx->cookie_num;
1722 
1723 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1724 		(me, "getent OK, new sequence # = %lld, len = %lld,"
1725 		    " data = >>%s<<\n", *seqnum_p,
1726 		    pbuf->data_len, (char *)buffer + pbuf->data_off);
1727 
1728 		_nscd_free_ctx_if_aborted(ctx);
1729 	} else {
1730 		/* release the resources used */
1731 		ctx = (nscd_getent_context_t *)contextp->ctx;
1732 		if (ctx != NULL && _nscd_is_getent_ctx_in_use(ctx) == 0) {
1733 			_nscd_put_getent_ctx(ctx);
1734 			contextp->ctx = NULL;
1735 		}
1736 		_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1737 		(me, "getent failed, status = %d, sequence # = %lld\n",
1738 		    status, *seqnum_p);
1739 	}
1740 
1741 	/* clear the TSD key used by the generic initf */
1742 	clear_initf_key();
1743 }
1744 
1745 void
1746 nss_pendent(void *buffer, size_t length)
1747 {
1748 	nss_getent_t		context = { 0 };
1749 	nss_getent_t		*contextp = &context;
1750 	nssuint_t		*seqnum_p;
1751 	nssuint_t		*cookie_num_p;
1752 	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1753 	char			*me = "nss_pendent";
1754 
1755 	if (buffer == NULL || length == 0) {
1756 		NSCD_RETURN_STATUS(pbuf, NSS_ERROR, EFAULT);
1757 	}
1758 
1759 	/* map the contextp from the cookie information */
1760 	nscd_map_contextp(buffer, contextp, &cookie_num_p, &seqnum_p, 0);
1761 	if (NSCD_STATUS_IS_NOT_OK(pbuf))
1762 		return;
1763 
1764 	if (contextp->ctx == NULL)
1765 		return;
1766 
1767 	_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1768 	(me, "endent, cookie = %lld, sequence # = %lld\n",
1769 	    *cookie_num_p, *seqnum_p);
1770 
1771 	/* Perform local endent and reset context */
1772 	nss_endent(NULL, NULL, contextp);
1773 
1774 	NSCD_RETURN_STATUS(pbuf, NSS_SUCCESS, 0);
1775 }
1776 
1777 /*ARGSUSED*/
1778 void
1779 nss_pdelete(void *buffer, size_t length)
1780 {
1781 	nss_pheader_t	*pbuf = (nss_pheader_t *)buffer;
1782 
1783 	/* unnecessary, kept for completeness */
1784 	NSCD_RETURN_STATUS_SUCCESS(pbuf);
1785 }
1786