Index: include/nsswitch.h =================================================================== RCS file: /home/ncvs/src/include/nsswitch.h,v retrieving revision 1.3 diff -u -b -B -u -p -r1.3 nsswitch.h --- include/nsswitch.h 17 Apr 2003 14:14:21 -0000 1.3 +++ include/nsswitch.h 14 May 2007 15:18:30 -0000 @@ -68,6 +68,7 @@ #define NSSRC_DNS "dns" /* DNS; IN for hosts, HS for others */ #define NSSRC_NIS "nis" /* YP/NIS */ #define NSSRC_COMPAT "compat" /* passwd,group in YP compat mode */ +#define NSSRC_ALL "*" /* All sources, used for fallbacks */ /* * currently implemented databases @@ -220,6 +221,24 @@ typedef struct _ns_mod { nss_module_unregister_fn unregister; /* called to unload module */ } ns_mod; +/* + * ns_fbtab `method' function signature. + */ +typedef int (*nss_fbmethod)(const ns_src *source, void *_retval, + void *_mdata, va_list _ap); + +/* + * ns_fbtab - `nsswitch fallback table' + * Contains an entry for each source and the appropriate function to + * call. ns_dtabs are used in the nsdispatch() API in order to allow + * the application to override built-in actions. + */ +typedef struct _ns_fbtab { + const char *src; /* Source this entry implements */ + nss_fbmethod method; /* Method to be called */ + void *mdata; /* Data passed to method */ +} ns_fbtab; + #endif /* _NS_PRIVATE */ @@ -230,6 +249,14 @@ extern int nsdispatch(void *, const ns_d const char *, const ns_src [], ...); #ifdef _NS_PRIVATE +extern int _nsdispatch_with_fb(void *, const ns_dtab [], + const ns_fbtab [], const char *, + const char *, const ns_src [], ...); +extern int _nsdispatch_callmethod(int*, const ns_src *, + const char *, const char *, + const ns_dtab [], const ns_fbtab [], + void *, void *, ...); + extern void _nsdbtaddsrc(ns_dbt *, const ns_src *); extern void _nsdbtput(const ns_dbt *); extern void _nsyyerror(const char *); Index: lib/libc/gen/getgrent.c =================================================================== RCS file: /home/ncvs/src/lib/libc/gen/getgrent.c,v retrieving revision 1.32.8.3 diff -u -b -B -u -p -r1.32.8.3 getgrent.c --- lib/libc/gen/getgrent.c 8 Oct 2006 05:45:57 -0000 1.32.8.3 +++ lib/libc/gen/getgrent.c 14 May 2007 15:18:30 -0000 @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/get #include #endif #include +#define _NS_PRIVATE #include #include #include @@ -54,6 +55,7 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/get #include #include #include +#include #include "un-namespace.h" #include "libc_private.h" #include "nss_tls.h" @@ -1070,6 +1069,188 @@ fin: fseeko(st->fp, pos, SEEK_SET); return (rv); #undef set_lookup_type +} + +static int +__gr_addgid(gid_t gid, gid_t *groups, int maxgrp, int *groupc) +{ + int ret, dupc; + + /* skip duplicates */ + for (dupc = 0; dupc < MIN(maxgrp, *groupc); dupc++) { + if (groups[dupc] == gid) + return 1; + } + + ret = 1; + if (*groupc < maxgrp) + /* add gid */ + groups[*groupc] = gid; + else + ret = 0; + + (*groupc)++; + + return ret; +} + +/* + * Fallback function for sources which don't have the getgroupmembership + * method. It uses the old and inefficient method of looping through all groups + * and their members. + */ +static int +getgroupmembership_fallback(const ns_src *source, + void *rv, void *mdata, va_list ap) +{ + static const ns_dtab dtab_getgrent[] = { + { NSSRC_FILES, files_group, (void *)nss_lt_all }, +#ifdef HESIOD + { NSSRC_DNS, dns_group, (void *)nss_lt_all }, +#endif +#ifdef YP + { NSSRC_NIS, nis_group, (void *)nss_lt_all }, +#endif + { NSSRC_COMPAT, compat_group, (void *)nss_lt_all }, + { NULL, NULL, NULL } + }; + + int *retval = va_arg(ap, int *); + const char *uname = va_arg(ap, const char *); + gid_t group = va_arg(ap, gid_t); + gid_t *groups = va_arg(ap, gid_t *); + int limit = va_arg(ap, int); + int *size = va_arg(ap, int *); + + struct group grp; + struct group *grp_p; + int i; + int lrv; + int lresult; + int lretval; + char *lbuf; + size_t lbufsize; + int ret_errno; + + lbuf = malloc(GRP_STORAGE_INITIAL); + if (lbuf == NULL) { + lrv = NS_UNAVAIL; + goto out; + } + lbufsize = GRP_STORAGE_INITIAL; + + /* Rewind */ + lrv = _nsdispatch_callmethod(&lresult, source, + NSDB_GROUP, "setgrent", + NULL, NULL, + &lretval, NULL, NULL); + + /* + * When installing primary group, duplicate it; + * the first element of groups is the effective gid + * and will be overwritten when a setgid file is executed. + */ + __gr_addgid(group, groups, limit, size); + + + *retval = 0; + + /* + * Scan source to find additional groups. + */ + while (1) { + /* We can't use getgrent() here because it wouldn't be safe for + * multithreaded programs. + */ + do { + ret_errno = 0; + grp_p = NULL; + lrv = _nsdispatch_callmethod(&lresult, source, + NSDB_GROUP, "getgrent_r", + dtab_getgrent, NULL, + &grp_p, (void *)nss_lt_all, &grp, lbuf, lbufsize, &ret_errno); + + if (grp_p == NULL && ret_errno == ERANGE) { + free(lbuf); + + if ((lbufsize << 1) > GRP_STORAGE_MAX) { + lbuf = NULL; + errno = ERANGE; + lrv = NS_UNAVAIL; + goto out; + } + + lbufsize <<= 1; + lbuf = malloc(lbufsize); + if (lbuf == NULL) { + lrv = NS_UNAVAIL; + goto out; + } + } + } while (grp_p == NULL && ret_errno == ERANGE); + + if (ret_errno != 0) { + errno = ret_errno; + lrv = NS_UNAVAIL; + goto out; + } + + if (grp_p == NULL) + break; + + /* Loop through group members */ + for (i = 0; grp.gr_mem[i]; i++) { + if (strcmp(grp.gr_mem[i], uname) == 0) { + if (!__gr_addgid(grp.gr_gid, groups, limit, size)) { + *retval = -1; + } + } + } + } + + /* Close database */ + lrv = _nsdispatch_callmethod(&lresult, source, + NSDB_GROUP, "endgrent", + NULL, NULL, + &lretval, NULL, NULL); + + lrv = NS_NOTFOUND; + +out: + free(lbuf); + + return lrv; +} + +/* + * getgroupmembership is about the same as getgrouplist(3), except it has a + * different interface that supports being dispatched by nss. + */ +int +getgroupmembership(const char *uname, gid_t agroup, gid_t *groups, + int maxgrp, int *groupc) +{ + static const ns_fbtab fallback[] = { + { NSSRC_ALL, getgroupmembership_fallback, NULL }, + { NULL, NULL, NULL } + }; + int rv, rerror, retval; + + assert(uname != NULL); + /* groups may be NULL if just sizing when invoked with maxgrp = 0 */ + assert(groupc != NULL); + + *groupc = 0; + + rv = _nsdispatch_with_fb(&retval, NULL, fallback, + NSDB_GROUP, "getgroupmembership", defaultsrc, + &rerror, uname, agroup, groups, maxgrp, groupc); + + /* too many groups found? */ + if (*groupc > maxgrp) + return -1; + else + return 0; } Index: lib/libc/gen/getgrouplist.c =================================================================== RCS file: /home/ncvs/src/lib/libc/gen/getgrouplist.c,v retrieving revision 1.14 diff -u -b -B -u -p -r1.14 getgrouplist.c --- lib/libc/gen/getgrouplist.c 3 May 2005 16:20:03 -0000 1.14 +++ lib/libc/gen/getgrouplist.c 14 May 2007 15:18:30 -0000 @@ -46,46 +46,12 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/get #include #include +extern int +getgroupmembership(const char *uname, gid_t agroup, gid_t *groups, + int maxgrp, int *groupc); + int getgrouplist(const char *uname, gid_t agroup, gid_t *groups, int *grpcnt) { - const struct group *grp; - int i, maxgroups, ngroups, ret; - - ret = 0; - ngroups = 0; - maxgroups = *grpcnt; - /* - * When installing primary group, duplicate it; - * the first element of groups is the effective gid - * and will be overwritten when a setgid file is executed. - */ - groups[ngroups++] = agroup; - if (maxgroups > 1) - groups[ngroups++] = agroup; - /* - * Scan the group file to find additional groups. - */ - setgrent(); - while ((grp = getgrent()) != NULL) { - for (i = 0; i < ngroups; i++) { - if (grp->gr_gid == groups[i]) - goto skip; - } - for (i = 0; grp->gr_mem[i]; i++) { - if (!strcmp(grp->gr_mem[i], uname)) { - if (ngroups >= maxgroups) { - ret = -1; - break; - } - groups[ngroups++] = grp->gr_gid; - break; - } - } -skip: - ; - } - endgrent(); - *grpcnt = ngroups; - return (ret); + return getgroupmembership(uname, agroup, groups, *grpcnt, grpcnt); } Index: lib/libc/net/nsdispatch.c =================================================================== RCS file: /home/ncvs/src/lib/libc/net/nsdispatch.c,v retrieving revision 1.12 diff -u -b -B -u -p -r1.12 nsdispatch.c --- lib/libc/net/nsdispatch.c 1 Apr 2004 19:12:45 -0000 1.12 +++ lib/libc/net/nsdispatch.c 14 May 2007 15:18:30 -0000 @@ -569,19 +572,108 @@ nss_method_lookup(const char *source, co return (NULL); } +/* + * Looks up a fallback method + */ +static nss_fbmethod +nss_fbmethod_lookup(const char *source, const char *method, + const ns_fbtab fallback[], void **mdata) +{ + int i; + + for (i = 0; fallback[i].src != NULL; i++) + if (strcasecmp(source, fallback[i].src) == 0 || + strcasecmp(NSSRC_ALL, fallback[i].src) == 0) { + *mdata = fallback[i].mdata; + return (fallback[i].method); + } -__weak_reference(_nsdispatch, nsdispatch); + *mdata = NULL; + return NULL; +} + +/* + * Calls a function from an nss source. If the function isn't defined, it uses + * the provided fallback table. For a description of the fallback table, see + * _nsdispatch_with_fbv. + */ +static int +_nsdispatch_callmethodv(int* result, const ns_src *source, + const char *database, const char *method_name, + const ns_dtab disp_tab[], const ns_fbtab fallback[], + void *retval, void *mdata, va_list ap) +{ + nss_method method; + nss_fbmethod fbmethod; + *result = NS_NOTFOUND; + + method = nss_method_lookup(source->name, database, + method_name, disp_tab, &mdata); + + if (method == NULL && fallback != NULL) { + /* Try to get fallback function */ + fbmethod = nss_fbmethod_lookup(source->name, + method_name, fallback, &mdata); + } else { + fbmethod = NULL; + } + + if (method != NULL || fbmethod != NULL) { + if (fbmethod != NULL) { + *result = fbmethod(source, retval, mdata, ap); + } else { + *result = method(retval, mdata, ap); + } + + if (*result & (source->flags)) + return NS_ACTION_RETURN; + } + + return NS_ACTION_CONTINUE; +} + +/* + * Wrapper around _nsdispatch_callmethodv + */ int -_nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, - const char *method_name, const ns_src defaults[], ...) +_nsdispatch_callmethod(int* result, const ns_src *source, + const char *database, const char *method_name, + const ns_dtab disp_tab[], const ns_fbtab fallback[], + void *retval, void *mdata, ...) { va_list ap; + int rv; + + va_start(ap, mdata); + rv = _nsdispatch_callmethodv(result, source, + database, method_name, + disp_tab, fallback, + retval, mdata, ap); + va_end(ap); + + return rv; +} + +/* + * nsdispatch function with fallback functionality. The fallbacks can be used + * to replace unimplemented functions in sources. + * + * A fallback table can contain any source name or the special entry of + * NSSRC_ALL to match all sources. If used, NSSRC_ALL must be the last entry. + * The fallbcak function is then called for each source which doesn't have an + * implementation of the wanted function. + */ +int +_nsdispatch_with_fbv(void *retval, const ns_dtab disp_tab[], + const ns_fbtab fallback[], const char *database, + const char *method_name, const ns_src defaults[], + va_list ap) +{ const ns_dbt *dbt; const ns_src *srclist; - nss_method method; void *mdata; - int isthreaded, serrno, i, result, srclistsize; + int isthreaded, serrno, i, result, srclistsize, rv; isthreaded = __isthreaded; serrno = errno; @@ -608,15 +702,13 @@ _nsdispatch(void *retval, const ns_dtab while (srclist[srclistsize].name != NULL) srclistsize++; } + for (i = 0; i < srclistsize; i++) { - result = NS_NOTFOUND; - method = nss_method_lookup(srclist[i].name, database, - method_name, disp_tab, &mdata); - if (method != NULL) { - va_start(ap, defaults); - result = method(retval, mdata, ap); - va_end(ap); - if (result & (srclist[i].flags)) + rv = _nsdispatch_callmethodv(&result, &srclist[i], + database, method_name, + disp_tab, fallback, + retval, mdata, ap); + if (rv == NS_ACTION_RETURN) { break; } } @@ -626,3 +718,43 @@ fin: errno = serrno; return (result); } + +/* + * Wrapper around _nsdispatch_with_fbv + */ +int +_nsdispatch_with_fb(void *retval, const ns_dtab disp_tab[], + const ns_fbtab fallback[], const char *database, + const char *method_name, const ns_src defaults[], + ...) +{ + va_list ap; + int rv; + + va_start(ap, defaults); + rv = _nsdispatch_with_fbv(retval, disp_tab, fallback, database, + method_name, defaults, ap); + va_end(ap); + + return rv; +} + +/* + * Original nsdispatch function without fallback functionality. + */ +int +_nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, + const char *method_name, const ns_src defaults[], ...) +{ + va_list ap; + int rv; + + va_start(ap, defaults); + rv = _nsdispatch_with_fbv(retval, disp_tab, NULL, database, + method_name, defaults, ap); + va_end(ap); + + return rv; +} + +__weak_reference(_nsdispatch, nsdispatch);