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