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