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