From 7b272177de4c6f78db7f2315f4e30e85ab7660bb Mon Sep 17 00:00:00 2001 From: Marc Dionne Date: Sat, 24 Oct 2009 22:10:46 -0400 Subject: [PATCH] Linux: Keyrings PAG handling changes 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 Tested-by: Derrick Brashear --- src/afs/LINUX/osi_groups.c | 36 +++++++++++++++++++ src/afs/LINUX/osi_prototypes.h | 1 + src/afs/afs_osi_pag.c | 66 +++++++++++++++++----------------- 3 files changed, 69 insertions(+), 34 deletions(-) diff --git a/src/afs/LINUX/osi_groups.c b/src/afs/LINUX/osi_groups.c index c9efc7503..8c86eef36 100644 --- a/src/afs/LINUX/osi_groups.c +++ b/src/afs/LINUX/osi_groups.c @@ -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) { diff --git a/src/afs/LINUX/osi_prototypes.h b/src/afs/LINUX/osi_prototypes.h index 7c4ece92e..bf5b50668 100644 --- a/src/afs/LINUX/osi_prototypes.h +++ b/src/afs/LINUX/osi_prototypes.h @@ -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 */ diff --git a/src/afs/afs_osi_pag.c b/src/afs/afs_osi_pag.c index 740d46030..4a8eb4443 100644 --- a/src/afs/afs_osi_pag.c +++ b/src/afs/afs_osi_pag.c @@ -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; } -- 2.39.5