]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - security/keys/key.c
KEYS: Add payload preparsing opportunity prior to key instantiate or update
[mirror_ubuntu-zesty-kernel.git] / security / keys / key.c
index 50d96d4e06f235c3e8950255c4b8bd5fd64aa7d2..1d039af99f50552c3c34b850ad22c020e04b9495 100644 (file)
@@ -412,8 +412,7 @@ EXPORT_SYMBOL(key_payload_reserve);
  * key_construction_mutex.
  */
 static int __key_instantiate_and_link(struct key *key,
-                                     const void *data,
-                                     size_t datalen,
+                                     struct key_preparsed_payload *prep,
                                      struct key *keyring,
                                      struct key *authkey,
                                      unsigned long *_prealloc)
@@ -431,7 +430,7 @@ static int __key_instantiate_and_link(struct key *key,
        /* can't instantiate twice */
        if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
                /* instantiate the key */
-               ret = key->type->instantiate(key, data, datalen);
+               ret = key->type->instantiate(key, prep);
 
                if (ret == 0) {
                        /* mark the key as being instantiated */
@@ -482,22 +481,37 @@ int key_instantiate_and_link(struct key *key,
                             struct key *keyring,
                             struct key *authkey)
 {
+       struct key_preparsed_payload prep;
        unsigned long prealloc;
        int ret;
 
+       memset(&prep, 0, sizeof(prep));
+       prep.data = data;
+       prep.datalen = datalen;
+       prep.quotalen = key->type->def_datalen;
+       if (key->type->preparse) {
+               ret = key->type->preparse(&prep);
+               if (ret < 0)
+                       goto error;
+       }
+
        if (keyring) {
                ret = __key_link_begin(keyring, key->type, key->description,
                                       &prealloc);
                if (ret < 0)
-                       return ret;
+                       goto error_free_preparse;
        }
 
-       ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey,
+       ret = __key_instantiate_and_link(key, &prep, keyring, authkey,
                                         &prealloc);
 
        if (keyring)
                __key_link_end(keyring, key->type, prealloc);
 
+error_free_preparse:
+       if (key->type->preparse)
+               key->type->free_preparse(&prep);
+error:
        return ret;
 }
 
@@ -706,7 +720,7 @@ void key_type_put(struct key_type *ktype)
  * if we get an error.
  */
 static inline key_ref_t __key_update(key_ref_t key_ref,
-                                    const void *payload, size_t plen)
+                                    struct key_preparsed_payload *prep)
 {
        struct key *key = key_ref_to_ptr(key_ref);
        int ret;
@@ -722,7 +736,7 @@ static inline key_ref_t __key_update(key_ref_t key_ref,
 
        down_write(&key->sem);
 
-       ret = key->type->update(key, payload, plen);
+       ret = key->type->update(key, prep);
        if (ret == 0)
                /* updating a negative key instantiates it */
                clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
@@ -774,6 +788,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
                               unsigned long flags)
 {
        unsigned long prealloc;
+       struct key_preparsed_payload prep;
        const struct cred *cred = current_cred();
        struct key_type *ktype;
        struct key *keyring, *key = NULL;
@@ -789,8 +804,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
        }
 
        key_ref = ERR_PTR(-EINVAL);
-       if (!ktype->match || !ktype->instantiate)
-               goto error_2;
+       if (!ktype->match || !ktype->instantiate ||
+           (!description && !ktype->preparse))
+               goto error_put_type;
 
        keyring = key_ref_to_ptr(keyring_ref);
 
@@ -798,18 +814,37 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
 
        key_ref = ERR_PTR(-ENOTDIR);
        if (keyring->type != &key_type_keyring)
-               goto error_2;
+               goto error_put_type;
+
+       memset(&prep, 0, sizeof(prep));
+       prep.data = payload;
+       prep.datalen = plen;
+       prep.quotalen = ktype->def_datalen;
+       if (ktype->preparse) {
+               ret = ktype->preparse(&prep);
+               if (ret < 0) {
+                       key_ref = ERR_PTR(ret);
+                       goto error_put_type;
+               }
+               if (!description)
+                       description = prep.description;
+               key_ref = ERR_PTR(-EINVAL);
+               if (!description)
+                       goto error_free_prep;
+       }
 
        ret = __key_link_begin(keyring, ktype, description, &prealloc);
-       if (ret < 0)
-               goto error_2;
+       if (ret < 0) {
+               key_ref = ERR_PTR(ret);
+               goto error_free_prep;
+       }
 
        /* if we're going to allocate a new key, we're going to have
         * to modify the keyring */
        ret = key_permission(keyring_ref, KEY_WRITE);
        if (ret < 0) {
                key_ref = ERR_PTR(ret);
-               goto error_3;
+               goto error_link_end;
        }
 
        /* if it's possible to update this type of key, search for an existing
@@ -840,25 +875,27 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
                        perm, flags);
        if (IS_ERR(key)) {
                key_ref = ERR_CAST(key);
-               goto error_3;
+               goto error_link_end;
        }
 
        /* instantiate it and link it into the target keyring */
-       ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL,
-                                        &prealloc);
+       ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &prealloc);
        if (ret < 0) {
                key_put(key);
                key_ref = ERR_PTR(ret);
-               goto error_3;
+               goto error_link_end;
        }
 
        key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
 
- error_3:
+error_link_end:
        __key_link_end(keyring, ktype, prealloc);
- error_2:
+error_free_prep:
+       if (ktype->preparse)
+               ktype->free_preparse(&prep);
+error_put_type:
        key_type_put(ktype);
- error:
+error:
        return key_ref;
 
  found_matching_key:
@@ -866,10 +903,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
         * - we can drop the locks first as we have the key pinned
         */
        __key_link_end(keyring, ktype, prealloc);
-       key_type_put(ktype);
 
-       key_ref = __key_update(key_ref, payload, plen);
-       goto error;
+       key_ref = __key_update(key_ref, &prep);
+       goto error_free_prep;
 }
 EXPORT_SYMBOL(key_create_or_update);
 
@@ -888,6 +924,7 @@ EXPORT_SYMBOL(key_create_or_update);
  */
 int key_update(key_ref_t key_ref, const void *payload, size_t plen)
 {
+       struct key_preparsed_payload prep;
        struct key *key = key_ref_to_ptr(key_ref);
        int ret;
 
@@ -900,18 +937,31 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)
 
        /* attempt to update it if supported */
        ret = -EOPNOTSUPP;
-       if (key->type->update) {
-               down_write(&key->sem);
-
-               ret = key->type->update(key, payload, plen);
-               if (ret == 0)
-                       /* updating a negative key instantiates it */
-                       clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
+       if (!key->type->update)
+               goto error;
 
-               up_write(&key->sem);
+       memset(&prep, 0, sizeof(prep));
+       prep.data = payload;
+       prep.datalen = plen;
+       prep.quotalen = key->type->def_datalen;
+       if (key->type->preparse) {
+               ret = key->type->preparse(&prep);
+               if (ret < 0)
+                       goto error;
        }
 
- error:
+       down_write(&key->sem);
+
+       ret = key->type->update(key, &prep);
+       if (ret == 0)
+               /* updating a negative key instantiates it */
+               clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
+
+       up_write(&key->sem);
+
+       if (key->type->preparse)
+               key->type->free_preparse(&prep);
+error:
        return ret;
 }
 EXPORT_SYMBOL(key_update);