Commit a484a497 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'keys-next-20200602' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs

Pull keyring updates from David Howells:

 - Fix a documentation warning.

 - Replace a zero-length array with a flexible one

 - Make the big_key key type use ChaCha20Poly1305 and use the crypto
   algorithm directly rather than going through the crypto layer.

 - Implement the update op for the big_key type.

* tag 'keys-next-20200602' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  keys: Implement update for the big_key type
  security/keys: rewrite big_key crypto to use library interface
  KEYS: Replace zero-length array with flexible-array
  Documentation: security: core.rst: add missing argument
parents 38b3a5aa b6f61c31
...@@ -920,10 +920,14 @@ The keyctl syscall functions are: ...@@ -920,10 +920,14 @@ The keyctl syscall functions are:
long keyctl(KEYCTL_PKEY_QUERY, long keyctl(KEYCTL_PKEY_QUERY,
key_serial_t key_id, unsigned long reserved, key_serial_t key_id, unsigned long reserved,
const char *params,
struct keyctl_pkey_query *info); struct keyctl_pkey_query *info);
Get information about an asymmetric key. The information is returned in Get information about an asymmetric key. Specific algorithms and
the keyctl_pkey_query struct:: encodings may be queried by using the ``params`` argument. This is a
string containing a space- or tab-separated string of key-value pairs.
Currently supported keys include ``enc`` and ``hash``. The information
is returned in the keyctl_pkey_query struct::
__u32 supported_ops; __u32 supported_ops;
__u32 key_size; __u32 key_size;
......
...@@ -18,5 +18,6 @@ extern void big_key_revoke(struct key *key); ...@@ -18,5 +18,6 @@ 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 *buffer, size_t buflen); extern long big_key_read(const struct key *key, char *buffer, size_t buflen);
extern int big_key_update(struct key *key, struct key_preparsed_payload *prep);
#endif /* _KEYS_BIG_KEY_TYPE_H */ #endif /* _KEYS_BIG_KEY_TYPE_H */
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
struct user_key_payload { struct user_key_payload {
struct rcu_head rcu; /* RCU destructor */ struct rcu_head rcu; /* RCU destructor */
unsigned short datalen; /* length of this data */ unsigned short datalen; /* length of this data */
char data[0] __aligned(__alignof__(u64)); /* actual data */ char data[] __aligned(__alignof__(u64)); /* actual data */
}; };
extern struct key_type key_type_user; extern struct key_type key_type_user;
......
...@@ -60,9 +60,7 @@ config BIG_KEYS ...@@ -60,9 +60,7 @@ config BIG_KEYS
bool "Large payload keys" bool "Large payload keys"
depends on KEYS depends on KEYS
depends on TMPFS depends on TMPFS
select CRYPTO depends on CRYPTO_LIB_CHACHA20POLY1305 = y
select CRYPTO_AES
select CRYPTO_GCM
help help
This option provides support for holding large keys within the kernel This option provides support for holding large keys within the kernel
(for example Kerberos ticket caches). The data may be stored out to (for example Kerberos ticket caches). The data may be stored out to
......
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* Large capacity key type /* Large capacity key type
* *
* Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2017-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
*/ */
...@@ -12,20 +12,10 @@ ...@@ -12,20 +12,10 @@
#include <linux/file.h> #include <linux/file.h>
#include <linux/shmem_fs.h> #include <linux/shmem_fs.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/scatterlist.h>
#include <linux/random.h> #include <linux/random.h>
#include <linux/vmalloc.h>
#include <keys/user-type.h> #include <keys/user-type.h>
#include <keys/big_key-type.h> #include <keys/big_key-type.h>
#include <crypto/aead.h> #include <crypto/chacha20poly1305.h>
#include <crypto/gcm.h>
struct big_key_buf {
unsigned int nr_pages;
void *virt;
struct scatterlist *sg;
struct page *pages[];
};
/* /*
* Layout of key payload words. * Layout of key payload words.
...@@ -37,14 +27,6 @@ enum { ...@@ -37,14 +27,6 @@ enum {
big_key_len, big_key_len,
}; };
/*
* Crypto operation with big_key data
*/
enum big_key_op {
BIG_KEY_ENC,
BIG_KEY_DEC,
};
/* /*
* If the data is under this limit, there's no point creating a shm file to * If the data is under this limit, there's no point creating a shm file to
* hold it as the permanently resident metadata for the shmem fs will be at * hold it as the permanently resident metadata for the shmem fs will be at
...@@ -52,16 +34,6 @@ enum big_key_op { ...@@ -52,16 +34,6 @@ enum big_key_op {
*/ */
#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry)) #define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))
/*
* Key size for big_key data encryption
*/
#define ENC_KEY_SIZE 32
/*
* Authentication tag length
*/
#define ENC_AUTHTAG_SIZE 16
/* /*
* big_key defined keys take an arbitrary string as the description and an * big_key defined keys take an arbitrary string as the description and an
* arbitrary blob of data as the payload * arbitrary blob of data as the payload
...@@ -75,136 +47,20 @@ struct key_type key_type_big_key = { ...@@ -75,136 +47,20 @@ struct key_type key_type_big_key = {
.destroy = big_key_destroy, .destroy = big_key_destroy,
.describe = big_key_describe, .describe = big_key_describe,
.read = big_key_read, .read = big_key_read,
/* no ->update(); don't add it without changing big_key_crypt() nonce */ .update = big_key_update,
}; };
/*
* Crypto names for big_key data authenticated encryption
*/
static const char big_key_alg_name[] = "gcm(aes)";
#define BIG_KEY_IV_SIZE GCM_AES_IV_SIZE
/*
* Crypto algorithms for big_key data authenticated encryption
*/
static struct crypto_aead *big_key_aead;
/*
* Since changing the key affects the entire object, we need a mutex.
*/
static DEFINE_MUTEX(big_key_aead_lock);
/*
* Encrypt/decrypt big_key data
*/
static int big_key_crypt(enum big_key_op op, struct big_key_buf *buf, size_t datalen, u8 *key)
{
int ret;
struct aead_request *aead_req;
/* We always use a zero nonce. The reason we can get away with this is
* because we're using a different randomly generated key for every
* different encryption. Notably, too, key_type_big_key doesn't define
* an .update function, so there's no chance we'll wind up reusing the
* key to encrypt updated data. Simply put: one key, one encryption.
*/
u8 zero_nonce[BIG_KEY_IV_SIZE];
aead_req = aead_request_alloc(big_key_aead, GFP_KERNEL);
if (!aead_req)
return -ENOMEM;
memset(zero_nonce, 0, sizeof(zero_nonce));
aead_request_set_crypt(aead_req, buf->sg, buf->sg, datalen, zero_nonce);
aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
aead_request_set_ad(aead_req, 0);
mutex_lock(&big_key_aead_lock);
if (crypto_aead_setkey(big_key_aead, key, ENC_KEY_SIZE)) {
ret = -EAGAIN;
goto error;
}
if (op == BIG_KEY_ENC)
ret = crypto_aead_encrypt(aead_req);
else
ret = crypto_aead_decrypt(aead_req);
error:
mutex_unlock(&big_key_aead_lock);
aead_request_free(aead_req);
return ret;
}
/*
* Free up the buffer.
*/
static void big_key_free_buffer(struct big_key_buf *buf)
{
unsigned int i;
if (buf->virt) {
memset(buf->virt, 0, buf->nr_pages * PAGE_SIZE);
vunmap(buf->virt);
}
for (i = 0; i < buf->nr_pages; i++)
if (buf->pages[i])
__free_page(buf->pages[i]);
kfree(buf);
}
/*
* Allocate a buffer consisting of a set of pages with a virtual mapping
* applied over them.
*/
static void *big_key_alloc_buffer(size_t len)
{
struct big_key_buf *buf;
unsigned int npg = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
unsigned int i, l;
buf = kzalloc(sizeof(struct big_key_buf) +
sizeof(struct page) * npg +
sizeof(struct scatterlist) * npg,
GFP_KERNEL);
if (!buf)
return NULL;
buf->nr_pages = npg;
buf->sg = (void *)(buf->pages + npg);
sg_init_table(buf->sg, npg);
for (i = 0; i < buf->nr_pages; i++) {
buf->pages[i] = alloc_page(GFP_KERNEL);
if (!buf->pages[i])
goto nomem;
l = min_t(size_t, len, PAGE_SIZE);
sg_set_page(&buf->sg[i], buf->pages[i], l, 0);
len -= l;
}
buf->virt = vmap(buf->pages, buf->nr_pages, VM_MAP, PAGE_KERNEL);
if (!buf->virt)
goto nomem;
return buf;
nomem:
big_key_free_buffer(buf);
return NULL;
}
/* /*
* Preparse a big key * Preparse a big key
*/ */
int big_key_preparse(struct key_preparsed_payload *prep) int big_key_preparse(struct key_preparsed_payload *prep)
{ {
struct big_key_buf *buf;
struct path *path = (struct path *)&prep->payload.data[big_key_path]; struct path *path = (struct path *)&prep->payload.data[big_key_path];
struct file *file; struct file *file;
u8 *enckey; u8 *buf, *enckey;
ssize_t written; ssize_t written;
size_t datalen = prep->datalen, enclen = datalen + ENC_AUTHTAG_SIZE; size_t datalen = prep->datalen;
size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE;
int ret; int ret;
if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data) if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data)
...@@ -220,28 +76,28 @@ int big_key_preparse(struct key_preparsed_payload *prep) ...@@ -220,28 +76,28 @@ int big_key_preparse(struct key_preparsed_payload *prep)
* to be swapped out if needed. * to be swapped out if needed.
* *
* File content is stored encrypted with randomly generated key. * File content is stored encrypted with randomly generated key.
* Since the key is random for each file, we can set the nonce
* to zero, provided we never define a ->update() call.
*/ */
loff_t pos = 0; loff_t pos = 0;
buf = big_key_alloc_buffer(enclen); buf = kvmalloc(enclen, GFP_KERNEL);
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
memcpy(buf->virt, prep->data, datalen);
/* generate random key */ /* generate random key */
enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL); enckey = kmalloc(CHACHA20POLY1305_KEY_SIZE, GFP_KERNEL);
if (!enckey) { if (!enckey) {
ret = -ENOMEM; ret = -ENOMEM;
goto error; goto error;
} }
ret = get_random_bytes_wait(enckey, ENC_KEY_SIZE); ret = get_random_bytes_wait(enckey, CHACHA20POLY1305_KEY_SIZE);
if (unlikely(ret)) if (unlikely(ret))
goto err_enckey; goto err_enckey;
/* encrypt aligned data */ /* encrypt data */
ret = big_key_crypt(BIG_KEY_ENC, buf, datalen, enckey); chacha20poly1305_encrypt(buf, prep->data, datalen, NULL, 0,
if (ret) 0, enckey);
goto err_enckey;
/* save aligned data to file */ /* save aligned data to file */
file = shmem_kernel_file_setup("", enclen, 0); file = shmem_kernel_file_setup("", enclen, 0);
...@@ -250,11 +106,11 @@ int big_key_preparse(struct key_preparsed_payload *prep) ...@@ -250,11 +106,11 @@ int big_key_preparse(struct key_preparsed_payload *prep)
goto err_enckey; goto err_enckey;
} }
written = kernel_write(file, buf->virt, enclen, &pos); written = kernel_write(file, buf, enclen, &pos);
if (written != enclen) { if (written != enclen) {
ret = written; ret = written;
if (written >= 0) if (written >= 0)
ret = -ENOMEM; ret = -EIO;
goto err_fput; goto err_fput;
} }
...@@ -265,7 +121,8 @@ int big_key_preparse(struct key_preparsed_payload *prep) ...@@ -265,7 +121,8 @@ int big_key_preparse(struct key_preparsed_payload *prep)
*path = file->f_path; *path = file->f_path;
path_get(path); path_get(path);
fput(file); fput(file);
big_key_free_buffer(buf); memzero_explicit(buf, enclen);
kvfree(buf);
} else { } else {
/* Just store the data in a buffer */ /* Just store the data in a buffer */
void *data = kmalloc(datalen, GFP_KERNEL); void *data = kmalloc(datalen, GFP_KERNEL);
...@@ -283,7 +140,8 @@ int big_key_preparse(struct key_preparsed_payload *prep) ...@@ -283,7 +140,8 @@ int big_key_preparse(struct key_preparsed_payload *prep)
err_enckey: err_enckey:
kzfree(enckey); kzfree(enckey);
error: error:
big_key_free_buffer(buf); memzero_explicit(buf, enclen);
kvfree(buf);
return ret; return ret;
} }
...@@ -333,6 +191,23 @@ void big_key_destroy(struct key *key) ...@@ -333,6 +191,23 @@ void big_key_destroy(struct key *key)
key->payload.data[big_key_data] = NULL; key->payload.data[big_key_data] = NULL;
} }
/*
* Update a big key
*/
int big_key_update(struct key *key, struct key_preparsed_payload *prep)
{
int ret;
ret = key_payload_reserve(key, prep->datalen);
if (ret < 0)
return ret;
if (key_is_positive(key))
big_key_destroy(key);
return generic_key_instantiate(key, prep);
}
/* /*
* describe the big_key key * describe the big_key key
*/ */
...@@ -361,14 +236,13 @@ long big_key_read(const struct key *key, char *buffer, size_t buflen) ...@@ -361,14 +236,13 @@ long big_key_read(const struct key *key, char *buffer, size_t buflen)
return datalen; return datalen;
if (datalen > BIG_KEY_FILE_THRESHOLD) { if (datalen > BIG_KEY_FILE_THRESHOLD) {
struct big_key_buf *buf;
struct path *path = (struct path *)&key->payload.data[big_key_path]; struct path *path = (struct path *)&key->payload.data[big_key_path];
struct file *file; struct file *file;
u8 *enckey = (u8 *)key->payload.data[big_key_data]; u8 *buf, *enckey = (u8 *)key->payload.data[big_key_data];
size_t enclen = datalen + ENC_AUTHTAG_SIZE; size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE;
loff_t pos = 0; loff_t pos = 0;
buf = big_key_alloc_buffer(enclen); buf = kvmalloc(enclen, GFP_KERNEL);
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
...@@ -379,25 +253,28 @@ long big_key_read(const struct key *key, char *buffer, size_t buflen) ...@@ -379,25 +253,28 @@ long big_key_read(const struct key *key, char *buffer, size_t buflen)
} }
/* read file to kernel and decrypt */ /* read file to kernel and decrypt */
ret = kernel_read(file, buf->virt, enclen, &pos); ret = kernel_read(file, buf, enclen, &pos);
if (ret >= 0 && ret != enclen) { if (ret != enclen) {
ret = -EIO; if (ret >= 0)
ret = -EIO;
goto err_fput; goto err_fput;
} }
ret = big_key_crypt(BIG_KEY_DEC, buf, enclen, enckey); ret = chacha20poly1305_decrypt(buf, buf, enclen, NULL, 0, 0,
if (ret) enckey) ? 0 : -EBADMSG;
if (unlikely(ret))
goto err_fput; goto err_fput;
ret = datalen; ret = datalen;
/* copy out decrypted data */ /* copy out decrypted data */
memcpy(buffer, buf->virt, datalen); memcpy(buffer, buf, datalen);
err_fput: err_fput:
fput(file); fput(file);
error: error:
big_key_free_buffer(buf); memzero_explicit(buf, enclen);
kvfree(buf);
} else { } else {
ret = datalen; ret = datalen;
memcpy(buffer, key->payload.data[big_key_data], datalen); memcpy(buffer, key->payload.data[big_key_data], datalen);
...@@ -411,39 +288,7 @@ long big_key_read(const struct key *key, char *buffer, size_t buflen) ...@@ -411,39 +288,7 @@ long big_key_read(const struct key *key, char *buffer, size_t buflen)
*/ */
static int __init big_key_init(void) static int __init big_key_init(void)
{ {
int ret; return register_key_type(&key_type_big_key);
/* init block cipher */
big_key_aead = crypto_alloc_aead(big_key_alg_name, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(big_key_aead)) {
ret = PTR_ERR(big_key_aead);
pr_err("Can't alloc crypto: %d\n", ret);
return ret;
}
if (unlikely(crypto_aead_ivsize(big_key_aead) != BIG_KEY_IV_SIZE)) {
WARN(1, "big key algorithm changed?");
ret = -EINVAL;
goto free_aead;
}
ret = crypto_aead_setauthsize(big_key_aead, ENC_AUTHTAG_SIZE);
if (ret < 0) {
pr_err("Can't set crypto auth tag len: %d\n", ret);
goto free_aead;
}
ret = register_key_type(&key_type_big_key);
if (ret < 0) {
pr_err("Can't register type: %d\n", ret);
goto free_aead;
}
return 0;
free_aead:
crypto_free_aead(big_key_aead);
return ret;
} }
late_initcall(big_key_init); late_initcall(big_key_init);
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