|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Copyright (C) 2019 Microsoft Corporation. | 
|  | * | 
|  | * Author:  Jaskaran Singh Khurana <jaskarankhurana@linux.microsoft.com> | 
|  | * | 
|  | */ | 
|  | #include <linux/device-mapper.h> | 
|  | #include <linux/verification.h> | 
|  | #include <keys/user-type.h> | 
|  | #include <linux/module.h> | 
|  | #include "dm-verity.h" | 
|  | #include "dm-verity-verify-sig.h" | 
|  |  | 
|  | #define DM_VERITY_VERIFY_ERR(s) DM_VERITY_ROOT_HASH_VERIFICATION " " s | 
|  |  | 
|  | static bool require_signatures; | 
|  | module_param(require_signatures, bool, 0444); | 
|  | MODULE_PARM_DESC(require_signatures, | 
|  | "Verify the roothash of dm-verity hash tree"); | 
|  |  | 
|  | #define DM_VERITY_IS_SIG_FORCE_ENABLED() \ | 
|  | (require_signatures != false) | 
|  |  | 
|  | bool verity_verify_is_sig_opt_arg(const char *arg_name) | 
|  | { | 
|  | return (!strcasecmp(arg_name, | 
|  | DM_VERITY_ROOT_HASH_VERIFICATION_OPT_SIG_KEY)); | 
|  | } | 
|  |  | 
|  | static int verity_verify_get_sig_from_key(const char *key_desc, | 
|  | struct dm_verity_sig_opts *sig_opts) | 
|  | { | 
|  | struct key *key; | 
|  | const struct user_key_payload *ukp; | 
|  | int ret = 0; | 
|  |  | 
|  | key = request_key(&key_type_user, | 
|  | key_desc, NULL); | 
|  | if (IS_ERR(key)) | 
|  | return PTR_ERR(key); | 
|  |  | 
|  | down_read(&key->sem); | 
|  |  | 
|  | ukp = user_key_payload_locked(key); | 
|  | if (!ukp) { | 
|  | ret = -EKEYREVOKED; | 
|  | goto end; | 
|  | } | 
|  |  | 
|  | sig_opts->sig = kmalloc(ukp->datalen, GFP_KERNEL); | 
|  | if (!sig_opts->sig) { | 
|  | ret = -ENOMEM; | 
|  | goto end; | 
|  | } | 
|  | sig_opts->sig_size = ukp->datalen; | 
|  |  | 
|  | memcpy(sig_opts->sig, ukp->data, sig_opts->sig_size); | 
|  |  | 
|  | end: | 
|  | up_read(&key->sem); | 
|  | key_put(key); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int verity_verify_sig_parse_opt_args(struct dm_arg_set *as, | 
|  | struct dm_verity *v, | 
|  | struct dm_verity_sig_opts *sig_opts, | 
|  | unsigned int *argc, | 
|  | const char *arg_name) | 
|  | { | 
|  | struct dm_target *ti = v->ti; | 
|  | int ret = 0; | 
|  | const char *sig_key = NULL; | 
|  |  | 
|  | if (!*argc) { | 
|  | ti->error = DM_VERITY_VERIFY_ERR("Signature key not specified"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | sig_key = dm_shift_arg(as); | 
|  | (*argc)--; | 
|  |  | 
|  | ret = verity_verify_get_sig_from_key(sig_key, sig_opts); | 
|  | if (ret < 0) | 
|  | ti->error = DM_VERITY_VERIFY_ERR("Invalid key specified"); | 
|  |  | 
|  | v->signature_key_desc = kstrdup(sig_key, GFP_KERNEL); | 
|  | if (!v->signature_key_desc) | 
|  | return -ENOMEM; | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * verify_verify_roothash - Verify the root hash of the verity hash device | 
|  | *			     using builtin trusted keys. | 
|  | * | 
|  | * @root_hash: For verity, the roothash/data to be verified. | 
|  | * @root_hash_len: Size of the roothash/data to be verified. | 
|  | * @sig_data: The trusted signature that verifies the roothash/data. | 
|  | * @sig_len: Size of the signature. | 
|  | * | 
|  | */ | 
|  | int verity_verify_root_hash(const void *root_hash, size_t root_hash_len, | 
|  | const void *sig_data, size_t sig_len) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | if (!root_hash || root_hash_len == 0) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (!sig_data  || sig_len == 0) { | 
|  | if (DM_VERITY_IS_SIG_FORCE_ENABLED()) | 
|  | return -ENOKEY; | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | ret = verify_pkcs7_signature(root_hash, root_hash_len, sig_data, | 
|  | sig_len, | 
|  | #ifdef CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG_SECONDARY_KEYRING | 
|  | VERIFY_USE_SECONDARY_KEYRING, | 
|  | #else | 
|  | NULL, | 
|  | #endif | 
|  | VERIFYING_UNSPECIFIED_SIGNATURE, NULL, NULL); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | void verity_verify_sig_opts_cleanup(struct dm_verity_sig_opts *sig_opts) | 
|  | { | 
|  | kfree(sig_opts->sig); | 
|  | sig_opts->sig = NULL; | 
|  | sig_opts->sig_size = 0; | 
|  | } |