NGROUPS_MAX on Linux

Tim Hockin thockin at sun.com
Fri Feb 20 11:19:53 EST 2004


Linux has just raised the NGROUPS_MAX limit from 32 to 64k.  In doing an
audit of various tools, openssh turned up as having incorrect groups
handling.  Almost no user-space apps really care about NGROUPS_MAX.

A proposed patch (untested, since the CVS build won't compile on my RH box..
:-/) :

What think?


Index: uidswap.c
===================================================================
RCS file: /cvs/openssh/uidswap.c,v
retrieving revision 1.42
diff -u -u -r1.42 uidswap.c
--- uidswap.c	17 Dec 2003 07:53:26 -0000	1.42
+++ uidswap.c	19 Feb 2004 23:50:38 -0000
@@ -38,7 +38,7 @@
 /* Saved effective uid. */
 static int	privileged = 0;
 static int	temporarily_use_uid_effective = 0;
-static gid_t	saved_egroups[NGROUPS_MAX], user_groups[NGROUPS_MAX];
+static gid_t	*saved_egroups, *user_groups;
 static int	saved_egroupslen = -1, user_groupslen = -1;
 
 /*
@@ -68,17 +68,27 @@
 
 	privileged = 1;
 	temporarily_use_uid_effective = 1;
-	saved_egroupslen = getgroups(NGROUPS_MAX, saved_egroups);
+
+	saved_egroupslen = getgroups(0, NULL);
 	if (saved_egroupslen < 0)
 		fatal("getgroups: %.100s", strerror(errno));
+	saved_egroups = xrealloc(saved_egroups,
+	    saved_egroupslen * sizeof(*saved_egroups));
+	if (getgroups(saved_egroupslen, saved_egroups) < 0)
+		fatal("getgroups: %.100s", strerror(errno));
 
 	/* set and save the user's groups */
 	if (user_groupslen == -1) {
 		if (initgroups(pw->pw_name, pw->pw_gid) < 0)
 			fatal("initgroups: %s: %.100s", pw->pw_name,
 			    strerror(errno));
-		user_groupslen = getgroups(NGROUPS_MAX, user_groups);
+
+		user_groupslen = getgroups(0, NULL);
 		if (user_groupslen < 0)
+			fatal("getgroups: %.100s", strerror(errno));
+		user_groups = xrealloc(user_groups,
+		    user_groupslena * sizeof(*user_groups));
+		if (getgroups(user_groupslen, user_groups) < 0)
 			fatal("getgroups: %.100s", strerror(errno));
 	}
 	/* Set the effective uid to the given (unprivileged) uid. */
Index: groupaccess.c
===================================================================
RCS file: /cvs/openssh/groupaccess.c,v
retrieving revision 1.7
diff -u -u -r1.7 groupaccess.c
--- groupaccess.c	14 May 2003 03:40:07 -0000	1.7
+++ groupaccess.c	19 Feb 2004 23:50:38 -0000
@@ -31,7 +31,7 @@
 #include "log.h"
 
 static int ngroups;
-static char *groups_byname[NGROUPS_MAX + 1];	/* +1 for base/primary group */
+static char **groups_byname;
 
 /*
  * Initialize group access list for user with primary (base) and
@@ -40,20 +40,33 @@
 int
 ga_init(const char *user, gid_t base)
 {
-	gid_t groups_bygid[NGROUPS_MAX + 1];
-	int i, j;
+	gid_t *groups_bygid;
+	int i;
 	struct group *gr;
 
 	if (ngroups > 0)
 		ga_free();
 
-	ngroups = sizeof(groups_bygid) / sizeof(gid_t);
+	getgrouplist(user, base, NULL, &ngroups);
+	groups_bygid = xmalloc(ngroups * sizeof(*groups_bygid));
+	groups_byname = xmalloc(ngroups * sizeof(*groups_byname));
+
 	if (getgrouplist(user, base, groups_bygid, &ngroups) == -1)
 		logit("getgrouplist: groups list too small");
-	for (i = 0, j = 0; i < ngroups; i++)
-		if ((gr = getgrgid(groups_bygid[i])) != NULL)
-			groups_byname[j++] = xstrdup(gr->gr_name);
-	return (ngroups = j);
+	for (i = 0; i < ngroups; i++) {
+		if ((gr = getgrgid(groups_bygid[i])) != NULL) {
+			groups_byname[i] = xstrdup(gr->gr_name);
+		} else {
+			char gidstr[32];
+
+			logit("getgrgid: unknown group id: %d",
+			    (int)groups_bygid[i]);
+			snprintf(gidstr, sizeof(gidstr), "%d",
+			    (int)groups_bygid[i]);
+			groups_byname[i] = xstrdup(gidstr);
+		}
+	}
+	return ngroups;
 }
 
 /*
@@ -84,5 +97,7 @@
 		for (i = 0; i < ngroups; i++)
 			xfree(groups_byname[i]);
 		ngroups = 0;
+		xfree(groups_byname);
+		xfree(groups_bygid);
 	}
 }
-- 
Tim Hockin
Sun Microsystems, Linux Software Engineering
thockin at sun.com
All opinions are my own, not Sun's




More information about the openssh-unix-dev mailing list