diff options
author | bushman <bushman@FreeBSD.org> | 2007-12-12 10:08:03 +0000 |
---|---|---|
committer | bushman <bushman@FreeBSD.org> | 2007-12-12 10:08:03 +0000 |
commit | b02556dae5c4b2e396b5bee2c663fe38f28d369b (patch) | |
tree | c40928e3eb8bab77c816a763945d3d4a10c59962 /lib/libc | |
parent | b52b3d4702084768bfb2db0f76743d7a3b993690 (diff) | |
download | FreeBSD-src-b02556dae5c4b2e396b5bee2c663fe38f28d369b.zip FreeBSD-src-b02556dae5c4b2e396b5bee2c663fe38f28d369b.tar.gz |
Implementing 'fallback' nsswitch source. 'fallback' source is used
when particular function can't be found in nsswitch-module. For
example, getgrouplist(3) will use module-supplied 'getgroupmembership'
function (which can work in an optimal way for such source as LDAP) and
will fall back to the stanard iterate-through-all-groups implementation
otherwise.
PR: ports/114655
Submitted by: Michael Hanselmann <freebsd AT hansmi DOT ch>
Reviewed by: brooks (mentor)
Diffstat (limited to 'lib/libc')
-rw-r--r-- | lib/libc/gen/getgrent.c | 264 | ||||
-rw-r--r-- | lib/libc/gen/getgrouplist.c | 43 | ||||
-rw-r--r-- | lib/libc/net/nsdispatch.c | 29 |
3 files changed, 220 insertions, 116 deletions
diff --git a/lib/libc/gen/getgrent.c b/lib/libc/gen/getgrent.c index 7da84f7..18f64a8 100644 --- a/lib/libc/gen/getgrent.c +++ b/lib/libc/gen/getgrent.c @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include <rpcsvc/yp_prot.h> #include <rpcsvc/ypclnt.h> #endif +#include <assert.h> #include <ctype.h> #include <errno.h> #ifdef HESIOD @@ -143,6 +144,9 @@ NSS_TLS_HANDLING(compat); static int compat_setgrent(void *, void *, va_list); static int compat_group(void *, void *, va_list); +static int gr_addgid(gid_t, gid_t *, int, int *); +static int getgroupmembership_fallback(void *, void *, va_list); + #ifdef NS_CACHING static int grp_id_func(char *, size_t *, va_list, void *); static int grp_marshal_func(char *, size_t *, void *, va_list, void *); @@ -361,89 +365,195 @@ grp_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, NSS_MP_CACHE_HANDLING(group); #endif /* NS_CACHING */ - -/* XXX IEEE Std 1003.1, 2003 specifies `void setgrent(void)' */ -int -setgrent(void) -{ #ifdef NS_CACHING - static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( - group, (void *)nss_lt_all, - NULL, NULL); +static const nss_cache_info setgrent_cache_info = NS_MP_CACHE_INFO_INITIALIZER( + group, (void *)nss_lt_all, + NULL, NULL); #endif - static const ns_dtab dtab[] = { - { NSSRC_FILES, files_setgrent, (void *)SETGRENT }, +static const ns_dtab setgrent_dtab[] = { + { NSSRC_FILES, files_setgrent, (void *)SETGRENT }, #ifdef HESIOD - { NSSRC_DNS, dns_setgrent, (void *)SETGRENT }, + { NSSRC_DNS, dns_setgrent, (void *)SETGRENT }, #endif #ifdef YP - { NSSRC_NIS, nis_setgrent, (void *)SETGRENT }, + { NSSRC_NIS, nis_setgrent, (void *)SETGRENT }, #endif - { NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT }, + { NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT }, #ifdef NS_CACHING - NS_CACHE_CB(&cache_info) + NS_CACHE_CB(&setgrent_cache_info) #endif - { NULL, NULL, NULL } - }; - (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc, 0); - return (1); -} - + { NULL, NULL, NULL } +}; -int -setgroupent(int stayopen) -{ #ifdef NS_CACHING - static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( - group, (void *)nss_lt_all, - NULL, NULL); +static const nss_cache_info endgrent_cache_info = NS_MP_CACHE_INFO_INITIALIZER( + group, (void *)nss_lt_all, + NULL, NULL); #endif - static const ns_dtab dtab[] = { - { NSSRC_FILES, files_setgrent, (void *)SETGRENT }, +static const ns_dtab endgrent_dtab[] = { + { NSSRC_FILES, files_setgrent, (void *)ENDGRENT }, #ifdef HESIOD - { NSSRC_DNS, dns_setgrent, (void *)SETGRENT }, + { NSSRC_DNS, dns_setgrent, (void *)ENDGRENT }, #endif #ifdef YP - { NSSRC_NIS, nis_setgrent, (void *)SETGRENT }, + { NSSRC_NIS, nis_setgrent, (void *)ENDGRENT }, #endif - { NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT }, + { NSSRC_COMPAT, compat_setgrent, (void *)ENDGRENT }, #ifdef NS_CACHING - NS_CACHE_CB(&cache_info) + NS_CACHE_CB(&endgrent_cache_info) #endif - { NULL, NULL, NULL } - }; - (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc, - stayopen); - return (1); -} - + { NULL, NULL, NULL } +}; -void -endgrent(void) -{ #ifdef NS_CACHING - static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( - group, (void *)nss_lt_all, - NULL, NULL); +static const nss_cache_info getgrent_r_cache_info = NS_MP_CACHE_INFO_INITIALIZER( + group, (void *)nss_lt_all, + grp_marshal_func, grp_unmarshal_func); #endif - static const ns_dtab dtab[] = { - { NSSRC_FILES, files_setgrent, (void *)ENDGRENT }, +static const ns_dtab getgrent_r_dtab[] = { + { NSSRC_FILES, files_group, (void *)nss_lt_all }, #ifdef HESIOD - { NSSRC_DNS, dns_setgrent, (void *)ENDGRENT }, + { NSSRC_DNS, dns_group, (void *)nss_lt_all }, #endif #ifdef YP - { NSSRC_NIS, nis_setgrent, (void *)ENDGRENT }, + { NSSRC_NIS, nis_group, (void *)nss_lt_all }, #endif - { NSSRC_COMPAT, compat_setgrent, (void *)ENDGRENT }, + { NSSRC_COMPAT, compat_group, (void *)nss_lt_all }, #ifdef NS_CACHING - NS_CACHE_CB(&cache_info) + NS_CACHE_CB(&getgrent_r_cache_info) #endif - { NULL, NULL, NULL } + { NULL, NULL, NULL } +}; + +static int +gr_addgid(gid_t gid, gid_t *groups, int maxgrp, int *grpcnt) +{ + int ret, dupc; + + for (dupc = 0; dupc < MIN(maxgrp, *grpcnt); dupc++) { + if (groups[dupc] == gid) + return 1; + } + + ret = 1; + if (*grpcnt < maxgrp) + groups[*grpcnt] = gid; + else + ret = 0; + + (*grpcnt)++; + + return ret; +} + +static int +getgroupmembership_fallback(void *retval, void *mdata, va_list ap) +{ + const ns_src src[] = { + { mdata, NS_SUCCESS }, + { NULL, 0} }; - (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "endgrent", defaultsrc); + struct group grp; + struct group *grp_p; + char *buf; + size_t bufsize; + const char *uname; + gid_t *groups; + gid_t agroup; + int maxgrp, *grpcnt; + int i, rv, ret_errno; + + /* + * As this is a fallback method, only provided src + * list will be respected during methods search. + */ + assert(src[0].name != NULL); + + uname = va_arg(ap, const char *); + agroup = va_arg(ap, gid_t); + groups = va_arg(ap, gid_t *); + maxgrp = va_arg(ap, int); + grpcnt = va_arg(ap, int *); + + rv = NS_UNAVAIL; + + buf = malloc(GRP_STORAGE_INITIAL); + if (buf == NULL) + goto out; + + bufsize = GRP_STORAGE_INITIAL; + + gr_addgid(agroup, groups, maxgrp, grpcnt); + + _nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", src, 0); + for (;;) { + do { + ret_errno = 0; + grp_p = NULL; + rv = _nsdispatch(&grp_p, getgrent_r_dtab, NSDB_GROUP, + "getgrent_r", src, &grp, buf, bufsize, &ret_errno); + + if (grp_p == NULL && ret_errno == ERANGE) { + free(buf); + if ((bufsize << 1) > GRP_STORAGE_MAX) { + buf = NULL; + errno = ERANGE; + goto out; + } + + bufsize <<= 1; + buf = malloc(bufsize); + if (buf == NULL) { + goto out; + } + } + } while (grp_p == NULL && ret_errno == ERANGE); + + if (ret_errno != 0) { + errno = ret_errno; + goto out; + } + + if (grp_p == NULL) + break; + + for (i = 0; grp.gr_mem[i]; i++) { + if (strcmp(grp.gr_mem[i], uname) == 0) + gr_addgid(grp.gr_gid, groups, maxgrp, grpcnt); + } + } + + _nsdispatch(NULL, endgrent_dtab, NSDB_GROUP, "endgrent", src); +out: + free(buf); + return (rv); +} + +/* XXX IEEE Std 1003.1, 2003 specifies `void setgrent(void)' */ +int +setgrent(void) +{ + (void)_nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", defaultsrc, 0); + return (1); +} + + +int +setgroupent(int stayopen) +{ + (void)_nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", defaultsrc, + stayopen); + return (1); +} + + +void +endgrent(void) +{ + (void)_nsdispatch(NULL, endgrent_dtab, NSDB_GROUP, "endgrent", defaultsrc); } @@ -451,31 +561,11 @@ 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( - group, (void *)nss_lt_all, - grp_marshal_func, grp_unmarshal_func); -#endif - - static const ns_dtab dtab[] = { - { 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 }, -#ifdef NS_CACHING - NS_CACHE_CB(&cache_info) -#endif - { NULL, NULL, NULL } - }; int rv, ret_errno; ret_errno = 0; *result = NULL; - rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrent_r", defaultsrc, + rv = _nsdispatch(result, getgrent_r_dtab, NSDB_GROUP, "getgrent_r", defaultsrc, grp, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); @@ -560,6 +650,30 @@ getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize, } + +int +__getgroupmembership(const char *uname, gid_t agroup, gid_t *groups, + int maxgrp, int *grpcnt) +{ + static const ns_dtab dtab[] = { + NS_FALLBACK_CB(getgroupmembership_fallback) + { NULL, NULL, NULL } + }; + int rv; + + assert(uname != NULL); + /* groups may be NULL if just sizing when invoked with maxgrp = 0 */ + assert(grpcnt != NULL); + + *grpcnt = 0; + rv = _nsdispatch(NULL, dtab, NSDB_GROUP, "getgroupmembership", + defaultsrc, uname, agroup, groups, maxgrp, grpcnt); + + /* too many groups found? */ + return (*grpcnt > maxgrp ? -1 : 0); +} + + static struct group grp; static char *grp_storage; static size_t grp_storage_size; @@ -1436,3 +1550,5 @@ __gr_parse_entry(char *line, size_t linesize, struct group *grp, char *membuf, return (NS_RETURN); } } + + diff --git a/lib/libc/gen/getgrouplist.c b/lib/libc/gen/getgrouplist.c index 3510a31..9eb55f9 100644 --- a/lib/libc/gen/getgrouplist.c +++ b/lib/libc/gen/getgrouplist.c @@ -42,46 +42,11 @@ __FBSDID("$FreeBSD$"); #include <string.h> #include <unistd.h> +extern int __getgroupmembership(const char *, gid_t, gid_t *, int, int *); + 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); } + diff --git a/lib/libc/net/nsdispatch.c b/lib/libc/net/nsdispatch.c index ad3f208..7f30e4f 100644 --- a/lib/libc/net/nsdispatch.c +++ b/lib/libc/net/nsdispatch.c @@ -136,6 +136,15 @@ static void *nss_cache_cycle_prevention_func = NULL; #endif /* + * When this is set to 1, nsdispatch won't use nsswitch.conf + * but will consult the 'defaults' source list only. + * NOTE: nested fallbacks (when nsdispatch calls fallback functions, + * which in turn calls nsdispatch, which should call fallback + * function) are not supported + */ +static int fallback_dispatch = 0; + +/* * Attempt to spew relatively uniform messages to syslog. */ #define nss_log(level, fmt, ...) \ @@ -600,7 +609,7 @@ _nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, va_list ap; const ns_dbt *dbt; const ns_src *srclist; - nss_method method; + nss_method method, fb_method; void *mdata; int isthreaded, serrno, i, result, srclistsize; @@ -609,6 +618,9 @@ _nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, nss_cache_data *cache_data_p; int cache_flag; #endif + + dbt = NULL; + fb_method = NULL; isthreaded = __isthreaded; serrno = errno; @@ -624,8 +636,13 @@ _nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, result = NS_UNAVAIL; goto fin; } - dbt = vector_search(&database, _nsmap, _nsmapsize, sizeof(*_nsmap), - string_compare); + if (fallback_dispatch == 0) { + dbt = vector_search(&database, _nsmap, _nsmapsize, sizeof(*_nsmap), + string_compare); + fb_method = nss_method_lookup(NSSRC_FALLBACK, database, + method_name, disp_tab, &mdata); + } + if (dbt != NULL) { srclist = dbt->srclist; srclistsize = dbt->srclistsize; @@ -684,6 +701,12 @@ _nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, if (result & (srclist[i].flags)) break; + } else if (fb_method != NULL) { + fallback_dispatch = 1; + va_start(ap, defaults); + result = fb_method(retval, (void *)srclist[i].name, ap); + va_end(ap); + fallback_dispatch = 0; } } |