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