]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
KEYS: Add an iovec version of KEYCTL_INSTANTIATE
authorDavid Howells <dhowells@redhat.com>
Mon, 7 Mar 2011 15:06:20 +0000 (15:06 +0000)
committerJames Morris <jmorris@namei.org>
Tue, 8 Mar 2011 00:17:22 +0000 (11:17 +1100)
Add a keyctl op (KEYCTL_INSTANTIATE_IOV) that is like KEYCTL_INSTANTIATE, but
takes an iovec array and concatenates the data in-kernel into one buffer.
Since the KEYCTL_INSTANTIATE copies the data anyway, this isn't too much of a
problem.

Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: James Morris <jmorris@namei.org>
Documentation/keys.txt
arch/x86/Kconfig
include/linux/keyctl.h
security/keys/compat.c
security/keys/internal.h
security/keys/keyctl.c

index a6a97fdfaddddc473193c2762a6d6734cb141b2e..6523a9e6f293675a104ce77d78a896d0e9d190e5 100644 (file)
@@ -637,6 +637,9 @@ The keyctl syscall functions are:
        long keyctl(KEYCTL_INSTANTIATE, key_serial_t key,
                    const void *payload, size_t plen,
                    key_serial_t keyring);
+       long keyctl(KEYCTL_INSTANTIATE_IOV, key_serial_t key,
+                   const struct iovec *payload_iov, unsigned ioc,
+                   key_serial_t keyring);
 
      If the kernel calls back to userspace to complete the instantiation of a
      key, userspace should use this call to supply data for the key before the
@@ -652,6 +655,9 @@ The keyctl syscall functions are:
 
      The payload and plen arguments describe the payload data as for add_key().
 
+     The payload_iov and ioc arguments describe the payload data in an iovec
+     array instead of a single buffer.
+
 
  (*) Negatively instantiate a partially constructed key.
 
@@ -1244,10 +1250,11 @@ hand the request off to (perhaps a path held in placed in another key by, for
 example, the KDE desktop manager).
 
 The program (or whatever it calls) should finish construction of the key by
-calling KEYCTL_INSTANTIATE, which also permits it to cache the key in one of
-the keyrings (probably the session ring) before returning. Alternatively, the
-key can be marked as negative with KEYCTL_NEGATE or KEYCTL_REJECT; this also
-permits the key to be cached in one of the keyrings.
+calling KEYCTL_INSTANTIATE or KEYCTL_INSTANTIATE_IOV, which also permits it to
+cache the key in one of the keyrings (probably the session ring) before
+returning.  Alternatively, the key can be marked as negative with KEYCTL_NEGATE
+or KEYCTL_REJECT; this also permits the key to be cached in one of the
+keyrings.
 
 If it returns with the key remaining in the unconstructed state, the key will
 be marked as being negative, it will be added to the session keyring, and an
index d5ed94d30aadf056130464e5184461654c99ad8a..b46b75bef496e67d21cce9cf64e3e19ec407906b 100644 (file)
@@ -2138,6 +2138,11 @@ config SYSVIPC_COMPAT
        def_bool y
        depends on COMPAT && SYSVIPC
 
+config KEYS_COMPAT
+       bool
+       depends on COMPAT && KEYS
+       default y
+
 endmenu
 
 
index 7022974def0c741dc0b71cd2b401b6e92c1f0194..9b0b865ce622e5620cb56aa1bfa9caa49c5bdc13 100644 (file)
@@ -54,5 +54,6 @@
 #define KEYCTL_GET_SECURITY            17      /* get key security label */
 #define KEYCTL_SESSION_TO_PARENT       18      /* apply session keyring to parent process */
 #define KEYCTL_REJECT                  19      /* reject a partially constructed key */
+#define KEYCTL_INSTANTIATE_IOV         20      /* instantiate a partially constructed key */
 
 #endif /*  _LINUX_KEYCTL_H */
index 17c99d0149ecfc84d740c9a5501432f387375ad3..338b510e90275b1da873ed83aa907c404997d353 100644 (file)
 #include <linux/syscalls.h>
 #include <linux/keyctl.h>
 #include <linux/compat.h>
+#include <linux/slab.h>
 #include "internal.h"
 
+/*
+ * Instantiate a key with the specified compatibility multipart payload and
+ * link the key into the destination keyring if one is given.
+ *
+ * The caller must have the appropriate instantiation permit set for this to
+ * work (see keyctl_assume_authority).  No other permissions are required.
+ *
+ * If successful, 0 will be returned.
+ */
+long compat_keyctl_instantiate_key_iov(
+       key_serial_t id,
+       const struct compat_iovec __user *_payload_iov,
+       unsigned ioc,
+       key_serial_t ringid)
+{
+       struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
+       long ret;
+
+       if (_payload_iov == 0 || ioc == 0)
+               goto no_payload;
+
+       ret = compat_rw_copy_check_uvector(WRITE, _payload_iov, ioc,
+                                          ARRAY_SIZE(iovstack),
+                                          iovstack, &iov);
+       if (ret < 0)
+               return ret;
+       if (ret == 0)
+               goto no_payload_free;
+
+       ret = keyctl_instantiate_key_common(id, iov, ioc, ret, ringid);
+
+       if (iov != iovstack)
+               kfree(iov);
+       return ret;
+
+no_payload_free:
+       if (iov != iovstack)
+               kfree(iov);
+no_payload:
+       return keyctl_instantiate_key_common(id, NULL, 0, 0, ringid);
+}
+
 /*
  * The key control system call, 32-bit compatibility version for 64-bit archs
  *
@@ -88,6 +131,10 @@ asmlinkage long compat_sys_keyctl(u32 option,
        case KEYCTL_REJECT:
                return keyctl_reject_key(arg2, arg3, arg4, arg5);
 
+       case KEYCTL_INSTANTIATE_IOV:
+               return compat_keyctl_instantiate_key_iov(
+                       arg2, compat_ptr(arg3), arg4, arg5);
+
        default:
                return -EOPNOTSUPP;
        }
index 286c0959ee5134979c7547a0b5caf5eae29ecf19..07a025f8190233faf66a384816dea03754ee770a 100644 (file)
@@ -215,6 +215,13 @@ extern long keyctl_get_security(key_serial_t keyid, char __user *buffer,
                                size_t buflen);
 extern long keyctl_session_to_parent(void);
 extern long keyctl_reject_key(key_serial_t, unsigned, unsigned, key_serial_t);
+extern long keyctl_instantiate_key_iov(key_serial_t,
+                                      const struct iovec __user *,
+                                      unsigned, key_serial_t);
+
+extern long keyctl_instantiate_key_common(key_serial_t,
+                                         const struct iovec __user *,
+                                         unsigned, size_t, key_serial_t);
 
 /*
  * Debugging key validation
index 0d7b1946ff94464dc4f700dea1e3dd2eaa82dcc2..427fddcaeb19b78049b3e1028349cb6b731ed8f9 100644 (file)
@@ -912,6 +912,21 @@ static int keyctl_change_reqkey_auth(struct key *key)
        return commit_creds(new);
 }
 
+/*
+ * Copy the iovec data from userspace
+ */
+static long copy_from_user_iovec(void *buffer, const struct iovec *iov,
+                                unsigned ioc)
+{
+       for (; ioc > 0; ioc--) {
+               if (copy_from_user(buffer, iov->iov_base, iov->iov_len) != 0)
+                       return -EFAULT;
+               buffer += iov->iov_len;
+               iov++;
+       }
+       return 0;
+}
+
 /*
  * Instantiate a key with the specified payload and link the key into the
  * destination keyring if one is given.
@@ -921,10 +936,11 @@ static int keyctl_change_reqkey_auth(struct key *key)
  *
  * If successful, 0 will be returned.
  */
-long keyctl_instantiate_key(key_serial_t id,
-                           const void __user *_payload,
-                           size_t plen,
-                           key_serial_t ringid)
+long keyctl_instantiate_key_common(key_serial_t id,
+                                  const struct iovec *payload_iov,
+                                  unsigned ioc,
+                                  size_t plen,
+                                  key_serial_t ringid)
 {
        const struct cred *cred = current_cred();
        struct request_key_auth *rka;
@@ -953,7 +969,7 @@ long keyctl_instantiate_key(key_serial_t id,
        /* pull the payload in if one was supplied */
        payload = NULL;
 
-       if (_payload) {
+       if (payload_iov) {
                ret = -ENOMEM;
                payload = kmalloc(plen, GFP_KERNEL);
                if (!payload) {
@@ -965,8 +981,8 @@ long keyctl_instantiate_key(key_serial_t id,
                                goto error;
                }
 
-               ret = -EFAULT;
-               if (copy_from_user(payload, _payload, plen) != 0)
+               ret = copy_from_user_iovec(payload, payload_iov, ioc);
+               if (ret < 0)
                        goto error2;
        }
 
@@ -996,6 +1012,72 @@ error:
        return ret;
 }
 
+/*
+ * Instantiate a key with the specified payload and link the key into the
+ * destination keyring if one is given.
+ *
+ * The caller must have the appropriate instantiation permit set for this to
+ * work (see keyctl_assume_authority).  No other permissions are required.
+ *
+ * If successful, 0 will be returned.
+ */
+long keyctl_instantiate_key(key_serial_t id,
+                           const void __user *_payload,
+                           size_t plen,
+                           key_serial_t ringid)
+{
+       if (_payload && plen) {
+               struct iovec iov[1] = {
+                       [0].iov_base = (void __user *)_payload,
+                       [0].iov_len  = plen
+               };
+
+               return keyctl_instantiate_key_common(id, iov, 1, plen, ringid);
+       }
+
+       return keyctl_instantiate_key_common(id, NULL, 0, 0, ringid);
+}
+
+/*
+ * Instantiate a key with the specified multipart payload and link the key into
+ * the destination keyring if one is given.
+ *
+ * The caller must have the appropriate instantiation permit set for this to
+ * work (see keyctl_assume_authority).  No other permissions are required.
+ *
+ * If successful, 0 will be returned.
+ */
+long keyctl_instantiate_key_iov(key_serial_t id,
+                               const struct iovec __user *_payload_iov,
+                               unsigned ioc,
+                               key_serial_t ringid)
+{
+       struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
+       long ret;
+
+       if (_payload_iov == 0 || ioc == 0)
+               goto no_payload;
+
+       ret = rw_copy_check_uvector(WRITE, _payload_iov, ioc,
+                                   ARRAY_SIZE(iovstack), iovstack, &iov);
+       if (ret < 0)
+               return ret;
+       if (ret == 0)
+               goto no_payload_free;
+
+       ret = keyctl_instantiate_key_common(id, iov, ioc, ret, ringid);
+
+       if (iov != iovstack)
+               kfree(iov);
+       return ret;
+
+no_payload_free:
+       if (iov != iovstack)
+               kfree(iov);
+no_payload:
+       return keyctl_instantiate_key_common(id, NULL, 0, 0, ringid);
+}
+
 /*
  * Negatively instantiate the key with the given timeout (in seconds) and link
  * the key into the destination keyring if one is given.
@@ -1528,6 +1610,13 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
                                         (unsigned) arg4,
                                         (key_serial_t) arg5);
 
+       case KEYCTL_INSTANTIATE_IOV:
+               return keyctl_instantiate_key_iov(
+                       (key_serial_t) arg2,
+                       (const struct iovec __user *) arg3,
+                       (unsigned) arg4,
+                       (key_serial_t) arg5);
+
        default:
                return -EOPNOTSUPP;
        }