1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * IMA support for appraising module-style appended signatures. |
4 | * |
5 | * Copyright (C) 2019 IBM Corporation |
6 | * |
7 | * Author: |
8 | * Thiago Jung Bauermann <bauerman@linux.ibm.com> |
9 | */ |
10 | |
11 | #include <linux/types.h> |
12 | #include <linux/module_signature.h> |
13 | #include <keys/asymmetric-type.h> |
14 | #include <crypto/pkcs7.h> |
15 | |
16 | #include "ima.h" |
17 | |
18 | struct modsig { |
19 | struct pkcs7_message *pkcs7_msg; |
20 | |
21 | enum hash_algo hash_algo; |
22 | |
23 | /* This digest will go in the 'd-modsig' field of the IMA template. */ |
24 | const u8 *digest; |
25 | u32 digest_size; |
26 | |
27 | /* |
28 | * This is what will go to the measurement list if the template requires |
29 | * storing the signature. |
30 | */ |
31 | int raw_pkcs7_len; |
32 | u8 raw_pkcs7[] __counted_by(raw_pkcs7_len); |
33 | }; |
34 | |
35 | /* |
36 | * ima_read_modsig - Read modsig from buf. |
37 | * |
38 | * Return: 0 on success, error code otherwise. |
39 | */ |
40 | int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len, |
41 | struct modsig **modsig) |
42 | { |
43 | const size_t marker_len = strlen(MODULE_SIG_STRING); |
44 | const struct module_signature *sig; |
45 | struct modsig *hdr; |
46 | size_t sig_len; |
47 | const void *p; |
48 | int rc; |
49 | |
50 | if (buf_len <= marker_len + sizeof(*sig)) |
51 | return -ENOENT; |
52 | |
53 | p = buf + buf_len - marker_len; |
54 | if (memcmp(p, MODULE_SIG_STRING, size: marker_len)) |
55 | return -ENOENT; |
56 | |
57 | buf_len -= marker_len; |
58 | sig = (const struct module_signature *)(p - sizeof(*sig)); |
59 | |
60 | rc = mod_check_sig(ms: sig, file_len: buf_len, name: func_tokens[func]); |
61 | if (rc) |
62 | return rc; |
63 | |
64 | sig_len = be32_to_cpu(sig->sig_len); |
65 | buf_len -= sig_len + sizeof(*sig); |
66 | |
67 | /* Allocate sig_len additional bytes to hold the raw PKCS#7 data. */ |
68 | hdr = kzalloc(struct_size(hdr, raw_pkcs7, sig_len), GFP_KERNEL); |
69 | if (!hdr) |
70 | return -ENOMEM; |
71 | |
72 | hdr->raw_pkcs7_len = sig_len; |
73 | hdr->pkcs7_msg = pkcs7_parse_message(data: buf + buf_len, datalen: sig_len); |
74 | if (IS_ERR(ptr: hdr->pkcs7_msg)) { |
75 | rc = PTR_ERR(ptr: hdr->pkcs7_msg); |
76 | kfree(objp: hdr); |
77 | return rc; |
78 | } |
79 | |
80 | memcpy(hdr->raw_pkcs7, buf + buf_len, sig_len); |
81 | |
82 | /* We don't know the hash algorithm yet. */ |
83 | hdr->hash_algo = HASH_ALGO__LAST; |
84 | |
85 | *modsig = hdr; |
86 | |
87 | return 0; |
88 | } |
89 | |
90 | /** |
91 | * ima_collect_modsig - Calculate the file hash without the appended signature. |
92 | * @modsig: parsed module signature |
93 | * @buf: data to verify the signature on |
94 | * @size: data size |
95 | * |
96 | * Since the modsig is part of the file contents, the hash used in its signature |
97 | * isn't the same one ordinarily calculated by IMA. Therefore PKCS7 code |
98 | * calculates a separate one for signature verification. |
99 | */ |
100 | void ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size) |
101 | { |
102 | int rc; |
103 | |
104 | /* |
105 | * Provide the file contents (minus the appended sig) so that the PKCS7 |
106 | * code can calculate the file hash. |
107 | */ |
108 | size -= modsig->raw_pkcs7_len + strlen(MODULE_SIG_STRING) + |
109 | sizeof(struct module_signature); |
110 | rc = pkcs7_supply_detached_data(pkcs7: modsig->pkcs7_msg, data: buf, datalen: size); |
111 | if (rc) |
112 | return; |
113 | |
114 | /* Ask the PKCS7 code to calculate the file hash. */ |
115 | rc = pkcs7_get_digest(pkcs7: modsig->pkcs7_msg, buf: &modsig->digest, |
116 | len: &modsig->digest_size, hash_algo: &modsig->hash_algo); |
117 | } |
118 | |
119 | int ima_modsig_verify(struct key *keyring, const struct modsig *modsig) |
120 | { |
121 | return verify_pkcs7_message_sig(NULL, len: 0, pkcs7: modsig->pkcs7_msg, trusted_keys: keyring, |
122 | usage: VERIFYING_MODULE_SIGNATURE, NULL, NULL); |
123 | } |
124 | |
125 | int ima_get_modsig_digest(const struct modsig *modsig, enum hash_algo *algo, |
126 | const u8 **digest, u32 *digest_size) |
127 | { |
128 | *algo = modsig->hash_algo; |
129 | *digest = modsig->digest; |
130 | *digest_size = modsig->digest_size; |
131 | |
132 | return 0; |
133 | } |
134 | |
135 | int ima_get_raw_modsig(const struct modsig *modsig, const void **data, |
136 | u32 *data_len) |
137 | { |
138 | *data = &modsig->raw_pkcs7; |
139 | *data_len = modsig->raw_pkcs7_len; |
140 | |
141 | return 0; |
142 | } |
143 | |
144 | void ima_free_modsig(struct modsig *modsig) |
145 | { |
146 | if (!modsig) |
147 | return; |
148 | |
149 | pkcs7_free_message(pkcs7: modsig->pkcs7_msg); |
150 | kfree(objp: modsig); |
151 | } |
152 | |