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