SOLARIS: Use AFS_PAG_ONEGROUP_ENV for Solaris 11 79/11979/5
authorAndrew Deason <adeason@dson.org>
Sat, 8 Aug 2015 21:49:50 +0000 (16:49 -0500)
committerBenjamin Kaduk <kaduk@mit.edu>
Sun, 8 Jan 2017 21:07:32 +0000 (16:07 -0500)
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 <kaduk@mit.edu>
Tested-by: BuildBot <buildbot@rampaginggeek.com>

src/afs/SOLARIS/osi_groups.c
src/afs/afs_osi_pag.c
src/afs/afs_pioctl.c
src/afs/afs_prototypes.h
src/config/param.sun4x_511.h
src/config/param.sunx86_511.h

index bc5e3ab..6358821 100644 (file)
@@ -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
index 354a844..6b9ae1f 100644 (file)
@@ -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 <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)
 {
index f4c0f5f..babb600 100644 (file)
@@ -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;
index 01e3214..3c503d6 100644 (file)
@@ -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
 
index 93823bf..714aa0f 100644 (file)
@@ -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"
index 5ebc56e..c94bd81 100644 (file)
@@ -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... */