1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2014 The FreeBSD Foundation
5 *
6 * This software was developed by Edward Tomasz Napierala under sponsorship
7 * from the FreeBSD Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 */
31
32/*
33 * PE format reference:
34 * http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
35 */
36
37#include <sys/cdefs.h>
38#include <assert.h>
39#include <err.h>
40#include <errno.h>
41#include <stddef.h>
42#include <stdio.h>
43#include <stdint.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
47
48#include "uefisign.h"
49
50#ifndef CTASSERT
51#define CTASSERT(x)		_CTASSERT(x, __LINE__)
52#define _CTASSERT(x, y)		__CTASSERT(x, y)
53#define __CTASSERT(x, y)	typedef char __assert_ ## y [(x) ? 1 : -1]
54#endif
55
56#define PE_ALIGMENT_SIZE	8
57
58struct mz_header {
59	uint8_t			mz_signature[2];
60	uint8_t			mz_dont_care[58];
61	uint16_t		mz_lfanew;
62} __attribute__((packed));
63
64struct coff_header {
65	uint8_t			coff_dont_care[2];
66	uint16_t		coff_number_of_sections;
67	uint8_t			coff_dont_care_either[16];
68} __attribute__((packed));
69
70#define	PE_SIGNATURE		0x00004550
71
72struct pe_header {
73	uint32_t		pe_signature;
74	struct coff_header	pe_coff;
75} __attribute__((packed));
76
77#define	PE_OPTIONAL_MAGIC_32		0x010B
78#define	PE_OPTIONAL_MAGIC_32_PLUS	0x020B
79
80#define	PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION	10
81#define	PE_OPTIONAL_SUBSYSTEM_EFI_BOOT		11
82#define	PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME	12
83
84struct pe_optional_header_32 {
85	uint16_t		po_magic;
86	uint8_t			po_dont_care[58];
87	uint32_t		po_size_of_headers;
88	uint32_t		po_checksum;
89	uint16_t		po_subsystem;
90	uint8_t			po_dont_care_either[22];
91	uint32_t		po_number_of_rva_and_sizes;
92} __attribute__((packed));
93
94CTASSERT(offsetof(struct pe_optional_header_32, po_size_of_headers) == 60);
95CTASSERT(offsetof(struct pe_optional_header_32, po_checksum) == 64);
96CTASSERT(offsetof(struct pe_optional_header_32, po_subsystem) == 68);
97CTASSERT(offsetof(struct pe_optional_header_32, po_number_of_rva_and_sizes) == 92);
98
99struct pe_optional_header_32_plus {
100	uint16_t		po_magic;
101	uint8_t			po_dont_care[58];
102	uint32_t		po_size_of_headers;
103	uint32_t		po_checksum;
104	uint16_t		po_subsystem;
105	uint8_t			po_dont_care_either[38];
106	uint32_t		po_number_of_rva_and_sizes;
107} __attribute__((packed));
108
109CTASSERT(offsetof(struct pe_optional_header_32_plus, po_size_of_headers) == 60);
110CTASSERT(offsetof(struct pe_optional_header_32_plus, po_checksum) == 64);
111CTASSERT(offsetof(struct pe_optional_header_32_plus, po_subsystem) == 68);
112CTASSERT(offsetof(struct pe_optional_header_32_plus, po_number_of_rva_and_sizes) == 108);
113
114#define	PE_DIRECTORY_ENTRY_CERTIFICATE	4
115
116struct pe_directory_entry {
117	uint32_t	pde_rva;
118	uint32_t	pde_size;
119} __attribute__((packed));
120
121struct pe_section_header {
122	uint8_t			psh_dont_care[16];
123	uint32_t		psh_size_of_raw_data;
124	uint32_t		psh_pointer_to_raw_data;
125	uint8_t			psh_dont_care_either[16];
126} __attribute__((packed));
127
128CTASSERT(offsetof(struct pe_section_header, psh_size_of_raw_data) == 16);
129CTASSERT(offsetof(struct pe_section_header, psh_pointer_to_raw_data) == 20);
130
131#define	PE_CERTIFICATE_REVISION		0x0200
132#define	PE_CERTIFICATE_TYPE		0x0002
133
134struct pe_certificate {
135	uint32_t	pc_len;
136	uint16_t	pc_revision;
137	uint16_t	pc_type;
138	char		pc_signature[0];
139} __attribute__((packed));
140
141void
142range_check(const struct executable *x, off_t off, size_t len,
143    const char *name)
144{
145
146	if (off < 0) {
147		errx(1, "%s starts at negative offset %jd",
148		    name, (intmax_t)off);
149	}
150	if (off >= (off_t)x->x_len) {
151		errx(1, "%s starts at %jd, past the end of executable at %zd",
152		    name, (intmax_t)off, x->x_len);
153	}
154	if (len >= x->x_len) {
155		errx(1, "%s size %zd is larger than the executable size %zd",
156		    name, len, x->x_len);
157	}
158	if (off + len > x->x_len) {
159		errx(1, "%s extends to %jd, past the end of executable at %zd",
160		    name, (intmax_t)(off + len), x->x_len);
161	}
162}
163
164size_t
165signature_size(const struct executable *x)
166{
167	const struct pe_directory_entry *pde;
168
169	range_check(x, x->x_certificate_entry_off,
170	    x->x_certificate_entry_len, "Certificate Directory");
171
172	pde = (struct pe_directory_entry *)
173	    (x->x_buf + x->x_certificate_entry_off);
174
175	if (pde->pde_rva != 0 && pde->pde_size == 0)
176		warnx("signature size is 0, but its RVA is %d", pde->pde_rva);
177	if (pde->pde_rva == 0 && pde->pde_size != 0)
178		warnx("signature RVA is 0, but its size is %d", pde->pde_size);
179
180	return (pde->pde_size);
181}
182
183void
184show_certificate(const struct executable *x)
185{
186	struct pe_certificate *pc;
187	const struct pe_directory_entry *pde;
188
189	range_check(x, x->x_certificate_entry_off,
190	    x->x_certificate_entry_len, "Certificate Directory");
191
192	pde = (struct pe_directory_entry *)
193	    (x->x_buf + x->x_certificate_entry_off);
194
195	if (signature_size(x) == 0) {
196		printf("file not signed\n");
197		return;
198	}
199
200#if 0
201	printf("certificate chunk at offset %zd, size %zd\n",
202	    pde->pde_rva, pde->pde_size);
203#endif
204
205	range_check(x, pde->pde_rva, pde->pde_size, "Certificate chunk");
206
207	pc = (struct pe_certificate *)(x->x_buf + pde->pde_rva);
208	if (pc->pc_revision != PE_CERTIFICATE_REVISION) {
209		errx(1, "wrong certificate chunk revision, is %d, should be %d",
210		    pc->pc_revision, PE_CERTIFICATE_REVISION);
211	}
212	if (pc->pc_type != PE_CERTIFICATE_TYPE) {
213		errx(1, "wrong certificate chunk type, is %d, should be %d",
214		    pc->pc_type, PE_CERTIFICATE_TYPE);
215	}
216	printf("to dump PKCS7:\n    "
217	    "dd if='%s' bs=1 skip=%zd | openssl pkcs7 -inform DER -print\n",
218	    x->x_path, pde->pde_rva + offsetof(struct pe_certificate, pc_signature));
219	printf("to dump raw ASN.1:\n    "
220	    "openssl asn1parse -i -inform DER -offset %zd -in '%s'\n",
221	    pde->pde_rva + offsetof(struct pe_certificate, pc_signature), x->x_path);
222}
223
224static void
225parse_section_table(struct executable *x, off_t off, int number_of_sections)
226{
227	const struct pe_section_header *psh;
228	int i;
229
230	range_check(x, off, sizeof(*psh) * number_of_sections,
231	    "section table");
232
233	if (x->x_headers_len < off + sizeof(*psh) * number_of_sections)
234		errx(1, "section table outside of headers");
235
236	psh = (const struct pe_section_header *)(x->x_buf + off);
237
238	if (number_of_sections >= MAX_SECTIONS) {
239		errx(1, "too many sections: got %d, should be %d",
240		    number_of_sections, MAX_SECTIONS);
241	}
242	x->x_nsections = number_of_sections;
243
244	for (i = 0; i < number_of_sections; i++) {
245		if (psh->psh_size_of_raw_data > 0 &&
246		    psh->psh_pointer_to_raw_data < x->x_headers_len)
247			errx(1, "section points inside the headers");
248
249		range_check(x, psh->psh_pointer_to_raw_data,
250		    psh->psh_size_of_raw_data, "section");
251#if 0
252		printf("section %d: start %d, size %d\n",
253		    i, psh->psh_pointer_to_raw_data, psh->psh_size_of_raw_data);
254#endif
255		x->x_section_off[i] = psh->psh_pointer_to_raw_data;
256		x->x_section_len[i] = psh->psh_size_of_raw_data;
257		psh++;
258	}
259}
260
261static void
262parse_directory(struct executable *x, off_t off,
263    int number_of_rva_and_sizes, int number_of_sections)
264{
265	//int i;
266	const struct pe_directory_entry *pde;
267
268	//printf("Data Directory at offset %zd\n", off);
269
270	if (number_of_rva_and_sizes <= PE_DIRECTORY_ENTRY_CERTIFICATE) {
271		errx(1, "wrong NumberOfRvaAndSizes %d; should be at least %d",
272		    number_of_rva_and_sizes, PE_DIRECTORY_ENTRY_CERTIFICATE);
273	}
274
275	range_check(x, off, sizeof(*pde) * number_of_rva_and_sizes,
276	    "PE Data Directory");
277	if (x->x_headers_len <= off + sizeof(*pde) * number_of_rva_and_sizes)
278		errx(1, "PE Data Directory outside of headers");
279
280	x->x_certificate_entry_off =
281	    off + sizeof(*pde) * PE_DIRECTORY_ENTRY_CERTIFICATE;
282	x->x_certificate_entry_len = sizeof(*pde);
283#if 0
284	printf("certificate directory entry at offset %zd, len %zd\n",
285	    x->x_certificate_entry_off, x->x_certificate_entry_len);
286
287	pde = (struct pe_directory_entry *)(x->x_buf + off);
288	for (i = 0; i < number_of_rva_and_sizes; i++) {
289		printf("rva %zd, size %zd\n", pde->pde_rva, pde->pde_size);
290		pde++;
291	}
292#endif
293
294	return (parse_section_table(x,
295	    off + sizeof(*pde) * number_of_rva_and_sizes, number_of_sections));
296}
297
298/*
299 * The PE checksum algorithm is undocumented; this code is mostly based on
300 * http://forum.sysinternals.com/optional-header-checksum-calculation_topic24214.html
301 *
302 * "Sum the entire image file, excluding the CheckSum field in the optional
303 * header, as an array of USHORTs, allowing any carry above 16 bits to be added
304 * back onto the low 16 bits. Then add the file size to get a 32-bit value."
305 *
306 * Note that most software does not care about the checksum at all; perhaps
307 * we could just set it to 0 instead.
308 *
309 * XXX: Endianness?
310 */
311static uint32_t
312compute_checksum(const struct executable *x)
313{
314	uint32_t cksum = 0;
315	uint16_t tmp;
316	int i;
317
318	range_check(x, x->x_checksum_off, x->x_checksum_len, "PE checksum");
319
320	assert(x->x_checksum_off % 2 == 0);
321
322	for (i = 0; i + sizeof(tmp) < x->x_len; i += 2) {
323		/*
324		 * Don't checksum the checksum.  The +2 is because the checksum
325		 * is 4 bytes, and here we're iterating over 2 byte chunks.
326		 */
327		if (i == x->x_checksum_off || i == x->x_checksum_off + 2) {
328			tmp = 0;
329		} else {
330			assert(i + sizeof(tmp) <= x->x_len);
331			memcpy(&tmp, x->x_buf + i, sizeof(tmp));
332		}
333
334		cksum += tmp;
335		cksum += cksum >> 16;
336		cksum &= 0xffff;
337	}
338
339	cksum += cksum >> 16;
340	cksum &= 0xffff;
341
342	cksum += x->x_len;
343
344	return (cksum);
345}
346
347static void
348parse_optional_32_plus(struct executable *x, off_t off,
349    int number_of_sections)
350{
351#if 0
352	uint32_t computed_checksum;
353#endif
354	const struct pe_optional_header_32_plus	*po;
355
356	range_check(x, off, sizeof(*po), "PE Optional Header");
357
358	po = (struct pe_optional_header_32_plus *)(x->x_buf + off);
359	switch (po->po_subsystem) {
360	case PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION:
361	case PE_OPTIONAL_SUBSYSTEM_EFI_BOOT:
362	case PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME:
363		break;
364	default:
365		errx(1, "wrong PE Optional Header subsystem 0x%x",
366		    po->po_subsystem);
367	}
368
369#if 0
370	printf("subsystem %d, checksum 0x%x, %d data directories\n",
371	    po->po_subsystem, po->po_checksum, po->po_number_of_rva_and_sizes);
372#endif
373
374	x->x_checksum_off = off +
375	    offsetof(struct pe_optional_header_32_plus, po_checksum);
376	x->x_checksum_len = sizeof(po->po_checksum);
377#if 0
378	printf("checksum 0x%x at offset %zd, len %zd\n",
379	    po->po_checksum, x->x_checksum_off, x->x_checksum_len);
380
381	computed_checksum = compute_checksum(x);
382	if (computed_checksum != po->po_checksum) {
383		warnx("invalid PE+ checksum; is 0x%x, should be 0x%x",
384		    po->po_checksum, computed_checksum);
385	}
386#endif
387
388	if (x->x_len < x->x_headers_len)
389		errx(1, "invalid SizeOfHeaders %d", po->po_size_of_headers);
390	x->x_headers_len = po->po_size_of_headers;
391	//printf("Size of Headers: %d\n", po->po_size_of_headers);
392
393	return (parse_directory(x, off + sizeof(*po),
394	    po->po_number_of_rva_and_sizes, number_of_sections));
395}
396
397static void
398parse_optional_32(struct executable *x, off_t off, int number_of_sections)
399{
400#if 0
401	uint32_t computed_checksum;
402#endif
403	const struct pe_optional_header_32 *po;
404
405	range_check(x, off, sizeof(*po), "PE Optional Header");
406
407	po = (struct pe_optional_header_32 *)(x->x_buf + off);
408	switch (po->po_subsystem) {
409	case PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION:
410	case PE_OPTIONAL_SUBSYSTEM_EFI_BOOT:
411	case PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME:
412		break;
413	default:
414		errx(1, "wrong PE Optional Header subsystem 0x%x",
415		    po->po_subsystem);
416	}
417
418#if 0
419	printf("subsystem %d, checksum 0x%x, %d data directories\n",
420	    po->po_subsystem, po->po_checksum, po->po_number_of_rva_and_sizes);
421#endif
422
423	x->x_checksum_off = off +
424	    offsetof(struct pe_optional_header_32, po_checksum);
425	x->x_checksum_len = sizeof(po->po_checksum);
426#if 0
427	printf("checksum at offset %zd, len %zd\n",
428	    x->x_checksum_off, x->x_checksum_len);
429
430	computed_checksum = compute_checksum(x);
431	if (computed_checksum != po->po_checksum) {
432		warnx("invalid PE checksum; is 0x%x, should be 0x%x",
433		    po->po_checksum, computed_checksum);
434	}
435#endif
436
437	if (x->x_len < x->x_headers_len)
438		errx(1, "invalid SizeOfHeaders %d", po->po_size_of_headers);
439	x->x_headers_len = po->po_size_of_headers;
440	//printf("Size of Headers: %d\n", po->po_size_of_headers);
441
442	return (parse_directory(x, off + sizeof(*po),
443	    po->po_number_of_rva_and_sizes, number_of_sections));
444}
445
446static void
447parse_optional(struct executable *x, off_t off, int number_of_sections)
448{
449	const struct pe_optional_header_32 *po;
450
451	//printf("Optional header offset %zd\n", off);
452
453	range_check(x, off, sizeof(*po), "PE Optional Header");
454
455	po = (struct pe_optional_header_32 *)(x->x_buf + off);
456
457	switch (po->po_magic) {
458	case PE_OPTIONAL_MAGIC_32:
459		return (parse_optional_32(x, off, number_of_sections));
460	case PE_OPTIONAL_MAGIC_32_PLUS:
461		return (parse_optional_32_plus(x, off, number_of_sections));
462	default:
463		errx(1, "wrong PE Optional Header magic 0x%x", po->po_magic);
464	}
465}
466
467static void
468parse_pe(struct executable *x, off_t off)
469{
470	const struct pe_header *pe;
471
472	//printf("PE offset %zd, PE size %zd\n", off, sizeof(*pe));
473
474	range_check(x, off, sizeof(*pe), "PE header");
475
476	pe = (struct pe_header *)(x->x_buf + off);
477	if (pe->pe_signature != PE_SIGNATURE)
478		errx(1, "wrong PE signature 0x%x", pe->pe_signature);
479
480	//printf("Number of sections: %d\n", pe->pe_coff.coff_number_of_sections);
481
482	parse_optional(x, off + sizeof(*pe),
483	    pe->pe_coff.coff_number_of_sections);
484}
485
486void
487parse(struct executable *x)
488{
489	const struct mz_header *mz;
490
491	range_check(x, 0, sizeof(*mz), "MZ header");
492
493	mz = (struct mz_header *)x->x_buf;
494	if (mz->mz_signature[0] != 'M' || mz->mz_signature[1] != 'Z')
495		errx(1, "MZ header not found");
496
497	return (parse_pe(x, mz->mz_lfanew));
498}
499
500static off_t
501append(struct executable *x, void *ptr, size_t len, size_t aligment)
502{
503	off_t off;
504
505	off = x->x_len;
506	x->x_buf = realloc(x->x_buf, x->x_len + len + aligment);
507	if (x->x_buf == NULL)
508		err(1, "realloc");
509	memcpy(x->x_buf + x->x_len, ptr, len);
510	memset(x->x_buf + x->x_len + len, 0, aligment);
511	x->x_len += len + aligment;
512
513	return (off);
514}
515
516void
517update(struct executable *x)
518{
519	uint32_t checksum;
520	struct pe_certificate *pc;
521	struct pe_directory_entry pde;
522	size_t pc_len;
523	size_t pc_aligment;
524	off_t pc_off;
525
526	pc_len = sizeof(*pc) + x->x_signature_len;
527	pc = calloc(1, pc_len);
528	if (pc == NULL)
529		err(1, "calloc");
530
531	if (pc_len % PE_ALIGMENT_SIZE > 0)
532		pc_aligment = PE_ALIGMENT_SIZE - (pc_len % PE_ALIGMENT_SIZE);
533	else
534		pc_aligment = 0;
535
536#if 0
537	/*
538	 * Note that pc_len is the length of pc_certificate,
539	 * not the whole structure.
540	 *
541	 * XXX: That's what the spec says - but it breaks at least
542	 *      sbverify and "pesign -S", so the spec is probably wrong.
543	 */
544	pc->pc_len = x->x_signature_len;
545#else
546	pc->pc_len = pc_len;
547#endif
548	pc->pc_revision = PE_CERTIFICATE_REVISION;
549	pc->pc_type = PE_CERTIFICATE_TYPE;
550	memcpy(&pc->pc_signature, x->x_signature, x->x_signature_len);
551
552	pc_off = append(x, pc, pc_len, pc_aligment);
553#if 0
554	printf("added signature chunk at offset %zd, len %zd\n",
555	    pc_off, pc_len);
556#endif
557
558	free(pc);
559
560	pde.pde_rva = pc_off;
561	pde.pde_size = pc_len + pc_aligment;
562	memcpy(x->x_buf + x->x_certificate_entry_off, &pde, sizeof(pde));
563
564	checksum = compute_checksum(x);
565	assert(sizeof(checksum) == x->x_checksum_len);
566	memcpy(x->x_buf + x->x_checksum_off, &checksum, sizeof(checksum));
567#if 0
568	printf("new checksum 0x%x\n", checksum);
569#endif
570}
571