]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
KEYS: Add KEYCTL_RESTRICT_KEYRING
authorMat Martineau <mathew.j.martineau@linux.intel.com>
Thu, 2 Mar 2017 00:44:09 +0000 (16:44 -0800)
committerMat Martineau <mathew.j.martineau@linux.intel.com>
Tue, 4 Apr 2017 21:10:12 +0000 (14:10 -0700)
Keyrings recently gained restrict_link capabilities that allow
individual keys to be validated prior to linking.  This functionality
was only available using internal kernel APIs.

With the KEYCTL_RESTRICT_KEYRING command existing keyrings can be
configured to check the content of keys before they are linked, and
then allow or disallow linkage of that key to the keyring.

To restrict a keyring, call:

  keyctl(KEYCTL_RESTRICT_KEYRING, key_serial_t keyring, const char *type,
         const char *restriction)

where 'type' is the name of a registered key type and 'restriction' is a
string describing how key linkage is to be restricted. The restriction
option syntax is specific to each key type.

Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Documentation/security/keys.txt
include/linux/key.h
include/uapi/linux/keyctl.h
security/keys/compat.c
security/keys/internal.h
security/keys/keyctl.c
security/keys/keyring.c

index 5fe04a7cc03d4708a98abc1d2e868e40c83fb61e..5f554aab8751ff8e4822542a5860bbbd1d486e06 100644 (file)
@@ -857,6 +857,31 @@ The keyctl syscall functions are:
      supported, error ENOKEY if the key could not be found, or error
      EACCES if the key is not readable by the caller.
 
+ (*) Restrict keyring linkage
+
+       long keyctl(KEYCTL_RESTRICT_KEYRING, key_serial_t keyring,
+                  const char *type, const char *restriction);
+
+     An existing keyring can restrict linkage of additional keys by evaluating
+     the contents of the key according to a restriction scheme.
+
+     "keyring" is the key ID for an existing keyring to apply a restriction
+     to. It may be empty or may already have keys linked. Existing linked keys
+     will remain in the keyring even if the new restriction would reject them.
+
+     "type" is a registered key type.
+
+     "restriction" is a string describing how key linkage is to be restricted.
+     The format varies depending on the key type, and the string is passed to
+     the lookup_restriction() function for the requested type.  It may specify
+     a method and relevant data for the restriction such as signature
+     verification or constraints on key payload. If the requested key type is
+     later unregistered, no keys may be added to the keyring after the key type
+     is removed.
+
+     To apply a keyring restriction the process must have Set Attribute
+     permission and the keyring must not be previously restricted.
+
 ===============
 KERNEL SERVICES
 ===============
index d2916363689c5d28deec6fea30692d18f577c0b4..0c9b93b0d1f7cd6133ea87515d3a83fb25f8258b 100644 (file)
@@ -219,7 +219,8 @@ struct key {
        /* This is set on a keyring to restrict the addition of a link to a key
         * to it.  If this structure isn't provided then it is assumed that the
         * keyring is open to any addition.  It is ignored for non-keyring
-        * keys.
+        * keys. Only set this value using keyring_restrict(), keyring_alloc(),
+        * or key_alloc().
         *
         * This is intended for use with rings of trusted keys whereby addition
         * to the keyring needs to be controlled.  KEY_ALLOC_BYPASS_RESTRICTION
@@ -328,6 +329,9 @@ extern key_ref_t keyring_search(key_ref_t keyring,
 extern int keyring_add_key(struct key *keyring,
                           struct key *key);
 
+extern int keyring_restrict(key_ref_t keyring, const char *type,
+                           const char *restriction);
+
 extern struct key *key_lookup(key_serial_t id);
 
 static inline key_serial_t key_serial(const struct key *key)
index 86eddd6241f36eb21897d65c6bb51f15bbb4ec58..ff79c44e49a31c2096291afcc4eadccd9e2de9c4 100644 (file)
@@ -60,6 +60,7 @@
 #define KEYCTL_INVALIDATE              21      /* invalidate a key */
 #define KEYCTL_GET_PERSISTENT          22      /* get a user's persistent keyring */
 #define KEYCTL_DH_COMPUTE              23      /* Compute Diffie-Hellman values */
+#define KEYCTL_RESTRICT_KEYRING                29      /* Restrict keys allowed to link to a keyring */
 
 /* keyctl structures */
 struct keyctl_dh_params {
index 36c80bf5b89c6f28a7c6010aa6b72f373bf4d1e1..bb98f2b8dd7df8951c479913cb6ab33e6cfb71f1 100644 (file)
@@ -136,6 +136,10 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
                return 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;
        }
index 24762ae9a198b07292dadd3fb02e1d9554d725de..6ce0163148973e1f78456453e985e0f858f2f319 100644 (file)
@@ -252,6 +252,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;
index 52c34532c78562643fce84832a5b536baf84988b..6ee2826a2d0654368a7b5e17e74b30ba3126bd93 100644 (file)
@@ -1582,6 +1582,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,6 +1746,11 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
                                         (char __user *) arg3, (size_t) arg4,
                                         (void __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;
        }
index 838334fec6cea457a2e24df87ae1daf62d7308c3..4d1678e4586f6ae29610bfb818c33000413947bf 100644 (file)
@@ -947,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.
  *