summary refs log tree commit diff
path: root/security
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2011-03-07 15:06:20 +0000
committerJames Morris <jmorris@namei.org>2011-03-08 11:17:22 +1100
commitee009e4a0d4555ed522a631bae9896399674f064 (patch)
treeee309fb4a98d9e7792cec99935c2d33652b3f440 /security
parentfdd1b94581782a2ddf9124414e5b7a5f48ce2f9c (diff)
downloadlinux-ee009e4a0d4555ed522a631bae9896399674f064.tar.gz
KEYS: Add an iovec version of KEYCTL_INSTANTIATE
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>
Diffstat (limited to 'security')
-rw-r--r--security/keys/compat.c47
-rw-r--r--security/keys/internal.h7
-rw-r--r--security/keys/keyctl.c103
3 files changed, 150 insertions, 7 deletions
diff --git a/security/keys/compat.c b/security/keys/compat.c
index 17c99d0149ec..338b510e9027 100644
--- a/security/keys/compat.c
+++ b/security/keys/compat.c
@@ -12,9 +12,52 @@
 #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
  *
  * This should only be called if the 64-bit arch uses weird pointers in 32-bit
@@ -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;
 	}
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 286c0959ee51..07a025f81902 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -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
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 0d7b1946ff94..427fddcaeb19 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -913,6 +913,21 @@ static int keyctl_change_reqkey_auth(struct key *key)
 }
 
 /*
+ * 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;
 	}
 
@@ -997,6 +1013,72 @@ error:
 }
 
 /*
+ * 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;
 	}