]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - scripts/insert-sys-cert.c
UBUNTU: Ubuntu-4.15.0-96.97
[mirror_ubuntu-bionic-kernel.git] / scripts / insert-sys-cert.c
1 /* Write the contents of the <certfile> into kernel symbol system_extra_cert
2 *
3 * Copyright (C) IBM Corporation, 2015
4 *
5 * Author: Mehmet Kayaalp <mkayaalp@linux.vnet.ibm.com>
6 *
7 * This software may be used and distributed according to the terms
8 * of the GNU General Public License, incorporated herein by reference.
9 *
10 * Usage: insert-sys-cert [-s <System.map>] -b <vmlinux> -c <certfile>
11 * [-s <System.map>] -z <bzImage> -c <certfile>
12 */
13
14 #define _GNU_SOURCE
15 #include <stdio.h>
16 #include <ctype.h>
17 #include <string.h>
18 #include <limits.h>
19 #include <stdbool.h>
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/mman.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <elf.h>
29
30 #define CERT_SYM "system_extra_cert"
31 #define USED_SYM "system_extra_cert_used"
32 #define LSIZE_SYM "system_certificate_list_size"
33
34 #define info(format, args...) fprintf(stderr, "INFO: " format, ## args)
35 #define warn(format, args...) fprintf(stdout, "WARNING: " format, ## args)
36 #define err(format, args...) fprintf(stderr, "ERROR: " format, ## args)
37
38 #if UINTPTR_MAX == 0xffffffff
39 #define CURRENT_ELFCLASS ELFCLASS32
40 #define Elf_Ehdr Elf32_Ehdr
41 #define Elf_Shdr Elf32_Shdr
42 #define Elf_Sym Elf32_Sym
43 #else
44 #define CURRENT_ELFCLASS ELFCLASS64
45 #define Elf_Ehdr Elf64_Ehdr
46 #define Elf_Shdr Elf64_Shdr
47 #define Elf_Sym Elf64_Sym
48 #endif
49
50 static unsigned char endianness(void)
51 {
52 uint16_t two_byte = 0x00FF;
53 uint8_t low_address = *((uint8_t *)&two_byte);
54
55 if (low_address == 0)
56 return ELFDATA2MSB;
57 else
58 return ELFDATA2LSB;
59 }
60
61 struct sym {
62 char *name;
63 unsigned long address;
64 unsigned long offset;
65 void *content;
66 int size;
67 };
68
69 static unsigned long get_offset_from_address(Elf_Ehdr *hdr, unsigned long addr)
70 {
71 Elf_Shdr *x;
72 unsigned int i, num_sections;
73
74 x = (void *)hdr + hdr->e_shoff;
75 if (hdr->e_shnum == SHN_UNDEF)
76 num_sections = x[0].sh_size;
77 else
78 num_sections = hdr->e_shnum;
79
80 for (i = 1; i < num_sections; i++) {
81 unsigned long start = x[i].sh_addr;
82 unsigned long end = start + x[i].sh_size;
83 unsigned long offset = x[i].sh_offset;
84
85 if (addr >= start && addr <= end)
86 return addr - start + offset;
87 }
88 return 0;
89 }
90
91
92 #define LINE_SIZE 100
93
94 static void get_symbol_from_map(Elf_Ehdr *hdr, FILE *f, char *name,
95 struct sym *s)
96 {
97 char l[LINE_SIZE];
98 char *w, *p, *n;
99
100 s->size = 0;
101 s->address = 0;
102 s->offset = 0;
103 if (fseek(f, 0, SEEK_SET) != 0) {
104 perror("File seek failed");
105 exit(EXIT_FAILURE);
106 }
107 while (fgets(l, LINE_SIZE, f)) {
108 p = strchr(l, '\n');
109 if (!p) {
110 err("Missing line ending.\n");
111 return;
112 }
113 n = strstr(l, name);
114 if (n)
115 break;
116 }
117 if (!n) {
118 err("Unable to find symbol: %s\n", name);
119 return;
120 }
121 w = strchr(l, ' ');
122 if (!w)
123 return;
124
125 *w = '\0';
126 s->address = strtoul(l, NULL, 16);
127 if (s->address == 0)
128 return;
129 s->offset = get_offset_from_address(hdr, s->address);
130 s->name = name;
131 s->content = (void *)hdr + s->offset;
132 }
133
134 static Elf_Sym *find_elf_symbol(Elf_Ehdr *hdr, Elf_Shdr *symtab, char *name)
135 {
136 Elf_Sym *sym, *symtab_start;
137 char *strtab, *symname;
138 unsigned int link;
139 Elf_Shdr *x;
140 int i, n;
141
142 x = (void *)hdr + hdr->e_shoff;
143 link = symtab->sh_link;
144 symtab_start = (void *)hdr + symtab->sh_offset;
145 n = symtab->sh_size / symtab->sh_entsize;
146 strtab = (void *)hdr + x[link].sh_offset;
147
148 for (i = 0; i < n; i++) {
149 sym = &symtab_start[i];
150 symname = strtab + sym->st_name;
151 if (strcmp(symname, name) == 0)
152 return sym;
153 }
154 err("Unable to find symbol: %s\n", name);
155 return NULL;
156 }
157
158 static void get_symbol_from_table(Elf_Ehdr *hdr, Elf_Shdr *symtab,
159 char *name, struct sym *s)
160 {
161 Elf_Shdr *sec;
162 int secndx;
163 Elf_Sym *elf_sym;
164 Elf_Shdr *x;
165
166 x = (void *)hdr + hdr->e_shoff;
167 s->size = 0;
168 s->address = 0;
169 s->offset = 0;
170 elf_sym = find_elf_symbol(hdr, symtab, name);
171 if (!elf_sym)
172 return;
173 secndx = elf_sym->st_shndx;
174 if (!secndx)
175 return;
176 sec = &x[secndx];
177 s->size = elf_sym->st_size;
178 s->address = elf_sym->st_value;
179 s->offset = s->address - sec->sh_addr
180 + sec->sh_offset;
181 s->name = name;
182 s->content = (void *)hdr + s->offset;
183 }
184
185 static Elf_Shdr *get_symbol_table(Elf_Ehdr *hdr)
186 {
187 Elf_Shdr *x;
188 unsigned int i, num_sections;
189
190 x = (void *)hdr + hdr->e_shoff;
191 if (hdr->e_shnum == SHN_UNDEF)
192 num_sections = x[0].sh_size;
193 else
194 num_sections = hdr->e_shnum;
195
196 for (i = 1; i < num_sections; i++)
197 if (x[i].sh_type == SHT_SYMTAB)
198 return &x[i];
199 return NULL;
200 }
201
202 static void *map_file(char *file_name, int *size)
203 {
204 struct stat st;
205 void *map;
206 int fd;
207
208 fd = open(file_name, O_RDWR);
209 if (fd < 0) {
210 perror(file_name);
211 return NULL;
212 }
213 if (fstat(fd, &st)) {
214 perror("Could not determine file size");
215 close(fd);
216 return NULL;
217 }
218 *size = st.st_size;
219 map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
220 if (map == MAP_FAILED) {
221 perror("Mapping to memory failed");
222 close(fd);
223 return NULL;
224 }
225 close(fd);
226 return map;
227 }
228
229 static char *read_file(char *file_name, int *size)
230 {
231 struct stat st;
232 char *buf;
233 int fd;
234
235 fd = open(file_name, O_RDONLY);
236 if (fd < 0) {
237 perror(file_name);
238 return NULL;
239 }
240 if (fstat(fd, &st)) {
241 perror("Could not determine file size");
242 close(fd);
243 return NULL;
244 }
245 *size = st.st_size;
246 buf = malloc(*size);
247 if (!buf) {
248 perror("Allocating memory failed");
249 close(fd);
250 return NULL;
251 }
252 if (read(fd, buf, *size) != *size) {
253 perror("File read failed");
254 close(fd);
255 return NULL;
256 }
257 close(fd);
258 return buf;
259 }
260
261 static void get_payload_info(char *bzimage, int *offset, int *size)
262 {
263 unsigned int system_offset;
264 unsigned char setup_sectors;
265
266 setup_sectors = bzimage[0x1f1] + 1;
267 system_offset = setup_sectors * 512;
268 *offset = system_offset + *((int*)&bzimage[0x248]);
269 *size = *((int*)&bzimage[0x24c]);
270 }
271
272 static void update_payload_info(char* bzimage, int new_size)
273 {
274 int offset, size;
275 get_payload_info(bzimage, &offset, &size);
276 *((int*)&bzimage[0x24c]) = new_size;
277 if (new_size < size)
278 memset(bzimage + offset + new_size, 0, size - new_size);
279 }
280
281 struct zipper {
282 unsigned char pattern[10];
283 int length;
284 char *command;
285 char *compress;
286 };
287
288 struct zipper zippers[] = {
289 {{0x7F,'E','L','F'}, 4, "cat", "cat"},
290 {{0x1F,0x8B}, 2, "gunzip", "gzip -n -f -9"},
291 {{0xFD,'7','z','X','Z',0}, 6, "unxz", "xz"},
292 {{'B','Z','h'},3, "bunzip2", "bzip2 -9"},
293 {{0xFF,'L','Z','M','A',0}, 6, "unlzma", "lzma -9"},
294 {{0xD3,'L','Z','O',0,'\r','\n',0x20,'\n'}, 9, "lzop -d", "lzop -9"}
295 };
296
297 static struct zipper* get_zipper(char *p) {
298 int i;
299 for (i = 0; i < sizeof(zippers)/sizeof(struct zipper); i++) {
300 if (memcmp(p, zippers[i].pattern, zippers[i].length) == 0)
301 return &zippers[i];
302 }
303 return NULL;
304 }
305
306 /*
307 * This only works for x86 bzImage
308 */
309 static void extract_vmlinux(char *bzimage, int bzimage_size,
310 char **file, struct zipper **zipper)
311 {
312 int r;
313 char src[15] = "vmlinux-XXXXXX";
314 char dest[15] = "vmlinux-XXXXXX";
315 char cmd[100];
316 int src_fd, dest_fd;
317 int offset, size;
318 struct zipper *z;
319
320 /* TODO: verify that bzImage is supported */
321
322 get_payload_info(bzimage, &offset, &size);
323 z = get_zipper(bzimage + offset);
324 if (z == NULL) {
325 err("Unable to determine the compression of vmlinux\n");
326 return;
327 }
328
329 src_fd = mkstemp(src);
330 if (src_fd == -1) {
331 perror("Could not create temp file");
332 return;
333 }
334
335 r = write(src_fd, bzimage + offset, size);
336 if (r != size) {
337 perror("Could not write vmlinux");
338 return;
339 }
340 dest_fd = mkstemp(dest);
341 if (dest_fd == -1) {
342 perror("Could not create temp file");
343 return;
344 }
345
346 snprintf(cmd, sizeof(cmd), "%s <%s >%s", z->command, src, dest);
347 info("Executing: %s\n", cmd);
348 r = system(cmd);
349 if (r!=0)
350 warn("Possible errors when extracting\n");
351
352 r = remove(src);
353 if (r!=0)
354 perror(src);
355
356 *file = strdup(dest);
357 *zipper = z;
358 }
359
360 static void repack_image(char *bzimage, int bzimage_size,
361 char* vmlinux_file, struct zipper *z)
362 {
363 char tmp[15] = "vmlinux-XXXXXX";
364 char cmd[100];
365 int fd;
366 struct stat st;
367 int new_size;
368 int r;
369 int offset, size;
370
371 get_payload_info(bzimage, &offset, &size);
372
373 fd = mkstemp(tmp);
374 if (fd == -1) {
375 perror("Could not create temp file");
376 return;
377 }
378 snprintf(cmd, sizeof(cmd), "%s <%s >%s",
379 z->compress, vmlinux_file, tmp);
380
381 info("Executing: %s\n", cmd);
382 r = system(cmd);
383 if (r!=0)
384 warn("Possible errors when compressing\n");
385
386 r = remove(vmlinux_file);
387 if (r!=0)
388 perror(vmlinux_file);
389
390 if (fstat(fd, &st)) {
391 perror("Could not determine file size");
392 close(fd);
393
394 }
395 new_size = st.st_size;
396 if (new_size > size) {
397 err("Increase in compressed size is not supported.\n");
398 err("Old size was %d, new size is %d\n", size, new_size);
399 exit(EXIT_FAILURE);
400 }
401
402 r = read(fd, bzimage + offset, new_size);
403 if (r != new_size)
404 perror(tmp);
405
406 r = remove(tmp);
407 if (r!=0)
408 perror(tmp);
409
410 /* x86 specific patching of bzimage */
411 update_payload_info(bzimage, new_size);
412
413 /* TODO: update CRC */
414
415 }
416
417 static void fill_random(unsigned char *p, int n) {
418 srand(0);
419 int i;
420 for (i = 0; i < n; i++)
421 p[i] = rand();
422 }
423
424 static void print_sym(Elf_Ehdr *hdr, struct sym *s)
425 {
426 info("sym: %s\n", s->name);
427 info("addr: 0x%lx\n", s->address);
428 info("size: %d\n", s->size);
429 info("offset: 0x%lx\n", (unsigned long)s->offset);
430 }
431
432 static void print_usage(char *e)
433 {
434 printf("Usage: %s [-s <System.map>] -b <vmlinux> -c <certfile>\n", e);
435 printf(" %s [-s <System.map>] -z <bzImage> -c <certfile>\n", e);
436 }
437
438 int main(int argc, char **argv)
439 {
440 char *system_map_file = NULL;
441 char *vmlinux_file = NULL;
442 char *bzimage_file = NULL;
443 char *cert_file = NULL;
444 int vmlinux_size;
445 int bzimage_size;
446 int cert_size;
447 Elf_Ehdr *hdr;
448 char *cert;
449 char *bzimage = NULL;
450 struct zipper *z = NULL;
451 FILE *system_map;
452 unsigned long *lsize;
453 int *used;
454 int opt;
455 Elf_Shdr *symtab = NULL;
456 struct sym cert_sym, lsize_sym, used_sym;
457
458 while ((opt = getopt(argc, argv, "b:z:c:s:")) != -1) {
459 switch (opt) {
460 case 's':
461 system_map_file = optarg;
462 break;
463 case 'b':
464 vmlinux_file = optarg;
465 break;
466 case 'z':
467 bzimage_file = optarg;
468 break;
469 case 'c':
470 cert_file = optarg;
471 break;
472 default:
473 break;
474 }
475 }
476
477 if (!cert_file ||
478 (!vmlinux_file && !bzimage_file) ||
479 (vmlinux_file && bzimage_file)) {
480 print_usage(argv[0]);
481 exit(EXIT_FAILURE);
482 }
483
484 cert = read_file(cert_file, &cert_size);
485 if (!cert)
486 exit(EXIT_FAILURE);
487
488 if (bzimage_file) {
489 bzimage = map_file(bzimage_file, &bzimage_size);
490 if (!bzimage)
491 exit(EXIT_FAILURE);
492
493 extract_vmlinux(bzimage, bzimage_size, &vmlinux_file, &z);
494 if (!vmlinux_file)
495 exit(EXIT_FAILURE);
496 }
497
498 hdr = map_file(vmlinux_file, &vmlinux_size);
499 if (!hdr)
500 exit(EXIT_FAILURE);
501
502 if (vmlinux_size < sizeof(*hdr)) {
503 err("Invalid ELF file.\n");
504 exit(EXIT_FAILURE);
505 }
506
507 if ((hdr->e_ident[EI_MAG0] != ELFMAG0) ||
508 (hdr->e_ident[EI_MAG1] != ELFMAG1) ||
509 (hdr->e_ident[EI_MAG2] != ELFMAG2) ||
510 (hdr->e_ident[EI_MAG3] != ELFMAG3)) {
511 err("Invalid ELF magic.\n");
512 exit(EXIT_FAILURE);
513 }
514
515 if (hdr->e_ident[EI_CLASS] != CURRENT_ELFCLASS) {
516 err("ELF class mismatch.\n");
517 exit(EXIT_FAILURE);
518 }
519
520 if (hdr->e_ident[EI_DATA] != endianness()) {
521 err("ELF endian mismatch.\n");
522 exit(EXIT_FAILURE);
523 }
524
525 if (hdr->e_shoff > vmlinux_size) {
526 err("Could not find section header.\n");
527 exit(EXIT_FAILURE);
528 }
529
530 symtab = get_symbol_table(hdr);
531 if (!symtab) {
532 warn("Could not find the symbol table.\n");
533 if (!system_map_file) {
534 err("Please provide a System.map file.\n");
535 print_usage(argv[0]);
536 exit(EXIT_FAILURE);
537 }
538
539 system_map = fopen(system_map_file, "r");
540 if (!system_map) {
541 perror(system_map_file);
542 exit(EXIT_FAILURE);
543 }
544 get_symbol_from_map(hdr, system_map, CERT_SYM, &cert_sym);
545 get_symbol_from_map(hdr, system_map, USED_SYM, &used_sym);
546 get_symbol_from_map(hdr, system_map, LSIZE_SYM, &lsize_sym);
547 cert_sym.size = used_sym.address - cert_sym.address;
548 } else {
549 info("Symbol table found.\n");
550 if (system_map_file)
551 warn("System.map is ignored.\n");
552 get_symbol_from_table(hdr, symtab, CERT_SYM, &cert_sym);
553 get_symbol_from_table(hdr, symtab, USED_SYM, &used_sym);
554 get_symbol_from_table(hdr, symtab, LSIZE_SYM, &lsize_sym);
555 }
556
557 if (!cert_sym.offset || !lsize_sym.offset || !used_sym.offset)
558 exit(EXIT_FAILURE);
559
560 print_sym(hdr, &cert_sym);
561 print_sym(hdr, &used_sym);
562 print_sym(hdr, &lsize_sym);
563
564 lsize = (unsigned long *)lsize_sym.content;
565 used = (int *)used_sym.content;
566
567 if (cert_sym.size < cert_size) {
568 err("Certificate is larger than the reserved area!\n");
569 exit(EXIT_FAILURE);
570 }
571
572 /* If the existing cert is the same, don't overwrite */
573 if (cert_size > 0 && cert_size == *used &&
574 strncmp(cert_sym.content, cert, cert_size) == 0) {
575 warn("Certificate was already inserted.\n");
576 exit(EXIT_SUCCESS);
577 }
578
579 if (*used > 0)
580 warn("Replacing previously inserted certificate.\n");
581
582 memcpy(cert_sym.content, cert, cert_size);
583
584 if (cert_size < cert_sym.size)
585 /* This makes the reserved space incompressable */
586 fill_random(cert_sym.content + cert_size,
587 cert_sym.size - cert_size);
588
589 *lsize = *lsize + cert_size - *used;
590 *used = cert_size;
591 info("Inserted the contents of %s into %lx.\n", cert_file,
592 cert_sym.address);
593 info("Used %d bytes out of %d bytes reserved.\n", *used,
594 cert_sym.size);
595
596 if (munmap(hdr, vmlinux_size) == -1) {
597 perror(vmlinux_file);
598 exit(EXIT_FAILURE);
599 }
600
601 if (bzimage) {
602 repack_image(bzimage, bzimage_size, vmlinux_file, z);
603 }
604
605 exit(EXIT_SUCCESS);
606 }