|
@@ -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,166 @@ 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.
|
|
|
+ */
|
|
|
+struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1,
|
|
|
+ size_t len_1,
|
|
|
+ const void *val_2,
|
|
|
+ size_t len_2)
|
|
|
+{
|
|
|
+ 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);
|
|
|
+
|
|
|
+/**
|
|
|
+ * asymmetric_key_id_same - Return true if two asymmetric keys IDs are the same.
|
|
|
+ * @kid_1, @kid_2: The key IDs to compare
|
|
|
*/
|
|
|
-int asymmetric_keyid_match(const char *kid, const char *id)
|
|
|
+bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1,
|
|
|
+ const struct asymmetric_key_id *kid2)
|
|
|
{
|
|
|
- size_t idlen, kidlen;
|
|
|
+ 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);
|
|
|
|
|
|
- if (!kid || !id)
|
|
|
- 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
|
|
|
+ */
|
|
|
+bool asymmetric_match_key_ids(const struct asymmetric_key_ids *kids,
|
|
|
+ const struct asymmetric_key_id *match_id)
|
|
|
+{
|
|
|
+ if (!kids || !match_id)
|
|
|
+ return false;
|
|
|
+ if (asymmetric_key_id_same(kids->id[0], match_id))
|
|
|
+ return true;
|
|
|
+ if (asymmetric_key_id_same(kids->id[1], match_id))
|
|
|
+ return true;
|
|
|
+ return false;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(asymmetric_match_key_ids);
|
|
|
|
|
|
- /* make it possible to use id as in the request: "id:<id>" */
|
|
|
- if (strncmp(id, "id:", 3) == 0)
|
|
|
- id += 3;
|
|
|
+/**
|
|
|
+ * 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;
|
|
|
|
|
|
- /* Anything after here requires a partial match on the ID string */
|
|
|
- idlen = strlen(id);
|
|
|
- kidlen = strlen(kid);
|
|
|
- if (idlen > kidlen)
|
|
|
- return 0;
|
|
|
+ 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;
|
|
|
+}
|
|
|
|
|
|
- kid += kidlen - idlen;
|
|
|
- if (strcasecmp(id, kid) != 0)
|
|
|
- return 0;
|
|
|
+/*
|
|
|
+ * Match asymmetric keys by ID.
|
|
|
+ */
|
|
|
+static bool asymmetric_key_cmp(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;
|
|
|
|
|
|
- return 1;
|
|
|
+ return asymmetric_match_key_ids(kids, 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:
|
|
|
+ * 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:
|
|
|
*
|
|
|
- * "<desc>" - request a key by description
|
|
|
- * "id:<id>" - request a key matching the ID
|
|
|
- * "<subtype>:<id>" - request a key of a subtype
|
|
|
+ * "id:<id>" - request a key by 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(const struct key *key, const void *description)
|
|
|
+static int asymmetric_key_match_preparse(struct key_match_data *match_data)
|
|
|
{
|
|
|
- const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
|
|
|
- const char *spec = description;
|
|
|
+ struct asymmetric_key_id *match_id;
|
|
|
+ const char *spec = match_data->raw_data;
|
|
|
const char *id;
|
|
|
- ptrdiff_t speclen;
|
|
|
|
|
|
- if (!subtype || !spec || !*spec)
|
|
|
- return 0;
|
|
|
-
|
|
|
- /* See if the full key description matches as is */
|
|
|
- if (key->description && strcmp(key->description, description) == 0)
|
|
|
- return 1;
|
|
|
-
|
|
|
- /* 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;
|
|
|
-
|
|
|
- speclen = id - spec;
|
|
|
- id++;
|
|
|
+ if (!spec || !*spec)
|
|
|
+ return -EINVAL;
|
|
|
+ if (spec[0] == 'i' &&
|
|
|
+ spec[1] == 'd' &&
|
|
|
+ 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 (!match_id)
|
|
|
+ return -ENOMEM;
|
|
|
|
|
|
- if (speclen == subtype->name_len &&
|
|
|
- memcmp(spec, subtype->name, speclen) == 0)
|
|
|
- return 1;
|
|
|
+ match_data->preparsed = match_id;
|
|
|
+ match_data->cmp = asymmetric_key_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 +190,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[0]) {
|
|
|
+ kid = kids->id[0];
|
|
|
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 > 8) {
|
|
|
+ p += n - 8;
|
|
|
+ n = 8;
|
|
|
+ }
|
|
|
+ seq_printf(m, "%*phN", n, p);
|
|
|
}
|
|
|
|
|
|
seq_puts(m, " [");
|
|
@@ -165,6 +250,7 @@ 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];
|
|
|
|
|
|
pr_devel("==>%s()\n", __func__);
|
|
|
|
|
@@ -172,7 +258,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) {
|
|
|
+ kfree(kids->id[0]);
|
|
|
+ kfree(kids->id[1]);
|
|
|
+ kfree(kids);
|
|
|
+ }
|
|
|
kfree(prep->description);
|
|
|
}
|
|
|
|
|
@@ -182,13 +272,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 +293,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);
|
|
|
|