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