Merge branch 'core-rcu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[platform/kernel/linux-rpi.git] / crypto / asymmetric_keys / asymmetric_type.c
index eb8cd46..bcbbbd7 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/seq_file.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/ctype.h>
 #include "asymmetric_keys.h"
 
 MODULE_LICENSE("GPL");
@@ -22,85 +23,209 @@ MODULE_LICENSE("GPL");
 static LIST_HEAD(asymmetric_key_parsers);
 static DECLARE_RWSEM(asymmetric_key_parsers_sem);
 
-/*
- * Match asymmetric key id with partial match
- * @id:                key id to match in a form "id:<id>"
+/**
+ * asymmetric_key_generate_id: Construct an asymmetric key ID
+ * @val_1: First binary blob
+ * @len_1: Length of first binary blob
+ * @val_2: Second binary blob
+ * @len_2: Length of second binary blob
+ *
+ * Construct an asymmetric key ID from a pair of binary blobs.
  */
-int asymmetric_keyid_match(const char *kid, const char *id)
+struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1,
+                                                    size_t len_1,
+                                                    const void *val_2,
+                                                    size_t len_2)
 {
-       size_t idlen, kidlen;
+       struct asymmetric_key_id *kid;
+
+       kid = kmalloc(sizeof(struct asymmetric_key_id) + len_1 + len_2,
+                     GFP_KERNEL);
+       if (!kid)
+               return ERR_PTR(-ENOMEM);
+       kid->len = len_1 + len_2;
+       memcpy(kid->data, val_1, len_1);
+       memcpy(kid->data + len_1, val_2, len_2);
+       return kid;
+}
+EXPORT_SYMBOL_GPL(asymmetric_key_generate_id);
 
-       if (!kid || !id)
-               return 0;
+/**
+ * asymmetric_key_id_same - Return true if two asymmetric keys IDs are the same.
+ * @kid_1, @kid_2: The key IDs to compare
+ */
+bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1,
+                           const struct asymmetric_key_id *kid2)
+{
+       if (!kid1 || !kid2)
+               return false;
+       if (kid1->len != kid2->len)
+               return false;
+       return memcmp(kid1->data, kid2->data, kid1->len) == 0;
+}
+EXPORT_SYMBOL_GPL(asymmetric_key_id_same);
 
-       /* make it possible to use id as in the request: "id:<id>" */
-       if (strncmp(id, "id:", 3) == 0)
-               id += 3;
+/**
+ * asymmetric_key_id_partial - Return true if two asymmetric keys IDs
+ * partially match
+ * @kid_1, @kid_2: The key IDs to compare
+ */
+bool asymmetric_key_id_partial(const struct asymmetric_key_id *kid1,
+                              const struct asymmetric_key_id *kid2)
+{
+       if (!kid1 || !kid2)
+               return false;
+       if (kid1->len < kid2->len)
+               return false;
+       return memcmp(kid1->data + (kid1->len - kid2->len),
+                     kid2->data, kid2->len) == 0;
+}
+EXPORT_SYMBOL_GPL(asymmetric_key_id_partial);
 
-       /* Anything after here requires a partial match on the ID string */
-       idlen = strlen(id);
-       kidlen = strlen(kid);
-       if (idlen > kidlen)
-               return 0;
+/**
+ * asymmetric_match_key_ids - Search asymmetric key IDs
+ * @kids: The list of key IDs to check
+ * @match_id: The key ID we're looking for
+ * @match: The match function to use
+ */
+static bool asymmetric_match_key_ids(
+       const struct asymmetric_key_ids *kids,
+       const struct asymmetric_key_id *match_id,
+       bool (*match)(const struct asymmetric_key_id *kid1,
+                     const struct asymmetric_key_id *kid2))
+{
+       int i;
+
+       if (!kids || !match_id)
+               return false;
+       for (i = 0; i < ARRAY_SIZE(kids->id); i++)
+               if (match(kids->id[i], match_id))
+                       return true;
+       return false;
+}
 
-       kid += kidlen - idlen;
-       if (strcasecmp(id, kid) != 0)
-               return 0;
+/**
+ * asymmetric_key_hex_to_key_id - Convert a hex string into a key ID.
+ * @id: The ID as a hex string.
+ */
+struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id)
+{
+       struct asymmetric_key_id *match_id;
+       size_t hexlen;
+       int ret;
 
-       return 1;
+       if (!*id)
+               return ERR_PTR(-EINVAL);
+       hexlen = strlen(id);
+       if (hexlen & 1)
+               return ERR_PTR(-EINVAL);
+
+       match_id = kmalloc(sizeof(struct asymmetric_key_id) + hexlen / 2,
+                          GFP_KERNEL);
+       if (!match_id)
+               return ERR_PTR(-ENOMEM);
+       match_id->len = hexlen / 2;
+       ret = hex2bin(match_id->data, id, hexlen / 2);
+       if (ret < 0) {
+               kfree(match_id);
+               return ERR_PTR(-EINVAL);
+       }
+       return match_id;
 }
-EXPORT_SYMBOL_GPL(asymmetric_keyid_match);
 
 /*
- * Match asymmetric keys on (part of) their name
- * We have some shorthand methods for matching keys.  We allow:
- *
- *     "<desc>"        - request a key by description
- *     "id:<id>"       - request a key matching the ID
- *     "<subtype>:<id>" - request a key of a subtype
+ * Match asymmetric keys by an exact match on an ID.
  */
-static int asymmetric_key_match(const struct key *key, const void *description)
+static bool asymmetric_key_cmp(const struct key *key,
+                              const struct key_match_data *match_data)
 {
-       const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
-       const char *spec = description;
-       const char *id;
-       ptrdiff_t speclen;
+       const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
+       const struct asymmetric_key_id *match_id = match_data->preparsed;
 
-       if (!subtype || !spec || !*spec)
-               return 0;
+       return asymmetric_match_key_ids(kids, match_id,
+                                       asymmetric_key_id_same);
+}
 
-       /* See if the full key description matches as is */
-       if (key->description && strcmp(key->description, description) == 0)
-               return 1;
+/*
+ * Match asymmetric keys by a partial match on an IDs.
+ */
+static bool asymmetric_key_cmp_partial(const struct key *key,
+                                      const struct key_match_data *match_data)
+{
+       const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
+       const struct asymmetric_key_id *match_id = match_data->preparsed;
 
-       /* All tests from here on break the criterion description into a
-        * specifier, a colon and then an identifier.
-        */
-       id = strchr(spec, ':');
-       if (!id)
-               return 0;
+       return asymmetric_match_key_ids(kids, match_id,
+                                       asymmetric_key_id_partial);
+}
+
+/*
+ * Preparse the match criterion.  If we don't set lookup_type and cmp,
+ * the default will be an exact match on the key description.
+ *
+ * There are some specifiers for matching key IDs rather than by the key
+ * description:
+ *
+ *     "id:<id>" - find a key by partial match on any available ID
+ *     "ex:<id>" - find a key by exact match on any available ID
+ *
+ * These have to be searched by iteration rather than by direct lookup because
+ * the key is hashed according to its description.
+ */
+static int asymmetric_key_match_preparse(struct key_match_data *match_data)
+{
+       struct asymmetric_key_id *match_id;
+       const char *spec = match_data->raw_data;
+       const char *id;
+       bool (*cmp)(const struct key *, const struct key_match_data *) =
+               asymmetric_key_cmp;
 
-       speclen = id - spec;
-       id++;
+       if (!spec || !*spec)
+               return -EINVAL;
+       if (spec[0] == 'i' &&
+           spec[1] == 'd' &&
+           spec[2] == ':') {
+               id = spec + 3;
+               cmp = asymmetric_key_cmp_partial;
+       } else if (spec[0] == 'e' &&
+                  spec[1] == 'x' &&
+                  spec[2] == ':') {
+               id = spec + 3;
+       } else {
+               goto default_match;
+       }
 
-       if (speclen == 2 && memcmp(spec, "id", 2) == 0)
-               return asymmetric_keyid_match(asymmetric_key_id(key), id);
+       match_id = asymmetric_key_hex_to_key_id(id);
+       if (IS_ERR(match_id))
+               return PTR_ERR(match_id);
 
-       if (speclen == subtype->name_len &&
-           memcmp(spec, subtype->name, speclen) == 0)
-               return 1;
+       match_data->preparsed = match_id;
+       match_data->cmp = cmp;
+       match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
+       return 0;
 
+default_match:
        return 0;
 }
 
 /*
+ * Free the preparsed the match criterion.
+ */
+static void asymmetric_key_match_free(struct key_match_data *match_data)
+{
+       kfree(match_data->preparsed);
+}
+
+/*
  * Describe the asymmetric key
  */
 static void asymmetric_key_describe(const struct key *key, struct seq_file *m)
 {
        const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
-       const char *kid = asymmetric_key_id(key);
-       size_t n;
+       const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
+       const struct asymmetric_key_id *kid;
+       const unsigned char *p;
+       int n;
 
        seq_puts(m, key->description);
 
@@ -108,13 +233,16 @@ static void asymmetric_key_describe(const struct key *key, struct seq_file *m)
                seq_puts(m, ": ");
                subtype->describe(key, m);
 
-               if (kid) {
+               if (kids && kids->id[1]) {
+                       kid = kids->id[1];
                        seq_putc(m, ' ');
-                       n = strlen(kid);
-                       if (n <= 8)
-                               seq_puts(m, kid);
-                       else
-                               seq_puts(m, kid + n - 8);
+                       n = kid->len;
+                       p = kid->data;
+                       if (n > 4) {
+                               p += n - 4;
+                               n = 4;
+                       }
+                       seq_printf(m, "%*phN", n, p);
                }
 
                seq_puts(m, " [");
@@ -165,6 +293,8 @@ static int asymmetric_key_preparse(struct key_preparsed_payload *prep)
 static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
 {
        struct asymmetric_key_subtype *subtype = prep->type_data[0];
+       struct asymmetric_key_ids *kids = prep->type_data[1];
+       int i;
 
        pr_devel("==>%s()\n", __func__);
 
@@ -172,7 +302,11 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
                subtype->destroy(prep->payload[0]);
                module_put(subtype->owner);
        }
-       kfree(prep->type_data[1]);
+       if (kids) {
+               for (i = 0; i < ARRAY_SIZE(kids->id); i++)
+                       kfree(kids->id[i]);
+               kfree(kids);
+       }
        kfree(prep->description);
 }
 
@@ -182,13 +316,20 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
 static void asymmetric_key_destroy(struct key *key)
 {
        struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
+       struct asymmetric_key_ids *kids = key->type_data.p[1];
+
        if (subtype) {
                subtype->destroy(key->payload.data);
                module_put(subtype->owner);
                key->type_data.p[0] = NULL;
        }
-       kfree(key->type_data.p[1]);
-       key->type_data.p[1] = NULL;
+
+       if (kids) {
+               kfree(kids->id[0]);
+               kfree(kids->id[1]);
+               kfree(kids);
+               key->type_data.p[1] = NULL;
+       }
 }
 
 struct key_type key_type_asymmetric = {
@@ -196,10 +337,10 @@ struct key_type key_type_asymmetric = {
        .preparse       = asymmetric_key_preparse,
        .free_preparse  = asymmetric_key_free_preparse,
        .instantiate    = generic_key_instantiate,
-       .match          = asymmetric_key_match,
+       .match_preparse = asymmetric_key_match_preparse,
+       .match_free     = asymmetric_key_match_free,
        .destroy        = asymmetric_key_destroy,
        .describe       = asymmetric_key_describe,
-       .def_lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE,
 };
 EXPORT_SYMBOL_GPL(key_type_asymmetric);