Linux Keyring error handling
[openafs.git] / src / afs / LINUX / osi_groups.c
index 1c25d30..cc48f6c 100644 (file)
@@ -26,8 +26,6 @@
 #include "afs/nfsclient.h"
 #include "osi_compat.h"
 
-#include <linux/smp_lock.h>
-
 #ifdef AFS_LINUX26_ONEGROUP_ENV
 # define NUMPAGGROUPS 1
 
@@ -89,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;
@@ -129,7 +127,7 @@ afs_setgroups(cred_t **cr, struct group_info *group_info, int change_parent)
 
     crset(*cr);
 
-#if defined(STRUCT_TASK_STRUCT_HAS_PARENT) && !defined(STRUCT_TASK_HAS_CRED)
+#if defined(STRUCT_TASK_STRUCT_HAS_PARENT) && !defined(STRUCT_TASK_STRUCT_HAS_CRED)
     if (change_parent) {
        old_info = current->parent->group_info;
        get_group_info(group_info);
@@ -143,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;
@@ -154,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;
 }
@@ -168,13 +170,14 @@ __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)
 {
     struct key *old;
     char desc[20];
     int code = -EINVAL;
-    int flags;
+    unsigned long flags;
 
     if (!__key_type_keyring)
        return code;
@@ -211,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) {
@@ -248,16 +253,30 @@ setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag,
            key = afs_linux_key_alloc(&key_type_afs_pag, "_pag", 0, 0, perm, KEY_ALLOC_NOT_IN_QUOTA);
 
            if (!IS_ERR(key)) {
-               key_instantiate_and_link(key, (void *) newpag, sizeof(afs_uint32),
+               code = key_instantiate_and_link(key, (void *) newpag, sizeof(afs_uint32),
                                         current_session_keyring(), NULL);
                key_put(key);
            } else {
                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;
 }
 
@@ -272,31 +291,28 @@ afs_xsetgroups(int gidsetsize, gid_t * grouplist)
     afs_uint32 junk;
     int old_pag;
 
-    lock_kernel();
     old_pag = PagInCred(cr);
     crfree(cr);
-    unlock_kernel();
 
     code = (*sys_setgroupsp) (gidsetsize, grouplist);
     if (code) {
        return code;
     }
 
-    lock_kernel();
     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);
-    unlock_kernel();
 
     /* Linux syscall ABI returns errno as negative */
     return (-code);
 }
 
 /* Intercept the standard uid32 system call. */
-extern asmlinkage long (*sys_setgroups32p) (int gidsetsize, gid_t * grouplist);
+extern asmlinkage int (*sys_setgroups32p) (int gidsetsize,
+                                          __kernel_gid32_t * grouplist);
 asmlinkage long
 afs_xsetgroups32(int gidsetsize, gid_t * grouplist)
 {
@@ -305,10 +321,8 @@ afs_xsetgroups32(int gidsetsize, gid_t * grouplist)
     afs_uint32 junk;
     int old_pag;
 
-    lock_kernel();
     old_pag = PagInCred(cr);
     crfree(cr);
-    unlock_kernel();
 
     code = (*sys_setgroups32p) (gidsetsize, grouplist);
 
@@ -316,14 +330,12 @@ afs_xsetgroups32(int gidsetsize, gid_t * grouplist)
        return code;
     }
 
-    lock_kernel();
     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);
-    unlock_kernel();
 
     /* Linux syscall ABI returns errno as negative */
     return (-code);
@@ -331,7 +343,7 @@ afs_xsetgroups32(int gidsetsize, gid_t * grouplist)
 
 #if defined(AFS_PPC64_LINUX20_ENV)
 /* Intercept the uid16 system call as used by 32bit programs. */
-extern long (*sys32_setgroupsp)(int gidsetsize, gid_t *grouplist);
+extern asmlinkage long (*sys32_setgroupsp)(int gidsetsize, gid_t *grouplist);
 asmlinkage long afs32_xsetgroups(int gidsetsize, gid_t *grouplist)
 {
     long code;
@@ -339,24 +351,20 @@ asmlinkage long afs32_xsetgroups(int gidsetsize, gid_t *grouplist)
     afs_uint32 junk;
     int old_pag;
     
-    lock_kernel();
     old_pag = PagInCred(cr);
     crfree(cr);
-    unlock_kernel();
     
     code = (*sys32_setgroupsp)(gidsetsize, grouplist);
     if (code) {
        return code;
     }
     
-    lock_kernel();
     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);
-    unlock_kernel();
     
     /* Linux syscall ABI returns errno as negative */
     return (-code);
@@ -365,7 +373,13 @@ asmlinkage long afs32_xsetgroups(int gidsetsize, gid_t *grouplist)
 
 #if defined(AFS_SPARC64_LINUX20_ENV) || defined(AFS_AMD64_LINUX20_ENV)
 /* Intercept the uid16 system call as used by 32bit programs. */
-extern long (*sys32_setgroupsp) (int gidsetsize, u16 * grouplist);
+#ifdef AFS_AMD64_LINUX20_ENV
+extern asmlinkage long (*sys32_setgroupsp) (int gidsetsize, u16 * grouplist);
+#endif /* AFS_AMD64_LINUX20_ENV */
+#ifdef AFS_SPARC64_LINUX26_ENV
+extern asmlinkage int (*sys32_setgroupsp) (int gidsetsize,
+                                          __kernel_gid32_t * grouplist);
+#endif /* AFS_SPARC64_LINUX26_ENV */
 asmlinkage long
 afs32_xsetgroups(int gidsetsize, u16 * grouplist)
 {
@@ -374,31 +388,33 @@ afs32_xsetgroups(int gidsetsize, u16 * grouplist)
     afs_uint32 junk;
     int old_pag;
     
-    lock_kernel();
     old_pag = PagInCred(cr);
     crfree(cr);
-    unlock_kernel();
     
     code = (*sys32_setgroupsp) (gidsetsize, grouplist);
     if (code) {
        return code;
     }
     
-    lock_kernel();
     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);
-    unlock_kernel();
     
     /* Linux syscall ABI returns errno as negative */
     return (-code);
 }
 
 /* Intercept the uid32 system call as used by 32bit programs. */
-extern long (*sys32_setgroups32p) (int gidsetsize, gid_t * grouplist);
+#ifdef AFS_AMD64_LINUX20_ENV
+extern asmlinkage long (*sys32_setgroups32p) (int gidsetsize, gid_t * grouplist);
+#endif /* AFS_AMD64_LINUX20_ENV */
+#ifdef AFS_SPARC64_LINUX26_ENV
+extern asmlinkage int (*sys32_setgroups32p) (int gidsetsize,
+                                            __kernel_gid32_t * grouplist);
+#endif /* AFS_SPARC64_LINUX26_ENV */
 asmlinkage long
 afs32_xsetgroups32(int gidsetsize, gid_t * grouplist)
 {
@@ -407,24 +423,20 @@ afs32_xsetgroups32(int gidsetsize, gid_t * grouplist)
     afs_uint32 junk;
     int old_pag;
 
-    lock_kernel();
     old_pag = PagInCred(cr);
     crfree(cr);
-    unlock_kernel();
 
     code = (*sys32_setgroups32p) (gidsetsize, grouplist);
     if (code) {
        return code;
     }
 
-    lock_kernel();
     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);
-    unlock_kernel();
 
     /* Linux syscall ABI returns errno as negative */
     return (-code);
@@ -440,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;
@@ -451,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 */
@@ -460,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;
 
@@ -575,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);