| /* Asymmetric public-key cryptography key type |
| * |
| * See Documentation/security/asymmetric-keys.txt |
| * |
| * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. |
| * Written by David Howells (dhowells@redhat.com) |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public Licence |
| * as published by the Free Software Foundation; either version |
| * 2 of the Licence, or (at your option) any later version. |
| */ |
| #include <keys/asymmetric-subtype.h> |
| #include <keys/asymmetric-parser.h> |
| #include <crypto/public_key.h> |
| #include <linux/seq_file.h> |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| #include <linux/ctype.h> |
| #include <keys/system_keyring.h> |
| #include "asymmetric_keys.h" |
| |
| MODULE_LICENSE("GPL"); |
| |
| 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); |
| 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); |
| |
| /** |
| * 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); |
| |
| /** |
| * 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; |
| } |
| |
| 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, |
| }; |
| 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); |