From: Andrew Deason Date: Sat, 8 Aug 2015 21:49:50 +0000 (-0500) Subject: SOLARIS: Use AFS_PAG_ONEGROUP_ENV for Solaris 11 X-Git-Tag: BP-openafs-stable-1_8_x~66 X-Git-Url: https://git.openafs.org/?p=openafs.git;a=commitdiff_plain;h=aab1e71628e6a4ce68c5e59e2f815867438280d1 SOLARIS: Use AFS_PAG_ONEGROUP_ENV for Solaris 11 On Solaris 11 (specifically, Solaris 11.1+), the supplemental group list for a process is supposed to be sorted. Starting with Solaris 11.2, more authorization checks are done that assume the list is sorted (e.g., to do a binary search), so having them out of order can cause incorrect behavior. For example: $ echo foo > /tmp/testfile $ chmod 660 /tmp/testfile $ sudo chown root:daemon /tmp/testfile $ cat /tmp/testfile foo $ id -a uid=100(adeason) gid=10(staff) groups=10(staff),12(daemon),20(games),21(ftp),50(gdm),60(xvm),90(postgres) $ pagsh $ cat /tmp/testfile cat: cannot open /tmp/testfile: Permission denied $ id -a uid=100(adeason) gid=10(staff) groups=33536,32514,10(staff),12(daemon),20(games),21(ftp),50(gdm),60(xvm),90(postgres) Solaris sorts the groups given to crsetgroups() on versions which required the group ids to be sorted, but we currently manually put our PAG groups in our own order in afs_setgroups(). This is currently required, since various places in the code assume that PAG groups are the first two groups in a process's group list. To get around this, do not require the PAG gids to be the first two gids anymore. To more easily identify PAG gids in group processes, use a single gid instead of two gids to identify a PAG, like modern Linux currently uses (under the AFS_PAG_ONEGROUP_ENV). High-numbered groups have been possible for quite a long time on Solaris, allegedly further back than Solaris 8. Only do this for Solaris 11, though, to reduce the platforms we affect. [mmeffie@sinenomine.net: Define AFS_PAG_ONEGROUP_ENV in param.h.] Change-Id: I44023ee8aa42f3f69bb0c8a8e9178abd513951a1 Reviewed-on: https://gerrit.openafs.org/11979 Reviewed-by: Benjamin Kaduk Tested-by: BuildBot --- diff --git a/src/afs/SOLARIS/osi_groups.c b/src/afs/SOLARIS/osi_groups.c index bc5e3ab..6358821 100644 --- a/src/afs/SOLARIS/osi_groups.c +++ b/src/afs/SOLARIS/osi_groups.c @@ -67,6 +67,91 @@ afs_xsetgroups(uap, rvp) return code; } +#ifdef AFS_PAG_ONEGROUP_ENV +/** + * Take a PAG, and put it into the given array of gids. + * + * @param[in] pagvalue The numeric id for the PAG to assign (must not be -1) + * @param[in] gidset An array of gids + * @param[inout] a_ngroups How many entries in 'gidset' have valid gids + * @param[in] gidset_sz The number of bytes allocated for 'gidset' + * + * @return error code + */ +static int +pag_to_gidset(afs_uint32 pagvalue, gid_t *gidset, int *a_ngroups, + size_t gidset_sz) +{ + int i; + gid_t *gidslot = NULL; + int ngroups = *a_ngroups; + + osi_Assert(pagvalue != -1); + + /* See if we already have a PAG gid */ + for (i = 0; i < ngroups; i++) { + if (((gidset[i] >> 24) & 0xff) == 'A') { + gidslot = &gidset[i]; + break; + } + } + + if (gidslot != NULL) { + /* If we don't already have a PAG, grow the groups list by one, and put + * our PAG in the new empty slot. */ + if ((sizeof(gidset[0])) * (ngroups + 1) > gidset_sz) { + return E2BIG; + } + ngroups += 1; + gidslot = &gidset[ngroups-1]; + } + + /* + * For newer Solaris releases (Solaris 11), we cannot control the order of + * the supplemental groups list of a process, so we can't store PAG gids as + * the first two gids anymore. To make finding a PAG gid easier to find, + * just use a single gid to represent a PAG, and just store the PAG id + * itself in there, like is currently done on Linux. Note that our PAG ids + * all start with the byte 0x41 ('A'), so we should not collide with + * anything. GIDs with the highest bit set are special (used for Windows + * SID mapping), but anything under that range should be fine. + */ + *gidslot = pagvalue; + + *a_ngroups = ngroups; + + return 0; +} +#else +/* For earlier Solaris releases, convert a PAG number into two gids, and store + * those gids as the first groups in the supplemental group list. */ +static int +pag_to_gidset(afs_uint32 pagvalue, gid_t *gidset, int *a_ngroups, + size_t gidset_sz) +{ + int j; + int ngroups = *a_ngroups; + + osi_Assert(pagvalue != -1); + + if (afs_get_pag_from_groups(gidset[0], gidset[1]) == NOPAG) { + /* We will have to shift grouplist to make room for pag */ + if ((sizeof(gidset[0])) * (ngroups + 2) > gidset_sz) { + return E2BIG; + } + for (j = ngroups - 1; j >= 0; j--) { + gidset[j + 2] = gidset[j]; + } + ngroups += 2; + } + afs_get_groups_from_pag(pagvalue, &gidset[0], &gidset[1]); + + *a_ngroups = ngroups; + + return 0; +} +#endif + int setpag(cred, pagvalue, newpag, change_parent) struct cred **cred; @@ -76,7 +161,6 @@ setpag(cred, pagvalue, newpag, change_parent) { gid_t *gidset; int ngroups, code; - int j; size_t gidset_sz; AFS_STATCNT(setpag); @@ -90,23 +174,19 @@ setpag(cred, pagvalue, newpag, change_parent) /* must use osi_Alloc, osi_AllocSmallSpace may not be enough. */ gidset = osi_Alloc(gidset_sz); + pagvalue = (pagvalue == -1 ? genpag() : pagvalue); + mutex_enter(&curproc->p_crlock); ngroups = afs_getgroups(*cred, gidset); - if (afs_get_pag_from_groups(gidset[0], gidset[1]) == NOPAG) { - /* We will have to shift grouplist to make room for pag */ - if ((sizeof gidset[0]) * (ngroups + 2) > gidset_sz) { - mutex_exit(&curproc->p_crlock); - code = E2BIG; - goto done; - } - for (j = ngroups - 1; j >= 0; j--) { - gidset[j + 2] = gidset[j]; - } - ngroups += 2; + code = pag_to_gidset(pagvalue, gidset, &ngroups, gidset_sz); + if (code != 0) { + mutex_exit(&curproc->p_crlock); + goto done; } - *newpag = (pagvalue == -1 ? genpag() : pagvalue); - afs_get_groups_from_pag(*newpag, &gidset[0], &gidset[1]); + + *newpag = pagvalue; + /* afs_setgroups will release curproc->p_crlock */ /* exit action is same regardless of code */ code = afs_setgroups(cred, ngroups, gidset, change_parent); @@ -144,7 +224,9 @@ static int afs_setgroups(struct cred **cred, int ngroups, gid_t * gidset, int change_parent) { +#ifndef AFS_PAG_ONEGROUP_ENV gid_t *gp; +#endif AFS_STATCNT(afs_setgroups); @@ -154,17 +236,42 @@ afs_setgroups(struct cred **cred, int ngroups, gid_t * gidset, } if (!change_parent) *cred = (struct cred *)crcopy(*cred); -#if defined(AFS_SUN510_ENV) + +#ifdef AFS_PAG_ONEGROUP_ENV crsetgroups(*cred, ngroups, gidset); - gp = crgetgroups(*cred); #else +# if defined(AFS_SUN510_ENV) + crsetgroups(*cred, ngroups, gidset); + gp = crgetgroups(*cred); +# else (*cred)->cr_ngroups = ngroups; gp = (*cred)->cr_groups; -#endif +# endif while (ngroups--) *gp++ = *gidset++; +#endif /* !AFS_PAG_ONEGROUP_ENV */ + mutex_exit(&curproc->p_crlock); if (!change_parent) crset(curproc, *cred); /* broadcast to all threads */ return (0); } + +#ifdef AFS_PAG_ONEGROUP_ENV +afs_int32 +osi_get_group_pag(struct cred *cred) { + gid_t *gidset; + int ngroups; + int i; + + gidset = crgetgroups(cred); + ngroups = crgetngroups(cred); + + for (i = 0; i < ngroups; i++) { + if (((gidset[i] >> 24) & 0xff) == 'A') { + return gidset[i]; + } + } + return NOPAG; +} +#endif diff --git a/src/afs/afs_osi_pag.c b/src/afs/afs_osi_pag.c index 354a844..6b9ae1f 100644 --- a/src/afs/afs_osi_pag.c +++ b/src/afs/afs_osi_pag.c @@ -534,7 +534,6 @@ afs_DestroyReq(struct vrequest *av) } } -#ifndef AFS_PAG_ONEGROUP_ENV afs_uint32 afs_get_pag_from_groups(gid_t g0a, gid_t g1a) { @@ -562,6 +561,7 @@ afs_get_pag_from_groups(gid_t g0a, gid_t g1a) return NOPAG; } +#ifndef AFS_PAG_ONEGROUP_ENV void afs_get_groups_from_pag(afs_uint32 pag, gid_t * g0p, gid_t * g1p) { @@ -590,7 +590,13 @@ afs_get_groups_from_pag(afs_uint32 pag, gid_t *g0p, gid_t *g1p) } #endif -#if !defined(AFS_LINUX26_ENV) && !defined(AFS_DARWIN110_ENV) +#ifdef AFS_LINUX26_ENV +/* osi_get_group_pag is defined in /osi_groups.c */ +#elif defined(AFS_PAG_ONEGROUP_ENV) +/* osi_get_group_pag is defined in /osi_groups.c */ +#elif defined(AFS_DARWIN110_ENV) +/* We don't have pags, so we do not define an osi_get_group_pag */ +#else static afs_int32 osi_get_group_pag(afs_ucred_t *cred) { diff --git a/src/afs/afs_pioctl.c b/src/afs/afs_pioctl.c index f4c0f5f..babb600 100644 --- a/src/afs/afs_pioctl.c +++ b/src/afs/afs_pioctl.c @@ -4602,9 +4602,14 @@ HandleClientContext(struct afs_ioctl *ablob, int *com, GROUP_AT(afs_cr_group_info(newcred), 1) = g1; # endif #elif defined(AFS_SUN510_ENV) +# ifdef AFS_PAG_ONEGROUP_ENV + gids[0] = afs_get_pag_from_groups(g0, g1); + crsetgroups(newcred, 1, gids); +# else gids[0] = g0; gids[1] = g1; crsetgroups(newcred, 2, gids); +# endif /* !AFS_PAG_ONEGROUP_ENV */ #else newcred->cr_groups[0] = g0; newcred->cr_groups[1] = g1; diff --git a/src/afs/afs_prototypes.h b/src/afs/afs_prototypes.h index 01e3214..3c503d6 100644 --- a/src/afs/afs_prototypes.h +++ b/src/afs/afs_prototypes.h @@ -743,7 +743,7 @@ extern int setpag(afs_proc_t *proc, struct ucred **cred, afs_uint32 pagvalue, # endif /* AFS_XBSD_ENV */ #endif /* UKERNEL */ -#if defined(AFS_LINUX26_ENV) +#if defined(AFS_LINUX26_ENV) || defined(AFS_PAG_ONEGROUP_ENV) extern afs_int32 osi_get_group_pag(afs_ucred_t *cred); #endif diff --git a/src/config/param.sun4x_511.h b/src/config/param.sun4x_511.h index 93823bf..714aa0f 100644 --- a/src/config/param.sun4x_511.h +++ b/src/config/param.sun4x_511.h @@ -36,6 +36,7 @@ #define AFS_GLOBAL_SUNLOCK 1 /* For global locking */ #define RXK_LISTENER_ENV 1 #define AFS_GCPAGS 1 /* if nonzero, garbage collect PAGs */ +#define AFS_PAG_ONEGROUP_ENV 1 /* Use a single gid to indicate a PAG */ /* File system entry (used if mount.h doesn't define MOUNT_AFS */ #define AFS_MOUNT_AFS "afs" diff --git a/src/config/param.sunx86_511.h b/src/config/param.sunx86_511.h index 5ebc56e..c94bd81 100644 --- a/src/config/param.sunx86_511.h +++ b/src/config/param.sunx86_511.h @@ -37,6 +37,7 @@ #define AFS_GLOBAL_SUNLOCK 1 /* For global locking */ #define RXK_LISTENER_ENV 1 #define AFS_GCPAGS 1 /* if nonzero, garbage collect PAGs */ +#define AFS_PAG_ONEGROUP_ENV 1 /* Use a single gid to indicate a PAG */ #ifdef AFS_NAMEI_ENV #define AFS_64BIT_IOPS_ENV 1 /* needed for NAMEI... */