123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /* Asymmetric public-key cryptography key type
- *
- * See Documentation/crypto/asymmetric-keys.txt
- *
- * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- */
- #ifndef __UBOOT__
- #include <log.h>
- #include <dm/devres.h>
- #include <keys/asymmetric-subtype.h>
- #include <keys/asymmetric-parser.h>
- #endif
- #include <crypto/public_key.h>
- #ifdef __UBOOT__
- #include <linux/bug.h>
- #include <linux/compat.h>
- #include <linux/ctype.h>
- #include <linux/err.h>
- #include <linux/string.h>
- #else
- #include <linux/seq_file.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/ctype.h>
- #endif
- #ifdef __UBOOT__
- #include <keys/asymmetric-type.h>
- #else
- #include <keys/system_keyring.h>
- #include <keys/user-type.h>
- #include "asymmetric_keys.h"
- #endif
- MODULE_LICENSE("GPL");
- #ifndef __UBOOT__
- const char *const key_being_used_for[NR__KEY_BEING_USED_FOR] = {
- [VERIFYING_MODULE_SIGNATURE] = "mod sig",
- [VERIFYING_FIRMWARE_SIGNATURE] = "firmware sig",
- [VERIFYING_KEXEC_PE_SIGNATURE] = "kexec PE sig",
- [VERIFYING_KEY_SIGNATURE] = "key sig",
- [VERIFYING_KEY_SELF_SIGNATURE] = "key self sig",
- [VERIFYING_UNSPECIFIED_SIGNATURE] = "unspec sig",
- };
- EXPORT_SYMBOL_GPL(key_being_used_for);
- static LIST_HEAD(asymmetric_key_parsers);
- static DECLARE_RWSEM(asymmetric_key_parsers_sem);
- /**
- * find_asymmetric_key - Find a key by ID.
- * @keyring: The keys to search.
- * @id_0: The first ID to look for or NULL.
- * @id_1: The second ID to look for or NULL.
- * @partial: Use partial match if true, exact if false.
- *
- * Find a key in the given keyring by identifier. The preferred identifier is
- * the id_0 and the fallback identifier is the id_1. If both are given, the
- * lookup is by the former, but the latter must also match.
- */
- struct key *find_asymmetric_key(struct key *keyring,
- const struct asymmetric_key_id *id_0,
- const struct asymmetric_key_id *id_1,
- bool partial)
- {
- struct key *key;
- key_ref_t ref;
- const char *lookup;
- char *req, *p;
- int len;
- BUG_ON(!id_0 && !id_1);
- if (id_0) {
- lookup = id_0->data;
- len = id_0->len;
- } else {
- lookup = id_1->data;
- len = id_1->len;
- }
- /* Construct an identifier "id:<keyid>". */
- p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL);
- if (!req)
- return ERR_PTR(-ENOMEM);
- if (partial) {
- *p++ = 'i';
- *p++ = 'd';
- } else {
- *p++ = 'e';
- *p++ = 'x';
- }
- *p++ = ':';
- p = bin2hex(p, lookup, len);
- *p = 0;
- pr_debug("Look up: \"%s\"\n", req);
- ref = keyring_search(make_key_ref(keyring, 1),
- &key_type_asymmetric, req, true);
- if (IS_ERR(ref))
- pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref));
- kfree(req);
- if (IS_ERR(ref)) {
- switch (PTR_ERR(ref)) {
- /* Hide some search errors */
- case -EACCES:
- case -ENOTDIR:
- case -EAGAIN:
- return ERR_PTR(-ENOKEY);
- default:
- return ERR_CAST(ref);
- }
- }
- key = key_ref_to_ptr(ref);
- if (id_0 && id_1) {
- const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
- if (!kids->id[1]) {
- pr_debug("First ID matches, but second is missing\n");
- goto reject;
- }
- if (!asymmetric_key_id_same(id_1, kids->id[1])) {
- pr_debug("First ID matches, but second does not\n");
- goto reject;
- }
- }
- pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key));
- return key;
- reject:
- key_put(key);
- return ERR_PTR(-EKEYREJECTED);
- }
- EXPORT_SYMBOL_GPL(find_asymmetric_key);
- #endif /* !__UBOOT__ */
- /**
- * 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
- */
- 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);
- /**
- * 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);
- #ifndef __UBOOT__
- /**
- * 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;
- }
- /* helper function can be called directly with pre-allocated memory */
- inline int __asymmetric_key_hex_to_key_id(const char *id,
- struct asymmetric_key_id *match_id,
- size_t hexlen)
- {
- match_id->len = hexlen;
- return hex2bin(match_id->data, id, hexlen);
- }
- /**
- * 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 asciihexlen;
- int ret;
- if (!*id)
- return ERR_PTR(-EINVAL);
- asciihexlen = strlen(id);
- if (asciihexlen & 1)
- return ERR_PTR(-EINVAL);
- match_id = kmalloc(sizeof(struct asymmetric_key_id) + asciihexlen / 2,
- GFP_KERNEL);
- if (!match_id)
- return ERR_PTR(-ENOMEM);
- ret = __asymmetric_key_hex_to_key_id(id, match_id, asciihexlen / 2);
- if (ret < 0) {
- kfree(match_id);
- return ERR_PTR(-EINVAL);
- }
- return match_id;
- }
- /*
- * Match asymmetric keys by an exact match on an 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 asymmetric_match_key_ids(kids, match_id,
- asymmetric_key_id_same);
- }
- /*
- * 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;
- 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;
- 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;
- }
- match_id = asymmetric_key_hex_to_key_id(id);
- if (IS_ERR(match_id))
- return PTR_ERR(match_id);
- 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 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);
- if (subtype) {
- seq_puts(m, ": ");
- subtype->describe(key, m);
- if (kids && kids->id[1]) {
- kid = kids->id[1];
- seq_putc(m, ' ');
- n = kid->len;
- p = kid->data;
- if (n > 4) {
- p += n - 4;
- n = 4;
- }
- seq_printf(m, "%*phN", n, p);
- }
- seq_puts(m, " [");
- /* put something here to indicate the key's capabilities */
- seq_putc(m, ']');
- }
- }
- /*
- * Preparse a asymmetric payload to get format the contents appropriately for the
- * internal payload to cut down on the number of scans of the data performed.
- *
- * We also generate a proposed description from the contents of the key that
- * can be used to name the key if the user doesn't want to provide one.
- */
- static int asymmetric_key_preparse(struct key_preparsed_payload *prep)
- {
- struct asymmetric_key_parser *parser;
- int ret;
- pr_devel("==>%s()\n", __func__);
- if (prep->datalen == 0)
- return -EINVAL;
- down_read(&asymmetric_key_parsers_sem);
- ret = -EBADMSG;
- list_for_each_entry(parser, &asymmetric_key_parsers, link) {
- pr_debug("Trying parser '%s'\n", parser->name);
- ret = parser->parse(prep);
- if (ret != -EBADMSG) {
- pr_debug("Parser recognised the format (ret %d)\n",
- ret);
- break;
- }
- }
- up_read(&asymmetric_key_parsers_sem);
- pr_devel("<==%s() = %d\n", __func__, ret);
- return ret;
- }
- /*
- * Clean up the key ID list
- */
- static void asymmetric_key_free_kids(struct asymmetric_key_ids *kids)
- {
- int i;
- if (kids) {
- for (i = 0; i < ARRAY_SIZE(kids->id); i++)
- kfree(kids->id[i]);
- kfree(kids);
- }
- }
- /*
- * Clean up the preparse data
- */
- static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
- {
- struct asymmetric_key_subtype *subtype = prep->payload.data[asym_subtype];
- struct asymmetric_key_ids *kids = prep->payload.data[asym_key_ids];
- pr_devel("==>%s()\n", __func__);
- if (subtype) {
- subtype->destroy(prep->payload.data[asym_crypto],
- prep->payload.data[asym_auth]);
- module_put(subtype->owner);
- }
- asymmetric_key_free_kids(kids);
- kfree(prep->description);
- }
- /*
- * dispose of the data dangling from the corpse of a asymmetric key
- */
- static void asymmetric_key_destroy(struct key *key)
- {
- struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
- struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids];
- void *data = key->payload.data[asym_crypto];
- void *auth = key->payload.data[asym_auth];
- key->payload.data[asym_crypto] = NULL;
- key->payload.data[asym_subtype] = NULL;
- key->payload.data[asym_key_ids] = NULL;
- key->payload.data[asym_auth] = NULL;
- if (subtype) {
- subtype->destroy(data, auth);
- module_put(subtype->owner);
- }
- asymmetric_key_free_kids(kids);
- }
- static struct key_restriction *asymmetric_restriction_alloc(
- key_restrict_link_func_t check,
- struct key *key)
- {
- struct key_restriction *keyres =
- kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
- if (!keyres)
- return ERR_PTR(-ENOMEM);
- keyres->check = check;
- keyres->key = key;
- keyres->keytype = &key_type_asymmetric;
- return keyres;
- }
- /*
- * look up keyring restrict functions for asymmetric keys
- */
- static struct key_restriction *asymmetric_lookup_restriction(
- const char *restriction)
- {
- char *restrict_method;
- char *parse_buf;
- char *next;
- struct key_restriction *ret = ERR_PTR(-EINVAL);
- if (strcmp("builtin_trusted", restriction) == 0)
- return asymmetric_restriction_alloc(
- restrict_link_by_builtin_trusted, NULL);
- if (strcmp("builtin_and_secondary_trusted", restriction) == 0)
- return asymmetric_restriction_alloc(
- restrict_link_by_builtin_and_secondary_trusted, NULL);
- parse_buf = kstrndup(restriction, PAGE_SIZE, GFP_KERNEL);
- if (!parse_buf)
- return ERR_PTR(-ENOMEM);
- next = parse_buf;
- restrict_method = strsep(&next, ":");
- if ((strcmp(restrict_method, "key_or_keyring") == 0) && next) {
- char *key_text;
- key_serial_t serial;
- struct key *key;
- key_restrict_link_func_t link_fn =
- restrict_link_by_key_or_keyring;
- bool allow_null_key = false;
- key_text = strsep(&next, ":");
- if (next) {
- if (strcmp(next, "chain") != 0)
- goto out;
- link_fn = restrict_link_by_key_or_keyring_chain;
- allow_null_key = true;
- }
- if (kstrtos32(key_text, 0, &serial) < 0)
- goto out;
- if ((serial == 0) && allow_null_key) {
- key = NULL;
- } else {
- key = key_lookup(serial);
- if (IS_ERR(key)) {
- ret = ERR_CAST(key);
- goto out;
- }
- }
- ret = asymmetric_restriction_alloc(link_fn, key);
- if (IS_ERR(ret))
- key_put(key);
- }
- out:
- kfree(parse_buf);
- return ret;
- }
- int asymmetric_key_eds_op(struct kernel_pkey_params *params,
- const void *in, void *out)
- {
- const struct asymmetric_key_subtype *subtype;
- struct key *key = params->key;
- int ret;
- pr_devel("==>%s()\n", __func__);
- if (key->type != &key_type_asymmetric)
- return -EINVAL;
- subtype = asymmetric_key_subtype(key);
- if (!subtype ||
- !key->payload.data[0])
- return -EINVAL;
- if (!subtype->eds_op)
- return -ENOTSUPP;
- ret = subtype->eds_op(params, in, out);
- pr_devel("<==%s() = %d\n", __func__, ret);
- return ret;
- }
- static int asymmetric_key_verify_signature(struct kernel_pkey_params *params,
- const void *in, const void *in2)
- {
- struct public_key_signature sig = {
- .s_size = params->in2_len,
- .digest_size = params->in_len,
- .encoding = params->encoding,
- .hash_algo = params->hash_algo,
- .digest = (void *)in,
- .s = (void *)in2,
- };
- return verify_signature(params->key, &sig);
- }
- struct key_type key_type_asymmetric = {
- .name = "asymmetric",
- .preparse = asymmetric_key_preparse,
- .free_preparse = asymmetric_key_free_preparse,
- .instantiate = generic_key_instantiate,
- .match_preparse = asymmetric_key_match_preparse,
- .match_free = asymmetric_key_match_free,
- .destroy = asymmetric_key_destroy,
- .describe = asymmetric_key_describe,
- .lookup_restriction = asymmetric_lookup_restriction,
- .asym_query = query_asymmetric_key,
- .asym_eds_op = asymmetric_key_eds_op,
- .asym_verify_signature = asymmetric_key_verify_signature,
- };
- EXPORT_SYMBOL_GPL(key_type_asymmetric);
- /**
- * register_asymmetric_key_parser - Register a asymmetric key blob parser
- * @parser: The parser to register
- */
- int register_asymmetric_key_parser(struct asymmetric_key_parser *parser)
- {
- struct asymmetric_key_parser *cursor;
- int ret;
- down_write(&asymmetric_key_parsers_sem);
- list_for_each_entry(cursor, &asymmetric_key_parsers, link) {
- if (strcmp(cursor->name, parser->name) == 0) {
- pr_err("Asymmetric key parser '%s' already registered\n",
- parser->name);
- ret = -EEXIST;
- goto out;
- }
- }
- list_add_tail(&parser->link, &asymmetric_key_parsers);
- pr_notice("Asymmetric key parser '%s' registered\n", parser->name);
- ret = 0;
- out:
- up_write(&asymmetric_key_parsers_sem);
- return ret;
- }
- EXPORT_SYMBOL_GPL(register_asymmetric_key_parser);
- /**
- * unregister_asymmetric_key_parser - Unregister a asymmetric key blob parser
- * @parser: The parser to unregister
- */
- void unregister_asymmetric_key_parser(struct asymmetric_key_parser *parser)
- {
- down_write(&asymmetric_key_parsers_sem);
- list_del(&parser->link);
- up_write(&asymmetric_key_parsers_sem);
- pr_notice("Asymmetric key parser '%s' unregistered\n", parser->name);
- }
- EXPORT_SYMBOL_GPL(unregister_asymmetric_key_parser);
- /*
- * Module stuff
- */
- static int __init asymmetric_key_init(void)
- {
- return register_key_type(&key_type_asymmetric);
- }
- static void __exit asymmetric_key_cleanup(void)
- {
- unregister_key_type(&key_type_asymmetric);
- }
- module_init(asymmetric_key_init);
- module_exit(asymmetric_key_cleanup);
- #endif /* !__UBOOT__ */
|