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