/* * 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 #include #include #include #include #include #include #include #include #include #include /* headers for key2str/str2key routines */ #include #include #include /* * 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. */ char ** _nss_netdb_aliases(const char *instr, int lenstr, char *buffer, int buflen) /* "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 */ { /* * 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 */ /*ARGSUSED*/ static nss_status_t nss_pack_key2str(void *buffer, size_t length, nss_XbyY_args_t *arg, const char *dbname, int dbop, 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)); } /*ARGSUSED*/ void nss_packed_set_status(void *buffer, size_t length, 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 */ /*ARGSUSED*/ static nss_status_t nss_upack_key2arg(void *buffer, size_t length, char **dbname, int *dbop, 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. */ /*ARGSUSED*/ static int str2packent( const char *instr, int lenstr, void *ent, /* 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 */ /*ARGSUSED*/ nss_status_t nss_packed_arg_init(void *buffer, size_t length, nss_db_root_t *db_root, 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 */ /*ARGSUSED*/ nss_status_t nss_packed_context_init(void *buffer, size_t length, nss_db_root_t *db_root, nss_db_initf_t *initf, nss_getent_t **contextp, 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); }