diff options
author | Mimi Zohar <zohar@linux.vnet.ibm.com> | 2015-11-10 08:34:46 -0500 |
---|---|---|
committer | Mimi Zohar <zohar@linux.vnet.ibm.com> | 2015-12-15 10:01:43 -0500 |
commit | d3600bcf9d64d88dc1d189a754dcfab960ce751f (patch) | |
tree | 0db5cee0ddb99cfe1cbd7f8314653f36ddf5005f | |
parent | 80eae209d63ac6361c7b445f7e7e41f39c044772 (diff) | |
download | linux-stable-d3600bcf9d64d88dc1d189a754dcfab960ce751f.tar.gz linux-stable-d3600bcf9d64d88dc1d189a754dcfab960ce751f.tar.bz2 linux-stable-d3600bcf9d64d88dc1d189a754dcfab960ce751f.zip |
KEYS: prevent keys from being removed from specified keyrings
Userspace should not be allowed to remove keys from certain keyrings
(eg. blacklist), though the keys themselves can expire.
This patch defines a new key flag named KEY_FLAG_KEEP to prevent
userspace from being able to unlink, revoke, invalidate or timed
out a key on a keyring. When this flag is set on the keyring, all
keys subsequently added are flagged.
In addition, when this flag is set, the keyring itself can not be
cleared.
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
Cc: David Howells <dhowells@redhat.com>
-rw-r--r-- | include/linux/key.h | 1 | ||||
-rw-r--r-- | security/keys/key.c | 6 | ||||
-rw-r--r-- | security/keys/keyctl.c | 56 |
3 files changed, 52 insertions, 11 deletions
diff --git a/include/linux/key.h b/include/linux/key.h index 66f705243985..7321ab8ef949 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -177,6 +177,7 @@ struct key { #define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */ #define KEY_FLAG_BUILTIN 10 /* set if key is builtin */ #define KEY_FLAG_ROOT_CAN_INVAL 11 /* set if key can be invalidated by root without permission */ +#define KEY_FLAG_KEEP 12 /* set if key should not be removed */ /* the key type and key description string * - the desc is used to match a key against search criteria diff --git a/security/keys/key.c b/security/keys/key.c index ab7997ded725..09ef276c4bdc 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -429,8 +429,12 @@ static int __key_instantiate_and_link(struct key *key, awaken = 1; /* and link it into the destination keyring */ - if (keyring) + if (keyring) { + if (test_bit(KEY_FLAG_KEEP, &keyring->flags)) + set_bit(KEY_FLAG_KEEP, &key->flags); + __key_link(key, _edit); + } /* disable the authorisation key */ if (authkey) diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index fb111eafcb89..e83ec6b9eb9d 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -358,11 +358,14 @@ error: * and any links to the key will be automatically garbage collected after a * certain amount of time (/proc/sys/kernel/keys/gc_delay). * + * Keys with KEY_FLAG_KEEP set should not be revoked. + * * If successful, 0 is returned. */ long keyctl_revoke_key(key_serial_t id) { key_ref_t key_ref; + struct key *key; long ret; key_ref = lookup_user_key(id, 0, KEY_NEED_WRITE); @@ -377,8 +380,13 @@ long keyctl_revoke_key(key_serial_t id) } } - key_revoke(key_ref_to_ptr(key_ref)); - ret = 0; + key = key_ref_to_ptr(key_ref); + if (test_bit(KEY_FLAG_KEEP, &key->flags)) + return -EPERM; + else { + key_revoke(key); + ret = 0; + } key_ref_put(key_ref); error: @@ -392,11 +400,14 @@ error: * The key and any links to the key will be automatically garbage collected * immediately. * + * Keys with KEY_FLAG_KEEP set should not be invalidated. + * * If successful, 0 is returned. */ long keyctl_invalidate_key(key_serial_t id) { key_ref_t key_ref; + struct key *key; long ret; kenter("%d", id); @@ -420,8 +431,13 @@ long keyctl_invalidate_key(key_serial_t id) } invalidate: - key_invalidate(key_ref_to_ptr(key_ref)); - ret = 0; + key = key_ref_to_ptr(key_ref); + if (test_bit(KEY_FLAG_KEEP, &key->flags)) + ret = -EPERM; + else { + key_invalidate(key); + ret = 0; + } error_put: key_ref_put(key_ref); error: @@ -433,12 +449,13 @@ error: * Clear the specified keyring, creating an empty process keyring if one of the * special keyring IDs is used. * - * The keyring must grant the caller Write permission for this to work. If - * successful, 0 will be returned. + * The keyring must grant the caller Write permission and not have + * KEY_FLAG_KEEP set for this to work. If successful, 0 will be returned. */ long keyctl_keyring_clear(key_serial_t ringid) { key_ref_t keyring_ref; + struct key *keyring; long ret; keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); @@ -460,7 +477,11 @@ long keyctl_keyring_clear(key_serial_t ringid) } clear: - ret = keyring_clear(key_ref_to_ptr(keyring_ref)); + keyring = key_ref_to_ptr(keyring_ref); + if (test_bit(KEY_FLAG_KEEP, &keyring->flags)) + ret = -EPERM; + else + ret = keyring_clear(keyring); error_put: key_ref_put(keyring_ref); error: @@ -511,11 +532,14 @@ error: * itself need not grant the caller anything. If the last link to a key is * removed then that key will be scheduled for destruction. * + * Keys or keyrings with KEY_FLAG_KEEP set should not be unlinked. + * * If successful, 0 will be returned. */ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid) { key_ref_t keyring_ref, key_ref; + struct key *keyring, *key; long ret; keyring_ref = lookup_user_key(ringid, 0, KEY_NEED_WRITE); @@ -530,7 +554,13 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid) goto error2; } - ret = key_unlink(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref)); + keyring = key_ref_to_ptr(keyring_ref); + key = key_ref_to_ptr(key_ref); + if (test_bit(KEY_FLAG_KEEP, &keyring->flags) && + test_bit(KEY_FLAG_KEEP, &key->flags)) + ret = -EPERM; + else + ret = key_unlink(keyring, key); key_ref_put(key_ref); error2: @@ -1289,6 +1319,8 @@ error: * the current time. The key and any links to the key will be automatically * garbage collected after the timeout expires. * + * Keys with KEY_FLAG_KEEP set should not be timed out. + * * If successful, 0 is returned. */ long keyctl_set_timeout(key_serial_t id, unsigned timeout) @@ -1320,10 +1352,14 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout) okay: key = key_ref_to_ptr(key_ref); - key_set_timeout(key, timeout); + if (test_bit(KEY_FLAG_KEEP, &key->flags)) + ret = -EPERM; + else { + key_set_timeout(key, timeout); + ret = 0; + } key_put(key); - ret = 0; error: return ret; } |