xref: /illumos-gate/usr/src/lib/libc/port/gen/nss_common.c (revision bea83d026ee1bd1b2a2419e1d0232f107a5d7d9b)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Shared code used by the name-service-switch frontends (e.g. getpwnam_r())
31  */
32 
33 #pragma weak nss_delete = _nss_delete
34 #pragma weak nss_endent = _nss_endent
35 #pragma weak nss_getent = _nss_getent
36 #pragma weak nss_search = _nss_search
37 #pragma weak nss_setent = _nss_setent
38 
39 #include "synonyms.h"
40 #include <mtlib.h>
41 #include <dlfcn.h>
42 #include <atomic.h>
43 
44 #define	__NSS_PRIVATE_INTERFACE
45 #include "nsswitch_priv.h"
46 #undef	__NSS_PRIVATE_INTERFACE
47 
48 #include <nss_common.h>
49 #include <nss_dbdefs.h>
50 #include <unistd.h>
51 #include <stdlib.h>
52 #include <stdio.h>
53 #include <string.h>
54 #include <thread.h>
55 #include <synch.h>
56 #include <pthread.h>
57 #include <sys/types.h>
58 #include <sys/mman.h>
59 #include <errno.h>
60 #include "libc.h"
61 #include "tsd.h"
62 
63 #include <getxby_door.h>
64 
65 /*
66  * policy component function interposing definitions:
67  * nscd if so desired can interpose it's own switch functions over
68  * the internal unlocked counterparts.  This will allow nscd to replace
69  * the switch policy state engine with one that uses it's internal
70  * components.
71  * Only nscd can change this through it's use of nss_config.
72  * The golden rule is: ptr == NULL checking is used in the switch to
73  * see if a function was interposed.  But nscd is responsible for seeing
74  * that mutex locking to change the values are observed when the data is
75  * changed.  Especially if it happens > once.  The switch does not lock
76  * the pointer with mutexs.
77  */
78 
79 typedef struct {
80 	void	*p;
81 #if 0
82 	void		(*nss_delete_fp)(nss_db_root_t *rootp);
83 	nss_status_t	(*nss_search_fp)(nss_db_root_t *rootp,
84 				nss_db_initf_t initf, int search_fnum,
85 				void *search_args);
86 	void		(*nss_setent_u_fp)(nss_db_root_t *,
87 				nss_db_initf_t, nss_getent_t *);
88 	nss_status_t	(*nss_getent_u_fp)(nss_db_root_t *,
89 				nss_db_initf_t, nss_getent_t *, void *);
90 	void		(*nss_endent_u_fp)(nss_db_root_t *,
91 				nss_db_initf_t, nss_getent_t *);
92 	void		(*end_iter_u_fp)(nss_db_root_t *rootp,
93 				struct nss_getent_context *contextp);
94 #endif
95 } nss_policyf_t;
96 
97 static mutex_t nss_policyf_lock = DEFAULTMUTEX;
98 static nss_policyf_t nss_policyf_ptrs =
99 	{ (void *)NULL };
100 
101 /*
102  * nsswitch db_root state machine definitions:
103  * The golden rule is:  if you hold a pointer to an nss_db_state struct and
104  * you don't hold the lock, you'd better have incremented the refcount
105  * while you held the lock;  otherwise, it may vanish or change
106  * significantly when you least expect it.
107  *
108  * The pointer in nss_db_root_t is one such, so the reference count >= 1.
109  * Ditto the pointer in struct nss_getent_context.
110  */
111 
112 /*
113  * State for one nsswitch database (e.g. "passwd", "hosts")
114  */
115 struct nss_db_state {
116 	nss_db_root_t		orphan_root;	/* XXX explain */
117 	unsigned		refcount;	/* One for the pointer in    */
118 						/*   nss_db_root_t, plus one */
119 						/*   for each active thread. */
120 	nss_db_params_t		p;
121 	struct __nsw_switchconfig_v1 *config;
122 	int			max_src;	/* is == config->num_lookups */
123 	struct nss_src_state	*src;		/* Pointer to array[max_src] */
124 };
125 
126 /*
127  * State for one of the sources (e.g. "nis", "compat") for a database
128  */
129 struct nss_src_state {
130 	struct __nsw_lookup_v1	*lkp;
131 	int			n_active;
132 	int			n_dormant;
133 	int			n_waiting;	/* ... on wanna_be */
134 	cond_t			wanna_be;
135 	union {
136 		nss_backend_t	*single; /* Efficiency hack for common case */
137 					    /* when limit_dead_backends == 1 */
138 		nss_backend_t	**multi; /* array[limit_dead_backends] of */
139 	} dormant;			    /* pointers to dormant backends */
140 	nss_backend_constr_t	be_constr;
141 	nss_backend_finder_t	*finder;
142 	void			*finder_priv;
143 };
144 
145 static struct nss_db_state	*_nss_db_state_constr(nss_db_initf_t);
146 void				_nss_db_state_destr(struct nss_db_state *);
147 
148 /* ==== null definitions if !MTSAFE?  Ditto lock field in nss_db_root_t */
149 
150 #define	NSS_ROOTLOCK(r, sp)	(cancel_safe_mutex_lock(&(r)->lock), \
151 				*(sp) = (r)->s)
152 
153 #define	NSS_UNLOCK(r)		(cancel_safe_mutex_unlock(&(r)->lock))
154 
155 #define	NSS_CHECKROOT(rp, s)	((s) != (*(rp))->s &&			\
156 			(cancel_safe_mutex_unlock(&(*(rp))->lock),	\
157 			cancel_safe_mutex_lock(&(s)->orphan_root.lock), \
158 			*(rp) = &(s)->orphan_root))
159 
160 #define	NSS_RELOCK(rp, s)	(cancel_safe_mutex_lock(&(*(rp))->lock), \
161 			NSS_CHECKROOT(rp, s))
162 
163 #define	NSS_STATE_REF_u(s)	(++(s)->refcount)
164 
165 #define	NSS_UNREF_UNLOCK(r, s)	(--(s)->refcount != 0			\
166 			? ((void)NSS_UNLOCK(r))				\
167 			: ((void)NSS_UNLOCK(r), (void)_nss_db_state_destr(s)))
168 
169 #define	NSS_LOCK_CHECK(r, f, sp)    (NSS_ROOTLOCK((r), (sp)),	\
170 				    *(sp) == 0 &&		\
171 				    (r->s = *(sp) = _nss_db_state_constr(f)))
172 /* === In the future, NSS_LOCK_CHECK() may also have to check that   */
173 /* === the config info hasn't changed (by comparing version numbers) */
174 
175 
176 /*
177  * NSS_OPTIONS/NIS_OPTIONS environment varibles data definitions:
178  * This remains for backwards compatibility.  But generally nscd will
179  * decide if/how this gets used.
180  */
181 static int checked_env = 0;		/* protected by "rootlock" */
182 
183 	/* allowing __nss_debug_file to be set could be a security hole. */
184 FILE *__nss_debug_file = stdout;
185 int __nss_debug_eng_loop;
186 
187 /* NIS_OPTIONS infrastructure (from linbsl/nis/cache/cache_api.cc) */
188 	/* allowing __nis_debug_file to be set could be a security hole. */
189 FILE *__nis_debug_file = stdout;
190 int __nis_debug_bind;
191 int __nis_debug_rpc;
192 int __nis_debug_calls;
193 char *__nis_prefsrv;
194 char *__nis_preftype;
195 char *__nis_server;   /* if set, use only this server for binding */
196 
197 #define	OPT_INT 1
198 #define	OPT_STRING 2
199 #ifdef DEBUG
200 #define	OPT_FILE 3
201 #endif
202 
203 struct option {
204 	char *name;
205 	int type;
206 	void *address;
207 };
208 
209 static struct option nss_options[] = {
210 #ifdef DEBUG
211 	/* allowing __nss_debug_file to be set could be a security hole. */
212 	{ "debug_file", OPT_FILE, &__nss_debug_file },
213 #endif
214 	{ "debug_eng_loop", OPT_INT, &__nss_debug_eng_loop },
215 	{ 0, 0, 0 },
216 };
217 
218 static struct option nis_options[] = {
219 #ifdef DEBUG
220 	/* allowing __nis_debug_file to be set could be a security hole. */
221 	{ "debug_file", OPT_FILE, &__nis_debug_file },
222 #endif
223 	{ "debug_bind", OPT_INT, &__nis_debug_bind },
224 	{ "debug_rpc", OPT_INT, &__nis_debug_rpc },
225 	{ "debug_calls", OPT_INT, &__nis_debug_calls },
226 	{ "server", OPT_STRING, &__nis_server },
227 	{ "pref_srvr", OPT_STRING, &__nis_prefsrv },
228 	{ "pref_type", OPT_STRING, &__nis_preftype },
229 	{ 0, 0, 0 },
230 };
231 
232 /*
233  * switch configuration parameter "database" definitions:
234  * The switch maintains a simmple read/write parameter database
235  * that nscd and the switch components can use to communicate
236  * nscd data to other components for configuration or out of band
237  * [IE no in the context of a getXbyY or putXbyY operation] data.
238  * The data passed are pointers to a lock  data buffer and a length.
239  * Use of this is treated as SunwPrivate between nscd and the switch
240  * unless other wise stated.
241  */
242 
243 typedef struct nss_cfgparam {
244 	char 		*name;
245 	mutex_t		*lock;
246 	void		*buffer;
247 	size_t		length;
248 } nss_cfgparam_t;
249 
250 typedef struct nss_cfglist {
251 	char 		*name;
252 	nss_cfgparam_t	*list;
253 	int		count;
254 	int		max;
255 } nss_cfglist_t;
256 
257 #define	NSS_CFG_INCR	16
258 
259 static nss_cfglist_t *nss_cfg = NULL;
260 static int nss_cfgcount = 0;
261 static int nss_cfgmax = 0;
262 static mutex_t nss_cfglock = DEFAULTMUTEX;
263 
264 static int nss_cfg_policy_init();
265 
266 /*
267  * A config parameters are in the form component:parameter
268  * as in: nss:parameter - switch (internal FE/policy/BE) parameter
269  *	  nscd:param - nscd application parameter
270  *	  ldap:param - nss_ldap BE parameter
271  *	  passwd:param - get/put passwd FE parameter
272  */
273 
274 #define	NSS_CONFIG_BRK	':'
275 
276 /*
277  * The policy components initial parameter list
278  */
279 static nss_config_t	nss_policy_params[] = {
280 	{ "nss:policyfunc", NSS_CONFIG_ADD, &nss_policyf_lock,
281 		(void *)&nss_policyf_ptrs, (size_t)sizeof (nss_policyf_t) },
282 	{ NULL,	NSS_CONFIG_ADD,	(mutex_t *)NULL, (void *)NULL, (size_t)0 },
283 };
284 
285 /*
286  * NSS parameter configuration routines
287  */
288 
289 /* compare config name (component:parameter) to a component name */
290 static int
291 nss_cfgcn_cmp(const char *cfgname, const char *compname)
292 {
293 	char *c;
294 	size_t len, len2;
295 
296 	/* this code assumes valid pointers */
297 	if ((c = strchr(cfgname, NSS_CONFIG_BRK)) == NULL)
298 		return (-1);
299 	len = (size_t)(c - cfgname);
300 	len2 = strlen(compname);
301 	if (len2 != len)
302 		return (-1);
303 	return (strncmp(cfgname, compname, len));
304 }
305 
306 /* init configuration arena */
307 static int
308 nss_cfg_init()
309 {
310 	nss_cfglist_t *cfg;
311 	int i;
312 
313 	/* First time caller? */
314 	if (nss_cfg != NULL) {
315 		membar_consumer();
316 		return (0);
317 	}
318 
319 	/* Initialize internal tables */
320 	lmutex_lock(&nss_cfglock);
321 	if (nss_cfg != NULL) {
322 		lmutex_unlock(&nss_cfglock);
323 		membar_consumer();
324 		return (0);
325 	}
326 	cfg = libc_malloc(NSS_CFG_INCR * sizeof (nss_cfglist_t));
327 	if (cfg == NULL) {
328 		errno = ENOMEM;
329 		lmutex_unlock(&nss_cfglock);
330 		return (-1);
331 	}
332 	for (i = 0; i < NSS_CFG_INCR; i++) {
333 		cfg[i].list = libc_malloc(
334 		    NSS_CFG_INCR * sizeof (nss_cfgparam_t));
335 		if (cfg[i].list == NULL) {
336 			while (--i >= 0)
337 				libc_free(cfg[i].list);
338 			libc_free(cfg);
339 			errno = ENOMEM;
340 			lmutex_unlock(&nss_cfglock);
341 			return (-1);
342 		}
343 		cfg[i].max = NSS_CFG_INCR;
344 	}
345 	nss_cfgmax = NSS_CFG_INCR;
346 	membar_producer();
347 	nss_cfg = cfg;
348 	lmutex_unlock(&nss_cfglock);
349 
350 	/* Initialize Policy Engine values */
351 	if (nss_cfg_policy_init() < 0) {
352 		return (-1);
353 	}
354 	return (0);
355 }
356 
357 /* find the name'd component list - create it if non-existent */
358 static nss_cfglist_t *
359 nss_cfgcomp_get(char *name, int add)
360 {
361 	nss_cfglist_t	*next;
362 	char	*c;
363 	int	i, len;
364 	size_t	nsize;
365 
366 	/* Make sure system is init'd */
367 	if (nss_cfg_init() < 0)
368 		return ((nss_cfglist_t *)NULL);
369 
370 	/* and check component:name validity */
371 	if (name == NULL || (c = strchr(name, NSS_CONFIG_BRK)) == NULL)
372 		return ((nss_cfglist_t *)NULL);
373 
374 	lmutex_lock(&nss_cfglock);
375 	next = nss_cfg;
376 	for (i = 0; i < nss_cfgcount; i++) {
377 		if (next->name && nss_cfgcn_cmp(name, next->name) == 0) {
378 			lmutex_unlock(&nss_cfglock);
379 			return (next);
380 		}
381 		next++;
382 	}
383 	if (!add) {
384 		lmutex_unlock(&nss_cfglock);
385 		return (NULL);
386 	}
387 
388 	/* not found, create a fresh one */
389 	if (nss_cfgcount >= nss_cfgmax) {
390 		/* realloc first */
391 		nsize = (nss_cfgmax + NSS_CFG_INCR) * sizeof (nss_cfgparam_t);
392 		next = (nss_cfglist_t *)libc_realloc(nss_cfg, nsize);
393 		if (next == NULL) {
394 			errno = ENOMEM;
395 			lmutex_unlock(&nss_cfglock);
396 			return ((nss_cfglist_t *)NULL);
397 		}
398 		(void) memset((void *)(next + nss_cfgcount), '\0',
399 			NSS_CFG_INCR * sizeof (nss_cfglist_t));
400 		nss_cfgmax += NSS_CFG_INCR;
401 		nss_cfg = next;
402 	}
403 	next = nss_cfg + nss_cfgcount;
404 	len = (size_t)(c - name) + 1;
405 	if ((next->name = libc_malloc(len)) == NULL) {
406 		errno = ENOMEM;
407 		lmutex_unlock(&nss_cfglock);
408 		return ((nss_cfglist_t *)NULL);
409 	}
410 	nss_cfgcount++;
411 	(void) strlcpy(next->name, name, len);
412 	lmutex_unlock(&nss_cfglock);
413 	return (next);
414 }
415 
416 /* find the name'd parameter - create it if non-existent */
417 static nss_cfgparam_t *
418 nss_cfgparam_get(char *name, int add)
419 {
420 	nss_cfglist_t	*comp;
421 	nss_cfgparam_t	*next;
422 	int	count, i;
423 	size_t	nsize;
424 
425 	if ((comp = nss_cfgcomp_get(name, add)) == NULL)
426 		return ((nss_cfgparam_t *)NULL);
427 	lmutex_lock(&nss_cfglock);
428 	count = comp->count;
429 	next = comp->list;
430 	for (i = 0; i < count; i++) {
431 		if (next->name && strcmp(name, next->name) == 0) {
432 			lmutex_unlock(&nss_cfglock);
433 			return (next);
434 		}
435 		next++;
436 	}
437 	if (!add) {
438 		lmutex_unlock(&nss_cfglock);
439 		return (NULL);
440 	}
441 
442 	/* not found, create a fresh one */
443 	if (count >= comp->max) {
444 		/* realloc first */
445 		nsize = (comp->max + NSS_CFG_INCR) * sizeof (nss_cfgparam_t);
446 		next = (nss_cfgparam_t *)libc_realloc(comp->list, nsize);
447 		if (next == NULL) {
448 			errno = ENOMEM;
449 			lmutex_unlock(&nss_cfglock);
450 			return ((nss_cfgparam_t *)NULL);
451 		}
452 		comp->max += NSS_CFG_INCR;
453 		comp->list = next;
454 	}
455 	next = comp->list + comp->count;
456 	if ((next->name = libc_strdup(name)) == NULL) {
457 		errno = ENOMEM;
458 		lmutex_unlock(&nss_cfglock);
459 		return ((nss_cfgparam_t *)NULL);
460 	}
461 	comp->count++;
462 	lmutex_unlock(&nss_cfglock);
463 	return (next);
464 }
465 
466 /* find the name'd parameter - delete it if it exists */
467 static void
468 nss_cfg_del(nss_config_t *cfgp)
469 {
470 	char		*name;
471 	nss_cfglist_t	*comp;
472 	nss_cfgparam_t	*next, *cur;
473 	int	count, i, j;
474 
475 	/* exit if component name does not already exist */
476 	if ((name = cfgp->name) == NULL ||
477 	    (comp = nss_cfgcomp_get(name, 0)) == NULL)
478 		return;
479 
480 	/* find it */
481 	lmutex_lock(&nss_cfglock);
482 	count = comp->count;
483 	next = comp->list;
484 	for (i = 0; i < count; i++) {
485 		if (next->name && strcmp(name, next->name) == 0) {
486 			break;	/* found it... */
487 		}
488 		next++;
489 	}
490 	if (i >= count) {
491 		/* not found, already deleted */
492 		lmutex_unlock(&nss_cfglock);
493 		return;
494 	}
495 
496 	/* copy down the remaining parameters, and clean up */
497 	/* don't try to clean up component tables */
498 	cur = next;
499 	next++;
500 	for (j = i+1; j < count; j++) {
501 		*cur = *next;
502 		cur++;
503 		next++;
504 	}
505 	/* erase the last one */
506 	if (cur->name) {
507 		libc_free(cur->name);
508 		cur->name = (char *)NULL;
509 	}
510 	cur->lock = (mutex_t *)NULL;
511 	cur->buffer = (void *)NULL;
512 	cur->length = 0;
513 	comp->count--;
514 	lmutex_unlock(&nss_cfglock);
515 }
516 
517 static int
518 nss_cfg_get(nss_config_t *next)
519 {
520 	nss_cfgparam_t	*param;
521 
522 	errno = 0;
523 	if ((param = nss_cfgparam_get(next->name, 0)) == NULL)
524 		return (-1);
525 	next->lock = param->lock;
526 	next->buffer = param->buffer;
527 	next->length = param->length;
528 	return (0);
529 }
530 
531 static int
532 nss_cfg_put(nss_config_t *next, int add)
533 {
534 	nss_cfgparam_t	*param;
535 
536 	errno = 0;
537 	if ((param = nss_cfgparam_get(next->name, add)) == NULL)
538 		return (-1);
539 	param->lock = next->lock;
540 	param->buffer = next->buffer;
541 	param->length = next->length;
542 	return (0);
543 }
544 
545 /*
546  * Policy engine configurator - set and get interface
547  * argument is a NULL terminated list of set/get requests
548  * with input/result buffers and lengths.  nss_cname is the
549  * specifier of a set or get operation and the property being
550  * managed.  The intent is limited functions and expandability.
551  */
552 
553 nss_status_t
554 nss_config(nss_config_t **plist, int cnt)
555 {
556 	nss_config_t	*next;
557 	int 	i;
558 
559 	/* interface is only available to nscd */
560 	if (_nsc_proc_is_cache() <= 0) {
561 		return (NSS_UNAVAIL);
562 	}
563 	if (plist == NULL || cnt <= 0)
564 		return (NSS_SUCCESS);
565 	for (i = 0; i < cnt; i++) {
566 		next = plist[i];
567 		if (next == NULL)
568 			break;
569 		if (next->name == NULL) {
570 			errno = EFAULT;
571 			return (NSS_ERROR);
572 		}
573 		switch (next->cop) {
574 		case NSS_CONFIG_GET:
575 			/* get current lock/buffer/length fields */
576 			if (nss_cfg_get(next) < 0) {
577 				return (NSS_ERROR);
578 			}
579 			break;
580 		case NSS_CONFIG_PUT:
581 			/* set new lock/buffer/length fields */
582 			if (nss_cfg_put(next, 0) < 0) {
583 				return (NSS_ERROR);
584 			}
585 			break;
586 		case NSS_CONFIG_ADD:
587 			/* add parameter & set new lock/buffer/length fields */
588 			if (nss_cfg_put(next, 1) < 0) {
589 				return (NSS_ERROR);
590 			}
591 			break;
592 		case NSS_CONFIG_DELETE:
593 			/* delete parameter - should always work... */
594 			nss_cfg_del(next);
595 			break;
596 		case NSS_CONFIG_LIST:
597 			break;
598 		default:
599 			continue;
600 		}
601 	}
602 	return (NSS_SUCCESS);
603 }
604 
605 /*
606  * This routine is called immediately after nss_cfg_init but prior to
607  * any commands from nscd being processed.  The intent here is to
608  * initialize the nss:* parameters allowed by the policy component
609  * so that nscd can then proceed and modify them if so desired.
610  *
611  * We know we can only get here if we are nscd so we can skip the
612  * preliminaries.
613  */
614 
615 static int
616 nss_cfg_policy_init()
617 {
618 	nss_config_t	*next = &nss_policy_params[0];
619 
620 	for (; next && next->name != NULL; next++) {
621 		if (nss_cfg_put(next, 1) < 0)
622 			return (-1);
623 	}
624 	return (0);
625 }
626 
627 /*
628  * NSS_OPTION & NIS_OPTION environment variable functions
629  */
630 
631 static
632 void
633 set_option(struct option *opt, char *name, char *val)
634 {
635 	int n;
636 	char *p;
637 #ifdef DEBUG
638 	FILE *fp;
639 #endif
640 
641 	for (; opt->name; opt++) {
642 		if (strcmp(name, opt->name) == 0) {
643 			switch (opt->type) {
644 			    case OPT_STRING:
645 				p = libc_strdup(val);
646 				*((char **)opt->address) = p;
647 				break;
648 
649 			    case OPT_INT:
650 				if (strcmp(val, "") == 0)
651 					n = 1;
652 				else
653 					n = atoi(val);
654 				*((int *)opt->address) = n;
655 				break;
656 #ifdef DEBUG
657 			    case OPT_FILE:
658 				fp = fopen(val, "wF");
659 				*((FILE **)opt->address) = fp;
660 				break;
661 #endif
662 			}
663 			break;
664 		}
665 	}
666 }
667 
668 static
669 void
670 __parse_environment(struct option *opt, char *p)
671 {
672 	char *base;
673 	char optname[100];
674 	char optval[100];
675 
676 	while (*p) {
677 		while (isspace(*p))
678 			p++;
679 		if (*p == '\0')
680 			break;
681 
682 		base = p;
683 		while (*p && *p != '=' && !isspace(*p))
684 			p++;
685 		/*
686 		 * play it safe and keep it simple, bail if an opt name
687 		 * is too long.
688 		 */
689 		if ((p-base) >= sizeof (optname))
690 			return;
691 
692 		(void) strncpy(optname, base, p-base);
693 		optname[p-base] = '\0';
694 
695 		if (*p == '=') {
696 			p++;
697 			base = p;
698 			while (*p && !isspace(*p))
699 				p++;
700 			/*
701 			 * play it safe and keep it simple, bail if an opt
702 			 * value is too long.
703 			 */
704 			if ((p-base) >= sizeof (optval))
705 				return;
706 
707 			(void) strncpy(optval, base, p-base);
708 			optval[p-base] = '\0';
709 		} else {
710 			optval[0] = '\0';
711 		}
712 
713 		set_option(opt, optname, optval);
714 	}
715 }
716 
717 static
718 void
719 nss_get_environment()
720 {
721 	char *p;
722 
723 /* NSS_OPTIONS is undocumented and should be used without nscd running. */
724 	p = getenv("NSS_OPTIONS");
725 	if (p == NULL)
726 		return;
727 	__parse_environment(nss_options, p);
728 }
729 
730 /*
731  * sole external routine called from libnsl/nis/cache/cache_api.cc in the
732  * routines _nis_CacheInit/__nis_CacheLocalInit/__nis_CacheMgrInit_discard
733  * Only after checking "checked_env" (which must be done with mutex
734  * "cur_cache_lock" held) and is done once, (then "checked_env" is set)
735  */
736 void
737 __nis_get_environment()
738 {
739 	char *p;
740 
741 	p = getenv("NIS_OPTIONS");
742 	if (p == NULL)
743 		return;
744 	__parse_environment(nis_options, p);
745 }
746 
747 
748 /*
749  * Switch policy component backend state machine functions
750  */
751 
752 static nss_backend_t *
753 nss_get_backend_u(nss_db_root_t **rootpp, struct nss_db_state *s, int n_src)
754 {
755 	struct nss_src_state	*src = &s->src[n_src];
756 	nss_backend_t		*be;
757 	int cancel_state;
758 
759 	for (;;) {
760 		if (src->n_dormant > 0) {
761 			src->n_dormant--;
762 			src->n_active++;
763 			if (s->p.max_dormant_per_src == 1) {
764 				be = src->dormant.single;
765 			} else {
766 				be = src->dormant.multi[src->n_dormant];
767 			}
768 			break;
769 		}
770 
771 		if (src->be_constr == 0) {
772 			nss_backend_finder_t	*bf;
773 
774 			for (bf = s->p.finders;  bf != 0;  bf = bf->next) {
775 				nss_backend_constr_t c;
776 
777 				c = (*bf->lookup)
778 					(bf->lookup_priv,
779 						s->p.name,
780 						src->lkp->service_name,
781 						&src->finder_priv);
782 				if (c != 0) {
783 					src->be_constr = c;
784 					src->finder = bf;
785 					break;
786 				}
787 			}
788 			if (src->be_constr == 0) {
789 				/* Couldn't find the backend anywhere */
790 				be = 0;
791 				break;
792 			}
793 		}
794 
795 		if (src->n_active < s->p.max_active_per_src) {
796 			be = (*src->be_constr)(s->p.name,
797 						src->lkp->service_name,
798 						0 /* === unimplemented */);
799 			if (be != 0) {
800 				src->n_active++;
801 				break;
802 			} else if (src->n_active == 0) {
803 				/* Something's wrong;  we should be */
804 				/*   able to create at least one    */
805 				/*   instance of the backend	    */
806 				break;
807 			}
808 			/*
809 			 * Else it's odd that we can't create another backend
810 			 *   instance, but don't sweat it;  instead, queue for
811 			 *   an existing backend instance.
812 			 */
813 		}
814 
815 		src->n_waiting++;
816 		(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
817 		    &cancel_state);
818 		(void) cond_wait(&src->wanna_be, &(*rootpp)->lock);
819 		(void) pthread_setcancelstate(cancel_state, NULL);
820 		NSS_CHECKROOT(rootpp, s);
821 		src->n_waiting--;
822 
823 		/*
824 		 * Loop and see whether things got better for us, or whether
825 		 *   someone else got scheduled first and we have to try
826 		 *   this again.
827 		 *
828 		 * === ?? Should count iterations, assume bug if many ??
829 		 */
830 	}
831 	return (be);
832 }
833 
834 static void
835 nss_put_backend_u(struct nss_db_state *s, int n_src, nss_backend_t *be)
836 {
837 	struct nss_src_state	*src = &s->src[n_src];
838 
839 	if (be == 0) {
840 		return;
841 	}
842 
843 	src->n_active--;
844 
845 	if (src->n_dormant < s->p.max_dormant_per_src) {
846 		if (s->p.max_dormant_per_src == 1) {
847 			src->dormant.single = be;
848 			src->n_dormant++;
849 		} else if (src->dormant.multi != 0 ||
850 			(src->dormant.multi =
851 			    libc_malloc(s->p.max_dormant_per_src *
852 			    sizeof (nss_backend_t *))) != NULL) {
853 			src->dormant.multi[src->n_dormant] = be;
854 			src->n_dormant++;
855 		} else {
856 			/* Can't store it, so toss it */
857 			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_DESTRUCTOR, 0);
858 		}
859 	} else {
860 		/* We've stored as many as we want, so toss it */
861 		(void) NSS_INVOKE_DBOP(be, NSS_DBOP_DESTRUCTOR, 0);
862 	}
863 	if (src->n_waiting > 0) {
864 		(void) cond_signal(&src->wanna_be);
865 	}
866 }
867 
868 static struct nss_db_state *
869 _nss_db_state_constr(nss_db_initf_t initf)
870 {
871 	struct nss_db_state	*s;
872 	struct __nsw_switchconfig_v1 *config = 0;
873 	struct __nsw_lookup_v1	*lkp;
874 	enum __nsw_parse_err	err;
875 	const char		*config_name;
876 	int			n_src;
877 
878 	if ((s = libc_malloc(sizeof (*s))) == 0) {
879 		return (0);
880 	}
881 	(void) _private_mutex_init(&s->orphan_root.lock, USYNC_THREAD, 0);
882 
883 	s->p.max_active_per_src	= 10;
884 	s->p.max_dormant_per_src = 1;
885 	s->p.finders = nss_default_finders;
886 	(*initf)(&s->p);
887 	if (s->p.name == 0) {
888 		_nss_db_state_destr(s);
889 		return (0);
890 	}
891 
892 	if (!checked_env) {
893 /* NSS_OPTIONS is undocumented and should be used without nscd running. */
894 		nss_get_environment();
895 		checked_env = 1;
896 	}
897 
898 	config_name = s->p.config_name ? s->p.config_name : s->p.name;
899 	if (! (s->p.flags & NSS_USE_DEFAULT_CONFIG)) {
900 		config = __nsw_getconfig_v1(config_name, &err);
901 		/* === ? test err ? */
902 	}
903 	if (config == 0) {
904 		/* getconfig failed, or frontend demanded default config */
905 
906 		char	*str;	/* _nsw_getoneconfig() clobbers its argument */
907 
908 		if ((str = libc_strdup(s->p.default_config)) != 0) {
909 			config = _nsw_getoneconfig_v1(config_name, str, &err);
910 			libc_free(str);
911 		}
912 		if (config == 0) {
913 			_nss_db_state_destr(s);
914 			return (0);
915 		}
916 	}
917 	s->config = config;
918 	if ((s->max_src = config->num_lookups) <= 0 ||
919 	    (s->src = libc_malloc(s->max_src * sizeof (*s->src))) == 0) {
920 		_nss_db_state_destr(s);
921 		return (0);
922 	}
923 	for (n_src = 0, lkp = config->lookups;
924 	    n_src < s->max_src; n_src++, lkp = lkp->next) {
925 		s->src[n_src].lkp = lkp;
926 		(void) cond_init(&s->src[n_src].wanna_be, USYNC_THREAD, 0);
927 	}
928 	s->refcount = 1;
929 	return (s);
930 }
931 
932 void
933 _nss_src_state_destr(struct nss_src_state *src, int max_dormant)
934 {
935 	if (max_dormant == 1) {
936 		if (src->n_dormant != 0) {
937 			(void) NSS_INVOKE_DBOP(src->dormant.single,
938 					NSS_DBOP_DESTRUCTOR, 0);
939 		};
940 	} else if (src->dormant.multi != 0) {
941 		int	n;
942 
943 		for (n = 0;  n < src->n_dormant;  n++) {
944 			(void) NSS_INVOKE_DBOP(src->dormant.multi[n],
945 					NSS_DBOP_DESTRUCTOR, 0);
946 		}
947 		libc_free(src->dormant.multi);
948 	}
949 
950 	/* cond_destroy(&src->wanna_be); */
951 
952 	if (src->finder != 0) {
953 		(*src->finder->delete)(src->finder_priv, src->be_constr);
954 	}
955 }
956 
957 /*
958  * _nss_db_state_destr() -- used by NSS_UNREF_UNLOCK() to free the entire
959  *	nss_db_state structure.
960  * Assumes that s has been ref-counted down to zero (in particular,
961  *	rootp->s has already been dealt with).
962  *
963  * Nobody else holds a pointer to *s (if they did, refcount != 0),
964  *   so we can clean up state *after* we drop the lock (also, by the
965  *   time we finish freeing the state structures, the lock may have
966  *   ceased to exist -- if we were using the orphan_root).
967  */
968 
969 void
970 _nss_db_state_destr(struct nss_db_state *s)
971 {
972 
973 	if (s == NULL)
974 		return;
975 
976 	/* === _private_mutex_destroy(&s->orphan_root.lock); */
977 	if (s->p.cleanup != 0) {
978 		(*s->p.cleanup)(&s->p);
979 	}
980 	if (s->config != 0) {
981 		(void) __nsw_freeconfig_v1(s->config);
982 	}
983 	if (s->src != 0) {
984 		int	n_src;
985 
986 		for (n_src = 0;  n_src < s->max_src;  n_src++) {
987 			_nss_src_state_destr(&s->src[n_src],
988 				s->p.max_dormant_per_src);
989 		}
990 		libc_free(s->src);
991 	}
992 	libc_free(s);
993 }
994 
995 
996 /*
997  * _nss_status_vec() returns a bit vector of all status codes returned during
998  * the most recent call to nss_search().
999  * _nss_status_vec_p() returns a pointer to this bit vector, or NULL on
1000  * failure.
1001  * These functions are private.  Don't use them externally without discussing
1002  * it with the switch maintainers.
1003  */
1004 static uint_t *
1005 _nss_status_vec_p()
1006 {
1007 	return (tsdalloc(_T_NSS_STATUS_VEC, sizeof (uint_t), NULL));
1008 }
1009 
1010 unsigned int
1011 _nss_status_vec(void)
1012 {
1013 	unsigned int *status_vec_p = _nss_status_vec_p();
1014 
1015 	return ((status_vec_p != NULL) ? *status_vec_p : (1 << NSS_UNAVAIL));
1016 }
1017 
1018 static void
1019 output_loop_diag_a(
1020 	int n,
1021 	char *dbase,
1022 	struct __nsw_lookup_v1 *lkp)
1023 
1024 {
1025 	(void) fprintf(__nss_debug_file,
1026 		"NSS_retry(%d): '%s': trying '%s' ... ",
1027 		n, dbase, lkp->service_name);
1028 	(void) fflush(__nss_debug_file);
1029 
1030 }
1031 
1032 static void
1033 output_loop_diag_b(
1034 	nss_status_t res,
1035 	struct __nsw_lookup_v1 *lkp)
1036 
1037 {
1038 	(void) fprintf(__nss_debug_file, "result=");
1039 	switch (res) {
1040 	case NSS_SUCCESS:
1041 		(void) fprintf(__nss_debug_file, "SUCCESS");
1042 		break;
1043 	case NSS_NOTFOUND:
1044 		(void) fprintf(__nss_debug_file, "NOTFOUND");
1045 		break;
1046 	case NSS_UNAVAIL:
1047 		(void) fprintf(__nss_debug_file, "UNAVAIL");
1048 		break;
1049 	case NSS_TRYAGAIN:
1050 		(void) fprintf(__nss_debug_file, "TRYAGAIN");
1051 		break;
1052 	case NSS_NISSERVDNS_TRYAGAIN:
1053 		(void) fprintf(__nss_debug_file, "NISSERVDNS_TRYAGAIN");
1054 		break;
1055 	default:
1056 		(void) fprintf(__nss_debug_file, "undefined");
1057 	}
1058 	(void) fprintf(__nss_debug_file, ", action=");
1059 	switch (lkp->actions[res]) {
1060 	case __NSW_CONTINUE:
1061 		(void) fprintf(__nss_debug_file, "CONTINUE");
1062 		break;
1063 	case  __NSW_RETURN:
1064 		(void) fprintf(__nss_debug_file, "RETURN");
1065 		break;
1066 	case __NSW_TRYAGAIN_FOREVER:
1067 		(void) fprintf(__nss_debug_file, "TRYAGAIN_FOREVER");
1068 		break;
1069 	case __NSW_TRYAGAIN_NTIMES:
1070 		(void) fprintf(__nss_debug_file, "TRYAGAIN_NTIMES (N=%d)",
1071 			lkp->max_retries);
1072 		break;
1073 	case __NSW_TRYAGAIN_PAUSED:
1074 		(void) fprintf(__nss_debug_file, "TRYAGAIN_PAUSED");
1075 		break;
1076 	default:
1077 		(void) fprintf(__nss_debug_file, "undefined");
1078 	}
1079 	(void) fprintf(__nss_debug_file, "\n");
1080 }
1081 
1082 #define	NSS_BACKOFF(n, b, t) \
1083 			((n) > ((b) + 3) ? t : (1 << ((n) - ((b) + 1))))
1084 
1085 static int
1086 retry_test(nss_status_t res, int n, struct __nsw_lookup_v1 *lkp)
1087 {
1088 	if (res != NSS_TRYAGAIN && res !=  NSS_NISSERVDNS_TRYAGAIN) {
1089 		if (res == NSS_SUCCESS) {
1090 			__NSW_UNPAUSE_ACTION(lkp->actions[__NSW_TRYAGAIN]);
1091 			__NSW_UNPAUSE_ACTION(
1092 				lkp->actions[__NSW_NISSERVDNS_TRYAGAIN]);
1093 		}
1094 		return (0);
1095 	}
1096 
1097 	if ((res == NSS_TRYAGAIN &&
1098 	    lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER) ||
1099 	    (res == NSS_NISSERVDNS_TRYAGAIN &&
1100 	    lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER))
1101 		return (1);
1102 
1103 	if (res == NSS_TRYAGAIN &&
1104 	    lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
1105 		if (n <= lkp->max_retries)
1106 			return (1);
1107 		else {
1108 			lkp->actions[__NSW_TRYAGAIN] = __NSW_TRYAGAIN_PAUSED;
1109 			return (0);
1110 		}
1111 
1112 	if (res == NSS_NISSERVDNS_TRYAGAIN &&
1113 	    lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
1114 		if (n <= lkp->max_retries)
1115 			return (1);
1116 		else {
1117 			lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] =
1118 			    __NSW_TRYAGAIN_PAUSED;
1119 			return (0);
1120 		}
1121 
1122 	return (0);
1123 }
1124 
1125 /*
1126  * Switch policy component functional interfaces
1127  */
1128 
1129 void
1130 nss_delete(nss_db_root_t *rootp)
1131 {
1132 	struct nss_db_state	*s;
1133 
1134 	/* no name service cache daemon divert here */
1135 	/* local nss_delete decrements state reference counts */
1136 	/* and may free up opened switch resources. */
1137 
1138 	NSS_ROOTLOCK(rootp, &s);
1139 	if (s == 0) {
1140 		NSS_UNLOCK(rootp);
1141 	} else {
1142 		rootp->s = 0;
1143 		NSS_UNREF_UNLOCK(rootp, s);
1144 	}
1145 }
1146 
1147 nss_status_t
1148 nss_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum,
1149 	void *search_args)
1150 {
1151 	nss_status_t		res = NSS_UNAVAIL;
1152 	struct nss_db_state	*s;
1153 	int			n_src;
1154 	unsigned int		*status_vec_p;
1155 
1156 	/* name service cache daemon divert */
1157 	res = _nsc_search(rootp, initf, search_fnum, search_args);
1158 	if (res != NSS_TRYLOCAL)
1159 		return (res);
1160 
1161 	/* fall through - process locally */
1162 	errno = 0;			/* just in case ... */
1163 	res = NSS_UNAVAIL;
1164 	status_vec_p = _nss_status_vec_p();
1165 
1166 	if (status_vec_p == NULL) {
1167 		return (NSS_UNAVAIL);
1168 	}
1169 	*status_vec_p = 0;
1170 
1171 	NSS_LOCK_CHECK(rootp, initf, &s);
1172 	if (s == 0) {
1173 		NSS_UNLOCK(rootp);
1174 		return (res);
1175 	}
1176 	NSS_STATE_REF_u(s);
1177 
1178 	for (n_src = 0;  n_src < s->max_src;  n_src++) {
1179 		nss_backend_t		*be;
1180 		nss_backend_op_t	funcp;
1181 
1182 		res = NSS_UNAVAIL;
1183 		if ((be = nss_get_backend_u(&rootp, s, n_src)) != 0) {
1184 			if ((funcp = NSS_LOOKUP_DBOP(be, search_fnum)) != 0) {
1185 				int n_loop = 0;
1186 				int no_backoff = 19;
1187 				int max_backoff = 5;	/* seconds */
1188 
1189 				do {
1190 					/*
1191 					 * Backend operation may take a while;
1192 					 * drop the lock so we don't serialize
1193 					 * more than necessary.
1194 					 */
1195 					NSS_UNLOCK(rootp);
1196 
1197 					/* After several tries, backoff... */
1198 					if (n_loop > no_backoff) {
1199 					    if (__nss_debug_eng_loop > 1)
1200 						(void) fprintf(__nss_debug_file,
1201 						"NSS: loop: sleeping %d ...\n",
1202 						    NSS_BACKOFF(n_loop,
1203 						    no_backoff, max_backoff));
1204 
1205 					    (void) sleep(NSS_BACKOFF(n_loop,
1206 						    no_backoff, max_backoff));
1207 					}
1208 
1209 					if (__nss_debug_eng_loop)
1210 						output_loop_diag_a(n_loop,
1211 							s->config->dbase,
1212 							s->src[n_src].lkp);
1213 
1214 
1215 					res = (*funcp)(be, search_args);
1216 					NSS_RELOCK(&rootp, s);
1217 					n_loop++;
1218 					if (__nss_debug_eng_loop)
1219 						output_loop_diag_b(res,
1220 							s->src[n_src].lkp);
1221 				} while (retry_test(res, n_loop,
1222 							s->src[n_src].lkp));
1223 			}
1224 			nss_put_backend_u(s, n_src, be);
1225 		}
1226 		*status_vec_p |= (1 << res);
1227 		if (__NSW_ACTION_V1(s->src[n_src].lkp, res) == __NSW_RETURN) {
1228 			if (__nss_debug_eng_loop)
1229 				(void) fprintf(__nss_debug_file,
1230 					"NSS: '%s': return.\n",
1231 					s->config->dbase);
1232 			break;
1233 		} else
1234 			if (__nss_debug_eng_loop)
1235 				(void) fprintf(__nss_debug_file,
1236 					"NSS: '%s': continue ...\n",
1237 					s->config->dbase);
1238 	}
1239 	NSS_UNREF_UNLOCK(rootp, s);
1240 	return (res);
1241 }
1242 
1243 
1244 /*
1245  * Start of nss_{setent|getent|endent}
1246  */
1247 
1248 /*
1249  * State (here called "context") for one setent/getent.../endent sequence.
1250  *   In principle there could be multiple contexts active for a single
1251  *   database;  in practice, since Posix and UI have helpfully said that
1252  *   getent() state is global rather than, say, per-thread or user-supplied,
1253  *   we have at most one of these per nss_db_state.
1254  *   XXX ? Is this statement still true?
1255  *
1256  * NSS2 - a client's context is maintained as a cookie delivered by and
1257  * passed to nscd.  The cookie is a 64 bit (nssuint_t) unique opaque value
1258  * created by nscd.
1259  * cookie states:
1260  *	NSCD_NEW_COOKIE		- cookie value uninitialized
1261  *	NSCD_LOCAL_COOKIE	- setent is a local setent
1262  *	all other		- NSCD unique opaque id for this setent
1263  * A client's context is also associated with a seq_num.  This is a nscd
1264  * opaque 64 bit (nssuint_t) value passed with a cookie, and used to by nscd
1265  * to validate the sequencing of the context.  The client treats this as
1266  * a pass through value.
1267  *
1268  * XXX ??  Use Cookie as cross-check info so that we can detect an
1269  * nss_context that missed an nss_delete() or similar.
1270  */
1271 
1272 struct nss_getent_context {
1273 	int			n_src;	/* >= max_src ==> end of sequence */
1274 	nss_backend_t		*be;
1275 	struct nss_db_state	*s;
1276 	nssuint_t		cookie;
1277 	nssuint_t		seq_num;
1278 	nssuint_t		cookie_setent;
1279 	nss_db_params_t		param;
1280 };
1281 
1282 static void		nss_setent_u(nss_db_root_t *,
1283 				    nss_db_initf_t,
1284 				    nss_getent_t *);
1285 static nss_status_t	nss_getent_u(nss_db_root_t *,
1286 				    nss_db_initf_t,
1287 				    nss_getent_t *,
1288 				    void *);
1289 static void		nss_endent_u(nss_db_root_t *,
1290 				    nss_db_initf_t,
1291 				    nss_getent_t *);
1292 
1293 void
1294 nss_setent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp)
1295 {
1296 	if (contextpp == 0) {
1297 		return;
1298 	}
1299 	cancel_safe_mutex_lock(&contextpp->lock);
1300 	nss_setent_u(rootp, initf, contextpp);
1301 	cancel_safe_mutex_unlock(&contextpp->lock);
1302 }
1303 
1304 nss_status_t
1305 nss_getent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp,
1306 	void *args)
1307 {
1308 	nss_status_t		status;
1309 
1310 	if (contextpp == 0) {
1311 		return (NSS_UNAVAIL);
1312 	}
1313 	cancel_safe_mutex_lock(&contextpp->lock);
1314 	status = nss_getent_u(rootp, initf, contextpp, args);
1315 	cancel_safe_mutex_unlock(&contextpp->lock);
1316 	return (status);
1317 }
1318 
1319 void
1320 nss_endent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp)
1321 {
1322 	if (contextpp == 0) {
1323 		return;
1324 	}
1325 	cancel_safe_mutex_lock(&contextpp->lock);
1326 	nss_endent_u(rootp, initf, contextpp);
1327 	cancel_safe_mutex_unlock(&contextpp->lock);
1328 }
1329 
1330 /*
1331  * Each of the _u versions of the nss interfaces assume that the context
1332  * lock is held.  No need to divert to nscd.  Private to local sequencing.
1333  */
1334 
1335 static void
1336 end_iter_u(nss_db_root_t *rootp, struct nss_getent_context *contextp)
1337 {
1338 	struct nss_db_state	*s;
1339 	nss_backend_t		*be;
1340 	int			n_src;
1341 
1342 	s = contextp->s;
1343 	n_src = contextp->n_src;
1344 	be = contextp->be;
1345 
1346 	if (s != 0) {
1347 		if (n_src < s->max_src && be != 0) {
1348 			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1349 			NSS_RELOCK(&rootp, s);
1350 			nss_put_backend_u(s, n_src, be);
1351 			contextp->be = 0;  /* Should be unnecessary, but hey */
1352 			NSS_UNREF_UNLOCK(rootp, s);
1353 		}
1354 		contextp->s = 0;
1355 	}
1356 }
1357 
1358 static void
1359 nss_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1360 	nss_getent_t *contextpp)
1361 {
1362 	nss_status_t		status;
1363 	struct nss_db_state	*s;
1364 	struct nss_getent_context *contextp;
1365 	nss_backend_t		*be;
1366 	int			n_src;
1367 
1368 	/* setup process wide context while locked */
1369 	if ((contextp = contextpp->ctx) == 0) {
1370 		if ((contextp = libc_malloc(sizeof (*contextp))) == 0) {
1371 			return;
1372 		}
1373 		contextpp->ctx = contextp;
1374 		contextp->cookie = NSCD_NEW_COOKIE;	/* cookie init */
1375 		contextp->seq_num = 0;			/* seq_num init */
1376 		s = 0;
1377 	} else {
1378 		s = contextp->s;
1379 		if (contextp->cookie != NSCD_LOCAL_COOKIE)
1380 			contextp->cookie = NSCD_NEW_COOKIE;
1381 	}
1382 
1383 	/* name service cache daemon divert */
1384 	if (contextp->cookie == NSCD_NEW_COOKIE) {
1385 		status = _nsc_setent_u(rootp, initf, contextpp);
1386 		if (status != NSS_TRYLOCAL)
1387 			return;
1388 	}
1389 
1390 	/* fall through - process locally */
1391 	if (s == 0) {
1392 		NSS_LOCK_CHECK(rootp, initf, &s);
1393 		if (s == 0) {
1394 			/* Couldn't set up state, so quit */
1395 			NSS_UNLOCK(rootp);
1396 			/* ==== is there any danger of not having done an */
1397 			/* end_iter() here, and hence of losing backends? */
1398 			contextpp->ctx = 0;
1399 			libc_free(contextp);
1400 			return;
1401 		}
1402 		NSS_STATE_REF_u(s);
1403 		contextp->s = s;
1404 	} else {
1405 		s	= contextp->s;
1406 		n_src	= contextp->n_src;
1407 		be	= contextp->be;
1408 		if (n_src == 0 && be != 0) {
1409 			/*
1410 			 * Optimization:  don't do endent, don't change
1411 			 *   backends, just do the setent.  Look Ma, no locks
1412 			 *   (nor any context that needs updating).
1413 			 */
1414 			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1415 			return;
1416 		}
1417 		if (n_src < s->max_src && be != 0) {
1418 			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1419 			NSS_RELOCK(&rootp, s);
1420 			nss_put_backend_u(s, n_src, be);
1421 			contextp->be = 0;	/* Play it safe */
1422 		} else {
1423 			NSS_RELOCK(&rootp, s);
1424 		}
1425 	}
1426 	for (n_src = 0, be = 0; n_src < s->max_src &&
1427 		(be = nss_get_backend_u(&rootp, s, n_src)) == 0; n_src++) {
1428 		;
1429 	}
1430 	NSS_UNLOCK(rootp);
1431 
1432 	contextp->n_src	= n_src;
1433 	contextp->be	= be;
1434 
1435 	if (be == 0) {
1436 		/* Things are broken enough that we can't do setent/getent */
1437 		nss_endent_u(rootp, initf, contextpp);
1438 		return;
1439 	}
1440 	(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1441 }
1442 
1443 static nss_status_t
1444 nss_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1445 	nss_getent_t *contextpp, void *args)
1446 {
1447 	nss_status_t		status;
1448 	struct nss_db_state	*s;
1449 	struct nss_getent_context *contextp;
1450 	int			n_src;
1451 	nss_backend_t		*be;
1452 
1453 	if ((contextp = contextpp->ctx) == 0) {
1454 		nss_setent_u(rootp, initf, contextpp);
1455 		if ((contextp = contextpp->ctx) == 0) {
1456 			/* Give up */
1457 			return (NSS_UNAVAIL);
1458 		}
1459 	}
1460 	/* name service cache daemon divert */
1461 	status = _nsc_getent_u(rootp, initf, contextpp, args);
1462 	if (status != NSS_TRYLOCAL)
1463 		return (status);
1464 
1465 	/* fall through - process locally */
1466 	s	= contextp->s;
1467 	n_src	= contextp->n_src;
1468 	be	= contextp->be;
1469 
1470 	if (s == 0) {
1471 		/*
1472 		 * We've done an end_iter() and haven't done nss_setent()
1473 		 * or nss_endent() since;  we should stick in this state
1474 		 * until the caller invokes one of those two routines.
1475 		 */
1476 		return (NSS_SUCCESS);
1477 	}
1478 
1479 	while (n_src < s->max_src) {
1480 		nss_status_t res;
1481 
1482 		if (be == 0) {
1483 			/* If it's null it's a bug, but let's play safe */
1484 			res = NSS_UNAVAIL;
1485 		} else {
1486 			res = NSS_INVOKE_DBOP(be, NSS_DBOP_GETENT, args);
1487 		}
1488 
1489 		if (__NSW_ACTION_V1(s->src[n_src].lkp, res) == __NSW_RETURN) {
1490 			if (res != __NSW_SUCCESS) {
1491 				end_iter_u(rootp, contextp);
1492 			}
1493 			return (res);
1494 		}
1495 		(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1496 		NSS_RELOCK(&rootp, s);
1497 		nss_put_backend_u(s, n_src, be);
1498 		do {
1499 			n_src++;
1500 		} while (n_src < s->max_src &&
1501 			(be = nss_get_backend_u(&rootp, s, n_src)) == 0);
1502 		if (be == 0) {
1503 			/*
1504 			 * This is the case where we failed to get the backend
1505 			 * for the last source. We exhausted all sources.
1506 			 */
1507 			NSS_UNLOCK(rootp);
1508 			nss_endent_u(rootp, initf, contextpp);
1509 			nss_delete(rootp);
1510 			return (NSS_SUCCESS);
1511 		}
1512 		NSS_UNLOCK(rootp);
1513 		contextp->n_src	= n_src;
1514 		contextp->be	= be;
1515 		(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1516 	}
1517 	/* Got to the end of the sources without finding another entry */
1518 	end_iter_u(rootp, contextp);
1519 	return (NSS_SUCCESS);
1520 	/* success is either a successful entry or end of the sources */
1521 }
1522 
1523 /*ARGSUSED*/
1524 static void
1525 nss_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1526 	nss_getent_t *contextpp)
1527 {
1528 	nss_status_t		status;
1529 	struct nss_getent_context *contextp;
1530 
1531 	if ((contextp = contextpp->ctx) == 0) {
1532 		/* nss_endent() on an unused context is a no-op */
1533 		return;
1534 	}
1535 
1536 	/* notify name service cache daemon */
1537 	status = _nsc_endent_u(rootp, initf, contextpp);
1538 	if (status != NSS_TRYLOCAL) {
1539 		/* clean up */
1540 		libc_free(contextp);
1541 		contextpp->ctx = 0;
1542 		return;
1543 	}
1544 
1545 	/* fall through - process locally */
1546 
1547 	/*
1548 	 * Existing code (BSD, SunOS) works in such a way that getXXXent()
1549 	 *   following an endXXXent() behaves as though the user had invoked
1550 	 *   setXXXent(), i.e. it iterates properly from the beginning.
1551 	 * We'd better not break this, so our choices are
1552 	 *	(1) leave the context structure around, and do nss_setent or
1553 	 *	    something equivalent,
1554 	 *   or	(2) free the context completely, and rely on the code in
1555 	 *	    nss_getent() that makes getXXXent() do the right thing
1556 	 *	    even without a preceding setXXXent().
1557 	 * The code below does (2), which frees up resources nicely but will
1558 	 * cost more if the user then does more getXXXent() operations.
1559 	 * Moral:  for efficiency, don't call endXXXent() prematurely.
1560 	 */
1561 	end_iter_u(rootp, contextp);
1562 	libc_free(contextp);
1563 	contextpp->ctx = 0;
1564 }
1565 
1566 /*
1567  * pack dbd data into header
1568  * Argment pointers assumed valid.
1569  * poff offset position pointer
1570  *   IN = starting offset for dbd header
1571  *   OUT = starting offset for next section
1572  */
1573 
1574 static nss_status_t
1575 nss_pack_dbd(void *buffer, size_t bufsize, nss_db_params_t *p, size_t *poff)
1576 {
1577 	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1578 	nss_dbd_t		*pdbd;
1579 	size_t			off = *poff;
1580 	size_t			len, blen;
1581 	size_t			n, nc, dc;
1582 	char			*bptr;
1583 
1584 	pbuf->dbd_off = (nssuint_t)off;
1585 	bptr = (char *)buffer + off;
1586 	blen = bufsize - off;
1587 	len = sizeof (nss_dbd_t);
1588 
1589 	n = nc = dc = 0;
1590 	if (p->name == NULL) {
1591 		errno = ERANGE;			/* actually EINVAL */
1592 		return (NSS_ERROR);
1593 	}
1594 
1595 	/* if default config not specified, the flag should be reset */
1596 	if (p->default_config == NULL) {
1597 		p->default_config = "<NULL>";
1598 		p->flags = p->flags & ~NSS_USE_DEFAULT_CONFIG;
1599 	}
1600 
1601 	n = strlen(p->name) + 1;
1602 	dc = strlen(p->default_config) + 1;
1603 	if (n < 2 || dc < 2) {			/* What no DB? */
1604 		errno = ERANGE;			/* actually EINVAL */
1605 		return (NSS_ERROR);
1606 	}
1607 	if (p->config_name != NULL) {
1608 		nc = strlen(p->config_name) + 1;
1609 	}
1610 	if ((len + n + nc + dc) >= blen) {
1611 		errno = ERANGE;			/* actually EINVAL */
1612 		return (NSS_ERROR);
1613 	}
1614 
1615 	pdbd = (nss_dbd_t *)((void *)bptr);
1616 	bptr += len;
1617 	pdbd->flags = p->flags;
1618 	pdbd->o_name = len;
1619 	(void) strlcpy(bptr, p->name, n);
1620 	len += n;
1621 	bptr += n;
1622 	if (nc == 0) {
1623 		pdbd->o_config_name = 0;
1624 	} else {
1625 		pdbd->o_config_name = len;
1626 		(void) strlcpy(bptr, p->config_name, nc);
1627 		bptr += nc;
1628 		len += nc;
1629 	}
1630 	pdbd->o_default_config = len;
1631 	(void) strlcpy(bptr, p->default_config, dc);
1632 	len += dc;
1633 	pbuf->dbd_len = (nssuint_t)len;
1634 	off += ROUND_UP(len, sizeof (nssuint_t));
1635 	*poff = off;
1636 	return (NSS_SUCCESS);
1637 }
1638 
1639 /*
1640  * Switch packed and _nsc (switch->nscd) interfaces
1641  * Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND
1642  */
1643 
1644 /*ARGSUSED*/
1645 nss_status_t
1646 nss_pack(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1647 	    nss_db_initf_t initf, int search_fnum, void *search_args)
1648 {
1649 	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1650 	nss_XbyY_args_t		*in = (nss_XbyY_args_t *)search_args;
1651 	nss_db_params_t		tparam = { 0 };
1652 	nss_status_t		ret = NSS_ERROR;
1653 	const char		*dbn;
1654 	size_t			blen, len, off = 0;
1655 	char			*bptr;
1656 	struct nss_groupsbymem	*gbm;
1657 
1658 	if (pbuf == NULL || in == NULL || initf == (nss_db_initf_t)NULL) {
1659 		errno = ERANGE;			/* actually EINVAL */
1660 		return (ret);
1661 	}
1662 	tparam.cleanup = NULL;
1663 	(*initf)(&tparam);
1664 	if ((dbn = tparam.name) == 0) {
1665 		if (tparam.cleanup != 0)
1666 			(tparam.cleanup)(&tparam);
1667 		errno = ERANGE;			/* actually EINVAL */
1668 		return (ret);
1669 	}
1670 
1671 	/* init buffer header */
1672 	pbuf->pbufsiz = (nssuint_t)bufsize;
1673 	pbuf->p_ruid = (uint32_t)getuid();
1674 	pbuf->p_euid = (uint32_t)geteuid();
1675 	pbuf->p_version = NSCD_HEADER_REV;
1676 	pbuf->p_status = 0;
1677 	pbuf->p_errno = 0;
1678 	pbuf->p_herrno = 0;
1679 
1680 	/* possible audituser init */
1681 	if (strcmp(dbn, NSS_DBNAM_AUTHATTR) == 0 && in->h_errno != 0)
1682 		pbuf->p_herrno = (uint32_t)in->h_errno;
1683 
1684 	pbuf->libpriv = 0;
1685 
1686 	off = sizeof (nss_pheader_t);
1687 
1688 	/* setup getXbyY operation - database and sub function */
1689 	pbuf->nss_dbop = (uint32_t)search_fnum;
1690 	ret = nss_pack_dbd(buffer, bufsize, &tparam, &off);
1691 	if (ret != NSS_SUCCESS) {
1692 		errno = ERANGE;			/* actually EINVAL */
1693 		return (ret);
1694 	}
1695 	ret = NSS_ERROR;
1696 	/* setup request key */
1697 	pbuf->key_off = (nssuint_t)off;
1698 	bptr = (char *)buffer + off;
1699 	blen = bufsize - off;
1700 	/* use key2str if provided, else call default getXbyY packer */
1701 	if (strcmp(dbn, NSS_DBNAM_NETGROUP) == 0) {
1702 		/* This has to run locally due to backend knowledge */
1703 		if (search_fnum == NSS_DBOP_NETGROUP_SET) {
1704 			errno = 0;
1705 			return (NSS_TRYLOCAL);
1706 		}
1707 		/* use default packer for known getXbyY ops */
1708 		ret = nss_default_key2str(bptr, blen, in, dbn,
1709 						search_fnum, &len);
1710 	} else if (in->key2str == NULL ||
1711 		(search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
1712 			strcmp(dbn, NSS_DBNAM_GROUP) == 0)) {
1713 		/* use default packer for known getXbyY ops */
1714 		ret = nss_default_key2str(bptr, blen, in, dbn,
1715 						search_fnum, &len);
1716 	} else {
1717 		ret = (*in->key2str)(bptr, blen, &in->key, &len);
1718 	}
1719 	if (tparam.cleanup != 0)
1720 		(tparam.cleanup)(&tparam);
1721 	if (ret != NSS_SUCCESS) {
1722 		errno = ERANGE;			/* actually ENOMEM */
1723 		return (ret);
1724 	}
1725 	pbuf->key_len = (nssuint_t)len;
1726 	off += ROUND_UP(len, sizeof (nssuint_t));
1727 
1728 	pbuf->data_off = (nssuint_t)off;
1729 	pbuf->data_len = (nssuint_t)(bufsize - off);
1730 	/*
1731 	 * Prime data return with first result if
1732 	 * the first result is passed in
1733 	 * [_getgroupsbymember oddness]
1734 	 */
1735 	gbm = (struct nss_groupsbymem *)search_args;
1736 	if (search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
1737 	    strcmp(dbn, NSS_DBNAM_GROUP) == 0 && gbm->numgids == 1) {
1738 		gid_t	*gidp;
1739 		gidp = (gid_t *)((void *)((char *)buffer + off));
1740 		*gidp = gbm->gid_array[0];
1741 	}
1742 
1743 	errno = 0;				/* just in case ... */
1744 	return (NSS_SUCCESS);
1745 }
1746 
1747 /*
1748  * Switch packed and _nsc (switch->nscd) {set/get/end}ent interfaces
1749  * Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND
1750  */
1751 
1752 /*ARGSUSED*/
1753 nss_status_t
1754 nss_pack_ent(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1755 	    nss_db_initf_t initf, nss_getent_t *contextpp)
1756 {
1757 	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1758 	struct nss_getent_context *contextp = contextpp->ctx;
1759 	nss_status_t		ret = NSS_ERROR;
1760 	size_t			blen, len = 0, off = 0;
1761 	char			*bptr;
1762 	nssuint_t		*nptr;
1763 
1764 	if (pbuf == NULL || initf == (nss_db_initf_t)NULL) {
1765 		errno = ERANGE;			/* actually EINVAL */
1766 		return (ret);
1767 	}
1768 
1769 	/* init buffer header */
1770 	pbuf->pbufsiz = (nssuint_t)bufsize;
1771 	pbuf->p_ruid = (uint32_t)getuid();
1772 	pbuf->p_euid = (uint32_t)geteuid();
1773 	pbuf->p_version = NSCD_HEADER_REV;
1774 	pbuf->p_status = 0;
1775 	pbuf->p_errno = 0;
1776 	pbuf->p_herrno = 0;
1777 	pbuf->libpriv = 0;
1778 
1779 	off = sizeof (nss_pheader_t);
1780 
1781 	/* setup getXXXent operation - database and sub function */
1782 	pbuf->nss_dbop = (uint32_t)0;	/* iterators have no dbop */
1783 	ret = nss_pack_dbd(buffer, bufsize, &contextp->param, &off);
1784 	if (ret != NSS_SUCCESS) {
1785 		errno = ERANGE;			/* actually EINVAL */
1786 		return (ret);
1787 	}
1788 	ret = NSS_ERROR;
1789 	off += ROUND_UP(len, sizeof (nssuint_t));
1790 
1791 	pbuf->key_off = (nssuint_t)off;
1792 	bptr = (char *)buffer + off;
1793 	blen = bufsize - off;
1794 	len = (size_t)(sizeof (nssuint_t) * 2);
1795 	if (len >= blen) {
1796 		errno = ERANGE;			/* actually EINVAL */
1797 		return (ret);
1798 	}
1799 	nptr = (nssuint_t *)((void *)bptr);
1800 	*nptr++ = contextp->cookie;
1801 	*nptr = contextp->seq_num;
1802 	pbuf->key_len = (nssuint_t)len;
1803 
1804 	off += len;
1805 	pbuf->data_off = (nssuint_t)off;
1806 	pbuf->data_len = (nssuint_t)(bufsize - off);
1807 	return (NSS_SUCCESS);
1808 }
1809 
1810 /*
1811  * Unpack packed arguments buffer
1812  * Return: status, errnos and results from requested operation.
1813  *
1814  * NOTES: When getgroupsbymember is being processed in the NSCD backend,
1815  * or via the backwards compatibility interfaces then the standard
1816  * str2group API is used in conjunction with process_cstr.  When,
1817  * processing a returned buffer, in NSS2 the return results are the
1818  * already digested groups array.  Therefore, unpack the digested results
1819  * back to the return buffer.
1820  *
1821  * Note: the digested results are nssuint_t quantities.  _getgroupsbymember
1822  * digests int quantities.  Therefore convert.  Assume input is in nssuint_t
1823  * quantities.  Store in an int array... Assume gid's are <= 32 bits...
1824  */
1825 
1826 /*ARGSUSED*/
1827 nss_status_t
1828 nss_unpack(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1829 	    nss_db_initf_t initf, int search_fnum, void *search_args)
1830 {
1831 	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1832 	nss_XbyY_args_t		*in = (nss_XbyY_args_t *)search_args;
1833 	nss_dbd_t		*pdbd;
1834 	char			*dbn;
1835 	nss_status_t		status;
1836 	char			*buf;
1837 	int			len;
1838 	int			ret;
1839 	int			i;
1840 	int			fmt_type;
1841 	gid_t			*gidp;
1842 	gid_t			*gptr;
1843 	struct nss_groupsbymem	*arg;
1844 
1845 
1846 	if (pbuf == NULL || in == NULL)
1847 		return (-1);
1848 	status = pbuf->p_status;
1849 	/* Identify odd cases */
1850 	pdbd = (nss_dbd_t *)((void *)((char *)buffer + pbuf->dbd_off));
1851 	dbn = (char *)pdbd + pdbd->o_name;
1852 	fmt_type = 0; /* nss_XbyY_args_t */
1853 	if (search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
1854 		strcmp(dbn, NSS_DBNAM_GROUP) == 0)
1855 		fmt_type = 1; /* struct nss_groupsbymem */
1856 	else if (search_fnum == NSS_DBOP_NETGROUP_IN &&
1857 		strcmp(dbn, NSS_DBNAM_NETGROUP) == 0)
1858 		fmt_type = 2; /* struct nss_innetgr_args */
1859 
1860 	/* if error - door's switch error */
1861 	/* extended data could contain additional information? */
1862 	if (status != NSS_SUCCESS) {
1863 		if (fmt_type == 0) {
1864 			in->h_errno = (int)pbuf->p_herrno;
1865 			if (pbuf->p_errno == ERANGE)
1866 				in->erange = 1;
1867 		}
1868 		return (status);
1869 	}
1870 
1871 	if (pbuf->data_off == 0 || pbuf->data_len == 0)
1872 		return (NSS_NOTFOUND);
1873 
1874 	buf = (char *)buffer + pbuf->data_off;
1875 	len = pbuf->data_len;
1876 
1877 	/* sidestep odd cases */
1878 	if (fmt_type == 1) {
1879 		arg = (struct nss_groupsbymem *)in;
1880 		/* copy returned gid array from returned nscd buffer */
1881 		i = len / sizeof (gid_t);
1882 		/* not enough buffer */
1883 		if (i > arg->maxgids) {
1884 			i = arg->maxgids;
1885 		}
1886 		arg->numgids = i;
1887 		gidp = arg->gid_array;
1888 		gptr = (gid_t *)((void *)buf);
1889 		memcpy(gidp, gptr, len);
1890 		return (NSS_SUCCESS);
1891 	}
1892 	if (fmt_type == 2) {
1893 		struct nss_innetgr_args *arg = (struct nss_innetgr_args *)in;
1894 
1895 		if (pbuf->p_status == NSS_SUCCESS) {
1896 			arg->status = NSS_NETGR_FOUND;
1897 			return (NSS_SUCCESS);
1898 		} else {
1899 			arg->status = NSS_NETGR_NO;
1900 			return (NSS_NOTFOUND);
1901 		}
1902 	}
1903 
1904 	/* process the normal cases */
1905 	/* marshall data directly into users buffer */
1906 	ret = (*in->str2ent)(buf, len, in->buf.result, in->buf.buffer,
1907 		in->buf.buflen);
1908 	if (ret == NSS_STR_PARSE_ERANGE) {
1909 		in->returnval = 0;
1910 		in->returnlen = 0;
1911 		in->erange    = 1;
1912 		ret = NSS_NOTFOUND;
1913 	} else if (ret == NSS_STR_PARSE_SUCCESS) {
1914 		in->returnval = in->buf.result;
1915 		in->returnlen =  len;
1916 		ret = NSS_SUCCESS;
1917 	}
1918 	in->h_errno = (int)pbuf->p_herrno;
1919 	return ((nss_status_t)ret);
1920 }
1921 
1922 /*
1923  * Unpack a returned packed {set,get,end}ent arguments buffer
1924  * Return: status, errnos, cookie info and results from requested operation.
1925  */
1926 
1927 /*ARGSUSED*/
1928 nss_status_t
1929 nss_unpack_ent(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1930 	    nss_db_initf_t initf, nss_getent_t *contextpp, void *args)
1931 {
1932 	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1933 	nss_XbyY_args_t		*in = (nss_XbyY_args_t *)args;
1934 	struct nss_getent_context *contextp = contextpp->ctx;
1935 	nssuint_t		*nptr;
1936 	nssuint_t		cookie;
1937 	nss_status_t		status;
1938 	char			*buf;
1939 	int			len;
1940 	int			ret;
1941 
1942 	if (pbuf == NULL)
1943 		return (-1);
1944 	status = pbuf->p_status;
1945 	/* if error - door's switch error */
1946 	/* extended data could contain additional information? */
1947 	if (status != NSS_SUCCESS)
1948 		return (status);
1949 
1950 	/* unpack assigned cookie from SET/GET/END request */
1951 	if (pbuf->key_off == 0 ||
1952 	    pbuf->key_len != (sizeof (nssuint_t) * 2))
1953 		return (NSS_NOTFOUND);
1954 
1955 	nptr = (nssuint_t *)((void *)((char *)buffer + pbuf->key_off));
1956 	cookie = contextp->cookie;
1957 	if (cookie != NSCD_NEW_COOKIE && cookie != contextp->cookie_setent &&
1958 		cookie != *nptr) {
1959 		/*
1960 		 * Should either be new, or the cookie returned by the last
1961 		 * setent (i.e., this is the first getent after the setent)
1962 		 * or a match, else error
1963 		 */
1964 		return (NSS_NOTFOUND);
1965 	}
1966 	/* save away for the next ent request */
1967 	contextp->cookie = *nptr++;
1968 	contextp->seq_num = *nptr;
1969 
1970 	/* All done if no marshalling is expected {set,end}ent */
1971 	if (args == NULL)
1972 		return (NSS_SUCCESS);
1973 
1974 	/* unmarshall the data */
1975 	if (pbuf->data_off == 0 || pbuf->data_len == 0)
1976 		return (NSS_NOTFOUND);
1977 	buf = (char *)buffer + pbuf->data_off;
1978 
1979 	len = pbuf->data_len;
1980 
1981 	/* marshall data directly into users buffer */
1982 	ret = (*in->str2ent)(buf, len, in->buf.result, in->buf.buffer,
1983 		in->buf.buflen);
1984 	if (ret == NSS_STR_PARSE_ERANGE) {
1985 		in->returnval = 0;
1986 		in->returnlen = 0;
1987 		in->erange    = 1;
1988 	} else if (ret == NSS_STR_PARSE_SUCCESS) {
1989 		in->returnval = in->buf.result;
1990 		in->returnlen =  len;
1991 	}
1992 	in->h_errno = (int)pbuf->p_herrno;
1993 	return ((nss_status_t)ret);
1994 }
1995 
1996 /*
1997  * Start of _nsc_{search|setent_u|getent_u|endent_u} NSCD interposition funcs
1998  */
1999 
2000 nss_status_t
2001 _nsc_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum,
2002 	void *search_args)
2003 {
2004 	nss_pheader_t		*pbuf;
2005 	void			*doorptr = NULL;
2006 	size_t			bufsize = 0;
2007 	size_t			datasize = 0;
2008 	nss_status_t		status;
2009 
2010 	if (_nsc_proc_is_cache() > 0) {
2011 		/* internal nscd call - don't use the door */
2012 		return (NSS_TRYLOCAL);
2013 	}
2014 
2015 	/* standard client calls nscd code */
2016 	if (search_args == NULL)
2017 		return (NSS_NOTFOUND);
2018 
2019 	/* get the door buffer  & configured size */
2020 	bufsize = ((nss_XbyY_args_t *)search_args)->buf.buflen;
2021 	if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
2022 		return (NSS_TRYLOCAL);
2023 	if (doorptr == NULL || bufsize == 0)
2024 		return (NSS_TRYLOCAL);
2025 
2026 	pbuf = (nss_pheader_t *)doorptr;
2027 	/* pack argument and request into door buffer */
2028 	pbuf->nsc_callnumber = NSCD_SEARCH;
2029 	/* copy relevant door request info into door buffer */
2030 	status = nss_pack((void *)pbuf, bufsize, rootp,
2031 			initf, search_fnum, search_args);
2032 
2033 	/* Packing error return error results */
2034 	if (status != NSS_SUCCESS)
2035 		return (status);
2036 
2037 	/* transfer packed switch request to nscd via door */
2038 	/* data_off can be used because it is header+dbd_len+key_len */
2039 	datasize = pbuf->data_off;
2040 	status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2041 
2042 	/* If unsuccessful fallback to standard nss logic */
2043 	if (status != NSS_SUCCESS) {
2044 		/*
2045 		 * check if doors reallocated the memory underneath us
2046 		 * if they did munmap it or suffer a memory leak
2047 		 */
2048 		if (doorptr != (void *)pbuf) {
2049 			_nsc_resizedoorbuf(bufsize);
2050 			munmap((void *)doorptr, bufsize);
2051 		}
2052 		return (NSS_TRYLOCAL);
2053 	}
2054 
2055 	/* unpack and marshall data/errors to user structure */
2056 	/* set any error conditions */
2057 	status = nss_unpack((void *)doorptr, bufsize, rootp, initf,
2058 			search_fnum, search_args);
2059 	/*
2060 	 * check if doors reallocated the memory underneath us
2061 	 * if they did munmap it or suffer a memory leak
2062 	 */
2063 	if (doorptr != (void *)pbuf) {
2064 		_nsc_resizedoorbuf(bufsize);
2065 		munmap((void *)doorptr, bufsize);
2066 	}
2067 	return (status);
2068 }
2069 
2070 /*
2071  * contact nscd for a cookie or to reset an existing cookie
2072  * if nscd fails (NSS_TRYLOCAL) then set cookie to -1 and
2073  * continue diverting to local
2074  */
2075 nss_status_t
2076 _nsc_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2077 	nss_getent_t *contextpp)
2078 {
2079 	nss_status_t		status = NSS_TRYLOCAL;
2080 	struct nss_getent_context *contextp = contextpp->ctx;
2081 	nss_pheader_t		*pbuf;
2082 	void			*doorptr = NULL;
2083 	size_t			bufsize = 0;
2084 	size_t			datasize = 0;
2085 
2086 	/* return if already in local mode */
2087 	if (contextp->cookie == NSCD_LOCAL_COOKIE)
2088 		return (NSS_TRYLOCAL);
2089 
2090 	if (_nsc_proc_is_cache() > 0) {
2091 		/* internal nscd call - don't try to use the door */
2092 		contextp->cookie = NSCD_LOCAL_COOKIE;
2093 		return (NSS_TRYLOCAL);
2094 	}
2095 
2096 	/* get the door buffer & configured size */
2097 	if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0) {
2098 		contextp->cookie = NSCD_LOCAL_COOKIE;
2099 		return (NSS_TRYLOCAL);
2100 	}
2101 	if (doorptr == NULL || bufsize == 0) {
2102 		contextp->cookie = NSCD_LOCAL_COOKIE;
2103 		return (NSS_TRYLOCAL);
2104 	}
2105 
2106 	pbuf = (nss_pheader_t *)doorptr;
2107 	pbuf->nsc_callnumber = NSCD_SETENT;
2108 
2109 	contextp->param.cleanup = NULL;
2110 	(*initf)(&contextp->param);
2111 	if (contextp->param.name == 0) {
2112 		if (contextp->param.cleanup != 0)
2113 			(contextp->param.cleanup)(&contextp->param);
2114 		errno = ERANGE;			/* actually EINVAL */
2115 		return (NSS_ERROR);
2116 	}
2117 
2118 	/* pack relevant setent request info into door buffer */
2119 	status = nss_pack_ent((void *)pbuf, bufsize, rootp,
2120 			initf, contextpp);
2121 	if (status != NSS_SUCCESS)
2122 		return (status);
2123 
2124 	/* transfer packed switch request to nscd via door */
2125 	/* data_off can be used because it is header+dbd_len+key_len */
2126 	datasize = pbuf->data_off;
2127 	status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2128 
2129 	/* If fallback to standard nss logic (door failure) if possible */
2130 	if (status != NSS_SUCCESS) {
2131 		if (contextp->cookie == NSCD_NEW_COOKIE) {
2132 			contextp->cookie = NSCD_LOCAL_COOKIE;
2133 			return (NSS_TRYLOCAL);
2134 		}
2135 		return (NSS_UNAVAIL);
2136 	}
2137 	/* unpack returned cookie stash it away */
2138 	status = nss_unpack_ent((void *)doorptr, bufsize, rootp,
2139 			initf, contextpp, NULL);
2140 	/* save the setent cookie for later use */
2141 	contextp->cookie_setent = contextp->cookie;
2142 	/*
2143 	 * check if doors reallocated the memory underneath us
2144 	 * if they did munmap it or suffer a memory leak
2145 	 */
2146 	if (doorptr != (void *)pbuf) {
2147 		_nsc_resizedoorbuf(bufsize);
2148 		munmap((void *)doorptr, bufsize);
2149 	}
2150 	return (status);
2151 }
2152 
2153 nss_status_t
2154 _nsc_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2155 	nss_getent_t *contextpp, void *args)
2156 {
2157 	nss_status_t		status = NSS_TRYLOCAL;
2158 	struct nss_getent_context *contextp = contextpp->ctx;
2159 	nss_pheader_t		*pbuf;
2160 	void			*doorptr = NULL;
2161 	size_t			bufsize = 0;
2162 	size_t			datasize = 0;
2163 
2164 	/* return if already in local mode */
2165 	if (contextp->cookie == NSCD_LOCAL_COOKIE)
2166 		return (NSS_TRYLOCAL);
2167 
2168 	/* _nsc_setent_u already checked for nscd local case ... proceed */
2169 	if (args == NULL)
2170 		return (NSS_NOTFOUND);
2171 
2172 	/* get the door buffer  & configured size */
2173 	bufsize = ((nss_XbyY_args_t *)args)->buf.buflen;
2174 	if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
2175 		return (NSS_UNAVAIL);
2176 	if (doorptr == NULL || bufsize == 0)
2177 		return (NSS_UNAVAIL);
2178 
2179 	pbuf = (nss_pheader_t *)doorptr;
2180 	pbuf->nsc_callnumber = NSCD_GETENT;
2181 
2182 	/* pack relevant setent request info into door buffer */
2183 	status = nss_pack_ent((void *)pbuf, bufsize, rootp,
2184 			initf, contextpp);
2185 	if (status != NSS_SUCCESS)
2186 		return (status);
2187 
2188 	/* transfer packed switch request to nscd via door */
2189 	/* data_off can be used because it is header+dbd_len+key_len */
2190 	datasize = pbuf->data_off;
2191 	status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2192 
2193 	/* If fallback to standard nss logic (door failure) if possible */
2194 	if (status != NSS_SUCCESS) {
2195 		if (status == NSS_TRYLOCAL ||
2196 				contextp->cookie == NSCD_NEW_COOKIE) {
2197 			contextp->cookie = NSCD_LOCAL_COOKIE;
2198 
2199 			/* init the local cookie */
2200 			nss_setent_u(rootp, initf, contextpp);
2201 			if (contextpp->ctx == 0)
2202 				return (NSS_UNAVAIL);
2203 			return (NSS_TRYLOCAL);
2204 		}
2205 		return (NSS_UNAVAIL);
2206 	}
2207 	/* check error, unpack and process results */
2208 	status = nss_unpack_ent((void *)doorptr, bufsize, rootp,
2209 			initf, contextpp, args);
2210 	/*
2211 	 * check if doors reallocated the memory underneath us
2212 	 * if they did munmap it or suffer a memory leak
2213 	 */
2214 	if (doorptr != (void *)pbuf) {
2215 		_nsc_resizedoorbuf(bufsize);
2216 		munmap((void *)doorptr, bufsize);
2217 	}
2218 	return (status);
2219 }
2220 
2221 nss_status_t
2222 _nsc_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2223 	nss_getent_t *contextpp)
2224 {
2225 	nss_status_t		status = NSS_TRYLOCAL;
2226 	struct nss_getent_context *contextp = contextpp->ctx;
2227 	nss_pheader_t		*pbuf;
2228 	void			*doorptr = NULL;
2229 	size_t			bufsize = 0;
2230 	size_t			datasize = 0;
2231 
2232 	/* return if already in local mode */
2233 	if (contextp->cookie == NSCD_LOCAL_COOKIE)
2234 		return (NSS_TRYLOCAL);
2235 
2236 	/* _nsc_setent_u already checked for nscd local case ... proceed */
2237 
2238 	/* get the door buffer  & configured size */
2239 	if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
2240 		return (NSS_UNAVAIL);
2241 	if (doorptr == NULL || bufsize == 0)
2242 		return (NSS_UNAVAIL);
2243 
2244 	/* pack up a NSCD_ENDGET request passing in the cookie */
2245 	pbuf = (nss_pheader_t *)doorptr;
2246 	pbuf->nsc_callnumber = NSCD_ENDENT;
2247 
2248 	/* pack relevant setent request info into door buffer */
2249 	status = nss_pack_ent((void *)pbuf, bufsize, rootp,
2250 			initf, contextpp);
2251 	if (status != NSS_SUCCESS)
2252 		return (status);
2253 
2254 	/* transfer packed switch request to nscd via door */
2255 	/* data_off can be used because it is header+dbd_len+key_len */
2256 	datasize = pbuf->data_off;
2257 	(void) _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2258 
2259 	/* error codes & unpacking ret values don't matter.  We're done */
2260 
2261 	/*
2262 	 * check if doors reallocated the memory underneath us
2263 	 * if they did munmap it or suffer a memory leak
2264 	 */
2265 	if (doorptr != (void *)pbuf) {
2266 		_nsc_resizedoorbuf(bufsize);
2267 		munmap((void *)doorptr, bufsize);
2268 	}
2269 
2270 	/* clean up initf setup */
2271 	if (contextp->param.cleanup != 0)
2272 		(contextp->param.cleanup)(&contextp->param);
2273 	contextp->param.cleanup = NULL;
2274 
2275 	/* clear cookie */
2276 	contextp->cookie = NSCD_NEW_COOKIE;
2277 	return (NSS_SUCCESS);
2278 }
2279