KEYS: prevent keys from being removed from specified keyrings
authorMimi Zohar <zohar@linux.vnet.ibm.com>
Tue, 10 Nov 2015 13:34:46 +0000 (08:34 -0500)
committerMimi Zohar <zohar@linux.vnet.ibm.com>
Tue, 15 Dec 2015 15:01:43 +0000 (10:01 -0500)
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>
include/linux/key.h
security/keys/key.c
security/keys/keyctl.c

index 66f70524398594598fb721cf284ebdf1403d8566..7321ab8ef949c8fb73da592e75c3dacddf723b6f 100644 (file)
@@ -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
index ab7997ded7255258365169abaf820e811e0a7768..09ef276c4bdcaf8e0c419df61d1cd7b07bf5253c 100644 (file)
@@ -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)
index fb111eafcb893e4f5146c74a84f8499d4e1553c4..e83ec6b9eb9dc6255dce0edc29d65c7d8a326f9c 100644 (file)
@@ -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;
 }