Browse Source

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>
David Howells 14 years ago
parent
commit
ee009e4a0d
6 changed files with 167 additions and 11 deletions
  1. 11 4
      Documentation/keys.txt
  2. 5 0
      arch/x86/Kconfig
  3. 1 0
      include/linux/keyctl.h
  4. 47 0
      security/keys/compat.c
  5. 7 0
      security/keys/internal.h
  6. 96 7
      security/keys/keyctl.c

+ 11 - 4
Documentation/keys.txt

@@ -637,6 +637,9 @@ The keyctl syscall functions are:
 	long keyctl(KEYCTL_INSTANTIATE, key_serial_t key,
 	long keyctl(KEYCTL_INSTANTIATE, key_serial_t key,
 		    const void *payload, size_t plen,
 		    const void *payload, size_t plen,
 		    key_serial_t keyring);
 		    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
      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
      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 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.
  (*) 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).
 example, the KDE desktop manager).
 
 
 The program (or whatever it calls) should finish construction of the key by
 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
 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
 be marked as being negative, it will be added to the session keyring, and an

+ 5 - 0
arch/x86/Kconfig

@@ -2138,6 +2138,11 @@ config SYSVIPC_COMPAT
 	def_bool y
 	def_bool y
 	depends on COMPAT && SYSVIPC
 	depends on COMPAT && SYSVIPC
 
 
+config KEYS_COMPAT
+	bool
+	depends on COMPAT && KEYS
+	default y
+
 endmenu
 endmenu
 
 
 
 

+ 1 - 0
include/linux/keyctl.h

@@ -54,5 +54,6 @@
 #define KEYCTL_GET_SECURITY		17	/* get key security label */
 #define KEYCTL_GET_SECURITY		17	/* get key security label */
 #define KEYCTL_SESSION_TO_PARENT	18	/* apply session keyring to parent process */
 #define KEYCTL_SESSION_TO_PARENT	18	/* apply session keyring to parent process */
 #define KEYCTL_REJECT			19	/* reject a partially constructed key */
 #define KEYCTL_REJECT			19	/* reject a partially constructed key */
+#define KEYCTL_INSTANTIATE_IOV		20	/* instantiate a partially constructed key */
 
 
 #endif /*  _LINUX_KEYCTL_H */
 #endif /*  _LINUX_KEYCTL_H */

+ 47 - 0
security/keys/compat.c

@@ -12,8 +12,51 @@
 #include <linux/syscalls.h>
 #include <linux/syscalls.h>
 #include <linux/keyctl.h>
 #include <linux/keyctl.h>
 #include <linux/compat.h>
 #include <linux/compat.h>
+#include <linux/slab.h>
 #include "internal.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
  * 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:
 	case KEYCTL_REJECT:
 		return keyctl_reject_key(arg2, arg3, arg4, arg5);
 		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:
 	default:
 		return -EOPNOTSUPP;
 		return -EOPNOTSUPP;
 	}
 	}

+ 7 - 0
security/keys/internal.h

@@ -215,6 +215,13 @@ extern long keyctl_get_security(key_serial_t keyid, char __user *buffer,
 				size_t buflen);
 				size_t buflen);
 extern long keyctl_session_to_parent(void);
 extern long keyctl_session_to_parent(void);
 extern long keyctl_reject_key(key_serial_t, unsigned, unsigned, key_serial_t);
 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
  * Debugging key validation

+ 96 - 7
security/keys/keyctl.c

@@ -912,6 +912,21 @@ static int keyctl_change_reqkey_auth(struct key *key)
 	return commit_creds(new);
 	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
  * Instantiate a key with the specified payload and link the key into the
  * destination keyring if one is given.
  * 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.
  * 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();
 	const struct cred *cred = current_cred();
 	struct request_key_auth *rka;
 	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 */
 	/* pull the payload in if one was supplied */
 	payload = NULL;
 	payload = NULL;
 
 
-	if (_payload) {
+	if (payload_iov) {
 		ret = -ENOMEM;
 		ret = -ENOMEM;
 		payload = kmalloc(plen, GFP_KERNEL);
 		payload = kmalloc(plen, GFP_KERNEL);
 		if (!payload) {
 		if (!payload) {
@@ -965,8 +981,8 @@ long keyctl_instantiate_key(key_serial_t id,
 				goto error;
 				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;
 			goto error2;
 	}
 	}
 
 
@@ -996,6 +1012,72 @@ error:
 	return ret;
 	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
  * Negatively instantiate the key with the given timeout (in seconds) and link
  * the key into the destination keyring if one is given.
  * 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,
 					 (unsigned) arg4,
 					 (key_serial_t) arg5);
 					 (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:
 	default:
 		return -EOPNOTSUPP;
 		return -EOPNOTSUPP;
 	}
 	}