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