bin/115196: [PATCH] Implement getgroupmembership(3) for massive
performance gain when using LDAP or Winbind
Craig Rodrigues
rodrigc at FreeBSD.org
Sat Aug 4 07:30:01 PDT 2007
>Number: 115196
>Category: bin
>Synopsis: [PATCH] Implement getgroupmembership(3) for massive performance gain when using LDAP or Winbind
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: change-request
>Submitter-Id: current-users
>Arrival-Date: Sat Aug 04 14:30:01 GMT 2007
>Closed-Date:
>Last-Modified:
>Originator: Craig Rodrigues
>Release:
>Organization:
>Environment:
>Description:
This patch was submitted by Michael Hanselm for improving LDAP
performance:
http://lists.freebsd.org/pipermail/freebsd-current/2007-July/075131.html
The only thing missing from this patch is a bump of __FreeBSD_version__
Also, see related patch against the port/nss_ldap port:
http://www.freebsd.org/cgi/query-pr.cgi?pr=ports/114655
>How-To-Repeat:
>Fix:
Patch attached with submission follows:
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 <hesiod.h>
#endif
#include <grp.h>
+#define _NS_PRIVATE
#include <nsswitch.h>
#include <pthread.h>
#include <pthread_np.h>
@@ -54,6 +55,7 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/get
#include <string.h>
#include <syslog.h>
#include <unistd.h>
+#include <assert.h>
#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 <string.h>
#include <unistd.h>
+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);
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 <hesiod.h>
#endif
#include <grp.h>
+#define _NS_PRIVATE
#include <nsswitch.h>
#include <pthread.h>
#include <pthread_np.h>
@@ -54,6 +55,7 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/get
#include <string.h>
#include <syslog.h>
#include <unistd.h>
+#include <assert.h>
#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 <grp.h>
#include <string.h>
#include <unistd.h>
+#include <stdio.h>
+
+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);
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-bugs
mailing list