kconfig: fix default value for choice input
[pandora-kernel.git] / security / keys / keyctl.c
index b7a468f..ed71d86 100644 (file)
 #include <linux/syscalls.h>
 #include <linux/keyctl.h>
 #include <linux/fs.h>
+#include <linux/capability.h>
+#include <linux/string.h>
 #include <linux/err.h>
 #include <asm/uaccess.h>
 #include "internal.h"
 
+static int key_get_type_from_user(char *type,
+                                 const char __user *_type,
+                                 unsigned len)
+{
+       int ret;
+
+       ret = strncpy_from_user(type, _type, len);
+
+       if (ret < 0)
+               return -EFAULT;
+
+       if (ret == 0 || ret >= len)
+               return -EINVAL;
+
+       if (type[0] == '.')
+               return -EPERM;
+
+       type[len - 1] = '\0';
+
+       return 0;
+}
+
 /*****************************************************************************/
 /*
  * extract the description of a new key from userspace and either add it as a
@@ -37,39 +61,22 @@ asmlinkage long sys_add_key(const char __user *_type,
        key_ref_t keyring_ref, key_ref;
        char type[32], *description;
        void *payload;
-       long dlen, ret;
+       long ret;
 
        ret = -EINVAL;
        if (plen > 32767)
                goto error;
 
        /* draw all the data into kernel space */
-       ret = strncpy_from_user(type, _type, sizeof(type) - 1);
+       ret = key_get_type_from_user(type, _type, sizeof(type));
        if (ret < 0)
                goto error;
-       type[31] = '\0';
-
-       ret = -EPERM;
-       if (type[0] == '.')
-               goto error;
-
-       ret = -EFAULT;
-       dlen = strnlen_user(_description, PAGE_SIZE - 1);
-       if (dlen <= 0)
-               goto error;
-
-       ret = -EINVAL;
-       if (dlen > PAGE_SIZE - 1)
-               goto error;
 
-       ret = -ENOMEM;
-       description = kmalloc(dlen + 1, GFP_KERNEL);
-       if (!description)
+       description = strndup_user(_description, PAGE_SIZE);
+       if (IS_ERR(description)) {
+               ret = PTR_ERR(description);
                goto error;
-
-       ret = -EFAULT;
-       if (copy_from_user(description, _description, dlen + 1) != 0)
-               goto error2;
+       }
 
        /* pull the payload in if one was supplied */
        payload = NULL;
@@ -134,57 +141,28 @@ asmlinkage long sys_request_key(const char __user *_type,
        struct key *key;
        key_ref_t dest_ref;
        char type[32], *description, *callout_info;
-       long dlen, ret;
+       long ret;
 
        /* pull the type into kernel space */
-       ret = strncpy_from_user(type, _type, sizeof(type) - 1);
+       ret = key_get_type_from_user(type, _type, sizeof(type));
        if (ret < 0)
                goto error;
-       type[31] = '\0';
-
-       ret = -EPERM;
-       if (type[0] == '.')
-               goto error;
 
        /* pull the description into kernel space */
-       ret = -EFAULT;
-       dlen = strnlen_user(_description, PAGE_SIZE - 1);
-       if (dlen <= 0)
-               goto error;
-
-       ret = -EINVAL;
-       if (dlen > PAGE_SIZE - 1)
-               goto error;
-
-       ret = -ENOMEM;
-       description = kmalloc(dlen + 1, GFP_KERNEL);
-       if (!description)
+       description = strndup_user(_description, PAGE_SIZE);
+       if (IS_ERR(description)) {
+               ret = PTR_ERR(description);
                goto error;
-
-       ret = -EFAULT;
-       if (copy_from_user(description, _description, dlen + 1) != 0)
-               goto error2;
+       }
 
        /* pull the callout info into kernel space */
        callout_info = NULL;
        if (_callout_info) {
-               ret = -EFAULT;
-               dlen = strnlen_user(_callout_info, PAGE_SIZE - 1);
-               if (dlen <= 0)
-                       goto error2;
-
-               ret = -EINVAL;
-               if (dlen > PAGE_SIZE - 1)
-                       goto error2;
-
-               ret = -ENOMEM;
-               callout_info = kmalloc(dlen + 1, GFP_KERNEL);
-               if (!callout_info)
+               callout_info = strndup_user(_callout_info, PAGE_SIZE);
+               if (IS_ERR(callout_info)) {
+                       ret = PTR_ERR(callout_info);
                        goto error2;
-
-               ret = -EFAULT;
-               if (copy_from_user(callout_info, _callout_info, dlen + 1) != 0)
-                       goto error3;
+               }
        }
 
        /* get the destination keyring if specified */
@@ -260,35 +238,21 @@ long keyctl_get_keyring_ID(key_serial_t id, int create)
 long keyctl_join_session_keyring(const char __user *_name)
 {
        char *name;
-       long nlen, ret;
+       long ret;
 
        /* fetch the name from userspace */
        name = NULL;
        if (_name) {
-               ret = -EFAULT;
-               nlen = strnlen_user(_name, PAGE_SIZE - 1);
-               if (nlen <= 0)
-                       goto error;
-
-               ret = -EINVAL;
-               if (nlen > PAGE_SIZE - 1)
-                       goto error;
-
-               ret = -ENOMEM;
-               name = kmalloc(nlen + 1, GFP_KERNEL);
-               if (!name)
+               name = strndup_user(_name, PAGE_SIZE);
+               if (IS_ERR(name)) {
+                       ret = PTR_ERR(name);
                        goto error;
-
-               ret = -EFAULT;
-               if (copy_from_user(name, _name, nlen + 1) != 0)
-                       goto error2;
+               }
        }
 
        /* join the session */
        ret = join_session_keyring(name);
 
- error2:
-       kfree(name);
  error:
        return ret;
 
@@ -561,31 +525,18 @@ long keyctl_keyring_search(key_serial_t ringid,
        struct key_type *ktype;
        key_ref_t keyring_ref, key_ref, dest_ref;
        char type[32], *description;
-       long dlen, ret;
+       long ret;
 
        /* pull the type and description into kernel space */
-       ret = strncpy_from_user(type, _type, sizeof(type) - 1);
+       ret = key_get_type_from_user(type, _type, sizeof(type));
        if (ret < 0)
                goto error;
-       type[31] = '\0';
-
-       ret = -EFAULT;
-       dlen = strnlen_user(_description, PAGE_SIZE - 1);
-       if (dlen <= 0)
-               goto error;
 
-       ret = -EINVAL;
-       if (dlen > PAGE_SIZE - 1)
+       description = strndup_user(_description, PAGE_SIZE);
+       if (IS_ERR(description)) {
+               ret = PTR_ERR(description);
                goto error;
-
-       ret = -ENOMEM;
-       description = kmalloc(dlen + 1, GFP_KERNEL);
-       if (!description)
-               goto error;
-
-       ret = -EFAULT;
-       if (copy_from_user(description, _description, dlen + 1) != 0)
-               goto error2;
+       }
 
        /* get the keyring at which to begin the search */
        keyring_ref = lookup_user_key(NULL, ringid, 0, 0, KEY_SEARCH);
@@ -834,6 +785,17 @@ long keyctl_instantiate_key(key_serial_t id,
        if (plen > 32767)
                goto error;
 
+       /* the appropriate instantiation authorisation key must have been
+        * assumed before calling this */
+       ret = -EPERM;
+       instkey = current->request_key_auth;
+       if (!instkey)
+               goto error;
+
+       rka = instkey->payload.data;
+       if (rka->target_key->serial != id)
+               goto error;
+
        /* pull the payload in if one was supplied */
        payload = NULL;
 
@@ -848,15 +810,6 @@ long keyctl_instantiate_key(key_serial_t id,
                        goto error2;
        }
 
-       /* find the instantiation authorisation key */
-       instkey = key_get_instantiation_authkey(id);
-       if (IS_ERR(instkey)) {
-               ret = PTR_ERR(instkey);
-               goto error2;
-       }
-
-       rka = instkey->payload.data;
-
        /* find the destination keyring amongst those belonging to the
         * requesting task */
        keyring_ref = NULL;
@@ -865,7 +818,7 @@ long keyctl_instantiate_key(key_serial_t id,
                                              KEY_WRITE);
                if (IS_ERR(keyring_ref)) {
                        ret = PTR_ERR(keyring_ref);
-                       goto error3;
+                       goto error2;
                }
        }
 
@@ -874,11 +827,17 @@ long keyctl_instantiate_key(key_serial_t id,
                                       key_ref_to_ptr(keyring_ref), instkey);
 
        key_ref_put(keyring_ref);
- error3:
-       key_put(instkey);
- error2:
+
+       /* discard the assumed authority if it's just been disabled by
+        * instantiation of the key */
+       if (ret == 0) {
+               key_put(current->request_key_auth);
+               current->request_key_auth = NULL;
+       }
+
+error2:
        kfree(payload);
- error:
+error:
        return ret;
 
 } /* end keyctl_instantiate_key() */
@@ -895,14 +854,16 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
        key_ref_t keyring_ref;
        long ret;
 
-       /* find the instantiation authorisation key */
-       instkey = key_get_instantiation_authkey(id);
-       if (IS_ERR(instkey)) {
-               ret = PTR_ERR(instkey);
+       /* the appropriate instantiation authorisation key must have been
+        * assumed before calling this */
+       ret = -EPERM;
+       instkey = current->request_key_auth;
+       if (!instkey)
                goto error;
-       }
 
        rka = instkey->payload.data;
+       if (rka->target_key->serial != id)
+               goto error;
 
        /* find the destination keyring if present (which must also be
         * writable) */
@@ -911,7 +872,7 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
                keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE);
                if (IS_ERR(keyring_ref)) {
                        ret = PTR_ERR(keyring_ref);
-                       goto error2;
+                       goto error;
                }
        }
 
@@ -920,9 +881,15 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
                                  key_ref_to_ptr(keyring_ref), instkey);
 
        key_ref_put(keyring_ref);
- error2:
-       key_put(instkey);
- error:
+
+       /* discard the assumed authority if it's just been disabled by
+        * instantiation of the key */
+       if (ret == 0) {
+               key_put(current->request_key_auth);
+               current->request_key_auth = NULL;
+       }
+
+error:
        return ret;
 
 } /* end keyctl_negate_key() */
@@ -965,6 +932,88 @@ long keyctl_set_reqkey_keyring(int reqkey_defl)
 
 } /* end keyctl_set_reqkey_keyring() */
 
+/*****************************************************************************/
+/*
+ * set or clear the timeout for a key
+ */
+long keyctl_set_timeout(key_serial_t id, unsigned timeout)
+{
+       struct timespec now;
+       struct key *key;
+       key_ref_t key_ref;
+       time_t expiry;
+       long ret;
+
+       key_ref = lookup_user_key(NULL, id, 1, 1, KEY_SETATTR);
+       if (IS_ERR(key_ref)) {
+               ret = PTR_ERR(key_ref);
+               goto error;
+       }
+
+       key = key_ref_to_ptr(key_ref);
+
+       /* make the changes with the locks held to prevent races */
+       down_write(&key->sem);
+
+       expiry = 0;
+       if (timeout > 0) {
+               now = current_kernel_time();
+               expiry = now.tv_sec + timeout;
+       }
+
+       key->expiry = expiry;
+
+       up_write(&key->sem);
+       key_put(key);
+
+       ret = 0;
+error:
+       return ret;
+
+} /* end keyctl_set_timeout() */
+
+/*****************************************************************************/
+/*
+ * assume the authority to instantiate the specified key
+ */
+long keyctl_assume_authority(key_serial_t id)
+{
+       struct key *authkey;
+       long ret;
+
+       /* special key IDs aren't permitted */
+       ret = -EINVAL;
+       if (id < 0)
+               goto error;
+
+       /* we divest ourselves of authority if given an ID of 0 */
+       if (id == 0) {
+               key_put(current->request_key_auth);
+               current->request_key_auth = NULL;
+               ret = 0;
+               goto error;
+       }
+
+       /* attempt to assume the authority temporarily granted to us whilst we
+        * instantiate the specified key
+        * - the authorisation key must be in the current task's keyrings
+        *   somewhere
+        */
+       authkey = key_get_instantiation_authkey(id);
+       if (IS_ERR(authkey)) {
+               ret = PTR_ERR(authkey);
+               goto error;
+       }
+
+       key_put(current->request_key_auth);
+       current->request_key_auth = authkey;
+       ret = authkey->serial;
+
+error:
+       return ret;
+
+} /* end keyctl_assume_authority() */
+
 /*****************************************************************************/
 /*
  * the key control system call
@@ -1038,6 +1087,13 @@ asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3,
        case KEYCTL_SET_REQKEY_KEYRING:
                return keyctl_set_reqkey_keyring(arg2);
 
+       case KEYCTL_SET_TIMEOUT:
+               return keyctl_set_timeout((key_serial_t) arg2,
+                                         (unsigned) arg3);
+
+       case KEYCTL_ASSUME_AUTHORITY:
+               return keyctl_assume_authority((key_serial_t) arg2);
+
        default:
                return -EOPNOTSUPP;
        }