162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * IMA support for appraising module-style appended signatures. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2019 IBM Corporation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: 862306a36Sopenharmony_ci * Thiago Jung Bauermann <bauerman@linux.ibm.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/module_signature.h> 1362306a36Sopenharmony_ci#include <keys/asymmetric-type.h> 1462306a36Sopenharmony_ci#include <crypto/pkcs7.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "ima.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct modsig { 1962306a36Sopenharmony_ci struct pkcs7_message *pkcs7_msg; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci enum hash_algo hash_algo; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci /* This digest will go in the 'd-modsig' field of the IMA template. */ 2462306a36Sopenharmony_ci const u8 *digest; 2562306a36Sopenharmony_ci u32 digest_size; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci /* 2862306a36Sopenharmony_ci * This is what will go to the measurement list if the template requires 2962306a36Sopenharmony_ci * storing the signature. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci int raw_pkcs7_len; 3262306a36Sopenharmony_ci u8 raw_pkcs7[]; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * ima_read_modsig - Read modsig from buf. 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * Return: 0 on success, error code otherwise. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ciint ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len, 4162306a36Sopenharmony_ci struct modsig **modsig) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci const size_t marker_len = strlen(MODULE_SIG_STRING); 4462306a36Sopenharmony_ci const struct module_signature *sig; 4562306a36Sopenharmony_ci struct modsig *hdr; 4662306a36Sopenharmony_ci size_t sig_len; 4762306a36Sopenharmony_ci const void *p; 4862306a36Sopenharmony_ci int rc; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (buf_len <= marker_len + sizeof(*sig)) 5162306a36Sopenharmony_ci return -ENOENT; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci p = buf + buf_len - marker_len; 5462306a36Sopenharmony_ci if (memcmp(p, MODULE_SIG_STRING, marker_len)) 5562306a36Sopenharmony_ci return -ENOENT; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci buf_len -= marker_len; 5862306a36Sopenharmony_ci sig = (const struct module_signature *)(p - sizeof(*sig)); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci rc = mod_check_sig(sig, buf_len, func_tokens[func]); 6162306a36Sopenharmony_ci if (rc) 6262306a36Sopenharmony_ci return rc; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci sig_len = be32_to_cpu(sig->sig_len); 6562306a36Sopenharmony_ci buf_len -= sig_len + sizeof(*sig); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* Allocate sig_len additional bytes to hold the raw PKCS#7 data. */ 6862306a36Sopenharmony_ci hdr = kzalloc(sizeof(*hdr) + sig_len, GFP_KERNEL); 6962306a36Sopenharmony_ci if (!hdr) 7062306a36Sopenharmony_ci return -ENOMEM; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci hdr->pkcs7_msg = pkcs7_parse_message(buf + buf_len, sig_len); 7362306a36Sopenharmony_ci if (IS_ERR(hdr->pkcs7_msg)) { 7462306a36Sopenharmony_ci rc = PTR_ERR(hdr->pkcs7_msg); 7562306a36Sopenharmony_ci kfree(hdr); 7662306a36Sopenharmony_ci return rc; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci memcpy(hdr->raw_pkcs7, buf + buf_len, sig_len); 8062306a36Sopenharmony_ci hdr->raw_pkcs7_len = sig_len; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* We don't know the hash algorithm yet. */ 8362306a36Sopenharmony_ci hdr->hash_algo = HASH_ALGO__LAST; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci *modsig = hdr; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/** 9162306a36Sopenharmony_ci * ima_collect_modsig - Calculate the file hash without the appended signature. 9262306a36Sopenharmony_ci * @modsig: parsed module signature 9362306a36Sopenharmony_ci * @buf: data to verify the signature on 9462306a36Sopenharmony_ci * @size: data size 9562306a36Sopenharmony_ci * 9662306a36Sopenharmony_ci * Since the modsig is part of the file contents, the hash used in its signature 9762306a36Sopenharmony_ci * isn't the same one ordinarily calculated by IMA. Therefore PKCS7 code 9862306a36Sopenharmony_ci * calculates a separate one for signature verification. 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_civoid ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci int rc; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* 10562306a36Sopenharmony_ci * Provide the file contents (minus the appended sig) so that the PKCS7 10662306a36Sopenharmony_ci * code can calculate the file hash. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci size -= modsig->raw_pkcs7_len + strlen(MODULE_SIG_STRING) + 10962306a36Sopenharmony_ci sizeof(struct module_signature); 11062306a36Sopenharmony_ci rc = pkcs7_supply_detached_data(modsig->pkcs7_msg, buf, size); 11162306a36Sopenharmony_ci if (rc) 11262306a36Sopenharmony_ci return; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* Ask the PKCS7 code to calculate the file hash. */ 11562306a36Sopenharmony_ci rc = pkcs7_get_digest(modsig->pkcs7_msg, &modsig->digest, 11662306a36Sopenharmony_ci &modsig->digest_size, &modsig->hash_algo); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ciint ima_modsig_verify(struct key *keyring, const struct modsig *modsig) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci return verify_pkcs7_message_sig(NULL, 0, modsig->pkcs7_msg, keyring, 12262306a36Sopenharmony_ci VERIFYING_MODULE_SIGNATURE, NULL, NULL); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ciint ima_get_modsig_digest(const struct modsig *modsig, enum hash_algo *algo, 12662306a36Sopenharmony_ci const u8 **digest, u32 *digest_size) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci *algo = modsig->hash_algo; 12962306a36Sopenharmony_ci *digest = modsig->digest; 13062306a36Sopenharmony_ci *digest_size = modsig->digest_size; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ciint ima_get_raw_modsig(const struct modsig *modsig, const void **data, 13662306a36Sopenharmony_ci u32 *data_len) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci *data = &modsig->raw_pkcs7; 13962306a36Sopenharmony_ci *data_len = modsig->raw_pkcs7_len; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_civoid ima_free_modsig(struct modsig *modsig) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci if (!modsig) 14762306a36Sopenharmony_ci return; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci pkcs7_free_message(modsig->pkcs7_msg); 15062306a36Sopenharmony_ci kfree(modsig); 15162306a36Sopenharmony_ci} 152