mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 02:32:09 -04:00
Merge tag 'keys-next-20170412' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs into next
This commit is contained in:
commit
30a83251dd
33 changed files with 1305 additions and 116 deletions
|
|
@ -81,18 +81,25 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
|||
int __init integrity_init_keyring(const unsigned int id)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
struct key_restriction *restriction;
|
||||
int err = 0;
|
||||
|
||||
if (!init_keyring)
|
||||
return 0;
|
||||
|
||||
restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
|
||||
if (!restriction)
|
||||
return -ENOMEM;
|
||||
|
||||
restriction->check = restrict_link_to_ima;
|
||||
|
||||
keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0),
|
||||
KGIDT_INIT(0), cred,
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ |
|
||||
KEY_USR_WRITE | KEY_USR_SEARCH),
|
||||
KEY_ALLOC_NOT_IN_QUOTA,
|
||||
restrict_link_to_ima, NULL);
|
||||
restriction, NULL);
|
||||
if (IS_ERR(keyring[id])) {
|
||||
err = PTR_ERR(keyring[id]);
|
||||
pr_info("Can't allocate %s keyring (%d)\n",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/cred.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <keys/system_keyring.h>
|
||||
|
||||
|
||||
|
|
@ -27,15 +28,23 @@ struct key *ima_blacklist_keyring;
|
|||
*/
|
||||
__init int ima_mok_init(void)
|
||||
{
|
||||
struct key_restriction *restriction;
|
||||
|
||||
pr_notice("Allocating IMA blacklist keyring.\n");
|
||||
|
||||
restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
|
||||
if (!restriction)
|
||||
panic("Can't allocate IMA blacklist restriction.");
|
||||
|
||||
restriction->check = restrict_link_by_builtin_trusted;
|
||||
|
||||
ima_blacklist_keyring = keyring_alloc(".ima_blacklist",
|
||||
KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
|
||||
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ |
|
||||
KEY_USR_WRITE | KEY_USR_SEARCH,
|
||||
KEY_ALLOC_NOT_IN_QUOTA,
|
||||
restrict_link_by_builtin_trusted, NULL);
|
||||
restriction, NULL);
|
||||
|
||||
if (IS_ERR(ima_blacklist_keyring))
|
||||
panic("Can't allocate IMA blacklist keyring.");
|
||||
|
|
|
|||
|
|
@ -90,6 +90,8 @@ config KEY_DH_OPERATIONS
|
|||
bool "Diffie-Hellman operations on retained keys"
|
||||
depends on KEYS
|
||||
select MPILIB
|
||||
select CRYPTO
|
||||
select CRYPTO_HASH
|
||||
help
|
||||
This option provides support for calculating Diffie-Hellman
|
||||
public keys and shared secrets using values stored as keys
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ obj-y := \
|
|||
request_key.o \
|
||||
request_key_auth.o \
|
||||
user_defined.o
|
||||
obj-$(CONFIG_KEYS_COMPAT) += compat.o
|
||||
compat-obj-$(CONFIG_KEY_DH_OPERATIONS) += compat_dh.o
|
||||
obj-$(CONFIG_KEYS_COMPAT) += compat.o $(compat-obj-y)
|
||||
obj-$(CONFIG_PROC_FS) += proc.o
|
||||
obj-$(CONFIG_SYSCTL) += sysctl.o
|
||||
obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
|
||||
|
|
|
|||
|
|
@ -133,8 +133,13 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
|
|||
return keyctl_get_persistent(arg2, arg3);
|
||||
|
||||
case KEYCTL_DH_COMPUTE:
|
||||
return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3),
|
||||
arg4, compat_ptr(arg5));
|
||||
return compat_keyctl_dh_compute(compat_ptr(arg2),
|
||||
compat_ptr(arg3),
|
||||
arg4, compat_ptr(arg5));
|
||||
|
||||
case KEYCTL_RESTRICT_KEYRING:
|
||||
return keyctl_restrict_keyring(arg2, compat_ptr(arg3),
|
||||
compat_ptr(arg4));
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
|
|
|
|||
38
security/keys/compat_dh.c
Normal file
38
security/keys/compat_dh.c
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/* 32-bit compatibility syscall for 64-bit systems for DH operations
|
||||
*
|
||||
* Copyright (C) 2016 Stephan Mueller <smueller@chronox.de>
|
||||
*
|
||||
* 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/uaccess.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* Perform the DH computation or DH based key derivation.
|
||||
*
|
||||
* If successful, 0 will be returned.
|
||||
*/
|
||||
long compat_keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
struct compat_keyctl_kdf_params __user *kdf)
|
||||
{
|
||||
struct keyctl_kdf_params kdfcopy;
|
||||
struct compat_keyctl_kdf_params compat_kdfcopy;
|
||||
|
||||
if (!kdf)
|
||||
return __keyctl_dh_compute(params, buffer, buflen, NULL);
|
||||
|
||||
if (copy_from_user(&compat_kdfcopy, kdf, sizeof(compat_kdfcopy)) != 0)
|
||||
return -EFAULT;
|
||||
|
||||
kdfcopy.hashname = compat_ptr(compat_kdfcopy.hashname);
|
||||
kdfcopy.otherinfo = compat_ptr(compat_kdfcopy.otherinfo);
|
||||
kdfcopy.otherinfolen = compat_kdfcopy.otherinfolen;
|
||||
|
||||
return __keyctl_dh_compute(params, buffer, buflen, &kdfcopy);
|
||||
}
|
||||
|
|
@ -11,6 +11,8 @@
|
|||
#include <linux/mpi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <keys/user-type.h>
|
||||
#include "internal.h"
|
||||
|
||||
|
|
@ -77,9 +79,146 @@ error:
|
|||
return ret;
|
||||
}
|
||||
|
||||
long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
void __user *reserved)
|
||||
struct kdf_sdesc {
|
||||
struct shash_desc shash;
|
||||
char ctx[];
|
||||
};
|
||||
|
||||
static int kdf_alloc(struct kdf_sdesc **sdesc_ret, char *hashname)
|
||||
{
|
||||
struct crypto_shash *tfm;
|
||||
struct kdf_sdesc *sdesc;
|
||||
int size;
|
||||
|
||||
/* allocate synchronous hash */
|
||||
tfm = crypto_alloc_shash(hashname, 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
pr_info("could not allocate digest TFM handle %s\n", hashname);
|
||||
return PTR_ERR(tfm);
|
||||
}
|
||||
|
||||
size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm);
|
||||
sdesc = kmalloc(size, GFP_KERNEL);
|
||||
if (!sdesc)
|
||||
return -ENOMEM;
|
||||
sdesc->shash.tfm = tfm;
|
||||
sdesc->shash.flags = 0x0;
|
||||
|
||||
*sdesc_ret = sdesc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kdf_dealloc(struct kdf_sdesc *sdesc)
|
||||
{
|
||||
if (!sdesc)
|
||||
return;
|
||||
|
||||
if (sdesc->shash.tfm)
|
||||
crypto_free_shash(sdesc->shash.tfm);
|
||||
|
||||
kzfree(sdesc);
|
||||
}
|
||||
|
||||
/* convert 32 bit integer into its string representation */
|
||||
static inline void crypto_kw_cpu_to_be32(u32 val, u8 *buf)
|
||||
{
|
||||
__be32 *a = (__be32 *)buf;
|
||||
|
||||
*a = cpu_to_be32(val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of the KDF in counter mode according to SP800-108 section 5.1
|
||||
* as well as SP800-56A section 5.8.1 (Single-step KDF).
|
||||
*
|
||||
* SP800-56A:
|
||||
* The src pointer is defined as Z || other info where Z is the shared secret
|
||||
* from DH and other info is an arbitrary string (see SP800-56A section
|
||||
* 5.8.1.2).
|
||||
*/
|
||||
static int kdf_ctr(struct kdf_sdesc *sdesc, const u8 *src, unsigned int slen,
|
||||
u8 *dst, unsigned int dlen)
|
||||
{
|
||||
struct shash_desc *desc = &sdesc->shash;
|
||||
unsigned int h = crypto_shash_digestsize(desc->tfm);
|
||||
int err = 0;
|
||||
u8 *dst_orig = dst;
|
||||
u32 i = 1;
|
||||
u8 iteration[sizeof(u32)];
|
||||
|
||||
while (dlen) {
|
||||
err = crypto_shash_init(desc);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
crypto_kw_cpu_to_be32(i, iteration);
|
||||
err = crypto_shash_update(desc, iteration, sizeof(u32));
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
if (src && slen) {
|
||||
err = crypto_shash_update(desc, src, slen);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (dlen < h) {
|
||||
u8 tmpbuffer[h];
|
||||
|
||||
err = crypto_shash_final(desc, tmpbuffer);
|
||||
if (err)
|
||||
goto err;
|
||||
memcpy(dst, tmpbuffer, dlen);
|
||||
memzero_explicit(tmpbuffer, h);
|
||||
return 0;
|
||||
} else {
|
||||
err = crypto_shash_final(desc, dst);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
dlen -= h;
|
||||
dst += h;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
memzero_explicit(dst_orig, dlen);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int keyctl_dh_compute_kdf(struct kdf_sdesc *sdesc,
|
||||
char __user *buffer, size_t buflen,
|
||||
uint8_t *kbuf, size_t kbuflen)
|
||||
{
|
||||
uint8_t *outbuf = NULL;
|
||||
int ret;
|
||||
|
||||
outbuf = kmalloc(buflen, GFP_KERNEL);
|
||||
if (!outbuf) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = kdf_ctr(sdesc, kbuf, kbuflen, outbuf, buflen);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = buflen;
|
||||
if (copy_to_user(buffer, outbuf, buflen) != 0)
|
||||
ret = -EFAULT;
|
||||
|
||||
err:
|
||||
kzfree(outbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
long __keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
struct keyctl_kdf_params *kdfcopy)
|
||||
{
|
||||
long ret;
|
||||
MPI base, private, prime, result;
|
||||
|
|
@ -88,6 +227,7 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
|||
uint8_t *kbuf;
|
||||
ssize_t keylen;
|
||||
size_t resultlen;
|
||||
struct kdf_sdesc *sdesc = NULL;
|
||||
|
||||
if (!params || (!buffer && buflen)) {
|
||||
ret = -EINVAL;
|
||||
|
|
@ -98,12 +238,34 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (reserved) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
if (kdfcopy) {
|
||||
char *hashname;
|
||||
|
||||
if (buflen > KEYCTL_KDF_MAX_OUTPUT_LEN ||
|
||||
kdfcopy->otherinfolen > KEYCTL_KDF_MAX_OI_LEN) {
|
||||
ret = -EMSGSIZE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* get KDF name string */
|
||||
hashname = strndup_user(kdfcopy->hashname, CRYPTO_MAX_ALG_NAME);
|
||||
if (IS_ERR(hashname)) {
|
||||
ret = PTR_ERR(hashname);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* allocate KDF from the kernel crypto API */
|
||||
ret = kdf_alloc(&sdesc, hashname);
|
||||
kfree(hashname);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
keylen = mpi_from_key(pcopy.prime, buflen, &prime);
|
||||
/*
|
||||
* If the caller requests postprocessing with a KDF, allow an
|
||||
* arbitrary output buffer size since the KDF ensures proper truncation.
|
||||
*/
|
||||
keylen = mpi_from_key(pcopy.prime, kdfcopy ? SIZE_MAX : buflen, &prime);
|
||||
if (keylen < 0 || !prime) {
|
||||
/* buflen == 0 may be used to query the required buffer size,
|
||||
* which is the prime key length.
|
||||
|
|
@ -133,12 +295,25 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
|||
goto error3;
|
||||
}
|
||||
|
||||
kbuf = kmalloc(resultlen, GFP_KERNEL);
|
||||
/* allocate space for DH shared secret and SP800-56A otherinfo */
|
||||
kbuf = kmalloc(kdfcopy ? (resultlen + kdfcopy->otherinfolen) : resultlen,
|
||||
GFP_KERNEL);
|
||||
if (!kbuf) {
|
||||
ret = -ENOMEM;
|
||||
goto error4;
|
||||
}
|
||||
|
||||
/*
|
||||
* Concatenate SP800-56A otherinfo past DH shared secret -- the
|
||||
* input to the KDF is (DH shared secret || otherinfo)
|
||||
*/
|
||||
if (kdfcopy && kdfcopy->otherinfo &&
|
||||
copy_from_user(kbuf + resultlen, kdfcopy->otherinfo,
|
||||
kdfcopy->otherinfolen) != 0) {
|
||||
ret = -EFAULT;
|
||||
goto error5;
|
||||
}
|
||||
|
||||
ret = do_dh(result, base, private, prime);
|
||||
if (ret)
|
||||
goto error5;
|
||||
|
|
@ -147,12 +322,17 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
|||
if (ret != 0)
|
||||
goto error5;
|
||||
|
||||
ret = nbytes;
|
||||
if (copy_to_user(buffer, kbuf, nbytes) != 0)
|
||||
ret = -EFAULT;
|
||||
if (kdfcopy) {
|
||||
ret = keyctl_dh_compute_kdf(sdesc, buffer, buflen, kbuf,
|
||||
resultlen + kdfcopy->otherinfolen);
|
||||
} else {
|
||||
ret = nbytes;
|
||||
if (copy_to_user(buffer, kbuf, nbytes) != 0)
|
||||
ret = -EFAULT;
|
||||
}
|
||||
|
||||
error5:
|
||||
kfree(kbuf);
|
||||
kzfree(kbuf);
|
||||
error4:
|
||||
mpi_free(result);
|
||||
error3:
|
||||
|
|
@ -162,5 +342,21 @@ error2:
|
|||
error1:
|
||||
mpi_free(prime);
|
||||
out:
|
||||
kdf_dealloc(sdesc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
struct keyctl_kdf_params __user *kdf)
|
||||
{
|
||||
struct keyctl_kdf_params kdfcopy;
|
||||
|
||||
if (!kdf)
|
||||
return __keyctl_dh_compute(params, buffer, buflen, NULL);
|
||||
|
||||
if (copy_from_user(&kdfcopy, kdf, sizeof(kdfcopy)) != 0)
|
||||
return -EFAULT;
|
||||
|
||||
return __keyctl_dh_compute(params, buffer, buflen, &kdfcopy);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -229,6 +229,9 @@ continue_scanning:
|
|||
set_bit(KEY_FLAG_DEAD, &key->flags);
|
||||
key->perm = 0;
|
||||
goto skip_dead_key;
|
||||
} else if (key->type == &key_type_keyring &&
|
||||
key->restrict_link) {
|
||||
goto found_restricted_keyring;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -334,6 +337,14 @@ found_unreferenced_key:
|
|||
gc_state |= KEY_GC_REAP_AGAIN;
|
||||
goto maybe_resched;
|
||||
|
||||
/* We found a restricted keyring and need to update the restriction if
|
||||
* it is associated with the dead key type.
|
||||
*/
|
||||
found_restricted_keyring:
|
||||
spin_unlock(&key_serial_lock);
|
||||
keyring_restriction_gc(key, key_gc_dead_keytype);
|
||||
goto maybe_resched;
|
||||
|
||||
/* We found a keyring and we need to check the payload for links to
|
||||
* dead or expired keys. We don't flag another reap immediately as we
|
||||
* have to wait for the old payload to be destroyed by RCU before we
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/task_work.h>
|
||||
#include <linux/keyctl.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
struct iovec;
|
||||
|
||||
|
|
@ -168,6 +169,8 @@ extern void key_change_session_keyring(struct callback_head *twork);
|
|||
extern struct work_struct key_gc_work;
|
||||
extern unsigned key_gc_delay;
|
||||
extern void keyring_gc(struct key *keyring, time_t limit);
|
||||
extern void keyring_restriction_gc(struct key *keyring,
|
||||
struct key_type *dead_type);
|
||||
extern void key_schedule_gc(time_t gc_at);
|
||||
extern void key_schedule_gc_links(void);
|
||||
extern void key_gc_keytype(struct key_type *ktype);
|
||||
|
|
@ -250,6 +253,9 @@ struct iov_iter;
|
|||
extern long keyctl_instantiate_key_common(key_serial_t,
|
||||
struct iov_iter *,
|
||||
key_serial_t);
|
||||
extern long keyctl_restrict_keyring(key_serial_t id,
|
||||
const char __user *_type,
|
||||
const char __user *_restriction);
|
||||
#ifdef CONFIG_PERSISTENT_KEYRINGS
|
||||
extern long keyctl_get_persistent(uid_t, key_serial_t);
|
||||
extern unsigned persistent_keyring_expiry;
|
||||
|
|
@ -262,14 +268,33 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring)
|
|||
|
||||
#ifdef CONFIG_KEY_DH_OPERATIONS
|
||||
extern long keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *,
|
||||
size_t, void __user *);
|
||||
size_t, struct keyctl_kdf_params __user *);
|
||||
extern long __keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *,
|
||||
size_t, struct keyctl_kdf_params *);
|
||||
#ifdef CONFIG_KEYS_COMPAT
|
||||
extern long compat_keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
struct compat_keyctl_kdf_params __user *kdf);
|
||||
#endif
|
||||
#define KEYCTL_KDF_MAX_OUTPUT_LEN 1024 /* max length of KDF output */
|
||||
#define KEYCTL_KDF_MAX_OI_LEN 64 /* max length of otherinfo */
|
||||
#else
|
||||
static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
void __user *reserved)
|
||||
struct keyctl_kdf_params __user *kdf)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KEYS_COMPAT
|
||||
static inline long compat_keyctl_dh_compute(
|
||||
struct keyctl_dh_params __user *params,
|
||||
char __user *buffer, size_t buflen,
|
||||
struct keyctl_kdf_params __user *kdf)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -201,12 +201,15 @@ 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.
|
||||
* @restrict_link: Optional link restriction 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
|
||||
* key before returning.
|
||||
*
|
||||
* The restrict_link structure (if not NULL) will be freed when the
|
||||
* keyring is destroyed, so it must be dynamically allocated.
|
||||
*
|
||||
* The user's key count quota is updated to reflect the creation of the key and
|
||||
* the user's key data quota has the default for the key type reserved. The
|
||||
* instantiation function should amend this as necessary. If insufficient
|
||||
|
|
@ -225,9 +228,7 @@ 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,
|
||||
int (*restrict_link)(struct key *,
|
||||
const struct key_type *,
|
||||
const union key_payload *))
|
||||
struct key_restriction *restrict_link)
|
||||
{
|
||||
struct key_user *user = NULL;
|
||||
struct key *key;
|
||||
|
|
@ -499,19 +500,23 @@ 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;
|
||||
|
||||
if (keyring->restrict_link && keyring->restrict_link->check) {
|
||||
struct key_restriction *keyres = keyring->restrict_link;
|
||||
|
||||
ret = keyres->check(keyring, key->type, &prep.payload,
|
||||
keyres->key);
|
||||
if (ret < 0)
|
||||
goto error_link_end;
|
||||
}
|
||||
}
|
||||
|
||||
ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit);
|
||||
|
||||
error_link_end:
|
||||
if (keyring)
|
||||
__key_link_end(keyring, &key->index_key, edit);
|
||||
|
||||
|
|
@ -806,9 +811,7 @@ 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;
|
||||
struct key_restriction *restrict_link = NULL;
|
||||
|
||||
/* look up the key type to see if it's one of the registered kernel
|
||||
* types */
|
||||
|
|
@ -854,20 +857,21 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
|||
}
|
||||
index_key.desc_len = strlen(index_key.description);
|
||||
|
||||
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) {
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error_free_prep;
|
||||
}
|
||||
|
||||
if (restrict_link && restrict_link->check) {
|
||||
ret = restrict_link->check(keyring, index_key.type,
|
||||
&prep.payload, restrict_link->key);
|
||||
if (ret < 0) {
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error_link_end;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we're going to allocate a new key, we're going to have
|
||||
* to modify the keyring */
|
||||
ret = key_permission(keyring_ref, KEY_NEED_WRITE);
|
||||
|
|
|
|||
|
|
@ -1582,6 +1582,59 @@ error_keyring:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply a restriction to a given keyring.
|
||||
*
|
||||
* The caller must have Setattr permission to change keyring restrictions.
|
||||
*
|
||||
* The requested type name may be a NULL pointer to reject all attempts
|
||||
* to link to the keyring. If _type is non-NULL, _restriction can be
|
||||
* NULL or a pointer to a string describing the restriction. If _type is
|
||||
* NULL, _restriction must also be NULL.
|
||||
*
|
||||
* Returns 0 if successful.
|
||||
*/
|
||||
long keyctl_restrict_keyring(key_serial_t id, const char __user *_type,
|
||||
const char __user *_restriction)
|
||||
{
|
||||
key_ref_t key_ref;
|
||||
bool link_reject = !_type;
|
||||
char type[32];
|
||||
char *restriction = NULL;
|
||||
long ret;
|
||||
|
||||
key_ref = lookup_user_key(id, 0, KEY_NEED_SETATTR);
|
||||
if (IS_ERR(key_ref))
|
||||
return PTR_ERR(key_ref);
|
||||
|
||||
if (_type) {
|
||||
ret = key_get_type_from_user(type, _type, sizeof(type));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (_restriction) {
|
||||
if (!_type) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
restriction = strndup_user(_restriction, PAGE_SIZE);
|
||||
if (IS_ERR(restriction)) {
|
||||
ret = PTR_ERR(restriction);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
ret = keyring_restrict(key_ref, link_reject ? NULL : type, restriction);
|
||||
kfree(restriction);
|
||||
|
||||
error:
|
||||
key_ref_put(key_ref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The key control system call
|
||||
*/
|
||||
|
|
@ -1691,7 +1744,12 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
|||
case KEYCTL_DH_COMPUTE:
|
||||
return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2,
|
||||
(char __user *) arg3, (size_t) arg4,
|
||||
(void __user *) arg5);
|
||||
(struct keyctl_kdf_params __user *) arg5);
|
||||
|
||||
case KEYCTL_RESTRICT_KEYRING:
|
||||
return keyctl_restrict_keyring((key_serial_t) arg2,
|
||||
(const char __user *) arg3,
|
||||
(const char __user *) arg4);
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
|
|
|
|||
|
|
@ -394,6 +394,13 @@ static void keyring_destroy(struct key *keyring)
|
|||
write_unlock(&keyring_name_lock);
|
||||
}
|
||||
|
||||
if (keyring->restrict_link) {
|
||||
struct key_restriction *keyres = keyring->restrict_link;
|
||||
|
||||
key_put(keyres->key);
|
||||
kfree(keyres);
|
||||
}
|
||||
|
||||
assoc_array_destroy(&keyring->keys, &keyring_assoc_array_ops);
|
||||
}
|
||||
|
||||
|
|
@ -492,9 +499,7 @@ 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,
|
||||
int (*restrict_link)(struct key *,
|
||||
const struct key_type *,
|
||||
const union key_payload *),
|
||||
struct key_restriction *restrict_link,
|
||||
struct key *dest)
|
||||
{
|
||||
struct key *keyring;
|
||||
|
|
@ -519,17 +524,19 @@ EXPORT_SYMBOL(keyring_alloc);
|
|||
* @keyring: The keyring being added to.
|
||||
* @type: The type of key being added.
|
||||
* @payload: The payload of the key intended to be added.
|
||||
* @data: Additional data for evaluating restriction.
|
||||
*
|
||||
* 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().
|
||||
* This is meant to be stored in a key_restriction structure which is passed
|
||||
* in the restrict_link parameter to keyring_alloc().
|
||||
*/
|
||||
int restrict_link_reject(struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const union key_payload *payload)
|
||||
const union key_payload *payload,
|
||||
struct key *restriction_key)
|
||||
{
|
||||
return -EPERM;
|
||||
}
|
||||
|
|
@ -940,6 +947,111 @@ key_ref_t keyring_search(key_ref_t keyring,
|
|||
}
|
||||
EXPORT_SYMBOL(keyring_search);
|
||||
|
||||
static struct key_restriction *keyring_restriction_alloc(
|
||||
key_restrict_link_func_t check)
|
||||
{
|
||||
struct key_restriction *keyres =
|
||||
kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
|
||||
|
||||
if (!keyres)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
keyres->check = check;
|
||||
|
||||
return keyres;
|
||||
}
|
||||
|
||||
/*
|
||||
* Semaphore to serialise restriction setup to prevent reference count
|
||||
* cycles through restriction key pointers.
|
||||
*/
|
||||
static DECLARE_RWSEM(keyring_serialise_restrict_sem);
|
||||
|
||||
/*
|
||||
* Check for restriction cycles that would prevent keyring garbage collection.
|
||||
* keyring_serialise_restrict_sem must be held.
|
||||
*/
|
||||
static bool keyring_detect_restriction_cycle(const struct key *dest_keyring,
|
||||
struct key_restriction *keyres)
|
||||
{
|
||||
while (keyres && keyres->key &&
|
||||
keyres->key->type == &key_type_keyring) {
|
||||
if (keyres->key == dest_keyring)
|
||||
return true;
|
||||
|
||||
keyres = keyres->key->restrict_link;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* keyring_restrict - Look up and apply a restriction to a keyring
|
||||
*
|
||||
* @keyring: The keyring to be restricted
|
||||
* @restriction: The restriction options to apply to the keyring
|
||||
*/
|
||||
int keyring_restrict(key_ref_t keyring_ref, const char *type,
|
||||
const char *restriction)
|
||||
{
|
||||
struct key *keyring;
|
||||
struct key_type *restrict_type = NULL;
|
||||
struct key_restriction *restrict_link;
|
||||
int ret = 0;
|
||||
|
||||
keyring = key_ref_to_ptr(keyring_ref);
|
||||
key_check(keyring);
|
||||
|
||||
if (keyring->type != &key_type_keyring)
|
||||
return -ENOTDIR;
|
||||
|
||||
if (!type) {
|
||||
restrict_link = keyring_restriction_alloc(restrict_link_reject);
|
||||
} else {
|
||||
restrict_type = key_type_lookup(type);
|
||||
|
||||
if (IS_ERR(restrict_type))
|
||||
return PTR_ERR(restrict_type);
|
||||
|
||||
if (!restrict_type->lookup_restriction) {
|
||||
ret = -ENOENT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
restrict_link = restrict_type->lookup_restriction(restriction);
|
||||
}
|
||||
|
||||
if (IS_ERR(restrict_link)) {
|
||||
ret = PTR_ERR(restrict_link);
|
||||
goto error;
|
||||
}
|
||||
|
||||
down_write(&keyring->sem);
|
||||
down_write(&keyring_serialise_restrict_sem);
|
||||
|
||||
if (keyring->restrict_link)
|
||||
ret = -EEXIST;
|
||||
else if (keyring_detect_restriction_cycle(keyring, restrict_link))
|
||||
ret = -EDEADLK;
|
||||
else
|
||||
keyring->restrict_link = restrict_link;
|
||||
|
||||
up_write(&keyring_serialise_restrict_sem);
|
||||
up_write(&keyring->sem);
|
||||
|
||||
if (ret < 0) {
|
||||
key_put(restrict_link->key);
|
||||
kfree(restrict_link);
|
||||
}
|
||||
|
||||
error:
|
||||
if (restrict_type)
|
||||
key_type_put(restrict_type);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(keyring_restrict);
|
||||
|
||||
/*
|
||||
* Search the given keyring for a key that might be updated.
|
||||
*
|
||||
|
|
@ -1220,9 +1332,10 @@ void __key_link_end(struct key *keyring,
|
|||
*/
|
||||
static int __key_link_check_restriction(struct key *keyring, struct key *key)
|
||||
{
|
||||
if (!keyring->restrict_link)
|
||||
if (!keyring->restrict_link || !keyring->restrict_link->check)
|
||||
return 0;
|
||||
return keyring->restrict_link(keyring, key->type, &key->payload);
|
||||
return keyring->restrict_link->check(keyring, key->type, &key->payload,
|
||||
keyring->restrict_link->key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1426,3 +1539,53 @@ do_gc:
|
|||
up_write(&keyring->sem);
|
||||
kleave(" [gc]");
|
||||
}
|
||||
|
||||
/*
|
||||
* Garbage collect restriction pointers from a keyring.
|
||||
*
|
||||
* Keyring restrictions are associated with a key type, and must be cleaned
|
||||
* up if the key type is unregistered. The restriction is altered to always
|
||||
* reject additional keys so a keyring cannot be opened up by unregistering
|
||||
* a key type.
|
||||
*
|
||||
* Not called with any keyring locks held. The keyring's key struct will not
|
||||
* be deallocated under us as only our caller may deallocate it.
|
||||
*
|
||||
* The caller is required to hold key_types_sem and dead_type->sem. This is
|
||||
* fulfilled by key_gc_keytype() holding the locks on behalf of
|
||||
* key_garbage_collector(), which it invokes on a workqueue.
|
||||
*/
|
||||
void keyring_restriction_gc(struct key *keyring, struct key_type *dead_type)
|
||||
{
|
||||
struct key_restriction *keyres;
|
||||
|
||||
kenter("%x{%s}", keyring->serial, keyring->description ?: "");
|
||||
|
||||
/*
|
||||
* keyring->restrict_link is only assigned at key allocation time
|
||||
* or with the key type locked, so the only values that could be
|
||||
* concurrently assigned to keyring->restrict_link are for key
|
||||
* types other than dead_type. Given this, it's ok to check
|
||||
* the key type before acquiring keyring->sem.
|
||||
*/
|
||||
if (!dead_type || !keyring->restrict_link ||
|
||||
keyring->restrict_link->keytype != dead_type) {
|
||||
kleave(" [no restriction gc]");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Lock the keyring to ensure that a link is not in progress */
|
||||
down_write(&keyring->sem);
|
||||
|
||||
keyres = keyring->restrict_link;
|
||||
|
||||
keyres->check = restrict_link_reject;
|
||||
|
||||
key_put(keyres->key);
|
||||
keyres->key = NULL;
|
||||
keyres->keytype = NULL;
|
||||
|
||||
up_write(&keyring->sem);
|
||||
|
||||
kleave(" [restriction gc]");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue