KEYS: Allow special keys (eg. DNS results) to be invalidated by CAP_SYS_ADMIN
authorDavid Howells <dhowells@redhat.com>
Thu, 17 Jul 2014 19:45:08 +0000 (20:45 +0100)
committerDavid Howells <dhowells@redhat.com>
Thu, 17 Jul 2014 19:45:08 +0000 (20:45 +0100)
Special kernel keys, such as those used to hold DNS results for AFS, CIFS and
NFS and those used to hold idmapper results for NFS, used to be
'invalidateable' with key_revoke().  However, since the default permissions for
keys were reduced:

Commit: 96b5c8fea6c0861621051290d705ec2e971963f1
KEYS: Reduce initial permissions on keys

it has become impossible to do this.

Add a key flag (KEY_FLAG_ROOT_CAN_INVAL) that will permit a key to be
invalidated by root.  This should not be used for system keyrings as the
garbage collector will try and remove any invalidate key.  For system keyrings,
KEY_FLAG_ROOT_CAN_CLEAR can be used instead.

After this, from userspace, keyctl_invalidate() and "keyctl invalidate" can be
used by any possessor of CAP_SYS_ADMIN (typically root) to invalidate DNS and
idmapper keys.  Invalidated keys are immediately garbage collected and will be
immediately rerequested if needed again.

Signed-off-by: David Howells <dhowells@redhat.com>
Tested-by: Steve Dickson <steved@redhat.com>
fs/nfs/idmap.c
include/linux/key.h
net/dns_resolver/dns_query.c
security/keys/keyctl.c

index 567983d2c0ebada3105f5c1ef42bc0c4595c2ca6..b7458d77f511e045fdb141ba26cafd8ad9633b9a 100644 (file)
@@ -282,6 +282,8 @@ static struct key *nfs_idmap_request_key(const char *name, size_t namelen,
                                                desc, "", 0, idmap);
                mutex_unlock(&idmap->idmap_mutex);
        }
+       if (!IS_ERR(rkey))
+               set_bit(KEY_FLAG_ROOT_CAN_INVAL, &rkey->flags);
 
        kfree(desc);
        return rkey;
index 017b0826642f04c070137114ee98958737677cd6..e37a4d8071859106041f6bb5013a760368793636 100644 (file)
@@ -170,6 +170,7 @@ struct key {
 #define KEY_FLAG_INVALIDATED   7       /* set if key has been invalidated */
 #define KEY_FLAG_TRUSTED       8       /* set if key is trusted */
 #define KEY_FLAG_TRUSTED_ONLY  9       /* set if keyring only accepts links to trusted keys */
+#define KEY_FLAG_ROOT_CAN_INVAL        11      /* set if key can be invalidated by root without permission */
 
        /* the key type and key description string
         * - the desc is used to match a key against search criteria
index 9acec61f54334f146d87711f145f1992dbf2c360..9a32f55cf9b9584cd0ca517681f6035e2aab3dc6 100644 (file)
@@ -129,6 +129,7 @@ int dns_query(const char *type, const char *name, size_t namelen,
        }
 
        down_read(&rkey->sem);
+       set_bit(KEY_FLAG_ROOT_CAN_INVAL, &rkey->flags);
        rkey->perm |= KEY_USR_VIEW;
 
        ret = key_validate(rkey);
index cd5bd0cef25df3fce5801e938567e156d47f275f..609f8d326ddc63d2aa5dc16b2520e0f74e70e101 100644 (file)
@@ -404,12 +404,25 @@ long keyctl_invalidate_key(key_serial_t id)
        key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
+
+               /* Root is permitted to invalidate certain special keys */
+               if (capable(CAP_SYS_ADMIN)) {
+                       key_ref = lookup_user_key(id, 0, 0);
+                       if (IS_ERR(key_ref))
+                               goto error;
+                       if (test_bit(KEY_FLAG_ROOT_CAN_INVAL,
+                                    &key_ref_to_ptr(key_ref)->flags))
+                               goto invalidate;
+                       goto error_put;
+               }
+
                goto error;
        }
 
+invalidate:
        key_invalidate(key_ref_to_ptr(key_ref));
        ret = 0;
-
+error_put:
        key_ref_put(key_ref);
 error:
        kleave(" = %ld", ret);