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