Linux 3.8: session_keyring changes
[openafs.git] / src / afs / LINUX / osi_groups.c
index 9b97103..e351344 100644 (file)
 #include "afs/nfsclient.h"
 #include "osi_compat.h"
 
-#include <linux/smp_lock.h>
-
-#if defined(LINUX_KEYRING_SUPPORT)
-# include <linux/rwsem.h>
-# include <linux/key.h>
-# if defined(HAVE_LINUX_KEY_TYPE_H)
-#  include <linux/key-type.h>
-# endif
-# ifndef KEY_ALLOC_IN_QUOTA
-/* Before these flags were added in Linux commit v2.6.18-rc1~816,
- * key_alloc just took a boolean not_in_quota */
-#  define KEY_ALLOC_IN_QUOTA 0
-#  define KEY_ALLOC_NOT_IN_QUOTA 1
-# endif
-#endif
-
 #ifdef AFS_LINUX26_ONEGROUP_ENV
 # define NUMPAGGROUPS 1
 
@@ -103,7 +87,7 @@ afs_linux_pag_from_groups(struct group_info *group_info) {
 
 static inline void
 afs_linux_pag_to_groups(afs_uint32 newpag,
-                       struct group_info *old, struct group_info *new) {
+                       struct group_info *old, struct group_info **new) {
     int need_space = 0;
     int i;
     gid_t g0;
@@ -157,7 +141,7 @@ afs_setgroups(cred_t **cr, struct group_info *group_info, int change_parent)
 
 int
 __setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag,
-         int change_parent)
+         int change_parent, struct group_info **old_groups)
 {
     struct group_info *group_info;
     struct group_info *tmp;
@@ -168,12 +152,16 @@ __setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag,
     *newpag = (pagvalue == -1 ? genpag() : pagvalue);
     afs_linux_pag_to_groups(*newpag, group_info, &tmp);
 
-    put_group_info(group_info);
-    group_info = tmp;
+    if (old_groups) {
+       *old_groups = group_info;
+    } else {
+       put_group_info(group_info);
+       group_info = NULL;
+    }
 
-    afs_setgroups(cr, group_info, change_parent);
+    afs_setgroups(cr, tmp, change_parent);
 
-    put_group_info(group_info);
+    put_group_info(tmp);
 
     return 0;
 }
@@ -182,6 +170,7 @@ __setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag,
 extern struct key_type key_type_keyring __attribute__((weak));
 static struct key_type *__key_type_keyring = &key_type_keyring;
 
+/* install_session_keyring returns negative error values */
 static int
 install_session_keyring(struct key *keyring)
 {
@@ -225,29 +214,31 @@ install_session_keyring(struct key *keyring)
     }
 
     /* install the keyring */
-    spin_lock_irq(&current->sighand->siglock);
-    old = task_session_keyring(current);
-    smp_wmb();
-    task_session_keyring(current) = keyring;
-    spin_unlock_irq(&current->sighand->siglock);
-
+    old = afs_set_session_keyring(keyring);
     if (old)
-           key_put(old);
+       key_put(old);
 
 out:
     return code;
 }
 #endif /* LINUX_KEYRING_SUPPORT */
 
+/* Error codes from setpag must be positive, otherwise they don't
+ * make it back into userspace properly. Error codes from the
+ * Linux keyring utilities, and from install_session_keyring()
+ * are negative. So we need to be careful to convert them correctly
+ * here
+ */
 int
 setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag,
        int change_parent)
 {
     int code;
+    struct group_info *old_groups = NULL;
 
     AFS_STATCNT(setpag);
 
-    code = __setpag(cr, pagvalue, newpag, change_parent);
+    code = __setpag(cr, pagvalue, newpag, change_parent, &old_groups);
 
 #ifdef LINUX_KEYRING_SUPPORT
     if (code == 0 && afs_cr_rgid(*cr) != NFSXLATOR_CRED) {
@@ -269,9 +260,23 @@ setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag,
                code = PTR_ERR(key);
            }
        }
+       if (code)
+           code = -code;
     }
 #endif /* LINUX_KEYRING_SUPPORT */
 
+    if (code) {
+       if (old_groups) {
+           afs_setgroups(cr, old_groups, change_parent);
+           put_group_info(old_groups);
+           old_groups = NULL;
+       }
+       if (*newpag > -1) {
+           afs_MarkUserExpired(*newpag);
+           *newpag = -1;
+       }
+    }
+
     return code;
 }
 
@@ -297,7 +302,7 @@ afs_xsetgroups(int gidsetsize, gid_t * grouplist)
     cr = crref();
     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
        /* re-install old pag if there's room. */
-       code = __setpag(&cr, old_pag, &junk, 0);
+       code = __setpag(&cr, old_pag, &junk, 0, NULL);
     }
     crfree(cr);
 
@@ -328,7 +333,7 @@ afs_xsetgroups32(int gidsetsize, gid_t * grouplist)
     cr = crref();
     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
        /* re-install old pag if there's room. */
-       code = __setpag(&cr, old_pag, &junk, 0);
+       code = __setpag(&cr, old_pag, &junk, 0, NULL);
     }
     crfree(cr);
 
@@ -357,7 +362,7 @@ asmlinkage long afs32_xsetgroups(int gidsetsize, gid_t *grouplist)
     cr = crref();
     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
        /* re-install old pag if there's room. */
-       code = __setpag(&cr, old_pag, &junk, 0);
+       code = __setpag(&cr, old_pag, &junk, 0, NULL);
     }
     crfree(cr);
     
@@ -394,7 +399,7 @@ afs32_xsetgroups(int gidsetsize, u16 * grouplist)
     cr = crref();
     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
        /* re-install old pag if there's room. */
-       code = __setpag(&cr, old_pag, &junk, 0);
+       code = __setpag(&cr, old_pag, &junk, 0, NULL);
     }
     crfree(cr);
     
@@ -429,7 +434,7 @@ afs32_xsetgroups32(int gidsetsize, gid_t * grouplist)
     cr = crref();
     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
        /* re-install old pag if there's room. */
-       code = __setpag(&cr, old_pag, &junk, 0);
+       code = __setpag(&cr, old_pag, &junk, 0, NULL);
     }
     crfree(cr);
 
@@ -447,7 +452,11 @@ static void afs_pag_describe(const struct key *key, struct seq_file *m)
     seq_printf(m, ": %u", key->datalen);
 }
 
+#if defined(STRUCT_KEY_TYPE_HAS_PREPARSE)
+static int afs_pag_instantiate(struct key *key, struct key_preparsed_payload *prep)
+#else
 static int afs_pag_instantiate(struct key *key, const void *data, size_t datalen)
+#endif
 {
     int code;
     afs_uint32 *userpag, pag = NOPAG;
@@ -458,7 +467,11 @@ static int afs_pag_instantiate(struct key *key, const void *data, size_t datalen
     code = -EINVAL;
     get_group_info(current_group_info());
 
+#if defined(STRUCT_KEY_TYPE_HAS_PREPARSE)
+    if (prep->datalen != sizeof(afs_uint32) || !prep->data)
+#else
     if (datalen != sizeof(afs_uint32) || !data)
+#endif
        goto error;
 
     /* ensure key being set matches current pag */
@@ -467,7 +480,11 @@ static int afs_pag_instantiate(struct key *key, const void *data, size_t datalen
     if (pag == NOPAG)
        goto error;
 
-    userpag = (afs_uint32 *) data;
+#if defined(STRUCT_KEY_TYPE_HAS_PREPARSE)
+    userpag = (afs_uint32 *)prep->data;
+#else
+    userpag = (afs_uint32 *)data;
+#endif
     if (*userpag != pag)
        goto error;
 
@@ -582,7 +599,7 @@ osi_get_keyring_pag(afs_ucred_t *cred)
                if (afs_linux_cred_is_current(cred) &&
                     ((keyring_pag >> 24) & 0xff) == 'A' &&
                    keyring_pag != afs_linux_pag_from_groups(current_group_info())) {
-                       __setpag(&cred, keyring_pag, &newpag, 0);
+                       __setpag(&cred, keyring_pag, &newpag, 0, NULL);
                }
            }
            key_put(key);