| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * IMA support for appraising module-style appended signatures. |
| * |
| * Copyright (C) 2019 IBM Corporation |
| * |
| * Author: |
| * Thiago Jung Bauermann <bauerman@linux.ibm.com> |
| */ |
| |
| #include <linux/types.h> |
| #include <linux/module_signature.h> |
| #include <keys/asymmetric-type.h> |
| #include <crypto/pkcs7.h> |
| |
| #include "ima.h" |
| |
| struct modsig { |
| struct pkcs7_message *pkcs7_msg; |
| |
| enum hash_algo hash_algo; |
| |
| /* This digest will go in the 'd-modsig' field of the IMA template. */ |
| const u8 *digest; |
| u32 digest_size; |
| |
| /* |
| * This is what will go to the measurement list if the template requires |
| * storing the signature. |
| */ |
| int raw_pkcs7_len; |
| u8 raw_pkcs7[]; |
| }; |
| |
| /* |
| * ima_read_modsig - Read modsig from buf. |
| * |
| * Return: 0 on success, error code otherwise. |
| */ |
| int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len, |
| struct modsig **modsig) |
| { |
| const size_t marker_len = strlen(MODULE_SIG_STRING); |
| const struct module_signature *sig; |
| struct modsig *hdr; |
| size_t sig_len; |
| const void *p; |
| int rc; |
| |
| if (buf_len <= marker_len + sizeof(*sig)) |
| return -ENOENT; |
| |
| p = buf + buf_len - marker_len; |
| if (memcmp(p, MODULE_SIG_STRING, marker_len)) |
| return -ENOENT; |
| |
| buf_len -= marker_len; |
| sig = (const struct module_signature *)(p - sizeof(*sig)); |
| |
| rc = mod_check_sig(sig, buf_len, func_tokens[func]); |
| if (rc) |
| return rc; |
| |
| sig_len = be32_to_cpu(sig->sig_len); |
| buf_len -= sig_len + sizeof(*sig); |
| |
| /* Allocate sig_len additional bytes to hold the raw PKCS#7 data. */ |
| hdr = kzalloc(sizeof(*hdr) + sig_len, GFP_KERNEL); |
| if (!hdr) |
| return -ENOMEM; |
| |
| hdr->pkcs7_msg = pkcs7_parse_message(buf + buf_len, sig_len); |
| if (IS_ERR(hdr->pkcs7_msg)) { |
| rc = PTR_ERR(hdr->pkcs7_msg); |
| kfree(hdr); |
| return rc; |
| } |
| |
| memcpy(hdr->raw_pkcs7, buf + buf_len, sig_len); |
| hdr->raw_pkcs7_len = sig_len; |
| |
| /* We don't know the hash algorithm yet. */ |
| hdr->hash_algo = HASH_ALGO__LAST; |
| |
| *modsig = hdr; |
| |
| return 0; |
| } |
| |
| /** |
| * ima_collect_modsig - Calculate the file hash without the appended signature. |
| * @modsig: parsed module signature |
| * @buf: data to verify the signature on |
| * @size: data size |
| * |
| * Since the modsig is part of the file contents, the hash used in its signature |
| * isn't the same one ordinarily calculated by IMA. Therefore PKCS7 code |
| * calculates a separate one for signature verification. |
| */ |
| void ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size) |
| { |
| int rc; |
| |
| /* |
| * Provide the file contents (minus the appended sig) so that the PKCS7 |
| * code can calculate the file hash. |
| */ |
| size -= modsig->raw_pkcs7_len + strlen(MODULE_SIG_STRING) + |
| sizeof(struct module_signature); |
| rc = pkcs7_supply_detached_data(modsig->pkcs7_msg, buf, size); |
| if (rc) |
| return; |
| |
| /* Ask the PKCS7 code to calculate the file hash. */ |
| rc = pkcs7_get_digest(modsig->pkcs7_msg, &modsig->digest, |
| &modsig->digest_size, &modsig->hash_algo); |
| } |
| |
| int ima_modsig_verify(struct key *keyring, const struct modsig *modsig) |
| { |
| return verify_pkcs7_message_sig(NULL, 0, modsig->pkcs7_msg, keyring, |
| VERIFYING_MODULE_SIGNATURE, NULL, NULL); |
| } |
| |
| int ima_get_modsig_digest(const struct modsig *modsig, enum hash_algo *algo, |
| const u8 **digest, u32 *digest_size) |
| { |
| *algo = modsig->hash_algo; |
| *digest = modsig->digest; |
| *digest_size = modsig->digest_size; |
| |
| return 0; |
| } |
| |
| int ima_get_raw_modsig(const struct modsig *modsig, const void **data, |
| u32 *data_len) |
| { |
| *data = &modsig->raw_pkcs7; |
| *data_len = modsig->raw_pkcs7_len; |
| |
| return 0; |
| } |
| |
| void ima_free_modsig(struct modsig *modsig) |
| { |
| if (!modsig) |
| return; |
| |
| pkcs7_free_message(modsig->pkcs7_msg); |
| kfree(modsig); |
| } |