mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-03 15:47:36 +00:00
Merge branch 'keys-misc' into keys-next
Miscellaneous keyrings changes. Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
commit
6e007f3186
11 changed files with 428 additions and 50 deletions
|
@ -823,6 +823,36 @@ The keyctl syscall functions are:
|
||||||
A process must have search permission on the key for this function to be
|
A process must have search permission on the key for this function to be
|
||||||
successful.
|
successful.
|
||||||
|
|
||||||
|
(*) Compute a Diffie-Hellman shared secret or public key
|
||||||
|
|
||||||
|
long keyctl(KEYCTL_DH_COMPUTE, struct keyctl_dh_params *params,
|
||||||
|
char *buffer, size_t buflen);
|
||||||
|
|
||||||
|
The params struct contains serial numbers for three keys:
|
||||||
|
|
||||||
|
- The prime, p, known to both parties
|
||||||
|
- The local private key
|
||||||
|
- The base integer, which is either a shared generator or the
|
||||||
|
remote public key
|
||||||
|
|
||||||
|
The value computed is:
|
||||||
|
|
||||||
|
result = base ^ private (mod prime)
|
||||||
|
|
||||||
|
If the base is the shared generator, the result is the local
|
||||||
|
public key. If the base is the remote public key, the result is
|
||||||
|
the shared secret.
|
||||||
|
|
||||||
|
The buffer length must be at least the length of the prime, or zero.
|
||||||
|
|
||||||
|
If the buffer length is nonzero, the length of the result is
|
||||||
|
returned when it is successfully calculated and copied in to the
|
||||||
|
buffer. When the buffer length is zero, the minimum required
|
||||||
|
buffer length is returned.
|
||||||
|
|
||||||
|
This function will return error EOPNOTSUPP if the key type is not
|
||||||
|
supported, error ENOKEY if the key could not be found, or error
|
||||||
|
EACCES if the key is not readable by the caller.
|
||||||
|
|
||||||
===============
|
===============
|
||||||
KERNEL SERVICES
|
KERNEL SERVICES
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#ifndef _LINUX_KEYCTL_H
|
#ifndef _LINUX_KEYCTL_H
|
||||||
#define _LINUX_KEYCTL_H
|
#define _LINUX_KEYCTL_H
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
/* special process keyring shortcut IDs */
|
/* special process keyring shortcut IDs */
|
||||||
#define KEY_SPEC_THREAD_KEYRING -1 /* - key ID for thread-specific keyring */
|
#define KEY_SPEC_THREAD_KEYRING -1 /* - key ID for thread-specific keyring */
|
||||||
#define KEY_SPEC_PROCESS_KEYRING -2 /* - key ID for process-specific keyring */
|
#define KEY_SPEC_PROCESS_KEYRING -2 /* - key ID for process-specific keyring */
|
||||||
|
@ -57,5 +59,13 @@
|
||||||
#define KEYCTL_INSTANTIATE_IOV 20 /* instantiate a partially constructed key */
|
#define KEYCTL_INSTANTIATE_IOV 20 /* instantiate a partially constructed key */
|
||||||
#define KEYCTL_INVALIDATE 21 /* invalidate a key */
|
#define KEYCTL_INVALIDATE 21 /* invalidate a key */
|
||||||
#define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */
|
#define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */
|
||||||
|
#define KEYCTL_DH_COMPUTE 23 /* Compute Diffie-Hellman values */
|
||||||
|
|
||||||
|
/* keyctl structures */
|
||||||
|
struct keyctl_dh_params {
|
||||||
|
__s32 private;
|
||||||
|
__s32 prime;
|
||||||
|
__s32 base;
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* _LINUX_KEYCTL_H */
|
#endif /* _LINUX_KEYCTL_H */
|
||||||
|
|
|
@ -35,7 +35,6 @@ config INTEGRITY_ASYMMETRIC_KEYS
|
||||||
default n
|
default n
|
||||||
select ASYMMETRIC_KEY_TYPE
|
select ASYMMETRIC_KEY_TYPE
|
||||||
select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
||||||
select PUBLIC_KEY_ALGO_RSA
|
|
||||||
select CRYPTO_RSA
|
select CRYPTO_RSA
|
||||||
select X509_CERTIFICATE_PARSER
|
select X509_CERTIFICATE_PARSER
|
||||||
help
|
help
|
||||||
|
|
|
@ -41,6 +41,10 @@ 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
|
||||||
|
select CRYPTO_AES
|
||||||
|
select CRYPTO_ECB
|
||||||
|
select CRYPTO_RNG
|
||||||
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
|
||||||
|
@ -81,3 +85,14 @@ config ENCRYPTED_KEYS
|
||||||
Userspace only ever sees/stores encrypted blobs.
|
Userspace only ever sees/stores encrypted blobs.
|
||||||
|
|
||||||
If you are unsure as to whether this is required, answer N.
|
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.
|
||||||
|
|
|
@ -19,6 +19,7 @@ obj-$(CONFIG_KEYS_COMPAT) += compat.o
|
||||||
obj-$(CONFIG_PROC_FS) += proc.o
|
obj-$(CONFIG_PROC_FS) += proc.o
|
||||||
obj-$(CONFIG_SYSCTL) += sysctl.o
|
obj-$(CONFIG_SYSCTL) += sysctl.o
|
||||||
obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
|
obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
|
||||||
|
obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o
|
||||||
|
|
||||||
#
|
#
|
||||||
# Key types
|
# Key types
|
||||||
|
|
|
@ -14,8 +14,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 <keys/user-type.h>
|
#include <keys/user-type.h>
|
||||||
#include <keys/big_key-type.h>
|
#include <keys/big_key-type.h>
|
||||||
|
#include <crypto/rng.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Layout of key payload words.
|
* Layout of key payload words.
|
||||||
|
@ -27,6 +29,14 @@ 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
|
||||||
|
@ -34,6 +44,11 @@ enum {
|
||||||
*/
|
*/
|
||||||
#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 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
|
||||||
|
@ -49,6 +64,54 @@ struct key_type key_type_big_key = {
|
||||||
.read = big_key_read,
|
.read = big_key_read,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
* Preparse a big key
|
||||||
*/
|
*/
|
||||||
|
@ -56,6 +119,8 @@ int big_key_preparse(struct key_preparsed_payload *prep)
|
||||||
{
|
{
|
||||||
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 *data = NULL;
|
||||||
ssize_t written;
|
ssize_t written;
|
||||||
size_t datalen = prep->datalen;
|
size_t datalen = prep->datalen;
|
||||||
int ret;
|
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
|
/* Create a shmem file to store the data in. This will permit the data
|
||||||
* to be swapped out if needed.
|
* 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);
|
size_t enclen = ALIGN(datalen, crypto_blkcipher_blocksize(big_key_blkcipher));
|
||||||
if (IS_ERR(file)) {
|
|
||||||
ret = PTR_ERR(file);
|
/* 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;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
written = kernel_write(file, prep->data, prep->datalen, 0);
|
ret = big_key_gen_enckey(enckey);
|
||||||
if (written != datalen) {
|
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);
|
||||||
|
if (IS_ERR(file)) {
|
||||||
|
ret = PTR_ERR(file);
|
||||||
|
goto err_enckey;
|
||||||
|
}
|
||||||
|
|
||||||
|
written = kernel_write(file, data, enclen, 0);
|
||||||
|
if (written != enclen) {
|
||||||
ret = written;
|
ret = written;
|
||||||
if (written >= 0)
|
if (written >= 0)
|
||||||
ret = -ENOMEM;
|
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
|
/* Pin the mount and dentry to the key so that we can open it again
|
||||||
* later
|
* later
|
||||||
*/
|
*/
|
||||||
|
prep->payload.data[big_key_data] = enckey;
|
||||||
*path = file->f_path;
|
*path = file->f_path;
|
||||||
path_get(path);
|
path_get(path);
|
||||||
fput(file);
|
fput(file);
|
||||||
|
kfree(data);
|
||||||
} 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);
|
||||||
|
|
||||||
if (!data)
|
if (!data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -108,7 +203,10 @@ int big_key_preparse(struct key_preparsed_payload *prep)
|
||||||
|
|
||||||
err_fput:
|
err_fput:
|
||||||
fput(file);
|
fput(file);
|
||||||
|
err_enckey:
|
||||||
|
kfree(enckey);
|
||||||
error:
|
error:
|
||||||
|
kfree(data);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,10 +217,10 @@ void big_key_free_preparse(struct key_preparsed_payload *prep)
|
||||||
{
|
{
|
||||||
if (prep->datalen > BIG_KEY_FILE_THRESHOLD) {
|
if (prep->datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||||
struct path *path = (struct path *)&prep->payload.data[big_key_path];
|
struct path *path = (struct path *)&prep->payload.data[big_key_path];
|
||||||
|
|
||||||
path_put(path);
|
path_put(path);
|
||||||
} else {
|
|
||||||
kfree(prep->payload.data[big_key_data]);
|
|
||||||
}
|
}
|
||||||
|
kfree(prep->payload.data[big_key_data]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -147,16 +245,16 @@ void big_key_destroy(struct key *key)
|
||||||
{
|
{
|
||||||
size_t datalen = (size_t)key->payload.data[big_key_len];
|
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];
|
struct path *path = (struct path *)&key->payload.data[big_key_path];
|
||||||
|
|
||||||
path_put(path);
|
path_put(path);
|
||||||
path->mnt = NULL;
|
path->mnt = NULL;
|
||||||
path->dentry = NULL;
|
path->dentry = NULL;
|
||||||
} else {
|
}
|
||||||
kfree(key->payload.data[big_key_data]);
|
kfree(key->payload.data[big_key_data]);
|
||||||
key->payload.data[big_key_data] = NULL;
|
key->payload.data[big_key_data] = NULL;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* describe the big_key key
|
* describe the big_key key
|
||||||
|
@ -188,17 +286,41 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
|
||||||
if (datalen > BIG_KEY_FILE_THRESHOLD) {
|
if (datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||||
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;
|
||||||
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());
|
file = dentry_open(path, O_RDONLY, current_cred());
|
||||||
if (IS_ERR(file))
|
if (IS_ERR(file)) {
|
||||||
return PTR_ERR(file);
|
ret = PTR_ERR(file);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
pos = 0;
|
/* read file to kernel and decrypt */
|
||||||
ret = vfs_read(file, buffer, datalen, &pos);
|
ret = kernel_read(file, 0, data, enclen);
|
||||||
fput(file);
|
if (ret >= 0 && ret != enclen) {
|
||||||
if (ret >= 0 && ret != datalen)
|
|
||||||
ret = -EIO;
|
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 {
|
} else {
|
||||||
ret = datalen;
|
ret = datalen;
|
||||||
if (copy_to_user(buffer, key->payload.data[big_key_data],
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Register key type
|
||||||
|
*/
|
||||||
static int __init big_key_init(void)
|
static int __init big_key_init(void)
|
||||||
{
|
{
|
||||||
return register_key_type(&key_type_big_key);
|
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);
|
device_initcall(big_key_init);
|
||||||
|
late_initcall(big_key_crypto_init);
|
||||||
|
|
|
@ -132,6 +132,10 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
|
||||||
case KEYCTL_GET_PERSISTENT:
|
case KEYCTL_GET_PERSISTENT:
|
||||||
return keyctl_get_persistent(arg2, arg3);
|
return keyctl_get_persistent(arg2, arg3);
|
||||||
|
|
||||||
|
case KEYCTL_DH_COMPUTE:
|
||||||
|
return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3),
|
||||||
|
arg4);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
160
security/keys/dh.c
Normal file
160
security/keys/dh.c
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/key-type.h>
|
#include <linux/key-type.h>
|
||||||
#include <linux/task_work.h>
|
#include <linux/task_work.h>
|
||||||
|
#include <linux/keyctl.h>
|
||||||
|
|
||||||
struct iovec;
|
struct iovec;
|
||||||
|
|
||||||
|
@ -257,6 +258,17 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_KEY_DH_OPERATIONS
|
||||||
|
extern long keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *,
|
||||||
|
size_t);
|
||||||
|
#else
|
||||||
|
static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||||
|
char __user *buffer, size_t buflen)
|
||||||
|
{
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Debugging key validation
|
* Debugging key validation
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1686,6 +1686,11 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
||||||
case KEYCTL_GET_PERSISTENT:
|
case KEYCTL_GET_PERSISTENT:
|
||||||
return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3);
|
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);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,45 +96,25 @@ EXPORT_SYMBOL_GPL(user_free_preparse);
|
||||||
*/
|
*/
|
||||||
int user_update(struct key *key, struct key_preparsed_payload *prep)
|
int user_update(struct key *key, struct key_preparsed_payload *prep)
|
||||||
{
|
{
|
||||||
struct user_key_payload *upayload, *zap;
|
struct user_key_payload *zap = NULL;
|
||||||
size_t datalen = prep->datalen;
|
|
||||||
int ret;
|
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 */
|
/* check the quota and attach the new data */
|
||||||
zap = upayload;
|
ret = key_payload_reserve(key, prep->datalen);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
ret = key_payload_reserve(key, datalen);
|
|
||||||
|
|
||||||
if (ret == 0) {
|
|
||||||
/* attach the new data, displacing the old */
|
/* attach the new data, displacing the old */
|
||||||
|
key->expiry = prep->expiry;
|
||||||
if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
|
if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
|
||||||
zap = key->payload.data[0];
|
zap = rcu_dereference_key(key);
|
||||||
else
|
rcu_assign_keypointer(key, prep->payload.data[0]);
|
||||||
zap = NULL;
|
prep->payload.data[0] = NULL;
|
||||||
rcu_assign_keypointer(key, upayload);
|
|
||||||
key->expiry = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zap)
|
if (zap)
|
||||||
kfree_rcu(zap, rcu);
|
kfree_rcu(zap, rcu);
|
||||||
|
|
||||||
error:
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL_GPL(user_update);
|
EXPORT_SYMBOL_GPL(user_update);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in a new issue