Commit 4c205c84 authored by Linus Torvalds's avatar Linus Torvalds

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
parents ea9448b2 4f088249
...@@ -17,6 +17,6 @@ extern void big_key_free_preparse(struct key_preparsed_payload *prep); ...@@ -17,6 +17,6 @@ extern void big_key_free_preparse(struct key_preparsed_payload *prep);
extern void big_key_revoke(struct key *key); extern void big_key_revoke(struct key *key);
extern void big_key_destroy(struct key *key); extern void big_key_destroy(struct key *key);
extern void big_key_describe(const struct key *big_key, struct seq_file *m); extern void big_key_describe(const struct key *big_key, struct seq_file *m);
extern long big_key_read(const struct key *key, char __user *buffer, size_t buflen); extern long big_key_read(const struct key *key, char *buffer, size_t buflen);
#endif /* _KEYS_BIG_KEY_TYPE_H */ #endif /* _KEYS_BIG_KEY_TYPE_H */
...@@ -41,8 +41,7 @@ extern int user_update(struct key *key, struct key_preparsed_payload *prep); ...@@ -41,8 +41,7 @@ extern int user_update(struct key *key, struct key_preparsed_payload *prep);
extern void user_revoke(struct key *key); extern void user_revoke(struct key *key);
extern void user_destroy(struct key *key); extern void user_destroy(struct key *key);
extern void user_describe(const struct key *user, struct seq_file *m); extern void user_describe(const struct key *user, struct seq_file *m);
extern long user_read(const struct key *key, extern long user_read(const struct key *key, char *buffer, size_t buflen);
char __user *buffer, size_t buflen);
static inline const struct user_key_payload *user_key_payload_rcu(const struct key *key) static inline const struct user_key_payload *user_key_payload_rcu(const struct key *key)
{ {
......
...@@ -127,7 +127,7 @@ struct key_type { ...@@ -127,7 +127,7 @@ struct key_type {
* much is copied into the buffer * much is copied into the buffer
* - shouldn't do the copy if the buffer is NULL * - shouldn't do the copy if the buffer is NULL
*/ */
long (*read)(const struct key *key, char __user *buffer, size_t buflen); long (*read)(const struct key *key, char *buffer, size_t buflen);
/* handle request_key() for this type instead of invoking /* handle request_key() for this type instead of invoking
* /sbin/request-key (optional) * /sbin/request-key (optional)
......
...@@ -302,7 +302,7 @@ static void dns_resolver_describe(const struct key *key, struct seq_file *m) ...@@ -302,7 +302,7 @@ static void dns_resolver_describe(const struct key *key, struct seq_file *m)
* - the key's semaphore is read-locked * - the key's semaphore is read-locked
*/ */
static long dns_resolver_read(const struct key *key, static long dns_resolver_read(const struct key *key,
char __user *buffer, size_t buflen) char *buffer, size_t buflen)
{ {
int err = PTR_ERR(key->payload.data[dns_key_error]); int err = PTR_ERR(key->payload.data[dns_key_error]);
......
...@@ -31,7 +31,7 @@ static void rxrpc_free_preparse_s(struct key_preparsed_payload *); ...@@ -31,7 +31,7 @@ static void rxrpc_free_preparse_s(struct key_preparsed_payload *);
static void rxrpc_destroy(struct key *); static void rxrpc_destroy(struct key *);
static void rxrpc_destroy_s(struct key *); static void rxrpc_destroy_s(struct key *);
static void rxrpc_describe(const struct key *, struct seq_file *); static void rxrpc_describe(const struct key *, struct seq_file *);
static long rxrpc_read(const struct key *, char __user *, size_t); static long rxrpc_read(const struct key *, char *, size_t);
/* /*
* rxrpc defined keys take an arbitrary string as the description and an * rxrpc defined keys take an arbitrary string as the description and an
...@@ -1042,12 +1042,12 @@ EXPORT_SYMBOL(rxrpc_get_null_key); ...@@ -1042,12 +1042,12 @@ EXPORT_SYMBOL(rxrpc_get_null_key);
* - this returns the result in XDR form * - this returns the result in XDR form
*/ */
static long rxrpc_read(const struct key *key, static long rxrpc_read(const struct key *key,
char __user *buffer, size_t buflen) char *buffer, size_t buflen)
{ {
const struct rxrpc_key_token *token; const struct rxrpc_key_token *token;
const struct krb5_principal *princ; const struct krb5_principal *princ;
size_t size; size_t size;
__be32 __user *xdr, *oldxdr; __be32 *xdr, *oldxdr;
u32 cnlen, toksize, ntoks, tok, zero; u32 cnlen, toksize, ntoks, tok, zero;
u16 toksizes[AFSTOKEN_MAX]; u16 toksizes[AFSTOKEN_MAX];
int loop; int loop;
...@@ -1124,30 +1124,25 @@ static long rxrpc_read(const struct key *key, ...@@ -1124,30 +1124,25 @@ static long rxrpc_read(const struct key *key,
if (!buffer || buflen < size) if (!buffer || buflen < size)
return size; return size;
xdr = (__be32 __user *) buffer; xdr = (__be32 *)buffer;
zero = 0; zero = 0;
#define ENCODE(x) \ #define ENCODE(x) \
do { \ do { \
__be32 y = htonl(x); \ *xdr++ = htonl(x); \
if (put_user(y, xdr++) < 0) \
goto fault; \
} while(0) } while(0)
#define ENCODE_DATA(l, s) \ #define ENCODE_DATA(l, s) \
do { \ do { \
u32 _l = (l); \ u32 _l = (l); \
ENCODE(l); \ ENCODE(l); \
if (copy_to_user(xdr, (s), _l) != 0) \ memcpy(xdr, (s), _l); \
goto fault; \ if (_l & 3) \
if (_l & 3 && \ memcpy((u8 *)xdr + _l, &zero, 4 - (_l & 3)); \
copy_to_user((u8 __user *)xdr + _l, &zero, 4 - (_l & 3)) != 0) \
goto fault; \
xdr += (_l + 3) >> 2; \ xdr += (_l + 3) >> 2; \
} while(0) } while(0)
#define ENCODE64(x) \ #define ENCODE64(x) \
do { \ do { \
__be64 y = cpu_to_be64(x); \ __be64 y = cpu_to_be64(x); \
if (copy_to_user(xdr, &y, 8) != 0) \ memcpy(xdr, &y, 8); \
goto fault; \
xdr += 8 >> 2; \ xdr += 8 >> 2; \
} while(0) } while(0)
#define ENCODE_STR(s) \ #define ENCODE_STR(s) \
...@@ -1238,8 +1233,4 @@ static long rxrpc_read(const struct key *key, ...@@ -1238,8 +1233,4 @@ static long rxrpc_read(const struct key *key,
ASSERTCMP((char __user *) xdr - buffer, ==, size); ASSERTCMP((char __user *) xdr - buffer, ==, size);
_leave(" = %zu", size); _leave(" = %zu", size);
return size; return size;
fault:
_leave(" = -EFAULT");
return -EFAULT;
} }
...@@ -352,7 +352,7 @@ void big_key_describe(const struct key *key, struct seq_file *m) ...@@ -352,7 +352,7 @@ void big_key_describe(const struct key *key, struct seq_file *m)
* read the key data * read the key data
* - the key's semaphore is read-locked * - 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]; size_t datalen = (size_t)key->payload.data[big_key_len];
long ret; long ret;
...@@ -391,9 +391,8 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) ...@@ -391,9 +391,8 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
ret = datalen; ret = datalen;
/* copy decrypted data to user */ /* copy out decrypted data */
if (copy_to_user(buffer, buf->virt, datalen) != 0) memcpy(buffer, buf->virt, datalen);
ret = -EFAULT;
err_fput: err_fput:
fput(file); fput(file);
...@@ -401,9 +400,7 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) ...@@ -401,9 +400,7 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
big_key_free_buffer(buf); big_key_free_buffer(buf);
} else { } else {
ret = datalen; ret = datalen;
if (copy_to_user(buffer, key->payload.data[big_key_data], memcpy(buffer, key->payload.data[big_key_data], datalen);
datalen) != 0)
ret = -EFAULT;
} }
return ret; return ret;
......
...@@ -902,14 +902,14 @@ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep) ...@@ -902,14 +902,14 @@ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep)
} }
/* /*
* encrypted_read - format and copy the encrypted data to userspace * encrypted_read - format and copy out the encrypted data
* *
* The resulting datablob format is: * The resulting datablob format is:
* <master-key name> <decrypted data length> <encrypted iv> <encrypted data> * <master-key name> <decrypted data length> <encrypted iv> <encrypted data>
* *
* On success, return to userspace the encrypted key datablob size. * 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) size_t buflen)
{ {
struct encrypted_key_payload *epayload; struct encrypted_key_payload *epayload;
...@@ -957,8 +957,7 @@ static long encrypted_read(const struct key *key, char __user *buffer, ...@@ -957,8 +957,7 @@ static long encrypted_read(const struct key *key, char __user *buffer,
key_put(mkey); key_put(mkey);
memzero_explicit(derived_key, sizeof(derived_key)); memzero_explicit(derived_key, sizeof(derived_key));
if (copy_to_user(buffer, ascii_buf, asciiblob_len) != 0) memcpy(buffer, ascii_buf, asciiblob_len);
ret = -EFAULT;
kzfree(ascii_buf); kzfree(ascii_buf);
return asciiblob_len; return asciiblob_len;
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#include <linux/keyctl.h> #include <linux/keyctl.h>
#include <linux/refcount.h> #include <linux/refcount.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
struct iovec; struct iovec;
...@@ -349,4 +351,14 @@ static inline void key_check(const struct key *key) ...@@ -349,4 +351,14 @@ static inline void key_check(const struct key *key)
#endif #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 */ #endif /* _INTERNAL_H */
...@@ -339,7 +339,7 @@ long keyctl_update_key(key_serial_t id, ...@@ -339,7 +339,7 @@ long keyctl_update_key(key_serial_t id,
payload = NULL; payload = NULL;
if (plen) { if (plen) {
ret = -ENOMEM; ret = -ENOMEM;
payload = kmalloc(plen, GFP_KERNEL); payload = kvmalloc(plen, GFP_KERNEL);
if (!payload) if (!payload)
goto error; goto error;
...@@ -360,7 +360,7 @@ long keyctl_update_key(key_serial_t id, ...@@ -360,7 +360,7 @@ long keyctl_update_key(key_serial_t id,
key_ref_put(key_ref); key_ref_put(key_ref);
error2: error2:
kzfree(payload); __kvzfree(payload, plen);
error: error:
return ret; return ret;
} }
...@@ -797,6 +797,21 @@ long keyctl_keyring_search(key_serial_t ringid, ...@@ -797,6 +797,21 @@ long keyctl_keyring_search(key_serial_t ringid,
return ret; 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. * Read a key's payload.
* *
...@@ -812,26 +827,28 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) ...@@ -812,26 +827,28 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
struct key *key; struct key *key;
key_ref_t key_ref; key_ref_t key_ref;
long ret; long ret;
char *key_data = NULL;
size_t key_data_len;
/* find the key first */ /* find the key first */
key_ref = lookup_user_key(keyid, 0, 0); key_ref = lookup_user_key(keyid, 0, 0);
if (IS_ERR(key_ref)) { if (IS_ERR(key_ref)) {
ret = -ENOKEY; ret = -ENOKEY;
goto error; goto out;
} }
key = key_ref_to_ptr(key_ref); key = key_ref_to_ptr(key_ref);
ret = key_read_state(key); ret = key_read_state(key);
if (ret < 0) if (ret < 0)
goto error2; /* Negatively instantiated */ goto key_put_out; /* Negatively instantiated */
/* see if we can read it directly */ /* see if we can read it directly */
ret = key_permission(key_ref, KEY_NEED_READ); ret = key_permission(key_ref, KEY_NEED_READ);
if (ret == 0) if (ret == 0)
goto can_read_key; goto can_read_key;
if (ret != -EACCES) if (ret != -EACCES)
goto error2; goto key_put_out;
/* we can't; see if it's searchable from this process's keyrings /* we can't; see if it's searchable from this process's keyrings
* - we automatically take account of the fact that it may be * - 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) ...@@ -839,26 +856,78 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
*/ */
if (!is_key_possessed(key_ref)) { if (!is_key_possessed(key_ref)) {
ret = -EACCES; ret = -EACCES;
goto error2; goto key_put_out;
} }
/* the key is probably readable - now try to read it */ /* the key is probably readable - now try to read it */
can_read_key: can_read_key:
ret = -EOPNOTSUPP; if (!key->type->read) {
if (key->type->read) { ret = -EOPNOTSUPP;
/* Read the data with the semaphore held (since we might sleep) goto key_put_out;
* to protect against the key being updated or revoked. }
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.
*/ */
down_read(&key->sem); if (ret > key_data_len) {
ret = key_validate(key); if (unlikely(key_data))
if (ret == 0) __kvzfree(key_data, key_data_len);
ret = key->type->read(key, buffer, buflen); key_data_len = ret;
up_read(&key->sem); continue; /* Allocate buffer */
}
if (copy_to_user(buffer, key_data, ret))
ret = -EFAULT;
break;
} }
__kvzfree(key_data, key_data_len);
error2: key_put_out:
key_put(key); key_put(key);
error: out:
return ret; return ret;
} }
......
...@@ -459,7 +459,6 @@ static int keyring_read_iterator(const void *object, void *data) ...@@ -459,7 +459,6 @@ static int keyring_read_iterator(const void *object, void *data)
{ {
struct keyring_read_iterator_context *ctx = data; struct keyring_read_iterator_context *ctx = data;
const struct key *key = keyring_ptr_to_key(object); const struct key *key = keyring_ptr_to_key(object);
int ret;
kenter("{%s,%d},,{%zu/%zu}", kenter("{%s,%d},,{%zu/%zu}",
key->type->name, key->serial, ctx->count, ctx->buflen); key->type->name, key->serial, ctx->count, ctx->buflen);
...@@ -467,10 +466,7 @@ static int keyring_read_iterator(const void *object, void *data) ...@@ -467,10 +466,7 @@ static int keyring_read_iterator(const void *object, void *data)
if (ctx->count >= ctx->buflen) if (ctx->count >= ctx->buflen)
return 1; return 1;
ret = put_user(key->serial, ctx->buffer); *ctx->buffer++ = key->serial;
if (ret < 0)
return ret;
ctx->buffer++;
ctx->count += sizeof(key->serial); ctx->count += sizeof(key->serial);
return 0; return 0;
} }
......
...@@ -22,7 +22,7 @@ static int request_key_auth_instantiate(struct key *, ...@@ -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_describe(const struct key *, struct seq_file *);
static void request_key_auth_revoke(struct key *); static void request_key_auth_revoke(struct key *);
static void request_key_auth_destroy(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. * The request-key authorisation key type definition.
...@@ -80,7 +80,7 @@ static void request_key_auth_describe(const struct key *key, ...@@ -80,7 +80,7 @@ static void request_key_auth_describe(const struct key *key,
* - the key's semaphore is read-locked * - the key's semaphore is read-locked
*/ */
static long request_key_auth_read(const struct key *key, 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); struct request_key_auth *rka = dereference_key_locked(key);
size_t datalen; size_t datalen;
...@@ -97,8 +97,7 @@ static long request_key_auth_read(const struct key *key, ...@@ -97,8 +97,7 @@ static long request_key_auth_read(const struct key *key,
if (buflen > datalen) if (buflen > datalen)
buflen = datalen; buflen = datalen;
if (copy_to_user(buffer, rka->callout_info, buflen) != 0) memcpy(buffer, rka->callout_info, buflen);
ret = -EFAULT;
} }
return ret; return ret;
......
...@@ -1130,11 +1130,10 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep) ...@@ -1130,11 +1130,10 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep)
* trusted_read - copy the sealed blob data to userspace in hex. * trusted_read - copy the sealed blob data to userspace in hex.
* On success, return to userspace the trusted key datablob size. * 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) size_t buflen)
{ {
const struct trusted_key_payload *p; const struct trusted_key_payload *p;
char *ascii_buf;
char *bufp; char *bufp;
int i; int i;
...@@ -1143,18 +1142,9 @@ static long trusted_read(const struct key *key, char __user *buffer, ...@@ -1143,18 +1142,9 @@ static long trusted_read(const struct key *key, char __user *buffer,
return -EINVAL; return -EINVAL;
if (buffer && buflen >= 2 * p->blob_len) { if (buffer && buflen >= 2 * p->blob_len) {
ascii_buf = kmalloc_array(2, p->blob_len, GFP_KERNEL); bufp = buffer;
if (!ascii_buf)
return -ENOMEM;
bufp = ascii_buf;
for (i = 0; i < p->blob_len; i++) for (i = 0; i < p->blob_len; i++)
bufp = hex_byte_pack(bufp, p->blob[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; return 2 * p->blob_len;
} }
......
...@@ -168,7 +168,7 @@ EXPORT_SYMBOL_GPL(user_describe); ...@@ -168,7 +168,7 @@ EXPORT_SYMBOL_GPL(user_describe);
* read the key data * read the key data
* - the key's semaphore is read-locked * - 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; const struct user_key_payload *upayload;
long ret; long ret;
...@@ -181,8 +181,7 @@ long user_read(const struct key *key, char __user *buffer, size_t buflen) ...@@ -181,8 +181,7 @@ long user_read(const struct key *key, char __user *buffer, size_t buflen)
if (buflen > upayload->datalen) if (buflen > upayload->datalen)
buflen = upayload->datalen; buflen = upayload->datalen;
if (copy_to_user(buffer, upayload->data, buflen) != 0) memcpy(buffer, upayload->data, buflen);
ret = -EFAULT;
} }
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment