getnetgrent.c revision cb5caa98562cf06753163f558cbcfe30b8f4673a
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
*
* nisplus/getnetgrent.c -- "nisplus" backend for nsswitch "netgroup"
* database
*
* The API for netgroups differs sufficiently from that for the average
* getXXXbyYYY function that we use very few of the support routines in
* nisplus_common.h.
*
* The implementation of setnetgrent()/getnetgrent() here follows the 4.x
* NIS (YP) code, inasmuch as the setnetgrent() routine does all the work
* of traversing the netgroup graph and building a (potentially large)
* list in memory, and getnetgrent() just steps down the list.
*
* An alternative, and probably better, implementation would lazy-eval
* the netgroup graph in response to getnetgrent() calls (though
* setnetgrent() should still check for the top-level netgroup name
* and return NSS_SUCCESS / NSS_NOTFOUND).
*
* ===> This file and the corresponding one in the "nis" backend contain
* substantial amounts of duplicate code. If there's a tidy way to
* share the code, we should make use of it.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include "nisplus_common.h"
#include "nisplus_tables.h"
#ifdef UNDEF /* <==== */
#include <ctype.h>
#include <malloc.h>
#endif /* UNDEF <==== */
/*
* The nss_backend_t for a getnetgrent() sequence; we actually give the
* netgroup frontend a pointer to one of these structures in response to
* a (successful) setnetgrent() call on the nis_netgr_be backend
* described further down in this file.
*/
/*
* These are really "nisplus_getnetgr..." rather than "nis_getnetgr...",
* but they turn out to be the same, so why fiddle with them?
*/
struct nis_getnetgr_be;
typedef nss_status_t (*nis_getnetgr_op_t)(struct nis_getnetgr_be *, void *);
struct nis_getnetgr_be {
nis_getnetgr_op_t *ops;
nss_dbop_t n_ops;
/*
* State for set/get/endnetgrent()
*/
char *netgroup;
struct grouplist *all_members;
struct grouplist *next_member;
};
struct grouplist { /* One element of the list generated by a setnetgrent() */
char *triple[NSS_NETGR_N];
struct grouplist *gl_nxt;
};
typedef const char *ccp;
typedef int (*top_down_lambda_t)(ccp triple[NSS_NETGR_N],
void *iter_args, nss_status_t *return_val);
static nss_status_t getnetgr_end(struct nis_getnetgr_be *be, void *dummy);
static nss_status_t top_down(nisplus_backend_ptr_t be,
const char **groups, int ngroups,
top_down_lambda_t func, void *iter_args);
/* these are not ANSI: */
extern char *strdup(const char *);
extern int strcasecmp(const char *, const char *);
extern int strncasecmp(const char *, const char *, size_t);
static nss_status_t
getnetgr_set(be, a)
struct nis_getnetgr_be *be;
void *a;
{
const char *netgroup = (const char *) a;
if (be->netgroup != 0 &&
strcmp(be->netgroup, netgroup) == 0) {
/* We already have the member-list; regurgitate it */
be->next_member = be->all_members;
return (NSS_SUCCESS);
}
return (NSS_NOTFOUND);
}
static nss_status_t
getnetgr_get(be, a)
struct nis_getnetgr_be *be;
void *a;
{
struct nss_getnetgrent_args *args = (struct nss_getnetgrent_args *)a;
struct grouplist *mem;
if ((mem = be->next_member) == 0) {
args->status = NSS_NETGR_NO;
} else {
char *buffer = args->buffer;
int buflen = args->buflen;
enum nss_netgr_argn i;
args->status = NSS_NETGR_FOUND;
for (i = 0; i < NSS_NETGR_N; i++) {
const char *str;
size_t len;
if ((str = mem->triple[i]) == 0) {
args->retp[i] = 0;
} else if ((len = strlen(str) + 1) <= buflen) {
args->retp[i] = buffer;
(void) memcpy(buffer, str, len);
buffer += len;
buflen -= len;
} else {
args->status = NSS_NETGR_NOMEM;
break;
/* %%% do we want to go to mem->gl_nxt? */
}
}
be->next_member = mem->gl_nxt;
}
return (NSS_SUCCESS); /* Yup, even for end-of-list, i.e. */
/* do NOT advance to next backend. */
}
/*ARGSUSED*/
static nss_status_t
getnetgr_end(be, dummy)
struct nis_getnetgr_be *be;
void *dummy;
{
struct grouplist *gl;
for (gl = be->all_members; gl != NULL; gl = gl->gl_nxt) {
enum nss_netgr_argn i;
for (i = NSS_NETGR_MACHINE; i < NSS_NETGR_N; i++) {
if (gl->triple[i] != 0) {
free(gl->triple[i]);
}
}
free(gl);
}
be->all_members = 0;
be->next_member = 0;
if (be->netgroup != 0) {
free(be->netgroup);
be->netgroup = 0;
}
return (NSS_SUCCESS);
}
/*ARGSUSED*/
static nss_status_t
getnetgr_destr(be, dummy)
struct nis_getnetgr_be *be;
void *dummy;
{
if (be != 0) {
(void) getnetgr_end(be, 0);
free(be);
}
return (NSS_SUCCESS);
}
static nis_getnetgr_op_t getnetgr_ops[] = {
getnetgr_destr,
getnetgr_end,
getnetgr_set,
getnetgr_get, /* getnetgrent_r() */
};
#ifdef UNDEF /* <==== */
#include <rpcsvc/nis.h>
#include <syslog.h>
#include <nsswitch.h>
#include "boundary.h"
#include "nisplus_common.h"
#include "nisplus_tables.h"
#endif /* UNDEF <==== */
/*
* Code to do top-down search in the graph defined by the 'netgroup' YP map
* ===> ^^^^^^^^ only half of the truth
*/
/*
* ===> This code is now used for setnetgrent(), not just innetgr().
* ===> need to document every_which_way too.
*
* If the easy way doesn't pan out, recursively search the 'netgroup' map.
* In order to do this, we:
*
* - remember all the netgroup names we've seen during this search,
* whether or not we've expanded them yet (we want fast insertion
* with duplicate-detection, so use yet another chained hash table),
*
* - keep a list of all the netgroups we haven't expanded yet (we just
* want fast insertion and pop-first, so a linked list will do fine).
* If we insert at the head, we get a depth-first search; insertion
* at the tail gives breadth-first (?), which seems preferable (?).
*
* A netgrnam struct contains pointers for both the hash-table and the list.
* It also contains the netgroup name; note that we embed the name at the
* end of the structure rather than holding a pointer to yet another
* malloc()ed region.
*
* A netgrtab structure contains the hash-chain heads and the head/tail
* pointers for the expansion list.
*
* Most of this code is common to at least the NIS and NIS+ backends; it
* should be generalized and, presumably, moved into the frontend.
* ==> Not any longer...
*/
struct netgrnam {
struct netgrnam *hash_chain;
struct netgrnam *expand_next;
char name[1]; /* Really [strlen(name) + 1] */
};
#define HASHMOD 113
struct netgrtab {
struct netgrnam *expand_first;
struct netgrnam **expand_lastp;
int n_new;
int n_total;
struct netgrnam *hash_heads[HASHMOD];
};
static void
ngt_init(ngt)
struct netgrtab *ngt;
{
(void) memset((void *)ngt, 0, sizeof (*ngt));
ngt->expand_lastp = &ngt->expand_first;
}
/* === ? Change ngt_init() and ngt_destroy() to malloc/free struct netgrtab */
static struct netgrnam **
ngt_prevp(ngt, name, namelen)
struct netgrtab *ngt;
const char *name;
size_t namelen;
{
unsigned hashval;
size_t i;
struct netgrnam *cur;
struct netgrnam **prevp;
static struct netgrnam *nullp = NULL;
static struct netgrnam **nullpp = &nullp;
if (namelen < 1)
return (nullpp);
for (hashval = 0, i = 0; i < namelen; i++) {
hashval = (hashval << 2) + hashval +
((const unsigned char *)name)[i];
}
for (prevp = &ngt->hash_heads[hashval % HASHMOD];
(cur = *prevp) != 0; prevp = &cur->hash_chain) {
if (strncmp(cur->name, name, namelen) == 0 &&
cur->name[namelen] == 0) {
/* Found it */
break;
}
}
return (prevp);
}
#define ngt_found(ngt, name, namelen) (*ngt_prevp(ngt, name, namelen) != 0)
static void
/* ==> ? Should return 'failed' (out-of-memory) status ? */
ngt_insert(ngt, name, namelen, breadth_first)
struct netgrtab *ngt;
const char *name;
size_t namelen;
int breadth_first;
{
struct netgrnam *cur;
struct netgrnam **prevp;
if (namelen < 1)
return;
prevp = ngt_prevp(ngt, name, namelen);
if (*prevp != 0) { /* Already in table, do nothing */
return;
}
#define dummy ((struct netgrnam *)0)
/* Create new netgrnam struct */
cur = (struct netgrnam *)
malloc(namelen + 1 + (char *)&dummy->name[0] - (char *)dummy);
if (cur == 0) {
return; /* Out of memory, too bad */
}
(void) memcpy(cur->name, name, namelen);
cur->name[namelen] = 0;
/* Insert in hash table */
cur->hash_chain = *prevp;
*prevp = cur;
/* Insert in expansion list, at start (depth-first) or end (breadth) */
if (breadth_first || ngt->expand_first == 0) {
cur->expand_next = 0;
*ngt->expand_lastp = cur;
ngt->expand_lastp = &cur->expand_next;
} else {
cur->expand_next = ngt->expand_first;
ngt->expand_first = cur;
}
ngt->n_new++;
ngt->n_total++;
}
static const char *
ngt_next(ngt)
struct netgrtab *ngt;
{
struct netgrnam *first;
if ((first = ngt->expand_first) == 0) {
return (0);
}
if ((ngt->expand_first = first->expand_next) == 0) {
ngt->expand_lastp = &ngt->expand_first;
}
ngt->n_new--;
return (first->name);
}
static void
ngt_destroy(ngt)
struct netgrtab *ngt;
{
struct netgrnam *cur;
struct netgrnam *next;
int i;
for (i = 0; i < HASHMOD; i++) {
for (cur = ngt->hash_heads[i];
cur != 0; /* no action */) {
next = cur->hash_chain;
free(cur);
cur = next;
}
}
/* Don't bother zeroing pointers; must do init if we want to reuse */
}
static int
domcmp(n1, n2)
const char *n1; /* See if n1 is n2 or an ancestor of it */
const char *n2; /* (in string terms, n1 is a suffix of n2) */
/* Returns ZERO for success, -1 for failure */
{
#define PASS 0
#define FAIL -1
size_t l1, l2;
if ((n1 == NULL) || (n2 == NULL))
return (-1);
l1 = strlen(n1);
l2 = strlen(n2);
/* Turn a blind eye to the presence or absence of trailing periods */
if (l1 != 0 && n1[l1 - 1] == '.') {
--l1;
}
if (l2 != 0 && n2[l2 - 1] == '.') {
--l2;
}
if (l1 > l2) { /* Can't be a suffix */
return (FAIL);
} else if (l1 == 0) { /* Trivially a suffix; */
/* (do we want this case?) */
return (PASS);
}
/* So 0 < l1 <= l2 */
if (l1 < l2 && n2[l2 - l1 - 1] != '.') {
return (FAIL);
}
if (strncasecmp(n1, &n2[l2 - l1], l1) == 0) {
return (PASS);
} else {
return (FAIL);
}
}
/*
* top_down() -- do innetgr() search down from netgroups toward triples.
*
* We use this when:
*
* - we're executing a setnetgrent() and so have to enumerate all
* the triples that belong to a given netgroup,
*
* - we're executing a __multi_innetgr() and believe that this will
* perform better than the every_which_way() routine below
* (e.g. both 'hosts' and 'users' are wildcarded).
*/
static nss_status_t
top_down(be, groups, ngroups, func, iter_args)
nisplus_backend_ptr_t be;
const char **groups;
int ngroups;
top_down_lambda_t func;
void *iter_args;
{
struct netgrtab *ngt;
/*
* netgrtab goes on the heap, not the stack, because it's large and
* stacks may not be all that big in multi-threaded programs.
* The same goes for search_crit[].
* ===> Should we just put them both in the backend struct and
* be done with it?
*/
char *search_crit;
const char *group;
int nfound;
int done;
nss_status_t result;
if ((search_crit = (char *)malloc(NIS_MAXNAMELEN)) == 0) {
return (NSS_UNAVAIL);
}
if ((ngt = (struct netgrtab *)malloc(sizeof (*ngt))) == 0) {
free(search_crit);
return (NSS_UNAVAIL);
}
ngt_init(ngt);
while (ngroups > 0) {
/* %%% check 4th arg value */
ngt_insert(ngt, *groups, strlen(*groups), 1);
groups++;
ngroups--;
}
done = 0; /* Set to 1 to indicate that we cut the iteration */
/* short (and 'result' holds the return value) */
nfound = 0; /* Number of successful netgroup nis_list calls */
while (!done && (group = ngt_next(ngt)) != 0) {
nis_result *r;
int i;
/* %%% was NETGR_NDX_NAME */
(void) snprintf(search_crit, NIS_MAXNAMELEN, "[%s=%s]%s",
NETGR_TAG_NAME, group, be->table_name);
result = _nss_nisplus_list(search_crit, 0, &r);
if (result != NSS_SUCCESS) {
#ifdef DEBUG
if (result == NSS_NOTFOUND) {
syslog(LOG_WARNING,
"innetgr: no such NIS+ netgroup as %s",
group);
} else {
syslog(LOG_WARNING,
"innetgr: nis_list returned [%s]",
(r == 0) ? "A null pointer !?" :
nis_sperrno(NIS_RES_STATUS(r)));
}
#endif /* DEBUG */
if (result != NSS_NOTFOUND)
done = 1; /* Give up, return result */
if (r != 0) {
nis_freeresult(r);
}
continue;
}
nfound++;
for (i = 0; !done && i < NIS_RES_NUMOBJ(r); i++) {
ccp triple[NSS_NETGR_N];
nis_object *obj = NIS_RES_OBJECT(r) + i;
char *val;
size_t len;
if (__type_of(obj) != NIS_ENTRY_OBJ ||
obj->EN_data.en_cols.en_cols_len < NETGR_COL) {
/* Not a netgroup entry !? */
continue;
}
len = ENTRY_LEN(obj, NETGR_NDX_GROUP);
val = ENTRY_VAL(obj, NETGR_NDX_GROUP);
if (len != 0 && val[0] != '\0') {
/* Recursive member, not triple */
ngt_insert(ngt, val, len - 1, 0);
continue;
}
#define COPY_NAME(entry_ndx, triple_ndx) \
/* do */ { \
len = ENTRY_LEN(obj, entry_ndx); \
val = ENTRY_VAL(obj, entry_ndx); \
if (len == 0 || val[0] == '\0') { \
triple[triple_ndx] = 0; \
} else { \
triple[triple_ndx] = val; \
} \
} /* while (0) */
COPY_NAME(NETGR_NDX_HOST, NSS_NETGR_MACHINE);
COPY_NAME(NETGR_NDX_USER, NSS_NETGR_USER);
COPY_NAME(NETGR_NDX_DOMAIN, NSS_NETGR_DOMAIN);
if (!(*func)(triple, iter_args, &result)) {
/* Return result, good or bad */
done = 1;
break;
}
}
/* End of inner loop over NIS_RES_OBJECT(r)[] */
nis_freeresult(r);
}
/* End of outer loop (!done && ngt_next(ngt) != 0) */
free(search_crit);
ngt_destroy(ngt);
free(ngt);
if (done) {
return (result);
} else if (nfound > 0) {
/* ==== ? Should only do this if all the top-level groups */
/* exist in NIS+? */
return (NSS_SUCCESS);
} else {
return (NSS_NOTFOUND);
}
}
/*
* Code for setnetgrent()
*/
/*
* Iterator function for setnetgrent(): copy triple, add to be->all_members
*/
static int
save_triple(trippp, headp, return_val)
ccp trippp[NSS_NETGR_N];
struct grouplist **headp;
nss_status_t *return_val;
{
struct grouplist *gl;
enum nss_netgr_argn i;
if ((gl = (struct grouplist *)malloc(sizeof (*gl))) == 0) {
/* Out of memory */
*return_val = NSS_UNAVAIL;
return (0);
}
for (i = NSS_NETGR_MACHINE; i < NSS_NETGR_N; i++) {
if (trippp[i] == 0) {
/* Wildcard */
gl->triple[i] = 0;
} else if ((gl->triple[i] = strdup(trippp[i])) == 0) {
/* Out of memory. Free any we've allocated */
enum nss_netgr_argn j;
for (j = NSS_NETGR_MACHINE; j < i; j++) {
if (gl->triple[j] != 0) {
free(gl->triple[j]);
}
}
*return_val = NSS_UNAVAIL;
return (0);
}
}
gl->gl_nxt = *headp;
*headp = gl;
return (1); /* Tell top_down() to keep iterating */
}
static nss_status_t
netgr_set(be, a)
nisplus_backend_ptr_t be;
void *a;
{
struct nss_setnetgrent_args *args = (struct nss_setnetgrent_args *)a;
struct nis_getnetgr_be *get_be;
nss_status_t res;
get_be = (struct nis_getnetgr_be *)malloc(sizeof (*get_be));
if (get_be == 0) {
return (NSS_UNAVAIL);
}
get_be->all_members = 0;
res = top_down(be, &args->netgroup, 1, (top_down_lambda_t)save_triple,
&get_be->all_members);
if (res == NSS_SUCCESS) {
get_be->ops = getnetgr_ops;
get_be->n_ops = sizeof (getnetgr_ops) /
sizeof (getnetgr_ops[0]);
get_be->netgroup = strdup(args->netgroup);
get_be->next_member = get_be->all_members;
args->iterator = (nss_backend_t *)get_be;
} else {
args->iterator = 0;
free(get_be);
}
return (res);
}
/*
* Code for innetgr()
*/
/*
* Iterator function for innetgr(): Check whether triple matches args
*/
static int
match_triple(triple, ia, return_val)
ccp triple[NSS_NETGR_N];
struct nss_innetgr_args *ia;
nss_status_t *return_val;
{
enum nss_netgr_argn i;
for (i = NSS_NETGR_MACHINE; i < NSS_NETGR_N; i++) {
int (*cmpf)(const char *, const char *);
char **argv;
int n;
const char *name = triple[i];
int argc = ia->arg[i].argc;
if (argc == 0 || name == 0) {
/* Wildcarded on one side or t'other */
continue;
}
argv = ia->arg[i].argv;
switch (i) {
case NSS_NETGR_MACHINE:
cmpf = strcasecmp;
break;
case NSS_NETGR_USER:
cmpf = strcmp;
break;
case NSS_NETGR_DOMAIN:
cmpf = domcmp;
break;
}
for (n = 0; n < argc; n++) {
if ((*cmpf)(argv[n], name) == 0) {
break;
}
}
if (n >= argc) {
/* Match failed, tell top_down() to keep looking */
return (1);
}
}
/* Matched on all three, so quit looking and declare victory */
ia->status = NSS_NETGR_FOUND;
*return_val = NSS_SUCCESS;
return (0);
}
/*
* every_which_way() -- assumes that at least one of 'hosts' and 'users' is
* NOT wildcarded. This makes it reasonable to start searching from
* the bottom (i.e. from triples up to groups). It's easy enough to
* search from top and bottom toward the middle, but for now we just
* search bottom-up. One reason for doing this is that mountd hands us
* long lists of putative netgroups, most of which are in fact hosts;
* it would be a shame to pay for a NIS+ lookup on each bogus netgroup.
*/
#undef SEARCH_DOWN_TOO
static nss_status_t
every_which_way(be, args)
nisplus_backend_ptr_t be;
struct nss_innetgr_args *args;
{
struct netgrtab *top; /* Search down from netgroup names */
struct netgrtab *bottom; /* Search up from (host,user,dom) */
/* triples */
char *search_crit;
nss_innetgr_argc ngroups, nhosts, nusers, ndomains;
nss_innetgr_argv pgroups, phosts, pusers, pdomains;
char *hosts, *users;
nis_result *r;
int i;
enum { BAD, BAD_NSS_TRYAGAIN, NO, YES } verdict;
/* Copy NIS+ search value; ==== Should really do NIS+ quoting */
#define catquoted(to, from) (void) strcat(to, from)
/*
* ====>
* remember to set errp in all cases;
* getting NOTFOUND vs SUCCESS/0 right may be expensive;
* should do negative caching of group-names.
*/
if ((search_crit = (char *)malloc(NIS_MAXNAMELEN)) == 0) {
return (NSS_UNAVAIL);
}
if ((top = (struct netgrtab *)malloc(sizeof (*top))) == 0) {
free(search_crit);
return (NSS_UNAVAIL);
}
if ((bottom = (struct netgrtab *)malloc(sizeof (*bottom))) == 0) {
free(search_crit);
free(top);
return (NSS_UNAVAIL);
}
ngt_init(top);
ngroups = args->groups.argc; /* Guaranteed >= 1 */
pgroups = args->groups.argv;
do {
/* ==== ?? check validity ?? */
ngt_insert(top, *pgroups, strlen(*pgroups), 1);
pgroups++;
ngroups--;
} while (ngroups != 0);
/* ==== if none valid, bag it */
/*
* For users and hosts:
* if (argc == 0) {
* wild-card (i.e. don't specify search criterion)
* } else {
* argc: try argv[0]
* argc-1: try argv[1]
* ...
* 1: try argv[argc - 1]
* 0: try the null string (wildcard in the table)
* }
* For domains we want the same logical behaviour, but do it
* differently: since the odds are that every entry in the
* netgroup table has domain set to either the null string or one
* value, we eschew search criteria and do the filtering at our end.
*
* Similarly, we don't use search criteria to check that the 'group'
* column is null; we check it on the client side.
*/
ngt_init(bottom);
search_crit[0] = '\0';
users = search_crit;
nusers = args->arg[NSS_NETGR_USER].argc; /* 0 ==> wildcard */
pusers = nusers ? args->arg[NSS_NETGR_USER].argv : 0;
do {
if (pusers != 0) {
(void) strcpy(users, ",user=");
if (nusers != 0) {
catquoted(users, *pusers);
pusers++;
} /* Else look for null strings (wildcards) in table */
} /* Else accept any value */
hosts = users + strlen(users);
nhosts = args->arg[NSS_NETGR_MACHINE].argc;
phosts = nhosts ? args->arg[NSS_NETGR_MACHINE].argv : 0;
do {
if (phosts != 0) {
(void) strcpy(hosts, ",host=");
if (nhosts != 0) {
catquoted(hosts, *phosts);
phosts++;
}
}
search_crit[0] = '['; /* Was temporarily a comma */
(void) strcat(search_crit, "]");
(void) strcat(search_crit, be->table_name);
switch (_nss_nisplus_list(search_crit, 0, &r)) {
case NSS_SUCCESS:
break;
case NSS_NOTFOUND:
nis_freeresult(r);
continue;
case NSS_TRYAGAIN:
verdict = BAD_NSS_TRYAGAIN;
goto wrapup;
default:
if (r != 0) {
nis_freeresult(r);
}
verdict = BAD;
goto wrapup;
}
for (i = 0; i < NIS_RES_NUMOBJ(r); i++) {
nis_object *obj = NIS_RES_OBJECT(r) + i;
char *val;
size_t len;
if (__type_of(obj) != NIS_ENTRY_OBJ ||
obj->EN_data.en_cols.en_cols_len
< NETGR_COL) {
/* Not a netgroup entry !? */
continue;
}
len = ENTRY_LEN(obj, NETGR_NDX_GROUP);
val = ENTRY_VAL(obj, NETGR_NDX_GROUP);
if (len != 0 && val[0] != '\0') {
/* Recursive member, not triple */
continue;
}
ndomains = args->arg[NSS_NETGR_DOMAIN].argc;
pdomains = args->arg[NSS_NETGR_DOMAIN].argv;
len = ENTRY_LEN(obj, NETGR_NDX_DOMAIN);
val = ENTRY_VAL(obj, NETGR_NDX_DOMAIN);
if (ndomains != 0 &&
len != 0 && val[0] != '\0') {
do {
if (domcmp(val, *pdomains++)
== 0) {
/* Got a match */
break;
}
} while (--ndomains != 0);
if (ndomains == 0) {
/* better luck with next obj */
continue;
}
}
/*
* Got one, either by wildcarding (one of the
* "...!= 0" tests failed) or by a match
* in the domain list.
*/
len = ENTRY_LEN(obj, NETGR_NDX_NAME);
val = ENTRY_VAL(obj, NETGR_NDX_NAME);
if (ngt_found(top, val, len - 1)) {
/* Good day to buy a lottery ticket */
nis_freeresult(r);
verdict = YES;
goto wrapup;
}
ngt_insert(bottom, val, len - 1, 1);
}
nis_freeresult(r);
} while (nhosts-- != 0); /* decrements unsigned just */
} while (nusers-- != 0); /* past zero; sleazy but OK */
while (top->n_new != 0 && bottom->n_new != 0) {
const char *group;
#ifdef SEARCH_DOWN_TOO
if (top->n_total < bottom->n_total /* and a fudge factor? */) {
group = ngt_next(top);
(void) snprintf(search_crit, NIS_MAXNAMELEN,
"[%s=%s]%s",
NETGR_NDX_NAME, group, be->table_name);
switch (_nss_nisplus_list(search_crit, 0, &r)) {
case NSS_SUCCESS:
break;
case NSS_NOTFOUND:
/* sysadmin SNAFU? This group doesn't exist */
nis_freeresult(r);
continue;
case NSS_TRYAGAIN:
verdict = BAD_NSS_TRYAGAIN;
goto wrapup;
default:
if (r != 0) {
nis_freeresult(r);
}
verdict = BAD;
goto wrapup;
}
for (i = 0; i < NIS_RES_NUMOBJ(r); i++) {
nis_object *obj = NIS_RES_OBJECT(r) + i;
char *val;
unsigned len;
if (__type_of(obj) != NIS_ENTRY_OBJ ||
obj->EN_data.en_cols.en_cols_len
< NETGR_COL) {
/* Not a netgroup entry? */
continue;
}
len = ENTRY_LEN(obj, NETGR_NDX_GROUP);
val = ENTRY_VAL(obj, NETGR_NDX_GROUP);
if (len == 0 || val[0] == '\0') {
/* Triple, not recursive netgroup */
continue;
}
if (ngt_found(bottom, val, len - 1)) {
/* Eureka */
nis_freeresult(r);
verdict = YES;
goto wrapup;
}
ngt_insert(top, val, len - 1, 1);
}
} else {
#endif /* SEARCH_DOWN_TOO */
group = ngt_next(bottom);
/* %%% was NETGR_NDX_GROUP */
(void) snprintf(search_crit, NIS_MAXNAMELEN,
"[%s=%s],%s",
NETGR_TAG_GROUP, group, be->table_name);
switch (_nss_nisplus_list(search_crit, 0, &r)) {
case NSS_SUCCESS:
break;
case NSS_NOTFOUND:
/* This group isn't included in any others */
nis_freeresult(r);
continue;
case NSS_TRYAGAIN:
verdict = BAD_NSS_TRYAGAIN;
goto wrapup;
default:
nis_freeresult(r);
verdict = BAD;
goto wrapup;
}
for (i = 0; i < NIS_RES_NUMOBJ(r); i++) {
nis_object *obj = NIS_RES_OBJECT(r) + i;
char *val;
size_t len;
if (__type_of(obj) != NIS_ENTRY_OBJ ||
obj->EN_data.en_cols.en_cols_len
< NETGR_COL) {
/* Not a netgroup entry !? */
continue;
}
len = ENTRY_LEN(obj, NETGR_NDX_NAME);
val = ENTRY_VAL(obj, NETGR_NDX_NAME);
if (ngt_found(top, val, len - 1)) {
/* Eureka */
nis_freeresult(r);
verdict = YES;
goto wrapup;
}
ngt_insert(bottom, val, len - 1, 0);
}
nis_freeresult(r);
#ifdef SEARCH_DOWN_TOO
}
#endif /* SEARCH_DOWN_TOO */
}
/*
* We arrive here if we didn't find any matches, i.e. none of the
* initial triples are in any of the initial groups. This could
* happen because either:
*
* (1) none of the initial groups are stored in NIS+, in which
* case we should return NSS_NOTFOUND so that the switch
* will try the next source (if any),
*
* or (2) at least one of the initial groups is stored in NIS+,
* in which case we should set ia->status = NETGR_NO and
* return NSS_SUCCESS ("don't try any more sources").
*
* However, distinguishing between these cases is expensive and quite
* likely to be wasted effort, so we'll pretend they're both (1).
*/
verdict = NO;
wrapup:
free(search_crit);
ngt_destroy(top);
free(top);
ngt_destroy(bottom);
free(bottom);
switch (verdict) {
case BAD:
default: /* keep gcc happy */
return (NSS_UNAVAIL);
case BAD_NSS_TRYAGAIN:
return (NSS_TRYAGAIN);
case NO:
return (NSS_NOTFOUND);
case YES:
args->status = NSS_NETGR_FOUND;
return (NSS_SUCCESS);
}
}
static nss_status_t
netgr_in(be, a)
nisplus_backend_ptr_t be;
void *a;
{
struct nss_innetgr_args *ia = (struct nss_innetgr_args *)a;
ia->status = NSS_NETGR_NO;
if (ia->arg[NSS_NETGR_MACHINE].argc == 0 &&
ia->arg[NSS_NETGR_USER].argc == 0) {
return (top_down(be, (const char **)ia->groups.argv,
ia->groups.argc,
(top_down_lambda_t)match_triple, ia));
} else {
return (every_which_way(be, ia));
}
}
/*ARGSUSED*/
static int
bogus_object2str(nobj, obj, be, argp)
int nobj;
nis_object *obj;
nisplus_backend_ptr_t be;
nss_XbyY_args_t *argp;
{
/*
* This should never get used in the netgroup backend;
* if it does, we should complain loudly.
*/
syslog(LOG_ERR, "logic bug in nsswitch nisplus/netgroup backend");
return (NSS_STR_PARSE_PARSE);
}
static nisplus_backend_op_t netgroup_ops[] = {
_nss_nisplus_destr,
0, /* No endent, because no setent/getent */
0, /* No setent; setnetgrent() is really a getXbyY() */
0, /* No getent in the normal sense */
netgr_in, /* innetgr() */
netgr_set, /* setnetgrent() */
};
/*ARGSUSED*/
nss_backend_t *
_nss_nisplus_netgroup_constr(dummy1, dummy2, dummy3)
const char *dummy1, *dummy2, *dummy3;
{
return (_nss_nisplus_constr(netgroup_ops,
sizeof (netgroup_ops) / sizeof (netgroup_ops[0]),
NETGR_TBLNAME, bogus_object2str));
}