Linux: Keyrings PAG handling changes
authorMarc Dionne <marc.c.dionne@gmail.com>
Sun, 25 Oct 2009 02:10:46 +0000 (22:10 -0400)
committerDerrick Brashear <shadow|account-1000005@unknown>
Mon, 23 Nov 2009 04:44:41 +0000 (20:44 -0800)
We can take advantage of the fact that PagInCred now receives
a kernel credentials structure as an argument (including any session
keyring) to make some improvements in the handling of PAGs
when keyrings are in use.

These changes are effective only if keyrings are in use and we
have a recent enough kernel where we can use the kernel
credentials structure.

1 - Search the session keyring of the passed credentials instead of
the current process' to determine the PAG, if any.  This was always
not really correct, and now we're able to do the right thing.
In some situations such as background writeback and pre-fetching,
this means that we'll now do it with the right credentials, even when
in a PAG.

2 - Don't use groups at all to determine PAG membership.  Doing so
can lead to some inconsistent situations such as the one described
in RT 125198, where a process gets access through a soon to be
deleted PAG.  Make PagInCred look exclusively at the keyrings.
Groups are still updated to try to reflect the current PAG for now,
if the passed credentials belong to the current process.

Note that a process can no longer get a PAG's privileges simply by
adding the corresponding groups to its group list.

No behaviour change for kernels prior to 2.6.29.

FIXES 125198

Change-Id: Ifb171993cc9ca9d6a97fb7312909485ec0666efb
Reviewed-on: http://gerrit.openafs.org/730
Reviewed-by: Derrick Brashear <shadow@dementia.org>
Tested-by: Derrick Brashear <shadow@dementia.org>

src/afs/LINUX/osi_groups.c
src/afs/LINUX/osi_prototypes.h
src/afs/afs_osi_pag.c

index c9efc75..8c86eef 100644 (file)
@@ -551,6 +551,42 @@ void osi_keyring_shutdown(void)
     unregister_key_type(&key_type_afs_pag);
 }
 
+afs_int32
+osi_get_keyring_pag(afs_ucred_t *cred)
+{
+    struct key *key;
+    afs_uint32 newpag;
+    afs_int32 keyring_pag = NOPAG;
+
+    if (afs_cr_rgid(cred) != NFSXLATOR_CRED) {
+
+#if defined(STRUCT_TASK_HAS_CRED)
+       /* If we have a kernel cred, search the passed credentials */
+       key = key_ref_to_ptr(keyring_search(make_key_ref(cred->tgcred->session_keyring, 1),
+               &key_type_afs_pag, "_pag"));
+#else
+       /* Search the keyrings of the current process */
+       key = request_key(&key_type_afs_pag, "_pag", NULL);
+#endif
+       if (!IS_ERR(key)) {
+           if (key_validate(key) == 0 && key->uid == 0) {      /* also verify in the session keyring? */
+               keyring_pag = key->payload.value;
+               /* Only set PAG in groups if needed, and the creds are from the current process */
+#if defined(STRUCT_TASK_HAS_CRED)
+               if (cred == current_cred() && ((keyring_pag >> 24) & 0xff) == 'A') {
+#else
+               if (((keyring_pag >> 24) & 0xff) == 'A') {
+#endif
+                   if (keyring_pag != afs_get_pag_from_groups(current_group_info()))
+                       __setpag(&cred, keyring_pag, &newpag, 0);
+               }
+           }
+           key_put(key);
+       }
+    }
+    return keyring_pag;
+}
+
 #else
 void osi_keyring_init(void)
 {
index 7c4ece9..bf5b506 100644 (file)
@@ -94,6 +94,7 @@ extern void osi_keyring_shutdown(void);
 extern int __setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag,
                    int change_parent);
 #ifdef LINUX_KEYRING_SUPPORT
+extern afs_int32 osi_get_keyring_pag(afs_ucred_t *);
 extern struct key_type key_type_afs_pag;
 #endif /* LINUX_KEYRING_SUPPORT */
 
index 740d460..4a8eb44 100644 (file)
@@ -537,9 +537,9 @@ afs_get_groups_from_pag(afs_uint32 pag, gid_t * g0p, gid_t * g1p)
 
 
 afs_int32
-PagInCred(afs_ucred_t *cred)
+afs_get_group_pag(afs_ucred_t *cred)
 {
-    afs_int32 pag;
+    afs_int32 pag = NOPAG;
 #if !defined(AFS_LINUX26_ONEGROUP_ENV)
     gid_t g0, g1;
 #endif
@@ -548,18 +548,13 @@ PagInCred(afs_ucred_t *cred)
     int ngroups;
 #endif
 
-    AFS_STATCNT(PagInCred);
-    if (cred == NULL || cred == afs_osi_credp) {
-       return NOPAG;
-    }
 #if defined(AFS_SUN510_ENV)
     gids = crgetgroups(cred);
     ngroups = crgetngroups(cred);
 #endif
 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
-    if (cred == NOCRED || cred == FSCRED) {
+    if (cred == NOCRED || cred == FSCRED)
        return NOPAG;
-    }
     if (cred->cr_ngroups < 3)
        return NOPAG;
     /* gid is stored in cr_groups[0] */
@@ -567,22 +562,18 @@ PagInCred(afs_ucred_t *cred)
     g1 = cred->cr_groups[2];
 #else
 #if defined(AFS_AIX_ENV)
-    if (cred->cr_ngrps < 2) {
+    if (cred->cr_ngrps < 2)
        return NOPAG;
-    }
 #elif defined(AFS_LINUX26_ENV)
-    if (afs_cr_group_info(cred)->ngroups < NUMPAGGROUPS) {
-       pag = NOPAG;
-       goto out;
-    }
+    if (afs_cr_group_info(cred)->ngroups < NUMPAGGROUPS)
+       return NOPAG;
 #elif defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_LINUX20_ENV) || defined(AFS_XBSD_ENV)
 #if defined(AFS_SUN510_ENV)
     if (ngroups < 2) {
 #else
     if (cred->cr_ngroups < 2) {
 #endif
-       pag = NOPAG;
-       goto out;
+       return NOPAG;
     }
 #endif
 #if defined(AFS_AIX51_ENV)
@@ -605,26 +596,33 @@ PagInCred(afs_ucred_t *cred)
 #else
     pag = (afs_int32) afs_get_pag_from_groups(g0, g1);
 #endif
-#if defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_LINUX20_ENV) || defined(AFS_XBSD_ENV)
-out:
+    return pag;
+}
+
+
+afs_int32
+PagInCred(afs_ucred_t *cred)
+{
+    afs_int32 pag = NOPAG;
+
+    AFS_STATCNT(PagInCred);
+    if (cred == NULL || cred == afs_osi_credp) {
+       return NOPAG;
+    }
+    /*
+     * If linux keyrings are in use and we carry the session keyring in our credentials
+     * structure, they should be the only criteria for determining
+     * if we're in a PAG.  Groups are updated for legacy reasons only for now,
+     * and should not be used to infer PAG membership
+     * With keyrings but no kernel credentials, look at groups first and fall back
+     * to looking at the keyrings.
+     */
+#if defined(AFS_LINUX26_ENV) && !defined(STRUCT_TASK_HAS_CRED)
+    pag = afs_get_group_pag(cred);
 #endif
 #if defined(AFS_LINUX26_ENV) && defined(LINUX_KEYRING_SUPPORT)
-    if (pag == NOPAG && afs_cr_rgid(cred) != NFSXLATOR_CRED) {
-       struct key *key;
-       afs_uint32 upag, newpag;
-
-       key = request_key(&key_type_afs_pag, "_pag", NULL);
-       if (!IS_ERR(key)) {
-           if (key_validate(key) == 0 && key->uid == 0) {      /* also verify in the session keyring? */
-               upag = (afs_uint32) key->payload.value;
-               if (((upag >> 24) & 0xff) == 'A') {
-                   __setpag(&cred, upag, &newpag, 0);
-                   pag = (afs_int32) upag;
-               }
-           }
-           key_put(key);
-       } 
-    }
+    if (pag == NOPAG)
+        pag = osi_get_keyring_pag(cred);
 #endif
     return pag;
 }