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
|
@ -823,6 +823,36 @@ The keyctl syscall functions are:
|
|||
A process must have search permission on the key for this function to be
|
||||
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
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#ifndef _LINUX_KEYCTL_H
|
||||
#define _LINUX_KEYCTL_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* special process keyring shortcut IDs */
|
||||
#define KEY_SPEC_THREAD_KEYRING -1 /* - key ID for thread-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_INVALIDATE 21 /* invalidate a key */
|
||||
#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 */
|
||||
|
|
|
@ -35,7 +35,6 @@ config INTEGRITY_ASYMMETRIC_KEYS
|
|||
default n
|
||||
select ASYMMETRIC_KEY_TYPE
|
||||
select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
||||
select PUBLIC_KEY_ALGO_RSA
|
||||
select CRYPTO_RSA
|
||||
select X509_CERTIFICATE_PARSER
|
||||
help
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
@ -27,6 +29,14 @@ enum {
|
|||
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
|
||||
* 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))
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
@ -49,6 +64,54 @@ struct key_type key_type_big_key = {
|
|||
.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
|
||||
*/
|
||||
|
@ -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 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);
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
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;
|
||||
}
|
||||
|
||||
written = kernel_write(file, prep->data, prep->datalen, 0);
|
||||
if (written != datalen) {
|
||||
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);
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
goto err_enckey;
|
||||
}
|
||||
|
||||
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,16 +245,16 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
|
|
@ -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/key-type.h>
|
||||
#include <linux/task_work.h>
|
||||
#include <linux/keyctl.h>
|
||||
|
||||
struct iovec;
|
||||
|
||||
|
@ -257,6 +258,17 @@ 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);
|
||||
#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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
|
|
@ -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, prep->datalen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = key_payload_reserve(key, datalen);
|
||||
|
||||
if (ret == 0) {
|
||||
/* attach the new data, displacing the old */
|
||||
key->expiry = prep->expiry;
|
||||
if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
|
||||
zap = key->payload.data[0];
|
||||
else
|
||||
zap = NULL;
|
||||
rcu_assign_keypointer(key, upayload);
|
||||
key->expiry = 0;
|
||||
}
|
||||
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);
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue