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