ports/114655: Implement getgroupmembership in nss_ldap
Michael Hanselmann
freebsd at hansmi.ch
Tue Jul 17 12:40:04 UTC 2007
>Number: 114655
>Category: ports
>Synopsis: Implement getgroupmembership in nss_ldap
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: freebsd-ports-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: update
>Submitter-Id: current-users
>Arrival-Date: Tue Jul 17 12:40:03 GMT 2007
>Closed-Date:
>Last-Modified:
>Originator: Michael Hanselmann
>Release: 6.x, 7
>Organization:
>Environment:
>Description:
(Copied from freebsd-current@)
I was working with a company which plans to migrate its FreeBSD servers
from using /etc/{passwd,group} to LDAP. They will have about 45'000
users and as much groups in the directory.
Tests showed that any function retrieving the groups a user is member
of, for example getgrouplist(3) or initgroups(3), is very slow. In our
case, it was about 7 seconds per invocation. Further investigation
showed how inefficient these functions are implemented through
getgrouplist(3). FreeBSD's implementation loops through all groups and
their members to check whether a user is member of it, in which case it
adds the group to a list. In our case, this means retrieving 45'000
search results from the LDAP server.
Directory services like LDAP or Winbind allow queries to have filters,
enabling us to write a much more efficient implementation. The attached
patches (nss-getgroupmembership-try9.diff for FreeBSD 6,
nss-getgroupmembership-fbsd7-try3.diff for FreeBSD 7) use an nss
module's getgroupmembership(3) function if available. Otherwise it uses
a fallback which then uses the old algorithm with some modifications.
After applying it, getgrouplist(3) takes only a few milliseconds to
retrieve all groups of a user.
Another patch, attached as bsdnss.diff, is needed for nss_ldap. It
applies to the ports/net/nss_ldap/files/bsdnss.c file and exports the
required getgroupmembership function. Most of the code there is from
NetBSD.
>How-To-Repeat:
>Fix:
Apply the patch.
Patch attached with submission follows:
--- bsdnss.c.orig Mon May 14 14:25:05 2007
+++ bsdnss.c Mon May 14 15:01:06 2007
@@ -1,9 +1,11 @@
+#include <stdlib.h>
#include <errno.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <pwd.h>
#include <grp.h>
#include <nss.h>
+#include <nsswitch.h>
#include <netdb.h>
extern enum nss_status _nss_ldap_getgrent_r(struct group *, char *, size_t,
@@ -34,12 +36,15 @@ extern enum nss_status _nss_ldap_gethost
extern enum nss_status _nss_ldap_gethostbyaddr_r (struct in_addr * addr, int len, int type,
struct hostent * result, char *buffer,
size_t buflen, int *errnop, int *h_errnop);
+extern enum nss_status _nss_ldap_initgroups_dyn(const char *, gid_t, long int *,
+ long int *, gid_t **, long int, int *);
NSS_METHOD_PROTOTYPE(__nss_compat_getgrnam_r);
NSS_METHOD_PROTOTYPE(__nss_compat_getgrgid_r);
NSS_METHOD_PROTOTYPE(__nss_compat_getgrent_r);
NSS_METHOD_PROTOTYPE(__nss_compat_setgrent);
NSS_METHOD_PROTOTYPE(__nss_compat_endgrent);
+static NSS_METHOD_PROTOTYPE(__freebsd_getgroupmembership);
NSS_METHOD_PROTOTYPE(__nss_compat_getpwnam_r);
NSS_METHOD_PROTOTYPE(__nss_compat_getpwuid_r);
@@ -57,6 +62,7 @@ static ns_mtab methods[] = {
{ NSDB_GROUP, "getgrent_r", __nss_compat_getgrent_r, _nss_ldap_getgrent_r },
{ NSDB_GROUP, "setgrent", __nss_compat_setgrent, _nss_ldap_setgrent },
{ NSDB_GROUP, "endgrent", __nss_compat_endgrent, _nss_ldap_endgrent },
+{ NSDB_GROUP, "getgroupmembership", __freebsd_getgroupmembership, NULL },
{ NSDB_PASSWD, "getpwnam_r", __nss_compat_getpwnam_r, _nss_ldap_getpwnam_r },
{ NSDB_PASSWD, "getpwuid_r", __nss_compat_getpwuid_r, _nss_ldap_getpwuid_r },
@@ -155,4 +161,62 @@ int __nss_compat_gethostbyaddr(void *ret
status = __nss_compat_result(status,errnop);
h_errno = h_errnop;
return (status);
+}
+
+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 this gid */
+ groups[*groupc] = gid;
+ else
+ ret = 0;
+ (*groupc)++;
+ return ret;
+}
+
+static int
+__freebsd_getgroupmembership(void *retval, void *mdata, va_list ap)
+{
+ int err;
+ enum nss_status s;
+ int *result = va_arg(ap, int *);
+ const char *user = 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*);
+ gid_t *tmpgroups;
+ long int lstart, lsize;
+ int i;
+
+ tmpgroups = malloc(limit * sizeof(gid_t));
+ if (tmpgroups == NULL)
+ return NS_TRYAGAIN;
+
+ /* insert primary membership */
+ __gr_addgid(group, groups, limit, size);
+
+ lstart = 0;
+ lsize = limit;
+ s = _nss_ldap_initgroups_dyn(user, group, &lstart, &lsize,
+ &tmpgroups, 0, &err);
+ if (s == NSS_STATUS_SUCCESS) {
+ for (i = 0; i < lstart; i++)
+ if (! __gr_addgid(tmpgroups[i], groups, limit, size))
+ *result = -1;
+ s = NSS_STATUS_NOTFOUND;
+ }
+
+ free(tmpgroups);
+
+ return __nss_compat_result(s, 0);
}
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-ports-bugs
mailing list