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);