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