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