Package: openafs
Version: 1.4.2-5
Severity: critical
Tags: patch
Justification: breaks unrelated software


OpenAFS associates PAGs with a user's session by modifying the user's
supplementary group list.  It inserts two groups (each of which is half
of a 32 bit PAG identifier) at the beginning of the group list.  When it
does so, however, it makes no effort to ensure that the group list gets
sorted by gid.  Linux depends on the groups being in numeric order in
order to determine whether a user is in a given group (it uses a binary
search tree algorithm to traverse the user's group list).  Because the
synthetic GIDs that AFS typically uses are large (In the 30000+ range),
this almost always results in a corrupt supplementary group list.  This
has the effect of removing the user from groups that they normally would
be in, which naturally breaks all sorts of unrelated software.

Upstream fixed the problem for version 1.4.3 in
http://www.openafs.org/cgi-bin/wdelta/STABLE14-linux26-large-pag-groups-20070115?diff&f=u
I've backported this fix to the version currently present in etch.  The
patch against 1.4.2-5, the version currently in etch, should be
attached.

noah

-- System Information:
Debian Release: 4.0
  APT prefers testing
  APT policy: (500, 'testing')
Architecture: amd64 (x86_64)
Shell:  /bin/sh linked to /bin/bash
Kernel: Linux 2.6.18.6-csail
Locale: LANG=C, LC_CTYPE=C (charmap=ANSI_X3.4-1968)
diff -ur openafs-1.4.2-pre/src/afs/LINUX/osi_groups.c openafs-1.4.2/src/afs/LINUX/osi_groups.c
--- openafs-1.4.2-pre/src/afs/LINUX/osi_groups.c	2006-09-27 17:14:28.000000000 -0400
+++ openafs-1.4.2/src/afs/LINUX/osi_groups.c	2007-03-14 12:54:00.000000000 -0400
@@ -29,6 +29,12 @@
 #include "h/smp_lock.h"
 #endif
 
+#ifdef AFS_LINUX26_ONEGROUP_ENV
+#define NUMPAGGROUPS 1
+#else
+#define NUMPAGGROUPS 2
+#endif
+
 #if defined(AFS_LINUX26_ENV)
 static int
 afs_setgroups(cred_t **cr, struct group_info *group_info, int change_parent)
@@ -160,26 +166,54 @@
     gid_t g0, g1;
     struct group_info *tmp;
     int i;
+#ifdef AFS_LINUX26_ONEGROUP_ENV
+    int j;
+#endif
+
     int need_space = 0;
 
     group_info = afs_getgroups(*cr);
-    if (group_info->ngroups < 2
-	||  afs_get_pag_from_groups(GROUP_AT(group_info, 0),
-				    GROUP_AT(group_info, 1)) == NOPAG) 
+    if (group_info->ngroups < NUMPAGGROUPS
+	||  afs_get_pag_from_groups(
+#ifdef AFS_LINUX26_ONEGROUP_ENV
+	    group_info
+#else
+	    GROUP_AT(group_info, 0) ,GROUP_AT(group_info, 1)
+#endif
+				    ) == NOPAG) 
 	/* We will have to make sure group_info is big enough for pag */
-      need_space = 2;
+	need_space = NUMPAGGROUPS;
 
     tmp = groups_alloc(group_info->ngroups + need_space);
     
+    *newpag = (pagvalue == -1 ? genpag() : pagvalue);
+#ifdef AFS_LINUX26_ONEGROUP_ENV
+    for (i = 0, j = 0; i < group_info->ngroups; ++i) {
+	int ths = GROUP_AT(group_info, i);
+	int last = i > 0 ? GROUP_AT(group_info, i-1) : 0;
+	if ((ths >> 24) == 'A')
+	    continue;
+	if (last <= *newpag && ths > *newpag) {
+	   GROUP_AT(tmp, j) = *newpag;
+	   j++;
+	}
+	GROUP_AT(tmp, j) = ths;
+	j++;
+    }
+    if (j != i + need_space)
+        GROUP_AT(tmp, j) = *newpag;
+#else
     for (i = 0; i < group_info->ngroups; ++i)
       GROUP_AT(tmp, i + need_space) = GROUP_AT(group_info, i);
+#endif
     put_group_info(group_info);
     group_info = tmp;
 
-    *newpag = (pagvalue == -1 ? genpag() : pagvalue);
+#ifndef AFS_LINUX26_ONEGROUP_ENV
     afs_get_groups_from_pag(*newpag, &g0, &g1);
     GROUP_AT(group_info, 0) = g0;
     GROUP_AT(group_info, 1) = g1;
+#endif
 
     afs_setgroups(cr, group_info, change_parent);
 
@@ -524,15 +558,19 @@
     if (datalen != sizeof(afs_uint32) || !data)
 	goto error;
 
-    if (current->group_info->ngroups < 2)
+    if (current->group_info->ngroups < NUMPAGGROUPS)
 	goto error;
 
     /* ensure key being set matches current pag */
 
+#ifdef AFS_LINUX26_ONEGROUP_ENV
+    pag = afs_get_pag_from_groups(current->group_info);
+#else
     g0 = GROUP_AT(current->group_info, 0);
     g1 = GROUP_AT(current->group_info, 1);
 
     pag = afs_get_pag_from_groups(g0, g1);
+#endif
     if (pag == NOPAG)
 	goto error;
 
diff -ur openafs-1.4.2-pre/src/afs/LINUX/osi_machdep.h openafs-1.4.2/src/afs/LINUX/osi_machdep.h
--- openafs-1.4.2-pre/src/afs/LINUX/osi_machdep.h	2006-09-16 15:12:15.000000000 -0400
+++ openafs-1.4.2/src/afs/LINUX/osi_machdep.h	2007-03-14 13:09:06.000000000 -0400
@@ -15,6 +15,11 @@
 #ifndef OSI_MACHDEP_H_
 #define OSI_MACHDEP_H_
 
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,4)
+#define AFS_LINUX26_ONEGROUP_ENV 1
+#endif
+
 /* Only needed for xdr.h in glibc 2.1.x */
 #ifndef quad_t
 #define quad_t __quad_t
diff -ur openafs-1.4.2-pre/src/afs/afs_osi_pag.c openafs-1.4.2/src/afs/afs_osi_pag.c
--- openafs-1.4.2-pre/src/afs/afs_osi_pag.c	2006-08-17 09:56:29.000000000 -0400
+++ openafs-1.4.2/src/afs/afs_osi_pag.c	2007-03-14 13:03:49.000000000 -0400
@@ -44,7 +44,13 @@
 afs_uint32 pagCounter = 0;
 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
 
-/* Local variables */
+#ifdef AFS_LINUX26_ONEGROUP_ENV
+#define NUMPAGGROUPS 1
+#else
+#define NUMPAGGROUPS 2
+#endif
+
+ /* Local variables */
 
 /*
  * Pags are implemented as follows: the set of groups whose long
@@ -363,6 +369,7 @@
     return (code);
 }
 
+#ifndef AFS_LINUX26_ONEGROUP_ENV
 int
 afs_getpag_val()
 {
@@ -375,9 +382,9 @@
     pagvalue = afs_get_pag_from_groups(gidset0, gidset1);
     return pagvalue;
 }
+#endif
 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
 
-
 /* Note - needs to be available on AIX, others can be static - rework this */
 #if defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
 int
@@ -430,7 +437,22 @@
     return 0;
 }
 
+#ifdef AFS_LINUX26_ONEGROUP_ENV
+afs_uint32
+afs_get_pag_from_groups(struct group_info *group_info)
+{
+    afs_uint32 g0 = 0;
+    afs_uint32 i;
+    AFS_STATCNT(afs_get_pag_from_groups);
+    for (i = 0; (i < group_info->ngroups && 
+		 (g0 = GROUP_AT(group_info, i)) != (gid_t) NOGROUP); i++) {
+	if (((g0 >> 24) & 0xff) == 'A')
+	    return g0;
+    }
+    return NOPAG;
+}
 
+#else
 
 afs_uint32
 afs_get_pag_from_groups(gid_t g0a, gid_t g1a)
@@ -457,7 +479,7 @@
     }
     return NOPAG;
 }
-
+#endif /* AFS_LINUX26_ONEGROUP_ENV */
 
 void
 afs_get_groups_from_pag(afs_uint32 pag, gid_t * g0p, gid_t * g1p)
@@ -466,6 +488,10 @@
 
 
     AFS_STATCNT(afs_get_groups_from_pag);
+#ifdef AFS_LINUX26_ONEGROUP_ENV
+    *g0p = pag;
+    *g1p = 0;
+#else
 #if !defined(UKERNEL) || !defined(AFS_WEB_ENHANCEMENTS)
     pag &= 0x7fffffff;
 #endif /* UKERNEL && AFS_WEB_ENHANCEMENTS */
@@ -475,6 +501,7 @@
     g1 |= ((pag >> 28) % 3) << 14;
     *g0p = g0 + 0x3f00;
     *g1p = g1 + 0x3f00;
+#endif
 }
 
 
@@ -507,7 +534,7 @@
 	return NOPAG;
     }
 #elif defined(AFS_LINUX26_ENV)
-    if (cred->cr_group_info->ngroups < 2) {
+    if (cred->cr_group_info->ngroups < NUMPAGGROUPS) {
 	pag = NOPAG;
 	goto out;
     }
@@ -520,6 +547,7 @@
 #if defined(AFS_AIX51_ENV)
     g0 = cred->cr_groupset.gs_union.un_groups[0];
     g1 = cred->cr_groupset.gs_union.un_groups[1];
+#elif defined(AFS_LINUX26_ONEGROUP_ENV)
 #elif defined(AFS_LINUX26_ENV)
     g0 = GROUP_AT(cred->cr_group_info, 0);
     g1 = GROUP_AT(cred->cr_group_info, 1);
@@ -528,7 +556,11 @@
     g1 = cred->cr_groups[1];
 #endif
 #endif
+#if defined(AFS_LINUX26_ONEGROUP_ENV)
+    pag = (afs_int32) afs_get_pag_from_groups(cred->cr_group_info);
+#else
     pag = (afs_int32) afs_get_pag_from_groups(g0, g1);
+#endif
 out:
 #if defined(AFS_LINUX26_ENV) && defined(LINUX_KEYRING_SUPPORT)
     if (pag == NOPAG) {
diff -ur openafs-1.4.2-pre/src/afs/afs_prototypes.h openafs-1.4.2/src/afs/afs_prototypes.h
--- openafs-1.4.2-pre/src/afs/afs_prototypes.h	2006-08-14 18:12:22.000000000 -0400
+++ openafs-1.4.2/src/afs/afs_prototypes.h	2007-03-14 13:08:02.000000000 -0400
@@ -508,7 +508,13 @@
 extern int AddPag(afs_int32 aval, struct AFS_UCRED **credpp);
 #endif
 extern int afs_InitReq(register struct vrequest *av, struct AFS_UCRED *acred);
+#if defined(UKERNEL) && defined(AFS_WEB_ENHANCEMENTS)
 extern afs_uint32 afs_get_pag_from_groups(gid_t g0a, gid_t g1a);
+#else
+#ifdef AFS_LINUX26_ONEGROUP_ENV
+extern afs_uint32 afs_get_pag_from_groups(struct group_info *gi);
+#endif
+#endif
 extern void afs_get_groups_from_pag(afs_uint32 pag, gid_t * g0p, gid_t * g1p);
 extern afs_int32 PagInCred(const struct AFS_UCRED *cred);
 

Reply via email to