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