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