Merge tag 'keys-fixes-20200329' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs
Pull keyrings fixes from David Howells: "Here's a couple of patches that fix a circular dependency between holding key->sem and mm->mmap_sem when reading data from a key. One potential issue is that a filesystem looking to use a key inside, say, ->readpages() could deadlock if the key being read is the key that's required and the buffer the key is being read into is on a page that needs to be fetched. The case actually detected is a bit more involved - with a filesystem calling request_key() and locking the target keyring for write - which could be being read" * tag 'keys-fixes-20200329' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs: KEYS: Avoid false positive ENOMEM error on key read KEYS: Don't write out to userspace while holding key semaphore
This commit is contained in:
@@ -352,7 +352,7 @@ void big_key_describe(const struct key *key, struct seq_file *m)
|
||||
* read the key data
|
||||
* - the key's semaphore is read-locked
|
||||
*/
|
||||
long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
|
||||
long big_key_read(const struct key *key, char *buffer, size_t buflen)
|
||||
{
|
||||
size_t datalen = (size_t)key->payload.data[big_key_len];
|
||||
long ret;
|
||||
@@ -391,9 +391,8 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
|
||||
|
||||
ret = datalen;
|
||||
|
||||
/* copy decrypted data to user */
|
||||
if (copy_to_user(buffer, buf->virt, datalen) != 0)
|
||||
ret = -EFAULT;
|
||||
/* copy out decrypted data */
|
||||
memcpy(buffer, buf->virt, datalen);
|
||||
|
||||
err_fput:
|
||||
fput(file);
|
||||
@@ -401,9 +400,7 @@ error:
|
||||
big_key_free_buffer(buf);
|
||||
} else {
|
||||
ret = datalen;
|
||||
if (copy_to_user(buffer, key->payload.data[big_key_data],
|
||||
datalen) != 0)
|
||||
ret = -EFAULT;
|
||||
memcpy(buffer, key->payload.data[big_key_data], datalen);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@@ -902,14 +902,14 @@ out:
|
||||
}
|
||||
|
||||
/*
|
||||
* encrypted_read - format and copy the encrypted data to userspace
|
||||
* encrypted_read - format and copy out the encrypted data
|
||||
*
|
||||
* The resulting datablob format is:
|
||||
* <master-key name> <decrypted data length> <encrypted iv> <encrypted data>
|
||||
*
|
||||
* On success, return to userspace the encrypted key datablob size.
|
||||
*/
|
||||
static long encrypted_read(const struct key *key, char __user *buffer,
|
||||
static long encrypted_read(const struct key *key, char *buffer,
|
||||
size_t buflen)
|
||||
{
|
||||
struct encrypted_key_payload *epayload;
|
||||
@@ -957,8 +957,7 @@ static long encrypted_read(const struct key *key, char __user *buffer,
|
||||
key_put(mkey);
|
||||
memzero_explicit(derived_key, sizeof(derived_key));
|
||||
|
||||
if (copy_to_user(buffer, ascii_buf, asciiblob_len) != 0)
|
||||
ret = -EFAULT;
|
||||
memcpy(buffer, ascii_buf, asciiblob_len);
|
||||
kzfree(ascii_buf);
|
||||
|
||||
return asciiblob_len;
|
||||
|
@@ -16,6 +16,8 @@
|
||||
#include <linux/keyctl.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
struct iovec;
|
||||
|
||||
@@ -349,4 +351,14 @@ static inline void key_check(const struct key *key)
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper function to clear and free a kvmalloc'ed memory object.
|
||||
*/
|
||||
static inline void __kvzfree(const void *addr, size_t len)
|
||||
{
|
||||
if (addr) {
|
||||
memset((void *)addr, 0, len);
|
||||
kvfree(addr);
|
||||
}
|
||||
}
|
||||
#endif /* _INTERNAL_H */
|
||||
|
@@ -339,7 +339,7 @@ long keyctl_update_key(key_serial_t id,
|
||||
payload = NULL;
|
||||
if (plen) {
|
||||
ret = -ENOMEM;
|
||||
payload = kmalloc(plen, GFP_KERNEL);
|
||||
payload = kvmalloc(plen, GFP_KERNEL);
|
||||
if (!payload)
|
||||
goto error;
|
||||
|
||||
@@ -360,7 +360,7 @@ long keyctl_update_key(key_serial_t id,
|
||||
|
||||
key_ref_put(key_ref);
|
||||
error2:
|
||||
kzfree(payload);
|
||||
__kvzfree(payload, plen);
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
@@ -797,6 +797,21 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call the read method
|
||||
*/
|
||||
static long __keyctl_read_key(struct key *key, char *buffer, size_t buflen)
|
||||
{
|
||||
long ret;
|
||||
|
||||
down_read(&key->sem);
|
||||
ret = key_validate(key);
|
||||
if (ret == 0)
|
||||
ret = key->type->read(key, buffer, buflen);
|
||||
up_read(&key->sem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a key's payload.
|
||||
*
|
||||
@@ -812,26 +827,28 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
|
||||
struct key *key;
|
||||
key_ref_t key_ref;
|
||||
long ret;
|
||||
char *key_data = NULL;
|
||||
size_t key_data_len;
|
||||
|
||||
/* find the key first */
|
||||
key_ref = lookup_user_key(keyid, 0, 0);
|
||||
if (IS_ERR(key_ref)) {
|
||||
ret = -ENOKEY;
|
||||
goto error;
|
||||
goto out;
|
||||
}
|
||||
|
||||
key = key_ref_to_ptr(key_ref);
|
||||
|
||||
ret = key_read_state(key);
|
||||
if (ret < 0)
|
||||
goto error2; /* Negatively instantiated */
|
||||
goto key_put_out; /* Negatively instantiated */
|
||||
|
||||
/* see if we can read it directly */
|
||||
ret = key_permission(key_ref, KEY_NEED_READ);
|
||||
if (ret == 0)
|
||||
goto can_read_key;
|
||||
if (ret != -EACCES)
|
||||
goto error2;
|
||||
goto key_put_out;
|
||||
|
||||
/* we can't; see if it's searchable from this process's keyrings
|
||||
* - we automatically take account of the fact that it may be
|
||||
@@ -839,26 +856,78 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
|
||||
*/
|
||||
if (!is_key_possessed(key_ref)) {
|
||||
ret = -EACCES;
|
||||
goto error2;
|
||||
goto key_put_out;
|
||||
}
|
||||
|
||||
/* the key is probably readable - now try to read it */
|
||||
can_read_key:
|
||||
ret = -EOPNOTSUPP;
|
||||
if (key->type->read) {
|
||||
/* Read the data with the semaphore held (since we might sleep)
|
||||
* to protect against the key being updated or revoked.
|
||||
*/
|
||||
down_read(&key->sem);
|
||||
ret = key_validate(key);
|
||||
if (ret == 0)
|
||||
ret = key->type->read(key, buffer, buflen);
|
||||
up_read(&key->sem);
|
||||
if (!key->type->read) {
|
||||
ret = -EOPNOTSUPP;
|
||||
goto key_put_out;
|
||||
}
|
||||
|
||||
error2:
|
||||
if (!buffer || !buflen) {
|
||||
/* Get the key length from the read method */
|
||||
ret = __keyctl_read_key(key, NULL, 0);
|
||||
goto key_put_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the data with the semaphore held (since we might sleep)
|
||||
* to protect against the key being updated or revoked.
|
||||
*
|
||||
* Allocating a temporary buffer to hold the keys before
|
||||
* transferring them to user buffer to avoid potential
|
||||
* deadlock involving page fault and mmap_sem.
|
||||
*
|
||||
* key_data_len = (buflen <= PAGE_SIZE)
|
||||
* ? buflen : actual length of key data
|
||||
*
|
||||
* This prevents allocating arbitrary large buffer which can
|
||||
* be much larger than the actual key length. In the latter case,
|
||||
* at least 2 passes of this loop is required.
|
||||
*/
|
||||
key_data_len = (buflen <= PAGE_SIZE) ? buflen : 0;
|
||||
for (;;) {
|
||||
if (key_data_len) {
|
||||
key_data = kvmalloc(key_data_len, GFP_KERNEL);
|
||||
if (!key_data) {
|
||||
ret = -ENOMEM;
|
||||
goto key_put_out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = __keyctl_read_key(key, key_data, key_data_len);
|
||||
|
||||
/*
|
||||
* Read methods will just return the required length without
|
||||
* any copying if the provided length isn't large enough.
|
||||
*/
|
||||
if (ret <= 0 || ret > buflen)
|
||||
break;
|
||||
|
||||
/*
|
||||
* The key may change (unlikely) in between 2 consecutive
|
||||
* __keyctl_read_key() calls. In this case, we reallocate
|
||||
* a larger buffer and redo the key read when
|
||||
* key_data_len < ret <= buflen.
|
||||
*/
|
||||
if (ret > key_data_len) {
|
||||
if (unlikely(key_data))
|
||||
__kvzfree(key_data, key_data_len);
|
||||
key_data_len = ret;
|
||||
continue; /* Allocate buffer */
|
||||
}
|
||||
|
||||
if (copy_to_user(buffer, key_data, ret))
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
__kvzfree(key_data, key_data_len);
|
||||
|
||||
key_put_out:
|
||||
key_put(key);
|
||||
error:
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@@ -459,7 +459,6 @@ static int keyring_read_iterator(const void *object, void *data)
|
||||
{
|
||||
struct keyring_read_iterator_context *ctx = data;
|
||||
const struct key *key = keyring_ptr_to_key(object);
|
||||
int ret;
|
||||
|
||||
kenter("{%s,%d},,{%zu/%zu}",
|
||||
key->type->name, key->serial, ctx->count, ctx->buflen);
|
||||
@@ -467,10 +466,7 @@ static int keyring_read_iterator(const void *object, void *data)
|
||||
if (ctx->count >= ctx->buflen)
|
||||
return 1;
|
||||
|
||||
ret = put_user(key->serial, ctx->buffer);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ctx->buffer++;
|
||||
*ctx->buffer++ = key->serial;
|
||||
ctx->count += sizeof(key->serial);
|
||||
return 0;
|
||||
}
|
||||
|
@@ -22,7 +22,7 @@ static int request_key_auth_instantiate(struct key *,
|
||||
static void request_key_auth_describe(const struct key *, struct seq_file *);
|
||||
static void request_key_auth_revoke(struct key *);
|
||||
static void request_key_auth_destroy(struct key *);
|
||||
static long request_key_auth_read(const struct key *, char __user *, size_t);
|
||||
static long request_key_auth_read(const struct key *, char *, size_t);
|
||||
|
||||
/*
|
||||
* The request-key authorisation key type definition.
|
||||
@@ -80,7 +80,7 @@ static void request_key_auth_describe(const struct key *key,
|
||||
* - the key's semaphore is read-locked
|
||||
*/
|
||||
static long request_key_auth_read(const struct key *key,
|
||||
char __user *buffer, size_t buflen)
|
||||
char *buffer, size_t buflen)
|
||||
{
|
||||
struct request_key_auth *rka = dereference_key_locked(key);
|
||||
size_t datalen;
|
||||
@@ -97,8 +97,7 @@ static long request_key_auth_read(const struct key *key,
|
||||
if (buflen > datalen)
|
||||
buflen = datalen;
|
||||
|
||||
if (copy_to_user(buffer, rka->callout_info, buflen) != 0)
|
||||
ret = -EFAULT;
|
||||
memcpy(buffer, rka->callout_info, buflen);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@@ -1130,11 +1130,10 @@ out:
|
||||
* trusted_read - copy the sealed blob data to userspace in hex.
|
||||
* On success, return to userspace the trusted key datablob size.
|
||||
*/
|
||||
static long trusted_read(const struct key *key, char __user *buffer,
|
||||
static long trusted_read(const struct key *key, char *buffer,
|
||||
size_t buflen)
|
||||
{
|
||||
const struct trusted_key_payload *p;
|
||||
char *ascii_buf;
|
||||
char *bufp;
|
||||
int i;
|
||||
|
||||
@@ -1143,18 +1142,9 @@ static long trusted_read(const struct key *key, char __user *buffer,
|
||||
return -EINVAL;
|
||||
|
||||
if (buffer && buflen >= 2 * p->blob_len) {
|
||||
ascii_buf = kmalloc_array(2, p->blob_len, GFP_KERNEL);
|
||||
if (!ascii_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
bufp = ascii_buf;
|
||||
bufp = buffer;
|
||||
for (i = 0; i < p->blob_len; i++)
|
||||
bufp = hex_byte_pack(bufp, p->blob[i]);
|
||||
if (copy_to_user(buffer, ascii_buf, 2 * p->blob_len) != 0) {
|
||||
kzfree(ascii_buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
kzfree(ascii_buf);
|
||||
}
|
||||
return 2 * p->blob_len;
|
||||
}
|
||||
|
@@ -168,7 +168,7 @@ EXPORT_SYMBOL_GPL(user_describe);
|
||||
* read the key data
|
||||
* - the key's semaphore is read-locked
|
||||
*/
|
||||
long user_read(const struct key *key, char __user *buffer, size_t buflen)
|
||||
long user_read(const struct key *key, char *buffer, size_t buflen)
|
||||
{
|
||||
const struct user_key_payload *upayload;
|
||||
long ret;
|
||||
@@ -181,8 +181,7 @@ long user_read(const struct key *key, char __user *buffer, size_t buflen)
|
||||
if (buflen > upayload->datalen)
|
||||
buflen = upayload->datalen;
|
||||
|
||||
if (copy_to_user(buffer, upayload->data, buflen) != 0)
|
||||
ret = -EFAULT;
|
||||
memcpy(buffer, upayload->data, buflen);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
Reference in New Issue
Block a user