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