summaryrefslogtreecommitdiff
path: root/security/keys
diff options
context:
space:
mode:
Diffstat (limited to 'security/keys')
-rw-r--r--security/keys/Kconfig15
-rw-r--r--security/keys/Makefile1
-rw-r--r--security/keys/big_key.c198
-rw-r--r--security/keys/compat.c4
-rw-r--r--security/keys/dh.c166
-rw-r--r--security/keys/internal.h13
-rw-r--r--security/keys/key.c42
-rw-r--r--security/keys/keyctl.c5
-rw-r--r--security/keys/keyring.c46
-rw-r--r--security/keys/persistent.c4
-rw-r--r--security/keys/process_keys.c16
-rw-r--r--security/keys/request_key.c4
-rw-r--r--security/keys/request_key_auth.c2
-rw-r--r--security/keys/user_defined.c42
14 files changed, 481 insertions, 77 deletions
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index fe4d74e12..f826e8739 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -41,6 +41,10 @@ config BIG_KEYS
bool "Large payload keys"
depends on KEYS
depends on TMPFS
+ select CRYPTO
+ select CRYPTO_AES
+ select CRYPTO_ECB
+ select CRYPTO_RNG
help
This option provides support for holding large keys within the kernel
(for example Kerberos ticket caches). The data may be stored out to
@@ -81,3 +85,14 @@ config ENCRYPTED_KEYS
Userspace only ever sees/stores encrypted blobs.
If you are unsure as to whether this is required, answer N.
+
+config KEY_DH_OPERATIONS
+ bool "Diffie-Hellman operations on retained keys"
+ depends on KEYS
+ select MPILIB
+ help
+ This option provides support for calculating Diffie-Hellman
+ public keys and shared secrets using values stored as keys
+ in the kernel.
+
+ If you are unsure as to whether this is required, answer N.
diff --git a/security/keys/Makefile b/security/keys/Makefile
index dfb3a7bed..1fd4a16e6 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o
obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
+obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o
#
# Key types
diff --git a/security/keys/big_key.c b/security/keys/big_key.c
index 5643fafcb..3d241f92e 100644
--- a/security/keys/big_key.c
+++ b/security/keys/big_key.c
@@ -14,8 +14,10 @@
#include <linux/file.h>
#include <linux/shmem_fs.h>
#include <linux/err.h>
+#include <linux/scatterlist.h>
#include <keys/user-type.h>
#include <keys/big_key-type.h>
+#include <crypto/rng.h>
/*
* Layout of key payload words.
@@ -28,6 +30,14 @@ enum {
};
/*
+ * 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
* hold it as the permanently resident metadata for the shmem fs will be at
* least as large as the data.
@@ -35,6 +45,11 @@ enum {
#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))
/*
+ * Key size for big_key data encryption
+ */
+#define ENC_KEY_SIZE 16
+
+/*
* big_key defined keys take an arbitrary string as the description and an
* arbitrary blob of data as the payload
*/
@@ -50,12 +65,62 @@ struct key_type key_type_big_key = {
};
/*
+ * Crypto names for big_key data encryption
+ */
+static const char big_key_rng_name[] = "stdrng";
+static const char big_key_alg_name[] = "ecb(aes)";
+
+/*
+ * Crypto algorithms for big_key data encryption
+ */
+static struct crypto_rng *big_key_rng;
+static struct crypto_blkcipher *big_key_blkcipher;
+
+/*
+ * Generate random key to encrypt big_key data
+ */
+static inline int big_key_gen_enckey(u8 *key)
+{
+ return crypto_rng_get_bytes(big_key_rng, key, ENC_KEY_SIZE);
+}
+
+/*
+ * Encrypt/decrypt big_key data
+ */
+static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key)
+{
+ int ret = -EINVAL;
+ struct scatterlist sgio;
+ struct blkcipher_desc desc;
+
+ if (crypto_blkcipher_setkey(big_key_blkcipher, key, ENC_KEY_SIZE)) {
+ ret = -EAGAIN;
+ goto error;
+ }
+
+ desc.flags = 0;
+ desc.tfm = big_key_blkcipher;
+
+ sg_init_one(&sgio, data, datalen);
+
+ if (op == BIG_KEY_ENC)
+ ret = crypto_blkcipher_encrypt(&desc, &sgio, &sgio, datalen);
+ else
+ ret = crypto_blkcipher_decrypt(&desc, &sgio, &sgio, datalen);
+
+error:
+ return ret;
+}
+
+/*
* Preparse a big key
*/
int big_key_preparse(struct key_preparsed_payload *prep)
{
struct path *path = (struct path *)&prep->payload.data[big_key_path];
struct file *file;
+ u8 *enckey;
+ u8 *data = NULL;
ssize_t written;
size_t datalen = prep->datalen;
int ret;
@@ -73,16 +138,43 @@ int big_key_preparse(struct key_preparsed_payload *prep)
/* Create a shmem file to store the data in. This will permit the data
* to be swapped out if needed.
*
- * TODO: Encrypt the stored data with a temporary key.
+ * File content is stored encrypted with randomly generated key.
*/
- file = shmem_kernel_file_setup("", datalen, 0, 0);
+ size_t enclen = ALIGN(datalen, crypto_blkcipher_blocksize(big_key_blkcipher));
+
+ /* prepare aligned data to encrypt */
+ data = kmalloc(enclen, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ memcpy(data, prep->data, datalen);
+ memset(data + datalen, 0x00, enclen - datalen);
+
+ /* generate random key */
+ enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL);
+ if (!enckey) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ ret = big_key_gen_enckey(enckey);
+ if (ret)
+ goto err_enckey;
+
+ /* encrypt aligned data */
+ ret = big_key_crypt(BIG_KEY_ENC, data, enclen, enckey);
+ if (ret)
+ goto err_enckey;
+
+ /* save aligned data to file */
+ file = shmem_kernel_file_setup("", enclen, 0, 0);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
- goto error;
+ goto err_enckey;
}
- written = kernel_write(file, prep->data, prep->datalen, 0);
- if (written != datalen) {
+ written = kernel_write(file, data, enclen, 0);
+ if (written != enclen) {
ret = written;
if (written >= 0)
ret = -ENOMEM;
@@ -92,12 +184,15 @@ int big_key_preparse(struct key_preparsed_payload *prep)
/* Pin the mount and dentry to the key so that we can open it again
* later
*/
+ prep->payload.data[big_key_data] = enckey;
*path = file->f_path;
path_get(path);
fput(file);
+ kfree(data);
} else {
/* Just store the data in a buffer */
void *data = kmalloc(datalen, GFP_KERNEL);
+
if (!data)
return -ENOMEM;
@@ -108,7 +203,10 @@ int big_key_preparse(struct key_preparsed_payload *prep)
err_fput:
fput(file);
+err_enckey:
+ kfree(enckey);
error:
+ kfree(data);
return ret;
}
@@ -119,10 +217,10 @@ void big_key_free_preparse(struct key_preparsed_payload *prep)
{
if (prep->datalen > BIG_KEY_FILE_THRESHOLD) {
struct path *path = (struct path *)&prep->payload.data[big_key_path];
+
path_put(path);
- } else {
- kfree(prep->payload.data[big_key_data]);
}
+ kfree(prep->payload.data[big_key_data]);
}
/*
@@ -147,15 +245,15 @@ void big_key_destroy(struct key *key)
{
size_t datalen = (size_t)key->payload.data[big_key_len];
- if (datalen) {
+ if (datalen > BIG_KEY_FILE_THRESHOLD) {
struct path *path = (struct path *)&key->payload.data[big_key_path];
+
path_put(path);
path->mnt = NULL;
path->dentry = NULL;
- } else {
- kfree(key->payload.data[big_key_data]);
- key->payload.data[big_key_data] = NULL;
}
+ kfree(key->payload.data[big_key_data]);
+ key->payload.data[big_key_data] = NULL;
}
/*
@@ -188,17 +286,41 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
if (datalen > BIG_KEY_FILE_THRESHOLD) {
struct path *path = (struct path *)&key->payload.data[big_key_path];
struct file *file;
- loff_t pos;
+ u8 *data;
+ u8 *enckey = (u8 *)key->payload.data[big_key_data];
+ size_t enclen = ALIGN(datalen, crypto_blkcipher_blocksize(big_key_blkcipher));
+
+ data = kmalloc(enclen, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
file = dentry_open(path, O_RDONLY, current_cred());
- if (IS_ERR(file))
- return PTR_ERR(file);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto error;
+ }
- pos = 0;
- ret = vfs_read(file, buffer, datalen, &pos);
- fput(file);
- if (ret >= 0 && ret != datalen)
+ /* read file to kernel and decrypt */
+ ret = kernel_read(file, 0, data, enclen);
+ if (ret >= 0 && ret != enclen) {
ret = -EIO;
+ goto err_fput;
+ }
+
+ ret = big_key_crypt(BIG_KEY_DEC, data, enclen, enckey);
+ if (ret)
+ goto err_fput;
+
+ ret = datalen;
+
+ /* copy decrypted data to user */
+ if (copy_to_user(buffer, data, datalen) != 0)
+ ret = -EFAULT;
+
+err_fput:
+ fput(file);
+error:
+ kfree(data);
} else {
ret = datalen;
if (copy_to_user(buffer, key->payload.data[big_key_data],
@@ -209,8 +331,48 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
return ret;
}
+/*
+ * Register key type
+ */
static int __init big_key_init(void)
{
return register_key_type(&key_type_big_key);
}
+
+/*
+ * Initialize big_key crypto and RNG algorithms
+ */
+static int __init big_key_crypto_init(void)
+{
+ int ret = -EINVAL;
+
+ /* init RNG */
+ big_key_rng = crypto_alloc_rng(big_key_rng_name, 0, 0);
+ if (IS_ERR(big_key_rng)) {
+ big_key_rng = NULL;
+ return -EFAULT;
+ }
+
+ /* seed RNG */
+ ret = crypto_rng_reset(big_key_rng, NULL, crypto_rng_seedsize(big_key_rng));
+ if (ret)
+ goto error;
+
+ /* init block cipher */
+ big_key_blkcipher = crypto_alloc_blkcipher(big_key_alg_name, 0, 0);
+ if (IS_ERR(big_key_blkcipher)) {
+ big_key_blkcipher = NULL;
+ ret = -EFAULT;
+ goto error;
+ }
+
+ return 0;
+
+error:
+ crypto_free_rng(big_key_rng);
+ big_key_rng = NULL;
+ return ret;
+}
+
device_initcall(big_key_init);
+late_initcall(big_key_crypto_init);
diff --git a/security/keys/compat.c b/security/keys/compat.c
index 25430a3aa..36c80bf5b 100644
--- a/security/keys/compat.c
+++ b/security/keys/compat.c
@@ -132,6 +132,10 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
case KEYCTL_GET_PERSISTENT:
return keyctl_get_persistent(arg2, arg3);
+ case KEYCTL_DH_COMPUTE:
+ return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3),
+ arg4, compat_ptr(arg5));
+
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/dh.c b/security/keys/dh.c
new file mode 100644
index 000000000..531ed2ec1
--- /dev/null
+++ b/security/keys/dh.c
@@ -0,0 +1,166 @@
+/* Crypto operations using stored keys
+ *
+ * Copyright (c) 2016, Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/mpi.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <keys/user-type.h>
+#include "internal.h"
+
+/*
+ * Public key or shared secret generation function [RFC2631 sec 2.1.1]
+ *
+ * ya = g^xa mod p;
+ * or
+ * ZZ = yb^xa mod p;
+ *
+ * where xa is the local private key, ya is the local public key, g is
+ * the generator, p is the prime, yb is the remote public key, and ZZ
+ * is the shared secret.
+ *
+ * Both are the same calculation, so g or yb are the "base" and ya or
+ * ZZ are the "result".
+ */
+static int do_dh(MPI result, MPI base, MPI xa, MPI p)
+{
+ return mpi_powm(result, base, xa, p);
+}
+
+static ssize_t mpi_from_key(key_serial_t keyid, size_t maxlen, MPI *mpi)
+{
+ struct key *key;
+ key_ref_t key_ref;
+ long status;
+ ssize_t ret;
+
+ key_ref = lookup_user_key(keyid, 0, KEY_NEED_READ);
+ if (IS_ERR(key_ref)) {
+ ret = -ENOKEY;
+ goto error;
+ }
+
+ key = key_ref_to_ptr(key_ref);
+
+ ret = -EOPNOTSUPP;
+ if (key->type == &key_type_user) {
+ down_read(&key->sem);
+ status = key_validate(key);
+ if (status == 0) {
+ const struct user_key_payload *payload;
+
+ payload = user_key_payload(key);
+
+ if (maxlen == 0) {
+ *mpi = NULL;
+ ret = payload->datalen;
+ } else if (payload->datalen <= maxlen) {
+ *mpi = mpi_read_raw_data(payload->data,
+ payload->datalen);
+ if (*mpi)
+ ret = payload->datalen;
+ } else {
+ ret = -EINVAL;
+ }
+ }
+ up_read(&key->sem);
+ }
+
+ key_put(key);
+error:
+ return ret;
+}
+
+long keyctl_dh_compute(struct keyctl_dh_params __user *params,
+ char __user *buffer, size_t buflen,
+ void __user *reserved)
+{
+ long ret;
+ MPI base, private, prime, result;
+ unsigned nbytes;
+ struct keyctl_dh_params pcopy;
+ uint8_t *kbuf;
+ ssize_t keylen;
+ size_t resultlen;
+
+ if (!params || (!buffer && buflen)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (reserved) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ keylen = mpi_from_key(pcopy.prime, buflen, &prime);
+ if (keylen < 0 || !prime) {
+ /* buflen == 0 may be used to query the required buffer size,
+ * which is the prime key length.
+ */
+ ret = keylen;
+ goto out;
+ }
+
+ /* The result is never longer than the prime */
+ resultlen = keylen;
+
+ keylen = mpi_from_key(pcopy.base, SIZE_MAX, &base);
+ if (keylen < 0 || !base) {
+ ret = keylen;
+ goto error1;
+ }
+
+ keylen = mpi_from_key(pcopy.private, SIZE_MAX, &private);
+ if (keylen < 0 || !private) {
+ ret = keylen;
+ goto error2;
+ }
+
+ result = mpi_alloc(0);
+ if (!result) {
+ ret = -ENOMEM;
+ goto error3;
+ }
+
+ kbuf = kmalloc(resultlen, GFP_KERNEL);
+ if (!kbuf) {
+ ret = -ENOMEM;
+ goto error4;
+ }
+
+ ret = do_dh(result, base, private, prime);
+ if (ret)
+ goto error5;
+
+ ret = mpi_read_buffer(result, kbuf, resultlen, &nbytes, NULL);
+ if (ret != 0)
+ goto error5;
+
+ ret = nbytes;
+ if (copy_to_user(buffer, kbuf, nbytes) != 0)
+ ret = -EFAULT;
+
+error5:
+ kfree(kbuf);
+error4:
+ mpi_free(result);
+error3:
+ mpi_free(private);
+error2:
+ mpi_free(base);
+error1:
+ mpi_free(prime);
+out:
+ return ret;
+}
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 5105c2c2d..a705a7d92 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -15,6 +15,7 @@
#include <linux/sched.h>
#include <linux/key-type.h>
#include <linux/task_work.h>
+#include <linux/keyctl.h>
struct iovec;
@@ -257,6 +258,18 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring)
}
#endif
+#ifdef CONFIG_KEY_DH_OPERATIONS
+extern long keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *,
+ size_t, void __user *);
+#else
+static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params,
+ char __user *buffer, size_t buflen,
+ void __user *reserved)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
/*
* Debugging key validation
*/
diff --git a/security/keys/key.c b/security/keys/key.c
index af7f6821d..346fbf201 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -201,6 +201,7 @@ serial_exists:
* @cred: The credentials specifying UID namespace.
* @perm: The permissions mask of the new key.
* @flags: Flags specifying quota properties.
+ * @restrict_link: Optional link restriction method for new keyrings.
*
* Allocate a key of the specified type with the attributes given. The key is
* returned in an uninstantiated state and the caller needs to instantiate the
@@ -223,7 +224,10 @@ serial_exists:
*/
struct key *key_alloc(struct key_type *type, const char *desc,
kuid_t uid, kgid_t gid, const struct cred *cred,
- key_perm_t perm, unsigned long flags)
+ key_perm_t perm, unsigned long flags,
+ int (*restrict_link)(struct key *,
+ const struct key_type *,
+ const union key_payload *))
{
struct key_user *user = NULL;
struct key *key;
@@ -291,11 +295,10 @@ struct key *key_alloc(struct key_type *type, const char *desc,
key->uid = uid;
key->gid = gid;
key->perm = perm;
+ key->restrict_link = restrict_link;
if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
key->flags |= 1 << KEY_FLAG_IN_QUOTA;
- if (flags & KEY_ALLOC_TRUSTED)
- key->flags |= 1 << KEY_FLAG_TRUSTED;
if (flags & KEY_ALLOC_BUILT_IN)
key->flags |= 1 << KEY_FLAG_BUILTIN;
@@ -496,6 +499,12 @@ int key_instantiate_and_link(struct key *key,
}
if (keyring) {
+ if (keyring->restrict_link) {
+ ret = keyring->restrict_link(keyring, key->type,
+ &prep.payload);
+ if (ret < 0)
+ goto error;
+ }
ret = __key_link_begin(keyring, &key->index_key, &edit);
if (ret < 0)
goto error;
@@ -551,8 +560,12 @@ int key_reject_and_link(struct key *key,
awaken = 0;
ret = -EBUSY;
- if (keyring)
+ if (keyring) {
+ if (keyring->restrict_link)
+ return -EPERM;
+
link_ret = __key_link_begin(keyring, &key->index_key, &edit);
+ }
mutex_lock(&key_construction_mutex);
@@ -793,6 +806,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
struct key *keyring, *key = NULL;
key_ref_t key_ref;
int ret;
+ int (*restrict_link)(struct key *,
+ const struct key_type *,
+ const union key_payload *) = NULL;
/* look up the key type to see if it's one of the registered kernel
* types */
@@ -811,6 +827,10 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
key_check(keyring);
+ key_ref = ERR_PTR(-EPERM);
+ if (!(flags & KEY_ALLOC_BYPASS_RESTRICTION))
+ restrict_link = keyring->restrict_link;
+
key_ref = ERR_PTR(-ENOTDIR);
if (keyring->type != &key_type_keyring)
goto error_put_type;
@@ -819,7 +839,6 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
prep.data = payload;
prep.datalen = plen;
prep.quotalen = index_key.type->def_datalen;
- prep.trusted = flags & KEY_ALLOC_TRUSTED;
prep.expiry = TIME_T_MAX;
if (index_key.type->preparse) {
ret = index_key.type->preparse(&prep);
@@ -835,10 +854,13 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
}
index_key.desc_len = strlen(index_key.description);
- key_ref = ERR_PTR(-EPERM);
- if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags))
- goto error_free_prep;
- flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0;
+ if (restrict_link) {
+ ret = restrict_link(keyring, index_key.type, &prep.payload);
+ if (ret < 0) {
+ key_ref = ERR_PTR(ret);
+ goto error_free_prep;
+ }
+ }
ret = __key_link_begin(keyring, &index_key, &edit);
if (ret < 0) {
@@ -879,7 +901,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
/* allocate a new key */
key = key_alloc(index_key.type, index_key.description,
- cred->fsuid, cred->fsgid, cred, perm, flags);
+ cred->fsuid, cred->fsgid, cred, perm, flags, NULL);
if (IS_ERR(key)) {
key_ref = ERR_CAST(key);
goto error_link_end;
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index ed73c6c1c..d580ad06b 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1686,6 +1686,11 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
case KEYCTL_GET_PERSISTENT:
return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3);
+ case KEYCTL_DH_COMPUTE:
+ return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2,
+ (char __user *) arg3, (size_t) arg4,
+ (void __user *) arg5);
+
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index f931ccfee..c91e4e0ce 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -491,13 +491,17 @@ static long keyring_read(const struct key *keyring,
*/
struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
const struct cred *cred, key_perm_t perm,
- unsigned long flags, struct key *dest)
+ unsigned long flags,
+ int (*restrict_link)(struct key *,
+ const struct key_type *,
+ const union key_payload *),
+ struct key *dest)
{
struct key *keyring;
int ret;
keyring = key_alloc(&key_type_keyring, description,
- uid, gid, cred, perm, flags);
+ uid, gid, cred, perm, flags, restrict_link);
if (!IS_ERR(keyring)) {
ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL);
if (ret < 0) {
@@ -510,6 +514,26 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
}
EXPORT_SYMBOL(keyring_alloc);
+/**
+ * restrict_link_reject - Give -EPERM to restrict link
+ * @keyring: The keyring being added to.
+ * @type: The type of key being added.
+ * @payload: The payload of the key intended to be added.
+ *
+ * Reject the addition of any links to a keyring. It can be overridden by
+ * passing KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when
+ * adding a key to a keyring.
+ *
+ * This is meant to be passed as the restrict_link parameter to
+ * keyring_alloc().
+ */
+int restrict_link_reject(struct key *keyring,
+ const struct key_type *type,
+ const union key_payload *payload)
+{
+ return -EPERM;
+}
+
/*
* By default, we keys found by getting an exact match on their descriptions.
*/
@@ -1191,6 +1215,16 @@ void __key_link_end(struct key *keyring,
up_write(&keyring->sem);
}
+/*
+ * Check addition of keys to restricted keyrings.
+ */
+static int __key_link_check_restriction(struct key *keyring, struct key *key)
+{
+ if (!keyring->restrict_link)
+ return 0;
+ return keyring->restrict_link(keyring, key->type, &key->payload);
+}
+
/**
* key_link - Link a key to a keyring
* @keyring: The keyring to make the link in.
@@ -1221,14 +1255,12 @@ int key_link(struct key *keyring, struct key *key)
key_check(keyring);
key_check(key);
- if (test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags) &&
- !test_bit(KEY_FLAG_TRUSTED, &key->flags))
- return -EPERM;
-
ret = __key_link_begin(keyring, &key->index_key, &edit);
if (ret == 0) {
kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage));
- ret = __key_link_check_live_key(keyring, key);
+ ret = __key_link_check_restriction(keyring, key);
+ if (ret == 0)
+ ret = __key_link_check_live_key(keyring, key);
if (ret == 0)
__key_link(key, &edit);
__key_link_end(keyring, &key->index_key, edit);
diff --git a/security/keys/persistent.c b/security/keys/persistent.c
index c9fae5ea8..2ef45b319 100644
--- a/security/keys/persistent.c
+++ b/security/keys/persistent.c
@@ -26,7 +26,7 @@ static int key_create_persistent_register(struct user_namespace *ns)
current_cred(),
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ),
- KEY_ALLOC_NOT_IN_QUOTA, NULL);
+ KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
if (IS_ERR(reg))
return PTR_ERR(reg);
@@ -60,7 +60,7 @@ static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid,
uid, INVALID_GID, current_cred(),
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ),
- KEY_ALLOC_NOT_IN_QUOTA,
+ KEY_ALLOC_NOT_IN_QUOTA, NULL,
ns->persistent_keyring_register);
if (IS_ERR(persistent))
return ERR_CAST(persistent);
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index e6d501728..40a885239 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -76,7 +76,8 @@ int install_user_keyrings(void)
if (IS_ERR(uid_keyring)) {
uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID,
cred, user_keyring_perm,
- KEY_ALLOC_IN_QUOTA, NULL);
+ KEY_ALLOC_IN_QUOTA,
+ NULL, NULL);
if (IS_ERR(uid_keyring)) {
ret = PTR_ERR(uid_keyring);
goto error;
@@ -92,7 +93,8 @@ int install_user_keyrings(void)
session_keyring =
keyring_alloc(buf, user->uid, INVALID_GID,
cred, user_keyring_perm,
- KEY_ALLOC_IN_QUOTA, NULL);
+ KEY_ALLOC_IN_QUOTA,
+ NULL, NULL);
if (IS_ERR(session_keyring)) {
ret = PTR_ERR(session_keyring);
goto error_release;
@@ -134,7 +136,8 @@ int install_thread_keyring_to_cred(struct cred *new)
keyring = keyring_alloc("_tid", new->uid, new->gid, new,
KEY_POS_ALL | KEY_USR_VIEW,
- KEY_ALLOC_QUOTA_OVERRUN, NULL);
+ KEY_ALLOC_QUOTA_OVERRUN,
+ NULL, NULL);
if (IS_ERR(keyring))
return PTR_ERR(keyring);
@@ -180,7 +183,8 @@ int install_process_keyring_to_cred(struct cred *new)
keyring = keyring_alloc("_pid", new->uid, new->gid, new,
KEY_POS_ALL | KEY_USR_VIEW,
- KEY_ALLOC_QUOTA_OVERRUN, NULL);
+ KEY_ALLOC_QUOTA_OVERRUN,
+ NULL, NULL);
if (IS_ERR(keyring))
return PTR_ERR(keyring);
@@ -231,7 +235,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred,
KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,
- flags, NULL);
+ flags, NULL, NULL);
if (IS_ERR(keyring))
return PTR_ERR(keyring);
} else {
@@ -785,7 +789,7 @@ long join_session_keyring(const char *name)
keyring = keyring_alloc(
name, old->uid, old->gid, old,
KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK,
- KEY_ALLOC_IN_QUOTA, NULL);
+ KEY_ALLOC_IN_QUOTA, NULL, NULL);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error2;
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index c7a117c9a..a29e35547 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -116,7 +116,7 @@ static int call_sbin_request_key(struct key_construction *cons,
cred = get_current_cred();
keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred,
KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,
- KEY_ALLOC_QUOTA_OVERRUN, NULL);
+ KEY_ALLOC_QUOTA_OVERRUN, NULL, NULL);
put_cred(cred);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
@@ -355,7 +355,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
key = key_alloc(ctx->index_key.type, ctx->index_key.description,
ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred,
- perm, flags);
+ perm, flags, NULL);
if (IS_ERR(key))
goto alloc_failed;
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index 4f0f112fe..9db8b4a82 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -202,7 +202,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
authkey = key_alloc(&key_type_request_key_auth, desc,
cred->fsuid, cred->fsgid, cred,
KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
- KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA);
+ KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL);
if (IS_ERR(authkey)) {
ret = PTR_ERR(authkey);
goto error_alloc;
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
index 8705d79b2..66b1840b4 100644
--- a/security/keys/user_defined.c
+++ b/security/keys/user_defined.c
@@ -96,45 +96,25 @@ EXPORT_SYMBOL_GPL(user_free_preparse);
*/
int user_update(struct key *key, struct key_preparsed_payload *prep)
{
- struct user_key_payload *upayload, *zap;
- size_t datalen = prep->datalen;
+ struct user_key_payload *zap = NULL;
int ret;
- ret = -EINVAL;
- if (datalen <= 0 || datalen > 32767 || !prep->data)
- goto error;
-
- /* construct a replacement payload */
- ret = -ENOMEM;
- upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
- if (!upayload)
- goto error;
-
- upayload->datalen = datalen;
- memcpy(upayload->data, prep->data, datalen);
-
/* check the quota and attach the new data */
- zap = upayload;
-
- ret = key_payload_reserve(key, datalen);
-
- if (ret == 0) {
- /* attach the new data, displacing the old */
- if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
- zap = key->payload.data[0];
- else
- zap = NULL;
- rcu_assign_keypointer(key, upayload);
- key->expiry = 0;
- }
+ ret = key_payload_reserve(key, prep->datalen);
+ if (ret < 0)
+ return ret;
+
+ /* attach the new data, displacing the old */
+ key->expiry = prep->expiry;
+ if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
+ zap = rcu_dereference_key(key);
+ rcu_assign_keypointer(key, prep->payload.data[0]);
+ prep->payload.data[0] = NULL;
if (zap)
kfree_rcu(zap, rcu);
-
-error:
return ret;
}
-
EXPORT_SYMBOL_GPL(user_update);
/*