1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* Parse a signed PE binary |
3 | * |
4 | * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. |
5 | * Written by David Howells (dhowells@redhat.com) |
6 | */ |
7 | |
8 | #define pr_fmt(fmt) "PEFILE: "fmt |
9 | #include <linux/module.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/err.h> |
13 | #include <linux/pe.h> |
14 | #include <linux/asn1.h> |
15 | #include <linux/verification.h> |
16 | #include <crypto/hash.h> |
17 | #include "verify_pefile.h" |
18 | |
19 | /* |
20 | * Parse a PE binary. |
21 | */ |
22 | static int pefile_parse_binary(const void *pebuf, unsigned int pelen, |
23 | struct pefile_context *ctx) |
24 | { |
25 | const struct mz_hdr *mz = pebuf; |
26 | const struct pe_hdr *pe; |
27 | const struct pe32_opt_hdr *pe32; |
28 | const struct pe32plus_opt_hdr *pe64; |
29 | const struct data_directory *ddir; |
30 | const struct data_dirent *dde; |
31 | const struct section_header *secs, *sec; |
32 | size_t cursor, datalen = pelen; |
33 | |
34 | kenter("" ); |
35 | |
36 | #define chkaddr(base, x, s) \ |
37 | do { \ |
38 | if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \ |
39 | return -ELIBBAD; \ |
40 | } while (0) |
41 | |
42 | chkaddr(0, 0, sizeof(*mz)); |
43 | if (mz->magic != MZ_MAGIC) |
44 | return -ELIBBAD; |
45 | cursor = sizeof(*mz); |
46 | |
47 | chkaddr(cursor, mz->peaddr, sizeof(*pe)); |
48 | pe = pebuf + mz->peaddr; |
49 | if (pe->magic != PE_MAGIC) |
50 | return -ELIBBAD; |
51 | cursor = mz->peaddr + sizeof(*pe); |
52 | |
53 | chkaddr(0, cursor, sizeof(pe32->magic)); |
54 | pe32 = pebuf + cursor; |
55 | pe64 = pebuf + cursor; |
56 | |
57 | switch (pe32->magic) { |
58 | case PE_OPT_MAGIC_PE32: |
59 | chkaddr(0, cursor, sizeof(*pe32)); |
60 | ctx->image_checksum_offset = |
61 | (unsigned long)&pe32->csum - (unsigned long)pebuf; |
62 | ctx->header_size = pe32->header_size; |
63 | cursor += sizeof(*pe32); |
64 | ctx->n_data_dirents = pe32->data_dirs; |
65 | break; |
66 | |
67 | case PE_OPT_MAGIC_PE32PLUS: |
68 | chkaddr(0, cursor, sizeof(*pe64)); |
69 | ctx->image_checksum_offset = |
70 | (unsigned long)&pe64->csum - (unsigned long)pebuf; |
71 | ctx->header_size = pe64->header_size; |
72 | cursor += sizeof(*pe64); |
73 | ctx->n_data_dirents = pe64->data_dirs; |
74 | break; |
75 | |
76 | default: |
77 | pr_warn("Unknown PEOPT magic = %04hx\n" , pe32->magic); |
78 | return -ELIBBAD; |
79 | } |
80 | |
81 | pr_debug("checksum @ %x\n" , ctx->image_checksum_offset); |
82 | pr_debug("header size = %x\n" , ctx->header_size); |
83 | |
84 | if (cursor >= ctx->header_size || ctx->header_size >= datalen) |
85 | return -ELIBBAD; |
86 | |
87 | if (ctx->n_data_dirents > (ctx->header_size - cursor) / sizeof(*dde)) |
88 | return -ELIBBAD; |
89 | |
90 | ddir = pebuf + cursor; |
91 | cursor += sizeof(*dde) * ctx->n_data_dirents; |
92 | |
93 | ctx->cert_dirent_offset = |
94 | (unsigned long)&ddir->certs - (unsigned long)pebuf; |
95 | ctx->certs_size = ddir->certs.size; |
96 | |
97 | if (!ddir->certs.virtual_address || !ddir->certs.size) { |
98 | pr_warn("Unsigned PE binary\n" ); |
99 | return -ENODATA; |
100 | } |
101 | |
102 | chkaddr(ctx->header_size, ddir->certs.virtual_address, |
103 | ddir->certs.size); |
104 | ctx->sig_offset = ddir->certs.virtual_address; |
105 | ctx->sig_len = ddir->certs.size; |
106 | pr_debug("cert = %x @%x [%*ph]\n" , |
107 | ctx->sig_len, ctx->sig_offset, |
108 | ctx->sig_len, pebuf + ctx->sig_offset); |
109 | |
110 | ctx->n_sections = pe->sections; |
111 | if (ctx->n_sections > (ctx->header_size - cursor) / sizeof(*sec)) |
112 | return -ELIBBAD; |
113 | ctx->secs = secs = pebuf + cursor; |
114 | |
115 | return 0; |
116 | } |
117 | |
118 | /* |
119 | * Check and strip the PE wrapper from around the signature and check that the |
120 | * remnant looks something like PKCS#7. |
121 | */ |
122 | static int pefile_strip_sig_wrapper(const void *pebuf, |
123 | struct pefile_context *ctx) |
124 | { |
125 | struct win_certificate wrapper; |
126 | const u8 *pkcs7; |
127 | unsigned len; |
128 | |
129 | if (ctx->sig_len < sizeof(wrapper)) { |
130 | pr_warn("Signature wrapper too short\n" ); |
131 | return -ELIBBAD; |
132 | } |
133 | |
134 | memcpy(&wrapper, pebuf + ctx->sig_offset, sizeof(wrapper)); |
135 | pr_debug("sig wrapper = { %x, %x, %x }\n" , |
136 | wrapper.length, wrapper.revision, wrapper.cert_type); |
137 | |
138 | /* sbsign rounds up the length of certificate table (in optional |
139 | * header data directories) to 8 byte alignment. However, the PE |
140 | * specification states that while entries are 8-byte aligned, this is |
141 | * not included in their length, and as a result, pesign has not |
142 | * rounded up since 0.110. |
143 | */ |
144 | if (wrapper.length > ctx->sig_len) { |
145 | pr_warn("Signature wrapper bigger than sig len (%x > %x)\n" , |
146 | ctx->sig_len, wrapper.length); |
147 | return -ELIBBAD; |
148 | } |
149 | if (wrapper.revision != WIN_CERT_REVISION_2_0) { |
150 | pr_warn("Signature is not revision 2.0\n" ); |
151 | return -ENOTSUPP; |
152 | } |
153 | if (wrapper.cert_type != WIN_CERT_TYPE_PKCS_SIGNED_DATA) { |
154 | pr_warn("Signature certificate type is not PKCS\n" ); |
155 | return -ENOTSUPP; |
156 | } |
157 | |
158 | /* It looks like the pkcs signature length in wrapper->length and the |
159 | * size obtained from the data dir entries, which lists the total size |
160 | * of certificate table, are both aligned to an octaword boundary, so |
161 | * we may have to deal with some padding. |
162 | */ |
163 | ctx->sig_len = wrapper.length; |
164 | ctx->sig_offset += sizeof(wrapper); |
165 | ctx->sig_len -= sizeof(wrapper); |
166 | if (ctx->sig_len < 4) { |
167 | pr_warn("Signature data missing\n" ); |
168 | return -EKEYREJECTED; |
169 | } |
170 | |
171 | /* What's left should be a PKCS#7 cert */ |
172 | pkcs7 = pebuf + ctx->sig_offset; |
173 | if (pkcs7[0] != (ASN1_CONS_BIT | ASN1_SEQ)) |
174 | goto not_pkcs7; |
175 | |
176 | switch (pkcs7[1]) { |
177 | case 0 ... 0x7f: |
178 | len = pkcs7[1] + 2; |
179 | goto check_len; |
180 | case ASN1_INDEFINITE_LENGTH: |
181 | return 0; |
182 | case 0x81: |
183 | len = pkcs7[2] + 3; |
184 | goto check_len; |
185 | case 0x82: |
186 | len = ((pkcs7[2] << 8) | pkcs7[3]) + 4; |
187 | goto check_len; |
188 | case 0x83 ... 0xff: |
189 | return -EMSGSIZE; |
190 | default: |
191 | goto not_pkcs7; |
192 | } |
193 | |
194 | check_len: |
195 | if (len <= ctx->sig_len) { |
196 | /* There may be padding */ |
197 | ctx->sig_len = len; |
198 | return 0; |
199 | } |
200 | not_pkcs7: |
201 | pr_warn("Signature data not PKCS#7\n" ); |
202 | return -ELIBBAD; |
203 | } |
204 | |
205 | /* |
206 | * Compare two sections for canonicalisation. |
207 | */ |
208 | static int pefile_compare_shdrs(const void *a, const void *b) |
209 | { |
210 | const struct section_header *shdra = a; |
211 | const struct section_header *shdrb = b; |
212 | int rc; |
213 | |
214 | if (shdra->data_addr > shdrb->data_addr) |
215 | return 1; |
216 | if (shdrb->data_addr > shdra->data_addr) |
217 | return -1; |
218 | |
219 | if (shdra->virtual_address > shdrb->virtual_address) |
220 | return 1; |
221 | if (shdrb->virtual_address > shdra->virtual_address) |
222 | return -1; |
223 | |
224 | rc = strcmp(shdra->name, shdrb->name); |
225 | if (rc != 0) |
226 | return rc; |
227 | |
228 | if (shdra->virtual_size > shdrb->virtual_size) |
229 | return 1; |
230 | if (shdrb->virtual_size > shdra->virtual_size) |
231 | return -1; |
232 | |
233 | if (shdra->raw_data_size > shdrb->raw_data_size) |
234 | return 1; |
235 | if (shdrb->raw_data_size > shdra->raw_data_size) |
236 | return -1; |
237 | |
238 | return 0; |
239 | } |
240 | |
241 | /* |
242 | * Load the contents of the PE binary into the digest, leaving out the image |
243 | * checksum and the certificate data block. |
244 | */ |
245 | static int pefile_digest_pe_contents(const void *pebuf, unsigned int pelen, |
246 | struct pefile_context *ctx, |
247 | struct shash_desc *desc) |
248 | { |
249 | unsigned *canon, tmp, loop, i, hashed_bytes; |
250 | int ret; |
251 | |
252 | /* Digest the header and data directory, but leave out the image |
253 | * checksum and the data dirent for the signature. |
254 | */ |
255 | ret = crypto_shash_update(desc, data: pebuf, len: ctx->image_checksum_offset); |
256 | if (ret < 0) |
257 | return ret; |
258 | |
259 | tmp = ctx->image_checksum_offset + sizeof(uint32_t); |
260 | ret = crypto_shash_update(desc, data: pebuf + tmp, |
261 | len: ctx->cert_dirent_offset - tmp); |
262 | if (ret < 0) |
263 | return ret; |
264 | |
265 | tmp = ctx->cert_dirent_offset + sizeof(struct data_dirent); |
266 | ret = crypto_shash_update(desc, data: pebuf + tmp, len: ctx->header_size - tmp); |
267 | if (ret < 0) |
268 | return ret; |
269 | |
270 | canon = kcalloc(n: ctx->n_sections, size: sizeof(unsigned), GFP_KERNEL); |
271 | if (!canon) |
272 | return -ENOMEM; |
273 | |
274 | /* We have to canonicalise the section table, so we perform an |
275 | * insertion sort. |
276 | */ |
277 | canon[0] = 0; |
278 | for (loop = 1; loop < ctx->n_sections; loop++) { |
279 | for (i = 0; i < loop; i++) { |
280 | if (pefile_compare_shdrs(a: &ctx->secs[canon[i]], |
281 | b: &ctx->secs[loop]) > 0) { |
282 | memmove(&canon[i + 1], &canon[i], |
283 | (loop - i) * sizeof(canon[0])); |
284 | break; |
285 | } |
286 | } |
287 | canon[i] = loop; |
288 | } |
289 | |
290 | hashed_bytes = ctx->header_size; |
291 | for (loop = 0; loop < ctx->n_sections; loop++) { |
292 | i = canon[loop]; |
293 | if (ctx->secs[i].raw_data_size == 0) |
294 | continue; |
295 | ret = crypto_shash_update(desc, |
296 | data: pebuf + ctx->secs[i].data_addr, |
297 | len: ctx->secs[i].raw_data_size); |
298 | if (ret < 0) { |
299 | kfree(objp: canon); |
300 | return ret; |
301 | } |
302 | hashed_bytes += ctx->secs[i].raw_data_size; |
303 | } |
304 | kfree(objp: canon); |
305 | |
306 | if (pelen > hashed_bytes) { |
307 | tmp = hashed_bytes + ctx->certs_size; |
308 | ret = crypto_shash_update(desc, |
309 | data: pebuf + hashed_bytes, |
310 | len: pelen - tmp); |
311 | if (ret < 0) |
312 | return ret; |
313 | } |
314 | |
315 | return 0; |
316 | } |
317 | |
318 | /* |
319 | * Digest the contents of the PE binary, leaving out the image checksum and the |
320 | * certificate data block. |
321 | */ |
322 | static int pefile_digest_pe(const void *pebuf, unsigned int pelen, |
323 | struct pefile_context *ctx) |
324 | { |
325 | struct crypto_shash *tfm; |
326 | struct shash_desc *desc; |
327 | size_t digest_size, desc_size; |
328 | void *digest; |
329 | int ret; |
330 | |
331 | kenter(",%s" , ctx->digest_algo); |
332 | |
333 | /* Allocate the hashing algorithm we're going to need and find out how |
334 | * big the hash operational data will be. |
335 | */ |
336 | tfm = crypto_alloc_shash(alg_name: ctx->digest_algo, type: 0, mask: 0); |
337 | if (IS_ERR(ptr: tfm)) |
338 | return (PTR_ERR(ptr: tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(ptr: tfm); |
339 | |
340 | desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); |
341 | digest_size = crypto_shash_digestsize(tfm); |
342 | |
343 | if (digest_size != ctx->digest_len) { |
344 | pr_warn("Digest size mismatch (%zx != %x)\n" , |
345 | digest_size, ctx->digest_len); |
346 | ret = -EBADMSG; |
347 | goto error_no_desc; |
348 | } |
349 | pr_debug("Digest: desc=%zu size=%zu\n" , desc_size, digest_size); |
350 | |
351 | ret = -ENOMEM; |
352 | desc = kzalloc(size: desc_size + digest_size, GFP_KERNEL); |
353 | if (!desc) |
354 | goto error_no_desc; |
355 | |
356 | desc->tfm = tfm; |
357 | ret = crypto_shash_init(desc); |
358 | if (ret < 0) |
359 | goto error; |
360 | |
361 | ret = pefile_digest_pe_contents(pebuf, pelen, ctx, desc); |
362 | if (ret < 0) |
363 | goto error; |
364 | |
365 | digest = (void *)desc + desc_size; |
366 | ret = crypto_shash_final(desc, out: digest); |
367 | if (ret < 0) |
368 | goto error; |
369 | |
370 | pr_debug("Digest calc = [%*ph]\n" , ctx->digest_len, digest); |
371 | |
372 | /* Check that the PE file digest matches that in the MSCODE part of the |
373 | * PKCS#7 certificate. |
374 | */ |
375 | if (memcmp(p: digest, q: ctx->digest, size: ctx->digest_len) != 0) { |
376 | pr_warn("Digest mismatch\n" ); |
377 | ret = -EKEYREJECTED; |
378 | } else { |
379 | pr_debug("The digests match!\n" ); |
380 | } |
381 | |
382 | error: |
383 | kfree_sensitive(objp: desc); |
384 | error_no_desc: |
385 | crypto_free_shash(tfm); |
386 | kleave(" = %d" , ret); |
387 | return ret; |
388 | } |
389 | |
390 | /** |
391 | * verify_pefile_signature - Verify the signature on a PE binary image |
392 | * @pebuf: Buffer containing the PE binary image |
393 | * @pelen: Length of the binary image |
394 | * @trusted_keys: Signing certificate(s) to use as starting points |
395 | * @usage: The use to which the key is being put. |
396 | * |
397 | * Validate that the certificate chain inside the PKCS#7 message inside the PE |
398 | * binary image intersects keys we already know and trust. |
399 | * |
400 | * Returns, in order of descending priority: |
401 | * |
402 | * (*) -ELIBBAD if the image cannot be parsed, or: |
403 | * |
404 | * (*) -EKEYREJECTED if a signature failed to match for which we have a valid |
405 | * key, or: |
406 | * |
407 | * (*) 0 if at least one signature chain intersects with the keys in the trust |
408 | * keyring, or: |
409 | * |
410 | * (*) -ENODATA if there is no signature present. |
411 | * |
412 | * (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a |
413 | * chain. |
414 | * |
415 | * (*) -ENOKEY if we couldn't find a match for any of the signature chains in |
416 | * the message. |
417 | * |
418 | * May also return -ENOMEM. |
419 | */ |
420 | int verify_pefile_signature(const void *pebuf, unsigned pelen, |
421 | struct key *trusted_keys, |
422 | enum key_being_used_for usage) |
423 | { |
424 | struct pefile_context ctx; |
425 | int ret; |
426 | |
427 | kenter("" ); |
428 | |
429 | memset(&ctx, 0, sizeof(ctx)); |
430 | ret = pefile_parse_binary(pebuf, pelen, ctx: &ctx); |
431 | if (ret < 0) |
432 | return ret; |
433 | |
434 | ret = pefile_strip_sig_wrapper(pebuf, ctx: &ctx); |
435 | if (ret < 0) |
436 | return ret; |
437 | |
438 | ret = verify_pkcs7_signature(NULL, len: 0, |
439 | raw_pkcs7: pebuf + ctx.sig_offset, pkcs7_len: ctx.sig_len, |
440 | trusted_keys, usage, |
441 | view_content: mscode_parse, ctx: &ctx); |
442 | if (ret < 0) |
443 | goto error; |
444 | |
445 | pr_debug("Digest: %u [%*ph]\n" , |
446 | ctx.digest_len, ctx.digest_len, ctx.digest); |
447 | |
448 | /* Generate the digest and check against the PKCS7 certificate |
449 | * contents. |
450 | */ |
451 | ret = pefile_digest_pe(pebuf, pelen, ctx: &ctx); |
452 | |
453 | error: |
454 | kfree_sensitive(objp: ctx.digest); |
455 | return ret; |
456 | } |
457 | |