KEYS: Add a facility to restrict new links into a keyring
[cascardo/linux.git] / security / keys / key.c
index b287551..deb8817 100644 (file)
@@ -201,6 +201,7 @@ 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.
  *
  * 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
@@ -223,7 +224,11 @@ 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_perm_t perm, unsigned long flags,
+                     int (*restrict_link)(struct key *,
+                                          const struct key_type *,
+                                          unsigned long,
+                                          const union key_payload *))
 {
        struct key_user *user = NULL;
        struct key *key;
@@ -291,6 +296,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
        key->uid = uid;
        key->gid = gid;
        key->perm = perm;
+       key->restrict_link = restrict_link;
 
        if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
                key->flags |= 1 << KEY_FLAG_IN_QUOTA;
@@ -496,6 +502,12 @@ int key_instantiate_and_link(struct key *key,
        }
 
        if (keyring) {
+               if (keyring->restrict_link) {
+                       ret = keyring->restrict_link(keyring, key->type,
+                                                    key->flags, &prep.payload);
+                       if (ret < 0)
+                               goto error;
+               }
                ret = __key_link_begin(keyring, &key->index_key, &edit);
                if (ret < 0)
                        goto error;
@@ -551,8 +563,12 @@ int key_reject_and_link(struct key *key,
        awaken = 0;
        ret = -EBUSY;
 
-       if (keyring)
+       if (keyring) {
+               if (keyring->restrict_link)
+                       return -EPERM;
+
                link_ret = __key_link_begin(keyring, &key->index_key, &edit);
+       }
 
        mutex_lock(&key_construction_mutex);
 
@@ -793,6 +809,10 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
        struct key *keyring, *key = NULL;
        key_ref_t key_ref;
        int ret;
+       int (*restrict_link)(struct key *,
+                            const struct key_type *,
+                            unsigned long,
+                            const union key_payload *) = NULL;
 
        /* look up the key type to see if it's one of the registered kernel
         * types */
@@ -811,6 +831,10 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
 
        key_check(keyring);
 
+       key_ref = ERR_PTR(-EPERM);
+       if (!(flags & KEY_ALLOC_BYPASS_RESTRICTION))
+               restrict_link = keyring->restrict_link;
+
        key_ref = ERR_PTR(-ENOTDIR);
        if (keyring->type != &key_type_keyring)
                goto error_put_type;
@@ -835,10 +859,15 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
        }
        index_key.desc_len = strlen(index_key.description);
 
-       key_ref = ERR_PTR(-EPERM);
-       if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags))
-               goto error_free_prep;
-       flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0;
+       if (restrict_link) {
+               unsigned long kflags = prep.trusted ? KEY_FLAG_TRUSTED : 0;
+               ret = restrict_link(keyring,
+                                   index_key.type, kflags, &prep.payload);
+               if (ret < 0) {
+                       key_ref = ERR_PTR(ret);
+                       goto error_free_prep;
+               }
+       }
 
        ret = __key_link_begin(keyring, &index_key, &edit);
        if (ret < 0) {
@@ -879,7 +908,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
 
        /* allocate a new key */
        key = key_alloc(index_key.type, index_key.description,
-                       cred->fsuid, cred->fsgid, cred, perm, flags);
+                       cred->fsuid, cred->fsgid, cred, perm, flags, NULL);
        if (IS_ERR(key)) {
                key_ref = ERR_CAST(key);
                goto error_link_end;