summaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorMat Martineau <mathew.j.martineau@linux.intel.com>2016-09-01 01:05:43 +0200
committerMat Martineau <mathew.j.martineau@linux.intel.com>2017-04-04 23:10:10 +0200
commit2b6aa412ff23a02ac777ad307249c60a839cfd25 (patch)
tree317dced64727a10b3ce09ca84ac8e153c7dabf77 /security
parentKEYS: Add a key restriction struct (diff)
downloadlinux-2b6aa412ff23a02ac777ad307249c60a839cfd25.tar.xz
linux-2b6aa412ff23a02ac777ad307249c60a839cfd25.zip
KEYS: Use structure to capture key restriction function and data
Replace struct key's restrict_link function pointer with a pointer to the new struct key_restriction. The structure contains pointers to the restriction function as well as relevant data for evaluating the restriction. The garbage collector checks restrict_link->keytype when key types are unregistered. Restrictions involving a removed key type are converted to use restrict_link_reject so that restrictions cannot be removed by unregistering key types. Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Diffstat (limited to 'security')
-rw-r--r--security/integrity/digsig.c9
-rw-r--r--security/integrity/ima/ima_mok.c11
-rw-r--r--security/keys/gc.c11
-rw-r--r--security/keys/internal.h2
-rw-r--r--security/keys/key.c23
-rw-r--r--security/keys/keyring.c68
6 files changed, 108 insertions, 16 deletions
diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index 106e855e2d9d..06554c448dce 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -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",
diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c
index 74a279957464..073ddc9bce5b 100644
--- a/security/integrity/ima/ima_mok.c
+++ b/security/integrity/ima/ima_mok.c
@@ -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.");
diff --git a/security/keys/gc.c b/security/keys/gc.c
index 44789256c88c..15b9ddf510e4 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -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
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 6bee06ae026d..24762ae9a198 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -168,6 +168,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);
diff --git a/security/keys/key.c b/security/keys/key.c
index 27fc1bb40034..2ea5967121de 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -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,7 +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,
- key_restrict_link_func_t restrict_link)
+ struct key_restriction *restrict_link)
{
struct key_user *user = NULL;
struct key *key;
@@ -497,9 +500,11 @@ int key_instantiate_and_link(struct key *key,
}
if (keyring) {
- if (keyring->restrict_link) {
- ret = keyring->restrict_link(keyring, key->type,
- &prep.payload, NULL);
+ 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;
}
@@ -804,7 +809,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;
- key_restrict_link_func_t restrict_link = NULL;
+ struct key_restriction *restrict_link = NULL;
/* look up the key type to see if it's one of the registered kernel
* types */
@@ -850,9 +855,9 @@ 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,
- NULL);
+ 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_free_prep;
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 2ccc66ec35d7..838334fec6ce 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -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,7 +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,
- key_restrict_link_func_t restrict_link,
+ struct key_restriction *restrict_link,
struct key *dest)
{
struct key *keyring;
@@ -523,8 +530,8 @@ EXPORT_SYMBOL(keyring_alloc);
* 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,
@@ -1220,9 +1227,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, NULL);
+ return keyring->restrict_link->check(keyring, key->type, &key->payload,
+ keyring->restrict_link->key);
}
/**
@@ -1426,3 +1434,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]");
+}