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.]

Reviewed-on: https://gerrit.openafs.org/11979
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: BuildBot <buildbot@rampaginggeek.com>
(cherry picked from commit aab1e71628e6a4ce68c5e59e2f815867438280d1)

Change-Id: I54c1f4c1be4eed1804293aebae795b165954a3a4
Reviewed-on: https://gerrit.openafs.org/12526
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Mark Vitale <mvitale@sinenomine.net>
Reviewed-by: Michael Meffie <mmeffie@sinenomine.net>
Reviewed-by: Stephan Wiesand <stephan.wiesand@desy.de>
This commit is contained in:
Andrew Deason 2015-08-08 16:49:50 -05:00 committed by Stephan Wiesand
parent 4096ee7a75
commit 3457b8965b
6 changed files with 140 additions and 20 deletions

View File

@ -78,6 +78,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;
@ -87,7 +172,6 @@ setpag(cred, pagvalue, newpag, change_parent)
{
gid_t *gidset;
int ngroups, code;
int j;
size_t gidset_sz;
AFS_STATCNT(setpag);
@ -101,23 +185,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);
@ -155,7 +235,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);
@ -165,17 +247,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);
#else
# if defined(AFS_SUN510_ENV)
crsetgroups(*cred, ngroups, gidset);
gp = crgetgroups(*cred);
#else
# 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

View File

@ -535,7 +535,6 @@ afs_DestroyReq(struct vrequest *av)
}
}
#ifndef AFS_PAG_ONEGROUP_ENV
afs_uint32
afs_get_pag_from_groups(gid_t g0a, gid_t g1a)
{
@ -563,6 +562,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)
{
@ -591,7 +591,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 <ARCH>/osi_groups.c */
#elif defined(AFS_PAG_ONEGROUP_ENV)
/* osi_get_group_pag is defined in <ARCH>/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)
{

View File

@ -4573,9 +4573,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;

View File

@ -731,7 +731,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

View File

@ -45,6 +45,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"

View File

@ -46,6 +46,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... */