/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* Shared code used by the name-service-switch frontends (e.g. getpwnam_r())
*/
#include "lint.h"
#include <mtlib.h>
#include <dlfcn.h>
#include <atomic.h>
#define __NSS_PRIVATE_INTERFACE
#include "nsswitch_priv.h"
#include <nss_common.h>
#include <nss_dbdefs.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <thread.h>
#include <synch.h>
#include <pthread.h>
#include <errno.h>
#include "libc.h"
#include "tsd.h"
#include <getxby_door.h>
#include <strings.h>
#include <alloca.h>
/*
* configurable values for default buffer sizes
*/
/*
* PSARC/2005/133 updated the buffering mechanisms to handle
* up to 2^64 buffering. But sets a practical limit of 512*1024.
* The expectation is the practical limit will be dynamic from
* nscd. For now, set the group limit to this value.
*/
/*
* policy component function interposing definitions:
* nscd if so desired can interpose it's own switch functions over
* the internal unlocked counterparts. This will allow nscd to replace
* the switch policy state engine with one that uses it's internal
* components.
* Only nscd can change this through it's use of nss_config.
* The golden rule is: ptr == NULL checking is used in the switch to
* see if a function was interposed. But nscd is responsible for seeing
* that mutex locking to change the values are observed when the data is
* changed. Especially if it happens > once. The switch does not lock
* the pointer with mutexs.
*/
typedef struct {
void *p;
#if 0
void *search_args);
void (*nss_setent_u_fp)(nss_db_root_t *,
nss_db_initf_t, nss_getent_t *, void *);
void (*nss_endent_u_fp)(nss_db_root_t *,
struct nss_getent_context *contextp);
#endif
/*
* lock_array is used to record the address (and symbol information)
* of the mutex in the nss_db_root and nss_getent_t used by each backend.
* If a multithreaded program forks while a thread is doing a
* resulting in a deadlock.
* When we fork, nss_reinitlocks() reinitialises the mutexes in the array
* to prevent this deadlock.
* The array is maintained by storelockaddr() and removelockaddr().
* storelockaddr() stores the details of the lock in the array and
* is called by _nss_db_state_constr() and setent().
* removelockaddr() removes the address of the lock from the array and
* is called by _nss_db_state_destr() and endent().
*/
typedef struct array_entry {
void *addr;
static int lock_array_size = 0;
static int lock_array_nxt_free = 0;
{ (void *)NULL };
/*
* nsswitch db_root state machine definitions:
* The golden rule is: if you hold a pointer to an nss_db_state struct and
* you don't hold the lock, you'd better have incremented the refcount
* while you held the lock; otherwise, it may vanish or change
* significantly when you least expect it.
*
* The pointer in nss_db_root_t is one such, so the reference count >= 1.
* Ditto the pointer in struct nss_getent_context.
*/
/*
* State for one nsswitch database (e.g. "passwd", "hosts")
*/
struct nss_db_state {
/* nss_db_root_t, plus one */
/* for each active thread. */
};
/*
* State for one of the sources (e.g. "nis", "compat") for a database
*/
struct nss_src_state {
int n_active;
int n_dormant;
union {
/* when limit_dead_backends == 1 */
void *finder_priv;
};
void _nss_db_state_destr(struct nss_db_state *,
/* ==== null definitions if !MTSAFE? Ditto lock field in nss_db_root_t */
*(sp) = (r)->s)
*(rp) = &(s)->orphan_root))
NSS_CHECKROOT(rp, s))
? ((void)NSS_UNLOCK(r)) \
: ((void)NSS_UNLOCK(r), \
(void) _nss_db_state_destr(s, r)))
*(sp) == 0 && \
(r->s = *(sp) = _nss_db_state_constr(f, r)))
/* === In the future, NSS_LOCK_CHECK() may also have to check that */
/* === the config info hasn't changed (by comparing version numbers) */
/*
* NSS_OPTIONS/NIS_OPTIONS environment varibles data definitions:
* This remains for backwards compatibility. But generally nscd will
*/
/* allowing __nss_debug_file to be set could be a security hole. */
int __nss_debug_eng_loop;
/* NIS_OPTIONS infrastructure (from linbsl/nis/cache/cache_api.cc) */
/* allowing __nis_debug_file to be set could be a security hole. */
int __nis_debug_bind;
int __nis_debug_rpc;
int __nis_debug_calls;
char *__nis_prefsrv;
char *__nis_preftype;
#ifdef DEBUG
#endif
struct option {
char *name;
int type;
void *address;
};
#ifdef DEBUG
/* allowing __nss_debug_file to be set could be a security hole. */
#endif
{ 0, 0, 0 },
};
#ifdef DEBUG
/* allowing __nis_debug_file to be set could be a security hole. */
#endif
{ 0, 0, 0 },
};
/*
* switch configuration parameter "database" definitions:
* that nscd and the switch components can use to communicate
* nscd data to other components for configuration or out of band
* [IE no in the context of a getXbyY or putXbyY operation] data.
* The data passed are pointers to a lock data buffer and a length.
* Use of this is treated as SunwPrivate between nscd and the switch
* unless other wise stated.
*/
typedef struct nss_cfgparam {
char *name;
void *buffer;
typedef struct nss_cfglist {
char *name;
int count;
int max;
static int nss_cfgcount = 0;
static int nss_cfgmax = 0;
static int nss_cfg_policy_init();
/*
* A config parameters are in the form component:parameter
* nscd:param - nscd application parameter
* ldap:param - nss_ldap BE parameter
*/
/*
* The policy components initial parameter list
*/
};
/*
* Called by child after fork to reinitialise locks.
* To prevent access to invalid addresses if a library using nss is unmapped
* we check that the symbol information for the lock address has not changed.
*/
void
nss_reinitlocks(void)
{
int i;
if (lock_array == NULL)
return;
for (i = 0; i < lock_array_nxt_free; i++) {
/*
* It is possible that a library using nss_search()
* was subject to dlclose and is no longer mapped.
* To prevent coredump we check lock address is still
* valid before we reinitialise it.
*/
sizeof (Dl_info_t)) == 0) {
USYNC_THREAD, NULL);
}
}
}
}
/*
* Store the address of a mutex.
* If we fail to allocate memory to store the address
* then we will not reinitialise the associated lock
* after a fork.
*/
static void storelockaddr(void *r)
{
int i, size;
(void) mutex_lock(&lock_array_mutex);
/* If no array yet, allocate one */
if (lock_array == NULL) {
size = 25;
(void) mutex_unlock(&lock_array_mutex);
return;
}
lock_array = tmp;
lock_array_nxt_free = 0;
}
/*
* Check for existing entry for this lock address in array
* If we have one, get the associated symbol
* If the symbol has changed, update the array entry
* If no symbol info (an unexpected case) remove array entry
*/
for (i = 0; i < lock_array_nxt_free; i++) {
if (lock_array[i].addr == r) {
/* entry exists for this lock */
/*
* Symbol for lock found so
* store it (it may have changed)
*/
lock_array[i].dli
= newdli;
(void) mutex_unlock(&lock_array_mutex);
return;
} else {
/*
* Symbol for lock not found so
* remove array entry.
* To avoid gap in the array move
* last item to current position
* overwriting entry we don't want.
*/
if (i < lock_array_nxt_free - 1) {
lock_array[i] =
}
(void) mutex_unlock(&lock_array_mutex);
return;
}
}
}
/* Do we need to grow the array for a new entry? */
if (lock_array_nxt_free >= lock_array_size) {
(void) mutex_unlock(&lock_array_mutex);
return;
}
lock_array = tmp;
}
/* add new entry */
}
(void) mutex_unlock(&lock_array_mutex);
}
/*
* clear an existing entry from lock address array
*/
static void removelockaddr(void *r)
{
int i;
(void) mutex_lock(&lock_array_mutex);
if (lock_array == NULL) {
(void) mutex_unlock(&lock_array_mutex);
return;
}
for (i = 0; i < lock_array_nxt_free; i++) {
if (lock_array[i].addr == r) {
/*
* to avoid gap in the array
* move last item to current position
* overwriting entry we don't need
*/
if (i < lock_array_nxt_free - 1) {
lock_array[i] =
}
break;
}
}
(void) mutex_unlock(&lock_array_mutex);
}
/*
* NSS parameter configuration routines
*/
/* compare config name (component:parameter) to a component name */
static int
{
char *c;
/* this code assumes valid pointers */
return (-1);
return (-1);
}
/* init configuration arena */
static int
{
int i;
/* First time caller? */
return (0);
}
/* Initialize internal tables */
return (0);
}
return (-1);
}
for (i = 0; i < NSS_CFG_INCR; i++) {
NSS_CFG_INCR * sizeof (nss_cfgparam_t));
while (--i >= 0)
return (-1);
}
}
/* Initialize Policy Engine values */
if (nss_cfg_policy_init() < 0) {
return (-1);
}
return (0);
}
/* find the name'd component list - create it if non-existent */
static nss_cfglist_t *
{
char *c;
int i, len;
/* Make sure system is init'd */
if (nss_cfg_init() < 0)
return ((nss_cfglist_t *)NULL);
/* and check component:name validity */
return ((nss_cfglist_t *)NULL);
for (i = 0; i < nss_cfgcount; i++) {
return (next);
}
next++;
}
if (!add) {
return (NULL);
}
/* not found, create a fresh one */
if (nss_cfgcount >= nss_cfgmax) {
/* realloc first */
return ((nss_cfglist_t *)NULL);
}
NSS_CFG_INCR * sizeof (nss_cfglist_t));
}
return ((nss_cfglist_t *)NULL);
}
nss_cfgcount++;
return (next);
}
/* find the name'd parameter - create it if non-existent */
static nss_cfgparam_t *
{
int count, i;
return ((nss_cfgparam_t *)NULL);
for (i = 0; i < count; i++) {
return (next);
}
next++;
}
if (!add) {
return (NULL);
}
/* not found, create a fresh one */
/* realloc first */
return ((nss_cfgparam_t *)NULL);
}
}
return ((nss_cfgparam_t *)NULL);
}
return (next);
}
/* find the name'd parameter - delete it if it exists */
static void
{
char *name;
int count, i, j;
/* exit if component name does not already exist */
return;
/* find it */
for (i = 0; i < count; i++) {
break; /* found it... */
}
next++;
}
if (i >= count) {
/* not found, already deleted */
return;
}
/* copy down the remaining parameters, and clean up */
/* don't try to clean up component tables */
next++;
for (j = i+1; j < count; j++) {
cur++;
next++;
}
/* erase the last one */
}
}
static int
{
errno = 0;
return (-1);
return (0);
}
static int
{
errno = 0;
return (-1);
return (0);
}
/*
* Policy engine configurator - set and get interface
* specifier of a set or get operation and the property being
* managed. The intent is limited functions and expandability.
*/
{
int i;
/* interface is only available to nscd */
if (_nsc_proc_is_cache() <= 0) {
return (NSS_UNAVAIL);
}
return (NSS_SUCCESS);
for (i = 0; i < cnt; i++) {
break;
return (NSS_ERROR);
}
case NSS_CONFIG_GET:
if (nss_cfg_get(next) < 0) {
return (NSS_ERROR);
}
break;
case NSS_CONFIG_PUT:
if (nss_cfg_put(next, 0) < 0) {
return (NSS_ERROR);
}
break;
case NSS_CONFIG_ADD:
return (NSS_ERROR);
}
break;
case NSS_CONFIG_DELETE:
/* delete parameter - should always work... */
break;
case NSS_CONFIG_LIST:
break;
default:
continue;
}
}
return (NSS_SUCCESS);
}
/*
* This routine is called immediately after nss_cfg_init but prior to
* any commands from nscd being processed. The intent here is to
* initialize the nss:* parameters allowed by the policy component
* so that nscd can then proceed and modify them if so desired.
*
* We know we can only get here if we are nscd so we can skip the
* preliminaries.
*/
static int
{
return (-1);
}
return (0);
}
/*
* NSS_OPTION & NIS_OPTION environment variable functions
*/
static
void
{
int n;
char *p;
#ifdef DEBUG
#endif
case OPT_STRING:
p = libc_strdup(val);
break;
case OPT_INT:
n = 1;
else
break;
#ifdef DEBUG
case OPT_FILE:
break;
#endif
}
break;
}
}
}
static
void
{
char *base;
while (*p) {
while (isspace(*p))
p++;
if (*p == '\0')
break;
base = p;
while (*p && *p != '=' && !isspace(*p))
p++;
/*
* play it safe and keep it simple, bail if an opt name
* is too long.
*/
return;
if (*p == '=') {
p++;
base = p;
while (*p && !isspace(*p))
p++;
/*
* play it safe and keep it simple, bail if an opt
* value is too long.
*/
return;
} else {
optval[0] = '\0';
}
}
}
static
void
{
char *p;
/* NSS_OPTIONS is undocumented and should be used without nscd running. */
p = getenv("NSS_OPTIONS");
if (p == NULL)
return;
}
/*
* sole external routine called from libnsl/nis/cache/cache_api.cc in the
* routines _nis_CacheInit/__nis_CacheLocalInit/__nis_CacheMgrInit_discard
* Only after checking "checked_env" (which must be done with mutex
* "cur_cache_lock" held) and is done once, (then "checked_env" is set)
*/
void
{
char *p;
p = getenv("NIS_OPTIONS");
if (p == NULL)
return;
}
/*
* Switch policy component backend state machine functions
*/
static nss_backend_t *
{
int cancel_state;
for (;;) {
if (s->p.max_dormant_per_src == 1) {
} else {
}
break;
}
if (c != 0) {
break;
}
}
/* Couldn't find the backend anywhere */
be = 0;
break;
}
}
if (be != 0) {
break;
/* Something's wrong; we should be */
/* able to create at least one */
/* instance of the backend */
break;
}
/*
* Else it's odd that we can't create another backend
* instance, but don't sweat it; instead, queue for
* an existing backend instance.
*/
}
&cancel_state);
NSS_CHECKROOT(rootpp, s);
/*
* Loop and see whether things got better for us, or whether
* someone else got scheduled first and we have to try
* this again.
*
* === ?? Should count iterations, assume bug if many ??
*/
}
return (be);
}
static void
{
if (be == 0) {
return;
}
if (s->p.max_dormant_per_src == 1) {
libc_malloc(s->p.max_dormant_per_src *
sizeof (nss_backend_t *))) != NULL) {
} else {
/* Can't store it, so toss it */
}
} else {
/* We've stored as many as we want, so toss it */
}
}
}
static struct __nsw_switchconfig_v1 *
{
/* The falls back to the default backend below. */
if (config == 0)
return (0);
char *buf;
}
return (config);
}
(void) __nsw_freeconfig_v1(config);
return (0);
}
static struct nss_db_state *
{
struct nss_db_state *s;
const char *config_name;
int n_src;
if ((s = libc_malloc(sizeof (*s))) == 0) {
return (0);
}
s->p.max_active_per_src = 10;
s->p.max_dormant_per_src = 1;
s->p.finders = nss_default_finders;
(*initf)(&s->p);
if (s->p.name == 0) {
_nss_db_state_destr(s, rootp);
return (0);
}
if (!checked_env) {
/* NSS_OPTIONS is undocumented and should be used without nscd running. */
checked_env = 1;
}
if (! (s->p.flags & NSS_USE_DEFAULT_CONFIG)) {
/* === ? test err ? */
/* user_attr should use files nis, not compat */
(void) __nsw_freeconfig_v1(config);
}
}
if (config == 0) {
/* getconfig failed, or frontend demanded default config */
}
if (config == 0) {
_nss_db_state_destr(s, rootp);
return (0);
}
}
_nss_db_state_destr(s, rootp);
return (0);
}
}
s->refcount = 1;
return (s);
}
void
{
if (max_dormant == 1) {
NSS_DBOP_DESTRUCTOR, 0);
};
int n;
NSS_DBOP_DESTRUCTOR, 0);
}
}
/* cond_destroy(&src->wanna_be); */
}
}
/*
* _nss_db_state_destr() -- used by NSS_UNREF_UNLOCK() to free the entire
* nss_db_state structure.
* Assumes that s has been ref-counted down to zero (in particular,
* rootp->s has already been dealt with).
*
* Nobody else holds a pointer to *s (if they did, refcount != 0),
* so we can clean up state *after* we drop the lock (also, by the
* time we finish freeing the state structures, the lock may have
* ceased to exist -- if we were using the orphan_root).
*/
void
{
if (s == NULL)
return;
/* === mutex_destroy(&s->orphan_root.lock); */
if (s->p.cleanup != 0) {
(*s->p.cleanup)(&s->p);
}
if (s->config != 0) {
(void) __nsw_freeconfig_v1(s->config);
}
if (s->src != 0) {
int n_src;
s->p.max_dormant_per_src);
}
}
libc_free(s);
}
/*
* _nss_status_vec() returns a bit vector of all status codes returned during
* the most recent call to nss_search().
* _nss_status_vec_p() returns a pointer to this bit vector, or NULL on
* failure.
* These functions are private. Don't use them externally without discussing
* it with the switch maintainers.
*/
static uint_t *
{
}
unsigned int
_nss_status_vec(void)
{
}
static void
int n,
char *dbase,
struct __nsw_lookup_v1 *lkp)
{
(void) fprintf(__nss_debug_file,
"NSS_retry(%d): '%s': trying '%s' ... ",
(void) fflush(__nss_debug_file);
}
static void
struct __nsw_lookup_v1 *lkp)
{
switch (res) {
case NSS_SUCCESS:
break;
case NSS_NOTFOUND:
break;
case NSS_UNAVAIL:
break;
case NSS_TRYAGAIN:
break;
case NSS_NISSERVDNS_TRYAGAIN:
break;
default:
}
case __NSW_CONTINUE:
break;
case __NSW_RETURN:
break;
case __NSW_TRYAGAIN_FOREVER:
break;
case __NSW_TRYAGAIN_NTIMES:
lkp->max_retries);
break;
case __NSW_TRYAGAIN_PAUSED:
break;
default:
}
}
#define NSS_BACKOFF(n, b, t) \
((n) > ((b) + 3) ? t : (1 << ((n) - ((b) + 1))))
static int
{
if (res == NSS_SUCCESS) {
}
return (0);
}
if ((res == NSS_TRYAGAIN &&
(res == NSS_NISSERVDNS_TRYAGAIN &&
return (1);
if (res == NSS_TRYAGAIN &&
if (n <= lkp->max_retries)
return (1);
else {
return (0);
}
if (res == NSS_NISSERVDNS_TRYAGAIN &&
if (n <= lkp->max_retries)
return (1);
else {
return (0);
}
return (0);
}
/*
* Switch policy component functional interfaces
*/
void
{
struct nss_db_state *s;
/* no name service cache daemon divert here */
/* local nss_delete decrements state reference counts */
/* and may free up opened switch resources. */
NSS_ROOTLOCK(rootp, &s);
if (s == 0) {
} else {
rootp->s = 0;
NSS_UNREF_UNLOCK(rootp, s);
}
}
void *search_args)
{
struct nss_db_state *s;
int n_src;
unsigned int *status_vec_p;
/* name service cache daemon divert */
if (res != NSS_TRYLOCAL)
return (res);
/* fall through - process locally */
errno = 0; /* just in case ... */
res = NSS_UNAVAIL;
if (status_vec_p == NULL) {
return (NSS_UNAVAIL);
}
*status_vec_p = 0;
if (s == 0) {
return (res);
}
NSS_STATE_REF_u(s);
res = NSS_UNAVAIL;
int n_loop = 0;
do {
/*
* Backend operation may take a while;
* drop the lock so we don't serialize
* more than necessary.
*/
/* After several tries, backoff... */
if (n_loop > no_backoff) {
if (__nss_debug_eng_loop > 1)
(void) fprintf(
"NSS: loop: "
"sleeping %d ...\n",
max_backoff));
}
if (__nss_debug_eng_loop)
NSS_RELOCK(&rootp, s);
n_loop++;
if (__nss_debug_eng_loop)
}
}
if (__nss_debug_eng_loop)
(void) fprintf(__nss_debug_file,
"NSS: '%s': return.\n",
break;
} else
if (__nss_debug_eng_loop)
(void) fprintf(__nss_debug_file,
"NSS: '%s': continue ...\n",
}
NSS_UNREF_UNLOCK(rootp, s);
return (res);
}
/*
* Start of nss_{setent|getent|endent}
*/
/*
* In principle there could be multiple contexts active for a single
* database; in practice, since Posix and UI have helpfully said that
* getent() state is global rather than, say, per-thread or user-supplied,
* we have at most one of these per nss_db_state.
* XXX ? Is this statement still true?
*
* NSS2 - a client's context is maintained as a cookie delivered by and
* passed to nscd. The cookie is a 64 bit (nssuint_t) unique opaque value
* created by nscd.
* cookie states:
* NSCD_NEW_COOKIE - cookie value uninitialized
* NSCD_LOCAL_COOKIE - setent is a local setent
* all other - NSCD unique opaque id for this setent
* A client's context is also associated with a seq_num. This is a nscd
* opaque 64 bit (nssuint_t) value passed with a cookie, and used to by nscd
* to validate the sequencing of the context. The client treats this as
* a pass through value.
*
* XXX ?? Use Cookie as cross-check info so that we can detect an
* nss_context that missed an nss_delete() or similar.
*/
struct nss_getent_context {
struct nss_db_state *s;
};
static void nss_setent_u(nss_db_root_t *,
nss_getent_t *);
nss_getent_t *,
void *);
static void nss_endent_u(nss_db_root_t *,
nss_getent_t *);
void
{
if (contextpp == 0) {
return;
}
}
void *args)
{
if (contextpp == 0) {
return (NSS_UNAVAIL);
}
return (status);
}
void
{
if (contextpp == 0) {
return;
}
}
/*
* Each of the _u versions of the nss interfaces assume that the context
* lock is held. No need to divert to nscd. Private to local sequencing.
*/
static void
{
struct nss_db_state *s;
int n_src;
s = contextp->s;
if (s != 0) {
NSS_RELOCK(&rootp, s);
NSS_UNREF_UNLOCK(rootp, s);
}
contextp->s = 0;
}
}
static void
{
struct nss_db_state *s;
int n_src;
/* setup process wide context while locked */
return;
}
s = 0;
} else {
s = contextp->s;
}
/* name service cache daemon divert */
if (status != NSS_TRYLOCAL)
return;
}
/* fall through - process locally */
if (s == 0) {
if (s == 0) {
/* Couldn't set up state, so quit */
/* ==== is there any danger of not having done an */
/* end_iter() here, and hence of losing backends? */
return;
}
NSS_STATE_REF_u(s);
contextp->s = s;
} else {
s = contextp->s;
/*
* Optimization: don't do endent, don't change
* backends, just do the setent. Look Ma, no locks
* (nor any context that needs updating).
*/
return;
}
NSS_RELOCK(&rootp, s);
} else {
NSS_RELOCK(&rootp, s);
}
}
;
}
if (be == 0) {
return;
}
}
static nss_status_t
{
struct nss_db_state *s;
int n_src;
/* Give up */
return (NSS_UNAVAIL);
}
}
/* name service cache daemon divert */
if (status != NSS_TRYLOCAL)
return (status);
/* fall through - process locally */
s = contextp->s;
if (s == 0) {
/*
* We've done an end_iter() and haven't done nss_setent()
* or nss_endent() since; we should stick in this state
* until the caller invokes one of those two routines.
*/
return (NSS_SUCCESS);
}
if (be == 0) {
/* If it's null it's a bug, but let's play safe */
res = NSS_UNAVAIL;
} else {
}
if (res != __NSW_SUCCESS) {
}
return (res);
}
NSS_RELOCK(&rootp, s);
do {
n_src++;
if (be == 0) {
/*
* This is the case where we failed to get the backend
* for the last source. We exhausted all sources.
*
* We need to do cleanup ourselves because end_iter_u()
* does not do it for be == 0.
*/
NSS_UNREF_UNLOCK(rootp, s);
contextp->s = 0;
break;
} else {
}
}
/* Got to the end of the sources without finding another entry */
return (NSS_SUCCESS);
/* success is either a successful entry or end of the sources */
}
/*ARGSUSED*/
static void
{
/* nss_endent() on an unused context is a no-op */
return;
}
/* notify name service cache daemon */
if (status != NSS_TRYLOCAL) {
/* clean up */
return;
}
/* fall through - process locally */
/*
* Existing code (BSD, SunOS) works in such a way that getXXXent()
* following an endXXXent() behaves as though the user had invoked
* setXXXent(), i.e. it iterates properly from the beginning.
* We'd better not break this, so our choices are
* (1) leave the context structure around, and do nss_setent or
* something equivalent,
* or (2) free the context completely, and rely on the code in
* nss_getent() that makes getXXXent() do the right thing
* even without a preceding setXXXent().
* The code below does (2), which frees up resources nicely but will
* cost more if the user then does more getXXXent() operations.
* Moral: for efficiency, don't call endXXXent() prematurely.
*/
}
/*
* pack dbd data into header
* Argment pointers assumed valid.
* poff offset position pointer
* IN = starting offset for dbd header
* OUT = starting offset for next section
*/
static nss_status_t
{
char *bptr;
return (NSS_ERROR);
}
/* if default config not specified, the flag should be reset */
if (p->default_config == NULL) {
p->default_config = "<NULL>";
}
return (NSS_ERROR);
}
if (p->config_name != NULL) {
}
return (NSS_ERROR);
}
len += n;
bptr += n;
if (nc == 0) {
pdbd->o_config_name = 0;
} else {
}
return (NSS_SUCCESS);
}
/*
* Switch packed and _nsc (switch->nscd) interfaces
* Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND
*/
/*ARGSUSED*/
{
const char *dbn;
char *bptr;
return (ret);
}
return (ret);
}
/* init buffer header */
/* possible audituser init */
off = sizeof (nss_pheader_t);
/* setup getXbyY operation - database and sub function */
if (ret != NSS_SUCCESS) {
return (ret);
}
/* setup request key */
/* use key2str if provided, else call default getXbyY packer */
/* This has to run locally due to backend knowledge */
if (search_fnum == NSS_DBOP_NETGROUP_SET) {
errno = 0;
return (NSS_TRYLOCAL);
}
/* use default packer for known getXbyY ops */
search_fnum, &len);
/* use default packer for known getXbyY ops */
search_fnum, &len);
} else {
}
if (ret != NSS_SUCCESS) {
return (ret);
}
/*
* Prime data return with first result if
* the first result is passed in
* [_getgroupsbymember oddness]
*/
if (search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
}
errno = 0; /* just in case ... */
return (NSS_SUCCESS);
}
/*
* Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND
*/
/*ARGSUSED*/
{
char *bptr;
return (ret);
}
/* init buffer header */
off = sizeof (nss_pheader_t);
/* setup getXXXent operation - database and sub function */
if (ret != NSS_SUCCESS) {
return (ret);
}
return (ret);
}
return (NSS_SUCCESS);
}
/*
* Unpack packed arguments buffer
* Return: status, errnos and results from requested operation.
*
* NOTES: When getgroupsbymember is being processed in the NSCD backend,
* or via the backwards compatibility interfaces then the standard
* str2group API is used in conjunction with process_cstr. When,
* processing a returned buffer, in NSS2 the return results are the
* already digested groups array. Therefore, unpack the digested results
* back to the return buffer.
*
* Note: the digested results are nssuint_t quantities. _getgroupsbymember
* digests int quantities. Therefore convert. Assume input is in nssuint_t
* quantities. Store in an int array... Assume gid's are <= 32 bits...
*/
/*ARGSUSED*/
{
char *dbn;
char *buf;
int len;
int ret;
int i;
int fmt_type;
return (-1);
/* Identify odd cases */
fmt_type = 0; /* nss_XbyY_args_t */
if (search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
else if (search_fnum == NSS_DBOP_NETGROUP_IN &&
/* if error - door's switch error */
/* extended data could contain additional information? */
if (status != NSS_SUCCESS) {
if (fmt_type == 0) {
}
return (status);
}
return (NSS_NOTFOUND);
/* sidestep odd cases */
if (fmt_type == 1) {
/* copy returned gid array from returned nscd buffer */
/* not enough buffer */
}
return (NSS_SUCCESS);
}
if (fmt_type == 2) {
return (NSS_SUCCESS);
} else {
return (NSS_NOTFOUND);
}
}
/* process the normal cases */
/* marshall data directly into users buffer */
if (ret == NSS_STR_PARSE_ERANGE) {
ret = NSS_NOTFOUND;
} else if (ret == NSS_STR_PARSE_SUCCESS) {
ret = NSS_SUCCESS;
}
return ((nss_status_t)ret);
}
/*
* Unpack a returned packed {set,get,end}ent arguments buffer
* Return: status, errnos, cookie info and results from requested operation.
*/
/*ARGSUSED*/
{
char *buf;
int len;
int ret;
return (-1);
/* if error - door's switch error */
/* extended data could contain additional information? */
if (status != NSS_SUCCESS)
return (status);
return (NSS_NOTFOUND);
/*
* Should either be new, or the cookie returned by the last
* setent (i.e., this is the first getent after the setent)
* or a match, else error
*/
return (NSS_NOTFOUND);
}
/* save away for the next ent request */
/* All done if no marshalling is expected {set,end}ent */
return (NSS_SUCCESS);
/* unmarshall the data */
return (NSS_NOTFOUND);
/* marshall data directly into users buffer */
if (ret == NSS_STR_PARSE_ERANGE) {
} else if (ret == NSS_STR_PARSE_SUCCESS) {
}
return ((nss_status_t)ret);
}
/*
* Start of _nsc_{search|setent_u|getent_u|endent_u} NSCD interposition funcs
*/
void *search_args)
{
if (_nsc_proc_is_cache() > 0) {
/* internal nscd call - don't use the door */
return (NSS_TRYLOCAL);
}
/* standard client calls nscd code */
if (search_args == NULL)
return (NSS_NOTFOUND);
/* get the door buffer & configured size */
return (NSS_TRYLOCAL);
return (NSS_TRYLOCAL);
/* pack argument and request into door buffer */
/* copy relevant door request info into door buffer */
/* Packing error return error results */
if (status != NSS_SUCCESS)
return (status);
/* transfer packed switch request to nscd via door */
/* data_off can be used because it is header+dbd_len+key_len */
/* If unsuccessful fallback to standard nss logic */
if (status != NSS_SUCCESS) {
/*
* check if doors reallocated the memory underneath us
* if they did munmap it or suffer a memory leak
*/
}
return (NSS_TRYLOCAL);
}
/* set any error conditions */
/*
* check if doors reallocated the memory underneath us
* if they did munmap it or suffer a memory leak
*/
}
return (status);
}
/*
* contact nscd for a cookie or to reset an existing cookie
* if nscd fails (NSS_TRYLOCAL) then set cookie to -1 and
* continue diverting to local
*/
{
/* return if already in local mode */
return (NSS_TRYLOCAL);
if (_nsc_proc_is_cache() > 0) {
/* internal nscd call - don't try to use the door */
return (NSS_TRYLOCAL);
}
/* get the door buffer & configured size */
return (NSS_TRYLOCAL);
}
return (NSS_TRYLOCAL);
}
return (NSS_ERROR);
}
/* pack relevant setent request info into door buffer */
if (status != NSS_SUCCESS)
return (status);
/* transfer packed switch request to nscd via door */
/* data_off can be used because it is header+dbd_len+key_len */
/* If fallback to standard nss logic (door failure) if possible */
if (status != NSS_SUCCESS) {
return (NSS_TRYLOCAL);
}
return (NSS_UNAVAIL);
}
/* unpack returned cookie stash it away */
/* save the setent cookie for later use */
/*
* check if doors reallocated the memory underneath us
* if they did munmap it or suffer a memory leak
*/
}
return (status);
}
{
/* return if already in local mode */
return (NSS_TRYLOCAL);
/* _nsc_setent_u already checked for nscd local case ... proceed */
return (NSS_NOTFOUND);
/* get the door buffer & configured size */
return (NSS_UNAVAIL);
return (NSS_UNAVAIL);
/* pack relevant setent request info into door buffer */
if (status != NSS_SUCCESS)
return (status);
/* transfer packed switch request to nscd via door */
/* data_off can be used because it is header+dbd_len+key_len */
/* If fallback to standard nss logic (door failure) if possible */
if (status != NSS_SUCCESS) {
if (status == NSS_TRYLOCAL ||
/* init the local cookie */
return (NSS_UNAVAIL);
return (NSS_TRYLOCAL);
}
return (NSS_UNAVAIL);
}
/* check error, unpack and process results */
/*
* check if doors reallocated the memory underneath us
* if they did munmap it or suffer a memory leak
*/
}
return (status);
}
{
/* return if already in local mode */
return (NSS_TRYLOCAL);
/* _nsc_setent_u already checked for nscd local case ... proceed */
/* get the door buffer & configured size */
return (NSS_UNAVAIL);
return (NSS_UNAVAIL);
/* pack up a NSCD_ENDGET request passing in the cookie */
/* pack relevant setent request info into door buffer */
if (status != NSS_SUCCESS)
return (status);
/* transfer packed switch request to nscd via door */
/* data_off can be used because it is header+dbd_len+key_len */
/* error codes & unpacking ret values don't matter. We're done */
/*
* check if doors reallocated the memory underneath us
* if they did munmap it or suffer a memory leak
*/
}
/* clean up initf setup */
/* clear cookie */
return (NSS_SUCCESS);
}
/*
* Internal private API to return default suggested buffer sizes
* for nsswitch API requests.
*/
{
switch (arg) {
case _SC_GETGR_R_SIZE_MAX:
return (__nss_buflen_group);
}
return (__nss_buflen_default);
}