Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security

Pull security subsystem updates from James Morris:
 "Highlights:

  IMA:
   - provide ">" and "<" operators for fowner/uid/euid rules

  KEYS:
   - add a system blacklist keyring

   - add KEYCTL_RESTRICT_KEYRING, exposes keyring link restriction
     functionality to userland via keyctl()

  LSM:
   - harden LSM API with __ro_after_init

   - add prlmit security hook, implement for SELinux

   - revive security_task_alloc hook

  TPM:
   - implement contextual TPM command 'spaces'"

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (98 commits)
  tpm: Fix reference count to main device
  tpm_tis: convert to using locality callbacks
  tpm: fix handling of the TPM 2.0 event logs
  tpm_crb: remove a cruft constant
  keys: select CONFIG_CRYPTO when selecting DH / KDF
  apparmor: Make path_max parameter readonly
  apparmor: fix parameters so that the permission test is bypassed at boot
  apparmor: fix invalid reference to index variable of iterator line 836
  apparmor: use SHASH_DESC_ON_STACK
  security/apparmor/lsm.c: set debug messages
  apparmor: fix boolreturn.cocci warnings
  Smack: Use GFP_KERNEL for smk_netlbl_mls().
  smack: fix double free in smack_parse_opts_str()
  KEYS: add SP800-56A KDF support for DH
  KEYS: Keyring asymmetric key restrict method with chaining
  KEYS: Restrict asymmetric key linkage using a specific keychain
  KEYS: Add a lookup_restriction function for the asymmetric key type
  KEYS: Add KEYCTL_RESTRICT_KEYRING
  KEYS: Consistent ordering for __key_link_begin and restrict check
  KEYS: Add an optional lookup_restriction hook to key_type
  ...
This commit is contained in:
Linus Torvalds
2017-05-03 08:50:52 -07:00
95 changed files with 3243 additions and 1123 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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);
}

View File

@@ -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);
}

View File

@@ -220,7 +220,7 @@ continue_scanning:
key = rb_entry(cursor, struct key, serial_node);
cursor = rb_next(cursor);
if (atomic_read(&key->usage) == 0)
if (refcount_read(&key->usage) == 0)
goto found_unreferenced_key;
if (unlikely(gc_state & KEY_GC_REAPING_DEAD_1)) {
@@ -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

View File

@@ -17,6 +17,8 @@
#include <linux/key-type.h>
#include <linux/task_work.h>
#include <linux/keyctl.h>
#include <linux/refcount.h>
#include <linux/compat.h>
struct iovec;
@@ -53,7 +55,7 @@ struct key_user {
struct rb_node node;
struct mutex cons_lock; /* construction initiation lock */
spinlock_t lock;
atomic_t usage; /* for accessing qnkeys & qnbytes */
refcount_t usage; /* for accessing qnkeys & qnbytes */
atomic_t nkeys; /* number of keys */
atomic_t nikeys; /* number of instantiated keys */
kuid_t uid;
@@ -167,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);
@@ -249,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;
@@ -261,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
/*

View File

@@ -93,7 +93,7 @@ try_again:
/* if we get here, then the user record still hadn't appeared on the
* second pass - so we use the candidate record */
atomic_set(&candidate->usage, 1);
refcount_set(&candidate->usage, 1);
atomic_set(&candidate->nkeys, 0);
atomic_set(&candidate->nikeys, 0);
candidate->uid = uid;
@@ -110,7 +110,7 @@ try_again:
/* okay - we found a user record for this UID */
found:
atomic_inc(&user->usage);
refcount_inc(&user->usage);
spin_unlock(&key_user_lock);
kfree(candidate);
out:
@@ -122,7 +122,7 @@ out:
*/
void key_user_put(struct key_user *user)
{
if (atomic_dec_and_lock(&user->usage, &key_user_lock)) {
if (refcount_dec_and_lock(&user->usage, &key_user_lock)) {
rb_erase(&user->node, &key_user_tree);
spin_unlock(&key_user_lock);
@@ -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;
@@ -285,7 +286,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
if (!key->index_key.description)
goto no_memory_3;
atomic_set(&key->usage, 1);
refcount_set(&key->usage, 1);
init_rwsem(&key->sem);
lockdep_set_class(&key->sem, &type->lock_class);
key->index_key.type = type;
@@ -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);
@@ -621,7 +626,7 @@ void key_put(struct key *key)
if (key) {
key_check(key);
if (atomic_dec_and_test(&key->usage))
if (refcount_dec_and_test(&key->usage))
schedule_work(&key_gc_work);
}
}
@@ -656,7 +661,7 @@ not_found:
found:
/* pretend it doesn't exist if it is awaiting deletion */
if (atomic_read(&key->usage) == 0)
if (refcount_read(&key->usage) == 0)
goto not_found;
/* this races with key_put(), but that doesn't matter since key_put()
@@ -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);

View File

@@ -1584,6 +1584,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
*/
@@ -1693,7 +1746,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;

View File

@@ -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.
*
@@ -1033,7 +1145,7 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
/* we've got a match but we might end up racing with
* key_cleanup() if the keyring is currently 'dead'
* (ie. it has a zero usage count) */
if (!atomic_inc_not_zero(&keyring->usage))
if (!refcount_inc_not_zero(&keyring->usage))
continue;
keyring->last_used_at = current_kernel_time().tv_sec;
goto out;
@@ -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);
}
/**
@@ -1250,14 +1363,14 @@ int key_link(struct key *keyring, struct key *key)
struct assoc_array_edit *edit;
int ret;
kenter("{%d,%d}", keyring->serial, atomic_read(&keyring->usage));
kenter("{%d,%d}", keyring->serial, refcount_read(&keyring->usage));
key_check(keyring);
key_check(key);
ret = __key_link_begin(keyring, &key->index_key, &edit);
if (ret == 0) {
kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage));
kdebug("begun {%d,%d}", keyring->serial, refcount_read(&keyring->usage));
ret = __key_link_check_restriction(keyring, key);
if (ret == 0)
ret = __key_link_check_live_key(keyring, key);
@@ -1266,7 +1379,7 @@ int key_link(struct key *keyring, struct key *key)
__key_link_end(keyring, &key->index_key, edit);
}
kleave(" = %d {%d,%d}", ret, keyring->serial, atomic_read(&keyring->usage));
kleave(" = %d {%d,%d}", ret, keyring->serial, refcount_read(&keyring->usage));
return ret;
}
EXPORT_SYMBOL(key_link);
@@ -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]");
}

View File

@@ -252,7 +252,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT),
showflag(key, 'N', KEY_FLAG_NEGATIVE),
showflag(key, 'i', KEY_FLAG_INVALIDATED),
atomic_read(&key->usage),
refcount_read(&key->usage),
xbuf,
key->perm,
from_kuid_munged(seq_user_ns(m), key->uid),
@@ -340,7 +340,7 @@ static int proc_key_users_show(struct seq_file *m, void *v)
seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n",
from_kuid_munged(seq_user_ns(m), user->uid),
atomic_read(&user->usage),
refcount_read(&user->usage),
atomic_read(&user->nkeys),
atomic_read(&user->nikeys),
user->qnkeys,

View File

@@ -30,7 +30,7 @@ static DEFINE_MUTEX(key_user_keyring_mutex);
/* The root user's tracking struct */
struct key_user root_key_user = {
.usage = ATOMIC_INIT(3),
.usage = REFCOUNT_INIT(3),
.cons_lock = __MUTEX_INITIALIZER(root_key_user.cons_lock),
.lock = __SPIN_LOCK_UNLOCKED(root_key_user.lock),
.nkeys = ATOMIC_INIT(2),

View File

@@ -213,7 +213,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
if (ret < 0)
goto error_inst;
kleave(" = {%d,%d}", authkey->serial, atomic_read(&authkey->usage));
kleave(" = {%d,%d}", authkey->serial, refcount_read(&authkey->usage));
return authkey;
auth_key_revoked: