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
* 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
*
* 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;
struct nis_getnetgr_be {
/*
* 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];
};
typedef const char *ccp;
/* 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;
/* We already have the member-list; regurgitate it */
return (NSS_SUCCESS);
}
return (NSS_NOTFOUND);
}
static nss_status_t
getnetgr_get(be, a)
struct nis_getnetgr_be *be;
void *a;
{
} else {
enum nss_netgr_argn i;
for (i = 0; i < NSS_NETGR_N; i++) {
const char *str;
} else {
break;
/* %%% do we want to go to 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
struct nis_getnetgr_be *be;
void *dummy;
{
enum nss_netgr_argn i;
for (i = NSS_NETGR_MACHINE; i < NSS_NETGR_N; i++) {
}
}
}
be->all_members = 0;
be->next_member = 0;
}
return (NSS_SUCCESS);
}
/*ARGSUSED*/
static nss_status_t
struct nis_getnetgr_be *be;
void *dummy;
{
if (be != 0) {
(void) getnetgr_end(be, 0);
}
return (NSS_SUCCESS);
}
static nis_getnetgr_op_t getnetgr_ops[] = {
getnetgr_get, /* getnetgrent_r() */
};
#ifdef UNDEF /* <==== */
#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.
*
* 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;
};
#define HASHMOD 113
struct netgrtab {
struct netgrnam *expand_first;
struct netgrnam **expand_lastp;
int n_new;
int n_total;
};
static void
{
}
static struct netgrnam **
const char *name;
{
unsigned hashval;
size_t i;
if (namelen < 1)
return (nullpp);
((const unsigned char *)name)[i];
}
/* Found it */
break;
}
}
return (prevp);
}
static void
/* ==> ? Should return 'failed' (out-of-memory) status ? */
const char *name;
int breadth_first;
{
if (namelen < 1)
return;
if (*prevp != 0) { /* Already in table, do nothing */
return;
}
/* Create new netgrnam struct */
if (cur == 0) {
return; /* Out of memory, too bad */
}
/* Insert in hash table */
/* Insert in expansion list, at start (depth-first) or end (breadth) */
cur->expand_next = 0;
} else {
}
}
static const char *
{
return (0);
}
}
}
static void
{
int i;
for (i = 0; i < HASHMOD; i++) {
cur != 0; /* no action */) {
}
}
/* Don't bother zeroing pointers; must do init if we want to reuse */
}
static int
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
return (-1);
/* Turn a blind eye to the presence or absence of trailing periods */
--l1;
}
--l2;
}
return (FAIL);
} else if (l1 == 0) { /* Trivially a suffix; */
/* (do we want this case?) */
return (PASS);
}
/* So 0 < l1 <= l2 */
return (FAIL);
}
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
const char **groups;
int ngroups;
void *iter_args;
{
/*
* 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;
return (NSS_UNAVAIL);
}
return (NSS_UNAVAIL);
}
while (ngroups > 0) {
/* %%% check 4th arg value */
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 */
nis_result *r;
int i;
/* %%% was NETGR_NDX_NAME */
if (result != NSS_SUCCESS) {
#ifdef DEBUG
if (result == NSS_NOTFOUND) {
"innetgr: no such NIS+ netgroup as %s",
group);
} else {
"innetgr: nis_list returned [%s]",
(r == 0) ? "A null pointer !?" :
nis_sperrno(NIS_RES_STATUS(r)));
}
#endif /* DEBUG */
if (result != NSS_NOTFOUND)
if (r != 0) {
nis_freeresult(r);
}
continue;
}
nfound++;
for (i = 0; !done && i < NIS_RES_NUMOBJ(r); i++) {
char *val;
/* Not a netgroup entry !? */
continue;
}
/* Recursive member, not triple */
continue;
}
/* do */ { \
triple[triple_ndx] = 0; \
} else { \
} \
} /* while (0) */
/* 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) */
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
{
enum nss_netgr_argn i;
/* Out of memory */
return (0);
}
for (i = NSS_NETGR_MACHINE; i < NSS_NETGR_N; i++) {
if (trippp[i] == 0) {
/* Wildcard */
/* Out of memory. Free any we've allocated */
enum nss_netgr_argn j;
for (j = NSS_NETGR_MACHINE; j < i; j++) {
}
}
return (0);
}
}
return (1); /* Tell top_down() to keep iterating */
}
static nss_status_t
void *a;
{
struct nis_getnetgr_be *get_be;
if (get_be == 0) {
return (NSS_UNAVAIL);
}
get_be->all_members = 0;
&get_be->all_members);
if (res == NSS_SUCCESS) {
sizeof (getnetgr_ops[0]);
} else {
}
return (res);
}
/*
* Code for innetgr()
*/
/*
* Iterator function for innetgr(): Check whether triple matches args
*/
static int
struct nss_innetgr_args *ia;
{
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;
/* Wildcarded on one side or t'other */
continue;
}
switch (i) {
case NSS_NETGR_MACHINE:
cmpf = strcasecmp;
break;
case NSS_NETGR_USER:
break;
case NSS_NETGR_DOMAIN:
break;
}
for (n = 0; n < argc; n++) {
break;
}
}
if (n >= argc) {
/* Match failed, tell top_down() to keep looking */
return (1);
}
}
/* Matched on all three, so quit looking and declare victory */
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.
*/
static nss_status_t
struct nss_innetgr_args *args;
{
/* triples */
char *search_crit;
nis_result *r;
int i;
/* Copy NIS+ search value; ==== Should really do NIS+ quoting */
/*
* ====>
* remember to set errp in all cases;
* getting NOTFOUND vs SUCCESS/0 right may be expensive;
* should do negative caching of group-names.
*/
return (NSS_UNAVAIL);
}
return (NSS_UNAVAIL);
}
return (NSS_UNAVAIL);
}
do {
/* ==== ?? check validity ?? */
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.
*/
search_crit[0] = '\0';
users = search_crit;
do {
if (pusers != 0) {
if (nusers != 0) {
pusers++;
} /* Else look for null strings (wildcards) in table */
} /* Else accept any value */
do {
if (phosts != 0) {
if (nhosts != 0) {
phosts++;
}
}
switch (_nss_nisplus_list(search_crit, 0, &r)) {
case NSS_SUCCESS:
break;
case NSS_NOTFOUND:
nis_freeresult(r);
continue;
case NSS_TRYAGAIN:
goto wrapup;
default:
if (r != 0) {
nis_freeresult(r);
}
goto wrapup;
}
for (i = 0; i < NIS_RES_NUMOBJ(r); i++) {
char *val;
< NETGR_COL) {
/* Not a netgroup entry !? */
continue;
}
/* Recursive member, not triple */
continue;
}
if (ndomains != 0 &&
do {
== 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.
*/
/* Good day to buy a lottery ticket */
nis_freeresult(r);
goto wrapup;
}
}
nis_freeresult(r);
} while (nhosts-- != 0); /* decrements unsigned just */
} while (nusers-- != 0); /* past zero; sleazy but OK */
const char *group;
#ifdef SEARCH_DOWN_TOO
"[%s=%s]%s",
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:
goto wrapup;
default:
if (r != 0) {
nis_freeresult(r);
}
goto wrapup;
}
for (i = 0; i < NIS_RES_NUMOBJ(r); i++) {
char *val;
unsigned len;
< NETGR_COL) {
/* Not a netgroup entry? */
continue;
}
/* Triple, not recursive netgroup */
continue;
}
/* Eureka */
nis_freeresult(r);
goto wrapup;
}
}
} else {
#endif /* SEARCH_DOWN_TOO */
/* %%% was NETGR_NDX_GROUP */
"[%s=%s],%s",
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:
goto wrapup;
default:
nis_freeresult(r);
goto wrapup;
}
for (i = 0; i < NIS_RES_NUMOBJ(r); i++) {
char *val;
< NETGR_COL) {
/* Not a netgroup entry !? */
continue;
}
/* Eureka */
nis_freeresult(r);
goto wrapup;
}
}
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).
*/
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:
return (NSS_SUCCESS);
}
}
static nss_status_t
void *a;
{
} else {
}
}
/*ARGSUSED*/
static int
int nobj;
{
/*
* This should never get used in the netgroup backend;
* if it does, we should complain loudly.
*/
return (NSS_STR_PARSE_PARSE);
}
static nisplus_backend_op_t netgroup_ops[] = {
0, /* No setent; setnetgrent() is really a getXbyY() */
0, /* No getent in the normal sense */
netgr_in, /* innetgr() */
netgr_set, /* setnetgrent() */
};
/*ARGSUSED*/
{
return (_nss_nisplus_constr(netgroup_ops,
sizeof (netgroup_ops) / sizeof (netgroup_ops[0]),
}