Index: include/nsswitch.h =================================================================== RCS file: /home/ncvs/src/include/nsswitch.h,v retrieving revision 1.4 diff -u -b -B -u -p -r1.4 nsswitch.h --- include/nsswitch.h 28 Apr 2006 12:03:34 -0000 1.4 +++ include/nsswitch.h 12 Jun 2007 20:49:25 -0000 @@ -69,6 +69,7 @@ #define NSSRC_NIS "nis" /* YP/NIS */ #define NSSRC_COMPAT "compat" /* passwd,group in YP compat mode */ #define NSSRC_CACHE "cache" /* cache daemon */ +#define NSSRC_ALL "*" /* All sources, used for fallbacks */ /* * currently implemented databases @@ -222,6 +223,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 */ @@ -232,6 +251,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.36 diff -u -b -B -u -p -r1.36 getgrent.c --- lib/libc/gen/getgrent.c 18 Sep 2006 09:34:48 -0000 1.36 +++ lib/libc/gen/getgrent.c 12 Jun 2007 20:49:25 -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" @@ -447,17 +449,13 @@ endgrent(void) } -int -getgrent_r(struct group *grp, char *buffer, size_t bufsize, - struct group **result) -{ #ifdef NS_CACHING - static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( +static const nss_cache_info getgrent_cache_info = NS_MP_CACHE_INFO_INITIALIZER( group, (void *)nss_lt_all, grp_marshal_func, grp_unmarshal_func); #endif - static const ns_dtab dtab[] = { +static const ns_dtab getgrent_dtab[] = { { NSSRC_FILES, files_group, (void *)nss_lt_all }, #ifdef HESIOD { NSSRC_DNS, dns_group, (void *)nss_lt_all }, @@ -467,16 +465,21 @@ getgrent_r(struct group *grp, char *buff #endif { NSSRC_COMPAT, compat_group, (void *)nss_lt_all }, #ifdef NS_CACHING - NS_CACHE_CB(&cache_info) + NS_CACHE_CB(&getgrent_cache_info) #endif { NULL, NULL, NULL } - }; +}; + +int +getgrent_r(struct group *grp, char *buffer, size_t bufsize, + struct group **result) +{ int rv, ret_errno; ret_errno = 0; *result = NULL; - rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrent_r", defaultsrc, - grp, buffer, bufsize, &ret_errno); + rv = _nsdispatch(result, getgrent_dtab, NSDB_GROUP, "getgrent_r", + defaultsrc, grp, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else @@ -1346,6 +1349,175 @@ 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) +{ + 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", + getgrent_dtab, 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.15 diff -u -b -B -u -p -r1.15 getgrouplist.c --- lib/libc/gen/getgrouplist.c 9 Jan 2007 00:27:53 -0000 1.15 +++ lib/libc/gen/getgrouplist.c 12 Jun 2007 20:49:25 -0000 @@ -41,47 +41,14 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/get #include #include #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.14 diff -u -b -B -u -p -r1.14 nsdispatch.c --- lib/libc/net/nsdispatch.c 17 May 2007 03:33:23 -0000 1.14 +++ lib/libc/net/nsdispatch.c 12 Jun 2007 20:49:26 -0000 @@ -590,19 +590,147 @@ 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; -__weak_reference(_nsdispatch, nsdispatch); + 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); + } + + *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_callmethod_with_cache_v(int* result, const ns_src *source, + const char *database, const char *method_name, + const ns_dtab disp_tab[], const ns_fbtab fallback[], +#ifdef NS_CACHING + nss_cache_data *cache_data, nss_cache_data **cache_data_p, + int *cache_flag, +#endif + 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) { +#ifdef NS_CACHING + if (cache_data != NULL && cache_data_p != NULL && + cache_flag != NULL && + strcmp(source->name, NSSRC_CACHE) == 0 && + nss_cache_cycle_prevention_func == NULL) { +#ifdef NS_STRICT_LIBC_EID_CHECKING + if (issetugid() != 0) + return NS_ACTION_CONTINUE; +#endif + *cache_flag = 1; + + memset(cache_data, 0, sizeof(nss_cache_data)); + cache_data->info = (nss_cache_info const *)mdata; + *cache_data_p = cache_data; + + if (cache_data->info->id_func != NULL) + *result = __nss_common_cache_read(retval, + *cache_data_p, ap); + else if (cache_data->info->marshal_func != NULL) + *result = __nss_mp_cache_read(retval, + *cache_data_p, ap); + else + *result = __nss_mp_cache_end(retval, + *cache_data_p, ap); + } else { + *cache_flag = 0; +#endif + + if (fbmethod != NULL) { + *result = fbmethod(source, retval, mdata, ap); + } else { + *result = method(retval, mdata, ap); + } +#ifdef NS_CACHING + } +#endif + + if (*result & (source->flags)) + return NS_ACTION_RETURN; + } + + return NS_ACTION_CONTINUE; +} + +/* + * Wrapper around _nsdispatch_callmethod_with_cache_v + */ 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_callmethod_with_cache_v(result, source, + database, method_name, + disp_tab, fallback, +#ifdef NS_CACHING + NULL, NULL, NULL, +#endif + 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; #ifdef NS_CACHING nss_cache_data cache_data; @@ -640,57 +768,22 @@ _nsdispatch(void *retval, const ns_dtab cache_data_p = NULL; cache_flag = 0; #endif - 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) { + for (i = 0; i < srclistsize; i++) { + rv = _nsdispatch_callmethod_with_cache_v(&result, &srclist[i], + database, method_name, + disp_tab, fallback, #ifdef NS_CACHING - if (strcmp(srclist[i].name, NSSRC_CACHE) == 0 && - nss_cache_cycle_prevention_func == NULL) { -#ifdef NS_STRICT_LIBC_EID_CHECKING - if (issetugid() != 0) - continue; + &cache_data, &cache_data_p, &cache_flag, #endif - cache_flag = 1; - - memset(&cache_data, 0, sizeof(nss_cache_data)); - cache_data.info = (nss_cache_info const *)mdata; - cache_data_p = &cache_data; - - va_start(ap, defaults); - if (cache_data.info->id_func != NULL) - result = __nss_common_cache_read(retval, - cache_data_p, ap); - else if (cache_data.info->marshal_func != NULL) - result = __nss_mp_cache_read(retval, - cache_data_p, ap); - else - result = __nss_mp_cache_end(retval, - cache_data_p, ap); - va_end(ap); - } else { - cache_flag = 0; - va_start(ap, defaults); - result = method(retval, mdata, ap); - va_end(ap); - } -#else /* NS_CACHING */ - va_start(ap, defaults); - result = method(retval, mdata, ap); - va_end(ap); -#endif /* NS_CACHING */ - - if (result & (srclist[i].flags)) + retval, mdata, ap); + if (rv == NS_ACTION_RETURN) break; } - } #ifdef NS_CACHING if (cache_data_p != NULL && (result & (NS_NOTFOUND | NS_SUCCESS)) && cache_flag == 0) { - va_start(ap, defaults); if (result == NS_SUCCESS) { if (cache_data.info->id_func != NULL) __nss_common_cache_write(retval, cache_data_p, @@ -705,7 +798,6 @@ _nsdispatch(void *retval, const ns_dtab } else __nss_common_cache_write_negative(cache_data_p); } - va_end(ap); } #endif /* NS_CACHING */ @@ -715,3 +807,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);