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