root/usr/src/lib/libc/port/gen/nss_dbdefs.c
/*
 * 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
 * or http://www.opensolaris.org/os/licensing.
 * 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
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include "lint.h"
#include <mtlib.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <nss_dbdefs.h>
#include <limits.h>
#include <dlfcn.h>
#include <link.h>
#include <thread.h>
#include <atomic.h>
/* headers for key2str/str2key routines */
#include <sys/ethernet.h>
#include <exec_attr.h>
#include <grp.h>

/*
 * functions in nss_dbdefs.c deal more with the mechanics of
 * the data structures like nss_XbyY_args_t and the interaction
 * with the packed buffers etc.  versus the mechanics of the
 * actual policy component operations such as nss_search sequencing.
 */

/*
 * ALIGN? is there an official definition of this?
 * We use sizeof(long) to cover what we want
 * for both the 32-bit world and 64-bit world.
 */

#define ALIGN(x) ((((long)(x)) + sizeof (long) - 1) & ~(sizeof (long) - 1))

nss_XbyY_buf_t *
_nss_XbyY_buf_alloc(int struct_size, int buffer_size)
{
        nss_XbyY_buf_t  *b;

        /* Use one malloc for dbargs, result struct and buffer */
        b = (nss_XbyY_buf_t *)
            malloc(ALIGN(sizeof (*b)) + struct_size + buffer_size);
        if (b == 0) {
                return (0);
        }
        b->result = (void *)ALIGN(&b[1]);
        b->buffer = (char *)(b->result) + struct_size;
        b->buflen = buffer_size;
        return (b);
}

void
_nss_XbyY_buf_free(nss_XbyY_buf_t *b)
{
        if (b != 0) {
                free(b);
        }
}

/* === Comment:  used by fget{gr,pw,sp}ent */
/* ==== Should do ye olde syslog()ing of suspiciously long lines */

void
_nss_XbyY_fgets(FILE *f, nss_XbyY_args_t *b)
{
        char            buf[LINE_MAX];
        int             len, parsestat;

        if (fgets(buf, LINE_MAX, f) == 0) {
                /* End of file */
                b->returnval = 0;
                b->erange    = 0;
                return;
        }
        len = (int)strlen(buf);
        /* len >= 0 (otherwise we would have got EOF) */
        if (buf[len - 1] != '\n') {
                if ((len + 1) == LINE_MAX) {
                        /* Line too long for buffer; too bad */
                        while (fgets(buf, LINE_MAX, f) != 0 &&
                            buf[strlen(buf) - 1] != '\n') {
                                ;
                        }
                        b->returnval = 0;
                        b->erange    = 1;
                        return;
                }
                /* case where the file is not terminated with a Newline */
                len++;
        }
        parsestat = (*b->str2ent)(buf, (len - 1), b->buf.result, b->buf.buffer,
            b->buf.buflen);
        if (parsestat == NSS_STR_PARSE_ERANGE) {
                b->returnval = 0;
                b->erange    = 1;
        } else if (parsestat == NSS_STR_PARSE_SUCCESS) {
                b->returnval = b->buf.result;
        }
}

/*
 * parse the aliases string into the buffer and if successful return
 * a char ** pointer to the beginning of the aliases.
 *
 * CAUTION: (instr, instr+lenstr) and (buffer, buffer+buflen) are
 * non-intersecting memory areas. Since this is an internal interface,
 * we should be able to live with that.
 *
 * "instr" is the beginning of the aliases string
 * "buffer" has the return val for success
 * "buflen" is the length of the buffer available for aliases
 */
char **
_nss_netdb_aliases(const char *instr, int lenstr, char *buffer, int buflen)
{
        /*
         * Build the alias-list in the start of the buffer, and copy
         * the strings to the end of the buffer.
         */
        const char *instr_limit = instr + lenstr;
        char    *copyptr        = buffer + buflen;
        char    **aliasp        = (char **)ROUND_UP(buffer, sizeof (*aliasp));
        char    **alias_start   = aliasp;
        int     nstrings        = 0;

        for (;;) {
                const char      *str_start;
                size_t          str_len;

                while (instr < instr_limit && isspace(*instr)) {
                        instr++;
                }
                if (instr >= instr_limit || *instr == '#') {
                        break;
                }
                str_start = instr;
                while (instr < instr_limit && !isspace(*instr)) {
                        instr++;
                }

                ++nstrings;

                str_len = instr - str_start;
                copyptr -= str_len + 1;
                if (copyptr <= (char *)(&aliasp[nstrings + 1])) {
                        /* Has to be room for the pointer to */
                        /* the alias we're about to add,   */
                        /* as well as the final NULL ptr.  */
                        return (0);
                }
                *aliasp++ = copyptr;
                (void) memcpy(copyptr, str_start, str_len);
                copyptr[str_len] = '\0';
        }
        *aliasp++ = 0;
        return (alias_start);
}


extern nss_status_t process_cstr(const char *, int, struct nss_groupsbymem *);

/*
 * pack well known getXbyY keys to packed buffer prior to the door_call
 * to nscd.  Some consideration is given to ordering the tests based on
 * usage.  Note: buf is nssuint_t aligned.
 */

typedef struct {
        const char      *name;          /* NSS_DBNAM_* */
        const char      *defconf;       /* NSS_DEFCONF_* */
        const char      *initfn;        /* init function name */
        const char      *strfn;         /* str2X function name */
        const char      *cstrfn;        /* cstr2X function name */
        void            *initfnp;       /* init function pointer */
        void            *strfnp;        /* str2X function pointer */
        uint32_t        dbop;           /* NSS_DBOP_* */
        const char      *tostr;         /* key2str cvt str */
} getXbyY_to_dbop_t;

#define NSS_MK_GETXYDBOP(x, y, f, e)    \
        { NSS_DBNAM_##x, NSS_DEFCONF_##x, "_nss_initf_" f, "str2" f, \
                NULL, NULL, NULL, NSS_DBOP_##x##_##y, (e) }

#define NSS_MK_GETXYDBOPA(x, a, f, e)   \
        { NSS_DBNAM_##x, NSS_DEFCONF_##x, "_nss_initf_" f, "str2" f, \
                NULL, NULL, NULL, NSS_DBOP_##a, (e) }

#define NSS_MK_GETXYDBOPB(x, b, a, f, s, e)     \
        { NSS_DBNAM_##x, NSS_DEFCONF_##b, "_nss_initf_" f, s,  \
                NULL, NULL, NULL, NSS_DBOP_##a, (e) }

#define NSS_MK_GETXYDBOPC(x, a, f, s, e)        \
        { NSS_DBNAM_##x, NSS_DEFCONF_##x, "_nss_initf_" f, s, \
                NULL, NULL, NULL, NSS_DBOP_##x##_##a, (e) }

#define NSS_MK_GETXYDBOPD(x, y, i, f, e)        \
        { NSS_DBNAM_##x, NSS_DEFCONF_##x, "_nss_initf_" i, "str2" f, \
                NULL, NULL, NULL, NSS_DBOP_##x##_##y, (e) }

#define NSS_MK_GETXYDBOPCSTR(x, a, f, s, e)     \
        { NSS_DBNAM_##x, NSS_DEFCONF_##x, "_nss_initf_" f, s, \
                "process_cstr", NULL, NULL, NSS_DBOP_##x##_##a, (e) }

/*
 * The getXbyY_to_dbop structure is hashed on first call in order to
 * reduce the search time for the well known getXbyY operations.
 * A binary search was not fast enough.  There were on average
 * 3-4 tests (strcmps) per getXbyY call.
 *
 * DBOP_PRIME_HASH must be a prime number (reasonably small) but that
 * is sufficient to uniquely map the entries in the following table
 * without collision.
 *
 * The DBOP_PRIME_HASH was selected as the smallest hash value
 * for this table without collisions. Changing this table WILL
 * necessitate re-testing for possible collisions.
 */

#define DBOP_PRIME_HASH         227
#define DBOP_HASH_TAG           0xf0000000
static int getXbyYdbopHASH[DBOP_PRIME_HASH] = { 0 };
static mutex_t getXbydbop_hash_lock = DEFAULTMUTEX;
static int getXbyYdbop_hashed = 0;

/*
 * If the size of getXbyY_to_dbop[] is changed then hash function must be
 * corrected to be without collisions in nss_dbop_search().
 */
static getXbyY_to_dbop_t getXbyY_to_dbop[] = {
        /* NSS_MK_GETXYDBOP(ALIASES, ?, ?), */
        NSS_MK_GETXYDBOPD(AUDITUSER, BYNAME, "auuser", "audituser", "n"),
        NSS_MK_GETXYDBOP(AUTHATTR, BYNAME, "authattr", "n"),
        /* NSS_MK_GETXYDBOP(AUTOMOUNT, ?, ?), */
        NSS_MK_GETXYDBOP(BOOTPARAMS, BYNAME, "bootparams", "n"),
        NSS_MK_GETXYDBOPC(ETHERS, HOSTTON, "ethers", "str2ether", "n"),
        NSS_MK_GETXYDBOPC(ETHERS, NTOHOST, "ethers", "str2ether", "e"),
        NSS_MK_GETXYDBOP(EXECATTR, BYNAME, "execattr", "A"),
        NSS_MK_GETXYDBOP(EXECATTR, BYID, "execattr", "A"),
        NSS_MK_GETXYDBOP(EXECATTR, BYNAMEID, "execattr", "A"),
        NSS_MK_GETXYDBOP(GROUP, BYNAME, "group", "n"),
        NSS_MK_GETXYDBOP(GROUP, BYGID, "group", "g"),
        NSS_MK_GETXYDBOPCSTR(GROUP, BYMEMBER, "group", "str2group", "I"),
        NSS_MK_GETXYDBOPC(HOSTS, BYNAME, "hosts", "str2hostent", "n"),
        NSS_MK_GETXYDBOPC(HOSTS, BYADDR, "hosts", "str2hostent", "h"),
        NSS_MK_GETXYDBOPC(IPNODES, BYNAME, "ipnodes", "str2hostent", "i"),
        NSS_MK_GETXYDBOPC(IPNODES, BYADDR, "ipnodes", "str2hostent", "h"),
        NSS_MK_GETXYDBOP(NETGROUP, IN, "netgroup", "t"),
        NSS_MK_GETXYDBOP(NETGROUP, SET, "netgroup", "T"),
        NSS_MK_GETXYDBOPC(NETMASKS, BYNET, "netmasks", "str2addr", "n"),
        NSS_MK_GETXYDBOPC(NETWORKS, BYNAME, "net", "str2netent", "n"),
        NSS_MK_GETXYDBOPC(NETWORKS, BYADDR, "net", "str2netent", "a"),
        NSS_MK_GETXYDBOP(PASSWD, BYNAME, "passwd", "n"),
        NSS_MK_GETXYDBOP(PASSWD, BYUID, "passwd", "u"),
        NSS_MK_GETXYDBOP(PRINTERS, BYNAME, "printers", "n"),
        NSS_MK_GETXYDBOP(PROFATTR, BYNAME, "profattr", "n"),
        NSS_MK_GETXYDBOP(PROJECT, BYNAME, "project", "n"),
        NSS_MK_GETXYDBOP(PROJECT, BYID, "project", "p"),
        NSS_MK_GETXYDBOPC(PROTOCOLS, BYNAME, "proto", "str2protoent", "n"),
        NSS_MK_GETXYDBOPC(PROTOCOLS, BYNUMBER, "proto", "str2protoent", "N"),
        NSS_MK_GETXYDBOPA(PUBLICKEY, KEYS_BYNAME, "publickey", "k"),
        NSS_MK_GETXYDBOPC(RPC, BYNAME, "rpc", "str2rpcent", "n"),
        NSS_MK_GETXYDBOPC(RPC, BYNUMBER, "rpc", "str2rpcent", "N"),
        NSS_MK_GETXYDBOPC(SERVICES, BYNAME, "services", "str2servent", "s"),
        NSS_MK_GETXYDBOPC(SERVICES, BYPORT, "services", "str2servent", "S"),
        NSS_MK_GETXYDBOPB(SHADOW, PASSWD, PASSWD_BYNAME, "shadow",
            "str2spwd", "n"),
        NSS_MK_GETXYDBOPC(TSOL_RH, BYADDR, "tsol_rh", "str_to_rhstr", "h"),
        NSS_MK_GETXYDBOPC(TSOL_TP, BYNAME, "tsol_tp", "str_to_tpstr", "n"),
        NSS_MK_GETXYDBOPC(TSOL_ZC, BYNAME, "tsol_zc", "str_to_zcstr", "n"),
        NSS_MK_GETXYDBOP(USERATTR, BYNAME, "userattr", "n"),
};

static int
nss_dbop_search(const char *name, uint32_t dbop)
{
        getXbyY_to_dbop_t *hptr;
        int count = (sizeof (getXbyY_to_dbop) / sizeof (getXbyY_to_dbop_t));
        uint32_t hval, g;
        const char *cp;
        int i, idx;
        static const uint32_t hbits_tst = 0xf0000000;

        /* Uses a table size is known to have no collisions */
        if (getXbyYdbop_hashed == 0) {
                lmutex_lock(&getXbydbop_hash_lock);
                if (getXbyYdbop_hashed == 0) {
                        for (i = 0; i < count; i++) {
                                cp = getXbyY_to_dbop[i].name;
                                hval = 0;
                                while (*cp) {
                                        hval = (hval << 4) + *cp++;
                                        if ((g = (hval & hbits_tst)) != 0)
                                                hval ^= g >> 24;
                                        hval &= ~g;
                                }
                                hval += getXbyY_to_dbop[i].dbop;
                                hval %= DBOP_PRIME_HASH;
                                if (getXbyYdbopHASH[hval] != 0) {
                                        /* hash table collision-see above */
                                        lmutex_unlock(&getXbydbop_hash_lock);
                                        return (-1);
                                }
                                getXbyYdbopHASH[hval] = i | DBOP_HASH_TAG;
                        }
                        membar_producer();
                        getXbyYdbop_hashed = 1;
                }
                lmutex_unlock(&getXbydbop_hash_lock);
        }
        membar_consumer();
        cp = name;
        hval = 0;
        while (*cp) {
                hval = (hval << 4) + *cp++;
                if ((g = (hval & hbits_tst)) != 0)
                        hval ^= g >> 24;
                hval &= ~g;
        }
        hval += dbop;
        hval %= DBOP_PRIME_HASH;
        idx = getXbyYdbopHASH[hval];
        if ((idx & DBOP_HASH_TAG) != DBOP_HASH_TAG)
                return (-1);
        idx &= ~DBOP_HASH_TAG;
        if (idx >= count)
                return (-1);
        hptr = &getXbyY_to_dbop[idx];
        if (hptr->dbop != dbop || strcmp(name, hptr->name) != 0)
                return (-1);
        return (idx);
}

/*
 * nss_pack_key2str
 * Private key to string packing function for getXbyY routines
 * This routine performs a printf like parse over the argument
 * key, given a string of items to pack and assembles the key in
 * the packed structure.  This routine is called (currently) by
 * nss_default_key2str, but will be used by other external
 * APIs in the future.
 *
 * buffer - Start of the key buffer location [in packed buffer]
 * length - Length of key buffer component
 * Key offsets are relative to start of key buffer location.
 *
 * Pack fields                  Key
 *   key.name                   n
 *   key.number                 N
 *   key.uid                    u
 *   key.gid                    g
 *   key.hostaddr               h
 *   key.ipnode                 i
 *   key.projid                 p
 *   key.serv(name)             s
 *   key.serv(port)             S
 *   key.ether                  e
 *   key.pkey                   k
 *   key.netaddr                a
 *   key.attrp                  A
 *   groupsbymember             I
 *   innetgr_args               t
 *   setnetgr_args              T
 */

static nss_status_t
nss_pack_key2str(void *buffer, size_t length, nss_XbyY_args_t *arg,
    const char *dbname, int dbop __unused, size_t *rlen, const char *typestr)
{
        int                             i, j;
        size_t                          len, len2, len3, len4, len5, slop;
        nssuint_t                       *uptr, offv, offc;
        struct nss_setnetgrent_args     *sng;
        struct nss_innetgr_args         *ing;
        struct nss_groupsbymem          *gbm;
        char                            **cv, *dptr;
        nss_pnetgr_t                    *pptr;
        _priv_execattr                  *pe;

        if (buffer == NULL || length == 0 || arg == NULL ||
            dbname == NULL || rlen == NULL || typestr == NULL)
                return (NSS_ERROR);

        while (typestr && *typestr) {
                switch (*typestr++) {
                case 'n':
                        if (arg->key.name == NULL)
                                return (NSS_NOTFOUND);
                        len = strlen(arg->key.name) + 1;
                        if (len >= length)
                                return (NSS_ERROR);
                        (void) strlcpy(buffer, arg->key.name, len);
                        *rlen = len;
                        break;
                case 'N':
                        len = sizeof (nssuint_t);
                        if (len >= length)
                                return (NSS_ERROR);
                        *(nssuint_t *)buffer = (nssuint_t)arg->key.number;
                        *rlen = len;
                        break;
                case 'u':
                        len = sizeof (nssuint_t);
                        if (len >= length)
                                return (NSS_ERROR);
                        *(nssuint_t *)buffer = (nssuint_t)arg->key.uid;
                        *rlen = len;
                        break;
                case 'g':
                        len = sizeof (nssuint_t);
                        if (len >= length)
                                return (NSS_ERROR);
                        *(nssuint_t *)buffer = (nssuint_t)arg->key.gid;
                        *rlen = len;
                        break;
                case 'h':
                        if (arg->key.hostaddr.addr == NULL)
                                return (-1);
                        len = arg->key.hostaddr.len;
                        len = ROUND_UP(len, sizeof (nssuint_t));
                        len2 = (sizeof (nssuint_t) * 2) + len;
                        if (len2 >= length)
                                return (NSS_ERROR);
                        *(nssuint_t *)buffer =
                            (nssuint_t)arg->key.hostaddr.len;
                        buffer = (void *)((char *)buffer + sizeof (nssuint_t));
                        *(nssuint_t *)buffer =
                            (nssuint_t)arg->key.hostaddr.type;
                        buffer = (void *)((char *)buffer + sizeof (nssuint_t));
                        (void) memcpy(buffer, arg->key.hostaddr.addr,
                            arg->key.hostaddr.len);
                        *rlen = len2;
                        break;
                case 'i':
                        if (arg->key.ipnode.name == NULL)
                                return (NSS_NOTFOUND);
                        len = strlen(arg->key.ipnode.name) + 1;
                        len = ROUND_UP(len, sizeof (nssuint_t));
                        len2 = (sizeof (nssuint_t) * 2) + len;
                        if (len2 >= length)
                                return (NSS_ERROR);
                        *(nssuint_t *)buffer =
                            (nssuint_t)arg->key.ipnode.af_family;
                        buffer = (void *)((char *)buffer + sizeof (nssuint_t));
                        *(nssuint_t *)buffer =
                            (nssuint_t)arg->key.ipnode.flags;
                        buffer = (void *)((char *)buffer + sizeof (nssuint_t));
                        (void) strlcpy(buffer, arg->key.ipnode.name, len);
                        *rlen = len2;
                        break;
                case 'p':
                        len = sizeof (nssuint_t);
                        if (len >= length)
                                return (NSS_ERROR);
                        *(nssuint_t *)buffer = (nssuint_t)arg->key.projid;
                        *rlen = len;
                        break;
                case 's':
                        if (arg->key.serv.serv.name == NULL)
                                return (NSS_NOTFOUND);
                        len = strlen(arg->key.serv.serv.name) + 1;
                        len2 = 1;
                        if (arg->key.serv.proto != NULL)
                                len2 += strlen(arg->key.serv.proto);
                        len3 = len + len2;
                        len3 = ROUND_UP(len3, sizeof (nssuint_t));
                        if (len3 >= length)
                                return (NSS_ERROR);
                        (void) strlcpy(buffer, arg->key.serv.serv.name, len);
                        buffer = (void *)((char *)buffer + len);
                        if (len2 > 1)
                                (void) strlcpy(buffer, arg->key.serv.proto,
                                    len2);
                        else
                                *(char *)buffer = '\0';
                        *rlen = len3;
                        break;
                case 'S':
                        len2 = 0;
                        if (arg->key.serv.proto != NULL)
                                len2 = strlen(arg->key.serv.proto) + 1;
                        len = sizeof (nssuint_t) + len2;
                        if (len >= length)
                                return (NSS_ERROR);
                        uptr = (nssuint_t *)buffer;
                        *uptr++ = (nssuint_t)arg->key.serv.serv.port;
                        if (len2) {
                                (void) strlcpy((char *)uptr,
                                    arg->key.serv.proto, len2);
                        }
                        *rlen = len;
                        break;
                case 'e':
                        if (arg->key.ether == NULL)
                                return (NSS_NOTFOUND);
                        len = sizeof (struct ether_addr);
                        len = ROUND_UP(len, sizeof (nssuint_t));
                        if (len >= length)
                                return (NSS_ERROR);
                        *(struct ether_addr *)buffer =
                            *(struct ether_addr *)arg->key.ether;
                        *rlen = len;
                        break;
                case 'k':
                        if (arg->key.pkey.name == NULL ||
                            arg->key.pkey.keytype == NULL)
                                return (NSS_NOTFOUND);
                        len = strlen(arg->key.pkey.name) + 1;
                        len2 = strlen(arg->key.pkey.keytype) + 1;
                        len3 = len + len2;
                        len3 = ROUND_UP(len3, sizeof (nssuint_t));
                        if (len3 >= length)
                                return (NSS_ERROR);
                        (void) strlcpy(buffer, arg->key.pkey.name, len);
                        buffer = (void *)((char *)buffer + len);
                        (void) strlcpy(buffer, arg->key.pkey.keytype, len2);
                        *rlen = len3;
                        break;
                case 'a':
                        uptr = (nssuint_t *)buffer;
                        len = sizeof (nssuint_t) * 2;
                        if (len >= length)
                                return (NSS_ERROR);
                        *uptr++ = (nssuint_t)arg->key.netaddr.net;
                        *uptr++ = (nssuint_t)arg->key.netaddr.type;
                        *rlen = len;
                        break;
                case 'A':
                        pe = (_priv_execattr *)(arg->key.attrp);
                        if (pe == NULL)
                                return (NSS_NOTFOUND);
                        /* for search flag */
                        len = sizeof (nssuint_t);
                        /* for sizeof (_priv_execattr) static buffer */
                        /* Plus lots of slop just in case... */
                        slop = sizeof (nssuint_t) * 16;
                        len += slop;

                        len2 = len3 = len4 = len5 = 1;
                        if (pe->name != NULL)
                                len2 = strlen(pe->name) + 1;
                        if (pe->type != NULL)
                                len3 = strlen(pe->type) + 1;
                        if (pe->id != NULL)
                                len4 = strlen(pe->id) + 1;
                        if (pe->policy != NULL)
                                len5 = strlen(pe->policy) + 1;
                        /* head_exec, prev_exec - are client side only... */
                        len += len2 + len3 + len4 + len5;
                        len = ROUND_UP(len, sizeof (nssuint_t));
                        if (len >= length)
                                return (NSS_ERROR);
                        (void) memset((void *)buffer, 0, slop);
                        uptr = (nssuint_t *)((void *)((char *)buffer + slop));
                        *uptr++ = (nssuint_t)pe->search_flag;
                        dptr = (char *)uptr;
                        if (len2 == 1)
                                *dptr++ = '\0';
                        else {
                                (void) strlcpy(dptr, pe->name, len2);
                                dptr += len2;
                        }
                        if (len3 == 1)
                                *dptr++ = '\0';
                        else {
                                (void) strlcpy(dptr, pe->type, len3);
                                dptr += len3;
                        }
                        if (len4 == 1)
                                *dptr++ = '\0';
                        else {
                                (void) strlcpy(dptr, pe->id, len4);
                                dptr += len4;
                        }
                        if (len5 == 1)
                                *dptr++ = '\0';
                        else
                                (void) strlcpy(dptr, pe->policy, len5);
                        *rlen = len;
                        break;
                case 'I':
                        gbm = (struct nss_groupsbymem *)arg;
                        if (gbm->username == NULL)
                                return (NSS_NOTFOUND);
                        len = strlen(gbm->username) + 1;
                        len2 = sizeof (nssuint_t) * 4;
                        len2 += ROUND_UP(len, sizeof (nssuint_t));
                        if (len2 >= length)
                                return (NSS_ERROR);
                        uptr = (nssuint_t *)buffer;
                        *uptr++ = (nssuint_t)gbm->force_slow_way;
                        *uptr++ = (nssuint_t)gbm->maxgids;
                        *uptr++ = (nssuint_t)gbm->numgids;
                        if (gbm->numgids == 1) {
                                *uptr++ = (nssuint_t)gbm->gid_array[0];
                        } else {
                                *uptr++ = (nssuint_t)0;
                        }
                        (void) strlcpy((void *)uptr, gbm->username, len);
                        *rlen = len2;
                        break;
                case 't':
                        pptr = (nss_pnetgr_t *)buffer;
                        ing = (struct nss_innetgr_args *)arg;
                        len = sizeof (nss_pnetgr_t);
                        len2 = ing->arg[NSS_NETGR_MACHINE].argc +
                            ing->arg[NSS_NETGR_USER].argc +
                            ing->arg[NSS_NETGR_DOMAIN].argc +
                            ing->groups.argc;
                        len2 *= sizeof (nssuint_t);
                        len3 = 0;
                        for (j = 0; j < NSS_NETGR_N; j++) {
                                cv = ing->arg[j].argv;
                                for (i = ing->arg[j].argc; --i >= 0; ) {
                                        if (*cv)
                                                len3 += strlen(*cv++) + 1;
                                }
                        }
                        cv = ing->groups.argv;
                        for (i = ing->groups.argc; --i >= 0; ) {
                                if (*cv)
                                        len3 += strlen(*cv++) + 1;
                        }
                        len3 = ROUND_UP(len3, sizeof (nssuint_t));
                        /*
                         * Double argv space. Reason:
                         *    First 1/2 offsets
                         *    Second 1/2 for client side pointer arrays
                         *    resolves malloc/free issues with unpacked argvs
                         */
                        if ((len + (len2 << 1) + len3) >= length)
                                return (NSS_ERROR);
                        *rlen = len + (len2 << 1) + len3;

                        pptr->machine_argc = ing->arg[NSS_NETGR_MACHINE].argc;
                        pptr->user_argc = ing->arg[NSS_NETGR_USER].argc;
                        pptr->domain_argc = ing->arg[NSS_NETGR_DOMAIN].argc;
                        pptr->groups_argc = ing->groups.argc;
                        offv = len;
                        uptr = (nssuint_t *)((void *)((char *)buffer + offv));
                        offc = len + (len2 << 1);
                        dptr = (char *)buffer + offc;
                        if (pptr->machine_argc == 0) {
                                pptr->machine_offv = (nssuint_t)0;
                        } else {
                                pptr->machine_offv = offv;
                                cv = ing->arg[NSS_NETGR_MACHINE].argv;
                                i = pptr->machine_argc;
                                offv += sizeof (nssuint_t) * i;
                                for (; --i >= 0; ) {
                                        *uptr++ = offc;
                                        len3 = strlen(*cv) + 1;
                                        (void) strlcpy(dptr, *cv++, len3);
                                        offc += len3;
                                        dptr += len3;
                                }
                        }
                        if (pptr->user_argc == 0) {
                                pptr->user_offv = (nssuint_t)0;
                        } else {
                                pptr->user_offv = offv;
                                cv = ing->arg[NSS_NETGR_USER].argv;
                                i = pptr->user_argc;
                                offv += sizeof (nssuint_t) * i;
                                for (; --i >= 0; ) {
                                        *uptr++ = offc;
                                        len3 = strlen(*cv) + 1;
                                        (void) strlcpy(dptr, *cv++, len3);
                                        offc += len3;
                                        dptr += len3;
                                }
                        }
                        if (pptr->domain_argc == 0) {
                                pptr->domain_offv = (nssuint_t)0;
                        } else {
                                pptr->domain_offv = offv;
                                cv = ing->arg[NSS_NETGR_DOMAIN].argv;
                                i = pptr->domain_argc;
                                offv += sizeof (nssuint_t) * i;
                                for (; --i >= 0; ) {
                                        *uptr++ = offc;
                                        len3 = strlen(*cv) + 1;
                                        (void) strlcpy(dptr, *cv++, len3);
                                        offc += len3;
                                        dptr += len3;
                                }
                        }
                        if (pptr->groups_argc == 0) {
                                pptr->groups_offv = (nssuint_t)0;
                        } else {
                                pptr->groups_offv = offv;
                                cv = ing->groups.argv;
                                i = pptr->groups_argc;
                                offv += sizeof (nssuint_t) * i;
                                for (; --i >= 0; ) {
                                        *uptr++ = offc;
                                        len3 = strlen(*cv) + 1;
                                        (void) strlcpy(dptr, *cv++, len3);
                                        offc += len3;
                                        dptr += len3;
                                }
                        }
                        break;
                case 'T':
                        sng = (struct nss_setnetgrent_args *)arg;
                        if (sng->netgroup == NULL)
                                return (NSS_NOTFOUND);
                        len = strlen(sng->netgroup) + 1;
                        if (len >= length)
                                return (NSS_ERROR);
                        (void) strlcpy(buffer, sng->netgroup, len);
                        *rlen = len;
                        break;
                default:
                        return (NSS_ERROR);
                }
        }
        return (NSS_SUCCESS);
}

nss_status_t
nss_default_key2str(void *buffer, size_t length, nss_XbyY_args_t *arg,
    const char *dbname, int dbop, size_t *rlen)
{
        int             index;

        if (buffer == NULL || length == 0 || arg == NULL ||
            dbname == NULL || rlen == NULL)
                return (NSS_ERROR);

        /*
         * If this is not one of the well known getXbyYs
         * (IE _printers special processing etc.) use a
         * local (non-nscd) getXbyY lookup.
         */
        if ((index = nss_dbop_search(dbname, (uint32_t)dbop)) < 0)
                return (NSS_TRYLOCAL);

        return (nss_pack_key2str(buffer, length, arg, dbname,
            dbop, rlen, getXbyY_to_dbop[index].tostr));
}

void
nss_packed_set_status(void *buffer, size_t length __unused, nss_status_t status,
    nss_XbyY_args_t *arg)
{
        nss_pheader_t   *pbuf = (nss_pheader_t *)buffer;
        nss_dbd_t       *pdbd;
        char            *dbn;

        /* sidestep odd cases */
        pdbd = (nss_dbd_t *)((void *)((char *)buffer + pbuf->dbd_off));
        dbn = (char *)pdbd + pdbd->o_name;
        if (pbuf->nss_dbop == NSS_DBOP_GROUP_BYMEMBER) {
                if (strcmp(dbn, NSS_DBNAM_GROUP) == 0) {
                        struct nss_groupsbymem *in =
                            (struct nss_groupsbymem *)arg;

                        if (in->numgids >= 0) {
                                pbuf->p_status = NSS_SUCCESS;
                                pbuf->data_len = in->numgids *
                                    sizeof (gid_t);
                                pbuf->p_herrno = 0;
                        } else {
                                pbuf->p_status = status;
                                pbuf->p_errno = errno;
                                pbuf->data_len = 0;
                                pbuf->p_herrno = (uint32_t)arg->h_errno;
                        }
                        return;
                }
        }
        if (pbuf->nss_dbop == NSS_DBOP_NETGROUP_IN) {
                if (strcmp(dbn, NSS_DBNAM_NETGROUP) == 0) {
                        struct nss_innetgr_args *in =
                            (struct nss_innetgr_args *)arg;

                        /* tell nss_unpack() operation is successful */
                        pbuf->data_len = 1;

                        if (status != NSS_SUCCESS && status != NSS_NOTFOUND) {
                                pbuf->p_status = status;
                                pbuf->p_errno = errno;
                                return;
                        }

                        if (in->status == NSS_NETGR_FOUND) {
                                pbuf->p_status = NSS_SUCCESS;
                        } else {
                                pbuf->p_status = NSS_NOTFOUND;
                                pbuf->p_errno = errno;
                        }
                        return;
                }
        }

        /* process normal cases */
        if ((pbuf->p_status = status) != NSS_SUCCESS) {
                if (arg->erange == 1)
                        pbuf->p_errno = ERANGE;
                else
                        pbuf->p_errno = errno;
        } else
                pbuf->p_errno = 0;
        if (arg != NULL) {
                pbuf->p_herrno = (uint32_t)arg->h_errno;
                pbuf->data_len = (nssuint_t)arg->returnlen;
        } else {
                pbuf->p_herrno = 0;
                pbuf->data_len = 0;
        }
}

/*
 * nss_upack_key2arg
 * Private string to key unpacking function for getXbyY routines
 * This routine performs a scanf/printf like parse over the packed
 * string, to uppack and re-assemble the key in the args structure.
 *
 * buffer - Start of the key buffer location [in packed buffer]
 * length - Length of key buffer component
 * Key offsets are relative to start of key buffer location.
 *
 * Unpack fields                Key
 *   key.name                   n
 *   key.number                 N
 *   key.uid                    u
 *   key.gid                    g
 *   key.hostaddr               h
 *   key.ipnode                 i
 *   key.projid                 p
 *   key.serv(name)             s
 *   key.serv(port)             S
 *   key.ether                  e
 *   key.pkey                   k
 *   key.netaddr                a
 *   key.attrp                  A
 *   groupsbymember             I
 *   innetgr_args               t
 *   setnetgr_args              T
 * Assumes arguments are all valid
 */

static nss_status_t
nss_upack_key2arg(void *buffer, size_t length __unused, char **dbname __unused,
    int *dbop __unused, nss_XbyY_args_t *arg, int index)
{
        nss_pheader_t                   *pbuf = (nss_pheader_t *)buffer;
        const char                      *strtype = NULL;
        nssuint_t                       off, *uptr, keysize;
        size_t                          len, slop;
        int                             i, j;
        char                            **cv, *bptr;
        struct nss_setnetgrent_args     *sng;
        struct nss_innetgr_args         *ing;
        struct nss_groupsbymem          *gbm;
        nss_pnetgr_t                    *pptr;
        _priv_execattr                  *pe;

        /* keysize is length of the key area */
        keysize = pbuf->data_off - pbuf->key_off;

        off = pbuf->key_off;
        bptr = (char *)buffer + off;
        uptr = (nssuint_t *)((void *)bptr);
        strtype = getXbyY_to_dbop[index].tostr;
        if (strtype == NULL)
                return (NSS_ERROR);
        while (*strtype) {
                switch (*strtype++) {
                case 'n':
                        arg->key.name = (const char *)bptr;
                        break;
                case 'N':
                        arg->key.number = (int)(*uptr);
                        break;
                case 'u':
                        arg->key.uid = (uid_t)(*uptr);
                        break;
                case 'g':
                        arg->key.gid = (gid_t)(*uptr);
                        break;
                case 'h':
                        arg->key.hostaddr.len = (int)(*uptr++);
                        arg->key.hostaddr.type = (int)(*uptr++);
                        arg->key.hostaddr.addr = (const char *)uptr;
                        break;
                case 'i':
                        arg->key.ipnode.af_family = (int)(*uptr++);
                        arg->key.ipnode.flags = (int)(*uptr++);
                        arg->key.ipnode.name = (const char *)uptr;
                        break;
                case 'p':
                        arg->key.projid = (projid_t)(*uptr);
                        break;
                case 's':
                        arg->key.serv.serv.name = (const char *)bptr;
                        len = strlen(arg->key.serv.serv.name) + 1;
                        bptr += len;
                        if (*(const char *)bptr == '\0')
                                arg->key.serv.proto = NULL;
                        else
                                arg->key.serv.proto = (const char *)bptr;
                        break;
                case 'S':
                        arg->key.serv.serv.port = (int)(*uptr++);
                        if (pbuf->key_len == sizeof (nssuint_t)) {
                                arg->key.serv.proto = NULL;
                        } else {
                                bptr += sizeof (nssuint_t);
                                arg->key.serv.proto = (const char *)bptr;
                        }
                        break;
                case 'e':
                        arg->key.ether = bptr;
                        break;
                case 'k':
                        arg->key.pkey.name = (const char *)bptr;
                        len = strlen(arg->key.pkey.name) + 1;
                        bptr += len;
                        arg->key.pkey.keytype = (const char *)bptr;
                        break;
                case 'a':
                        arg->key.netaddr.net = (uint32_t)(*uptr++);
                        arg->key.netaddr.type = (int)(*uptr++);
                        break;
                case 'A':
                        pe = (_priv_execattr *)((void *)bptr);
                        /* use slop space as priv_execattr structure */
                        arg->key.attrp = (void *)pe;
                        /* skip over slop ... */
                        slop = sizeof (nssuint_t) * 16;
                        uptr = (nssuint_t *)((void *)((char *)bptr + slop));
                        pe->search_flag = (int)*uptr++;
                        bptr = (char *)uptr;
                        if (*bptr == '\0') {
                                pe->name = NULL;
                                bptr++;
                        } else {
                                pe->name = (char *)bptr;
                                bptr += strlen(pe->name) + 1;
                        }
                        if (*bptr == '\0') {
                                pe->type = NULL;
                                bptr++;
                        } else {
                                pe->type = (char *)bptr;
                                bptr += strlen(pe->type) + 1;
                        }
                        if (*bptr == '\0') {
                                pe->id = NULL;
                                bptr++;
                        } else {
                                pe->id = (char *)bptr;
                                bptr += strlen(pe->id) + 1;
                        }
                        if (*bptr == '\0') {
                                pe->policy = NULL;
                        } else {
                                pe->policy = (char *)bptr;
                        }
                        pe->head_exec = NULL;
                        pe->prev_exec = NULL;
                        break;
                case 'I':
                        gbm = (struct nss_groupsbymem *)arg;
                        gbm->gid_array = (gid_t *)
                            ((void *)((char *)pbuf + pbuf->data_off));
                        gbm->force_slow_way = (int)(*uptr++);
                        gbm->maxgids = (int)(*uptr++);
                        gbm->numgids = (int)(*uptr++);
                        if (gbm->numgids == 1) {
                                /* insert initial group into data area */
                                gbm->gid_array[0] = (gid_t)(*uptr++);
                        } else
                                uptr++;
                        gbm->username = (const char *)uptr;
                        break;
                case 't':
                        pptr = (nss_pnetgr_t *)((void *)bptr);
                        ing = (struct nss_innetgr_args *)arg;
                        ing->arg[NSS_NETGR_MACHINE].argc = pptr->machine_argc;
                        ing->arg[NSS_NETGR_USER].argc = pptr->user_argc;
                        ing->arg[NSS_NETGR_DOMAIN].argc = pptr->domain_argc;
                        ing->groups.argc = pptr->groups_argc;

                        /*
                         * Start of argv pointer storage
                         */
                        off = ing->arg[NSS_NETGR_MACHINE].argc +
                            ing->arg[NSS_NETGR_USER].argc +
                            ing->arg[NSS_NETGR_DOMAIN].argc +
                            ing->groups.argc;
                        off *= sizeof (nssuint_t);
                        off += sizeof (nss_pnetgr_t);

                        cv = (char **)((void *)(bptr + off));
                        uptr = (nssuint_t *)
                            ((void *)(bptr + sizeof (nss_pnetgr_t)));
                        for (j = 0; j < NSS_NETGR_N; j++) {
                                ing->arg[j].argv = cv;
                                for (i = 0; i < ing->arg[j].argc; i++) {
                                        if (*uptr >= keysize)
                                                return (NSS_ERROR);
                                        *cv++ = (bptr + *uptr++);
                                }
                        }
                        ing->groups.argv = cv;
                        for (i = 0; i < ing->groups.argc; i++) {
                                if (*uptr >= keysize)
                                        return (NSS_ERROR);
                                *cv++ = (bptr + *uptr++);
                        }
                        break;
                case 'T':
                        sng = (struct nss_setnetgrent_args *)arg;
                        sng->netgroup = (const char *)bptr;
                        sng->iterator = 0;
                        break;

                default:
                        return (NSS_ERROR);
                }
        }
        return (NSS_SUCCESS);
}

static nss_status_t
nss_pinit_funcs(int index, nss_db_initf_t *initf, nss_str2ent_t *s2e)
{
        const char      *name;
        void            *htmp = NULL;
        void            *sym;
        static void     *handle = NULL;
        static mutex_t  handle_lock = DEFAULTMUTEX;
        static mutex_t  initf_lock = DEFAULTMUTEX;
        static mutex_t  s2e_lock = DEFAULTMUTEX;

        if (handle == NULL) {
                htmp = dlopen((const char *)0, RTLD_LAZY);

                lmutex_lock(&handle_lock);
                if (handle == NULL) {
                        if (htmp == NULL) {
                                lmutex_unlock(&handle_lock);
                                return (NSS_ERROR);
                        } else {
                                membar_producer();
                                handle = htmp;
                                htmp = NULL;
                        }
                }
                lmutex_unlock(&handle_lock);
                if (htmp)
                        (void) dlclose(htmp);
        }
        membar_consumer();

        if (initf) {
                if (getXbyY_to_dbop[index].initfnp == NULL) {
                        name = getXbyY_to_dbop[index].initfn;
                        if ((sym = dlsym(handle, name)) == NULL)
                                return (NSS_ERROR);
                        lmutex_lock(&initf_lock);
                        if (getXbyY_to_dbop[index].initfnp == NULL)
                                getXbyY_to_dbop[index].initfnp = sym;
                        membar_producer();
                        lmutex_unlock(&initf_lock);
                }
                membar_consumer();
                *initf = (nss_db_initf_t)getXbyY_to_dbop[index].initfnp;
        }
        if (s2e) {
                if (getXbyY_to_dbop[index].strfnp == NULL) {
                        name = getXbyY_to_dbop[index].strfn;
                        if ((sym = dlsym(handle, name)) == NULL)
                                return (NSS_ERROR);
                        lmutex_lock(&s2e_lock);
                        if (getXbyY_to_dbop[index].strfnp == NULL)
                                getXbyY_to_dbop[index].strfnp = sym;
                        membar_producer();
                        lmutex_unlock(&s2e_lock);
                }
                membar_consumer();
                *s2e = (nss_str2ent_t)getXbyY_to_dbop[index].strfnp;
        }

        return (NSS_SUCCESS);
}

nss_status_t
nss_packed_getkey(void *buffer, size_t length, char **dbname,
    int *dbop, nss_XbyY_args_t *arg)
{
        nss_pheader_t   *pbuf = (nss_pheader_t *)buffer;
        nss_dbd_t       *pdbd;
        nssuint_t       off, dbdsize;
        int             index;

        if (buffer == NULL || length == 0 || dbop == NULL ||
            arg == NULL || dbname == NULL)
                return (NSS_ERROR);

        *dbop = pbuf->nss_dbop;
        off = pbuf->dbd_off;
        pdbd = (nss_dbd_t *)((void *)((char *)buffer + off));
        dbdsize = pbuf->key_off - pbuf->dbd_off;
        if (pdbd->o_name >= dbdsize || pdbd->o_config_name >= dbdsize ||
            pdbd->o_default_config >= dbdsize)
                return (NSS_ERROR);
        *dbname = (char *)buffer + off + pdbd->o_name;
        if ((index = nss_dbop_search(*dbname, (uint32_t)*dbop)) < 0)
                return (NSS_ERROR);
        return (nss_upack_key2arg(buffer, length, dbname, dbop, arg, index));
}


/*
 * str2packent: Standard format interposed str2X function for normal APIs
 *
 * Return values: 0 = success, 1 = parse error, 2 = erange ...
 *
 * The structure pointer is ignored since this is a nscd side packed request.
 * The client side routine does all the real parsing; we just check limits and
 * store the entry in the buffer we were passed by the caller.
 */

static int
str2packent(
    const char *instr,
    int lenstr,
    void *ent __unused,         /* really (char *) */
    char *buffer,
    int buflen)
{
        if (buflen <= lenstr) {         /* not enough buffer */
                return (NSS_STR_PARSE_ERANGE);
        }
        (void) memmove(buffer, instr, lenstr);
        buffer[lenstr] = '\0';

        return (NSS_STR_PARSE_SUCCESS);
}

/*
 * Initialize db_root, initf, dbop and arg from a packed buffer
 */

nss_status_t
nss_packed_arg_init(void *buffer, size_t length,
    nss_db_root_t *db_root __unused,
    nss_db_initf_t *initf, int *dbop, nss_XbyY_args_t *arg)
{
        nss_pheader_t           *pbuf = (nss_pheader_t *)buffer;
        nss_str2ent_t           s2e = str2packent;
        nss_str2ent_t           real_s2e = NULL;
        nss_dbd_t               *pdbd;
        nssuint_t               off, dbdsize;
        char                    *dbname, *bptr;
        size_t                  len;
        int                     index;

        if (buffer == NULL || length == 0 ||
            dbop == NULL || arg == NULL)
                return (NSS_ERROR);

        /* init dbop */
        *dbop = pbuf->nss_dbop;
        off = pbuf->dbd_off;
        pdbd = (nss_dbd_t *)((void *)((char *)buffer + off));
        dbdsize = pbuf->key_off - pbuf->dbd_off;
        if (pdbd->o_name >= dbdsize || pdbd->o_config_name >= dbdsize ||
            pdbd->o_default_config >= dbdsize)
                return (NSS_ERROR);
        dbname = (char *)buffer + off + pdbd->o_name;
        if ((index = nss_dbop_search(dbname, (uint32_t)*dbop)) < 0)
                return (NSS_ERROR);

        /* db_root is initialized by nscd's based on door info */
        /* do nothing here */

        /* init key information - (and get dbname dbop etc...) */
        if (nss_upack_key2arg(buffer, length, &dbname,
            dbop, arg, index) != NSS_SUCCESS)
                return (NSS_ERROR);

        /* possible audituser init */
        if (strcmp(dbname, NSS_DBNAM_AUTHATTR) == 0)
                arg->h_errno = (int)pbuf->p_herrno;

        bptr = (char *)buffer + pbuf->data_off;
        len = (size_t)pbuf->data_len;

        /* sidestep odd arg cases */
        if (*dbop == NSS_DBOP_GROUP_BYMEMBER &&
            strcmp(dbname, NSS_DBNAM_GROUP) == 0) {
                /* get initf  and str2ent functions */
                if (nss_pinit_funcs(index, initf, &real_s2e) != NSS_SUCCESS)
                        return (NSS_ERROR);
                ((struct nss_groupsbymem *)arg)->str2ent = real_s2e;
                ((struct nss_groupsbymem *)arg)->process_cstr = process_cstr;
                return (NSS_SUCCESS);
        }
        if (pbuf->nss_dbop == NSS_DBOP_NETGROUP_IN &&
            strcmp(dbname, NSS_DBNAM_NETGROUP) == 0) {
                return (NSS_SUCCESS);
        }

        /* get initf  and str2ent functions */
        if (nss_pinit_funcs(index, initf, NULL) != NSS_SUCCESS)
                return (NSS_ERROR);

        /* init normal arg cases */
        NSS_XbyY_INIT(arg, NULL, bptr, len, s2e);
        arg->h_errno = 0;

        return (NSS_SUCCESS);
}

/*
 * Initialize db_root, initf, dbop, contextp and arg from a packed buffer
 */

nss_status_t
nss_packed_context_init(void *buffer, size_t length __unused,
    nss_db_root_t *db_root __unused,
    nss_db_initf_t *initf __unused, nss_getent_t **contextp __unused,
    nss_XbyY_args_t *arg)
{
        nss_pheader_t   *pbuf = (nss_pheader_t *)buffer;
        nss_str2ent_t   s2e = str2packent;
        char            *bptr;
        size_t          len;

        /* init arg */
        if (arg != NULL) {
                bptr = (char *)buffer + pbuf->data_off;
                len = (size_t)pbuf->data_len;
                NSS_XbyY_INIT(arg, NULL, bptr, len, s2e);
        }

        return (NSS_SUCCESS);
}