diff options
| author | David Howells <dhowells@redhat.com> | 2011-03-07 15:06:20 +0000 | 
|---|---|---|
| committer | James Morris <jmorris@namei.org> | 2011-03-08 11:17:22 +1100 | 
| commit | ee009e4a0d4555ed522a631bae9896399674f064 (patch) | |
| tree | ee309fb4a98d9e7792cec99935c2d33652b3f440 /security | |
| parent | fdd1b94581782a2ddf9124414e5b7a5f48ce2f9c (diff) | |
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.c | 47 | ||||
| -rw-r--r-- | security/keys/internal.h | 7 | ||||
| -rw-r--r-- | security/keys/keyctl.c | 103 | 
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;  	} | 
