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