]>
git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - scripts/insert-sys-cert.c
1 /* Write the contents of the <certfile> into kernel symbol system_extra_cert
3 * Copyright (C) IBM Corporation, 2015
5 * Author: Mehmet Kayaalp <mkayaalp@linux.vnet.ibm.com>
7 * This software may be used and distributed according to the terms
8 * of the GNU General Public License, incorporated herein by reference.
10 * Usage: insert-sys-cert [-s <System.map>] -b <vmlinux> -c <certfile>
11 * [-s <System.map>] -z <bzImage> -c <certfile>
23 #include <sys/types.h>
30 #define CERT_SYM "system_extra_cert"
31 #define USED_SYM "system_extra_cert_used"
32 #define LSIZE_SYM "system_certificate_list_size"
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)
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
44 #define CURRENT_ELFCLASS ELFCLASS64
45 #define Elf_Ehdr Elf64_Ehdr
46 #define Elf_Shdr Elf64_Shdr
47 #define Elf_Sym Elf64_Sym
50 static unsigned char endianness(void)
52 uint16_t two_byte
= 0x00FF;
53 uint8_t low_address
= *((uint8_t *)&two_byte
);
63 unsigned long address
;
69 static unsigned long get_offset_from_address(Elf_Ehdr
*hdr
, unsigned long addr
)
72 unsigned int i
, num_sections
;
74 x
= (void *)hdr
+ hdr
->e_shoff
;
75 if (hdr
->e_shnum
== SHN_UNDEF
)
76 num_sections
= x
[0].sh_size
;
78 num_sections
= hdr
->e_shnum
;
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
;
85 if (addr
>= start
&& addr
<= end
)
86 return addr
- start
+ offset
;
94 static void get_symbol_from_map(Elf_Ehdr
*hdr
, FILE *f
, char *name
,
103 if (fseek(f
, 0, SEEK_SET
) != 0) {
104 perror("File seek failed");
107 while (fgets(l
, LINE_SIZE
, f
)) {
110 err("Missing line ending.\n");
118 err("Unable to find symbol: %s\n", name
);
126 s
->address
= strtoul(l
, NULL
, 16);
129 s
->offset
= get_offset_from_address(hdr
, s
->address
);
131 s
->content
= (void *)hdr
+ s
->offset
;
134 static Elf_Sym
*find_elf_symbol(Elf_Ehdr
*hdr
, Elf_Shdr
*symtab
, char *name
)
136 Elf_Sym
*sym
, *symtab_start
;
137 char *strtab
, *symname
;
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
;
148 for (i
= 0; i
< n
; i
++) {
149 sym
= &symtab_start
[i
];
150 symname
= strtab
+ sym
->st_name
;
151 if (strcmp(symname
, name
) == 0)
154 err("Unable to find symbol: %s\n", name
);
158 static void get_symbol_from_table(Elf_Ehdr
*hdr
, Elf_Shdr
*symtab
,
159 char *name
, struct sym
*s
)
166 x
= (void *)hdr
+ hdr
->e_shoff
;
170 elf_sym
= find_elf_symbol(hdr
, symtab
, name
);
173 secndx
= elf_sym
->st_shndx
;
177 s
->size
= elf_sym
->st_size
;
178 s
->address
= elf_sym
->st_value
;
179 s
->offset
= s
->address
- sec
->sh_addr
182 s
->content
= (void *)hdr
+ s
->offset
;
185 static Elf_Shdr
*get_symbol_table(Elf_Ehdr
*hdr
)
188 unsigned int i
, num_sections
;
190 x
= (void *)hdr
+ hdr
->e_shoff
;
191 if (hdr
->e_shnum
== SHN_UNDEF
)
192 num_sections
= x
[0].sh_size
;
194 num_sections
= hdr
->e_shnum
;
196 for (i
= 1; i
< num_sections
; i
++)
197 if (x
[i
].sh_type
== SHT_SYMTAB
)
202 static void *map_file(char *file_name
, int *size
)
208 fd
= open(file_name
, O_RDWR
);
213 if (fstat(fd
, &st
)) {
214 perror("Could not determine file 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");
229 static char *read_file(char *file_name
, int *size
)
235 fd
= open(file_name
, O_RDONLY
);
240 if (fstat(fd
, &st
)) {
241 perror("Could not determine file size");
248 perror("Allocating memory failed");
252 if (read(fd
, buf
, *size
) != *size
) {
253 perror("File read failed");
261 static void get_payload_info(char *bzimage
, int *offset
, int *size
)
263 unsigned int system_offset
;
264 unsigned char setup_sectors
;
266 setup_sectors
= bzimage
[0x1f1] + 1;
267 system_offset
= setup_sectors
* 512;
268 *offset
= system_offset
+ *((int*)&bzimage
[0x248]);
269 *size
= *((int*)&bzimage
[0x24c]);
272 static void update_payload_info(char* bzimage
, int new_size
)
275 get_payload_info(bzimage
, &offset
, &size
);
276 *((int*)&bzimage
[0x24c]) = new_size
;
278 memset(bzimage
+ offset
+ new_size
, 0, size
- new_size
);
282 unsigned char pattern
[10];
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"}
297 static struct zipper
* get_zipper(char *p
) {
299 for (i
= 0; i
< sizeof(zippers
)/sizeof(struct zipper
); i
++) {
300 if (memcmp(p
, zippers
[i
].pattern
, zippers
[i
].length
) == 0)
307 * This only works for x86 bzImage
309 static void extract_vmlinux(char *bzimage
, int bzimage_size
,
310 char **file
, struct zipper
**zipper
)
313 char src
[15] = "vmlinux-XXXXXX";
314 char dest
[15] = "vmlinux-XXXXXX";
320 /* TODO: verify that bzImage is supported */
322 get_payload_info(bzimage
, &offset
, &size
);
323 z
= get_zipper(bzimage
+ offset
);
325 err("Unable to determine the compression of vmlinux\n");
329 src_fd
= mkstemp(src
);
331 perror("Could not create temp file");
335 r
= write(src_fd
, bzimage
+ offset
, size
);
337 perror("Could not write vmlinux");
340 dest_fd
= mkstemp(dest
);
342 perror("Could not create temp file");
346 snprintf(cmd
, sizeof(cmd
), "%s <%s >%s", z
->command
, src
, dest
);
347 info("Executing: %s\n", cmd
);
350 warn("Possible errors when extracting\n");
356 *file
= strdup(dest
);
360 static void repack_image(char *bzimage
, int bzimage_size
,
361 char* vmlinux_file
, struct zipper
*z
)
363 char tmp
[15] = "vmlinux-XXXXXX";
371 get_payload_info(bzimage
, &offset
, &size
);
375 perror("Could not create temp file");
378 snprintf(cmd
, sizeof(cmd
), "%s <%s >%s",
379 z
->compress
, vmlinux_file
, tmp
);
381 info("Executing: %s\n", cmd
);
384 warn("Possible errors when compressing\n");
386 r
= remove(vmlinux_file
);
388 perror(vmlinux_file
);
390 if (fstat(fd
, &st
)) {
391 perror("Could not determine file size");
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
);
402 r
= read(fd
, bzimage
+ offset
, new_size
);
410 /* x86 specific patching of bzimage */
411 update_payload_info(bzimage
, new_size
);
413 /* TODO: update CRC */
417 static void fill_random(unsigned char *p
, int n
) {
420 for (i
= 0; i
< n
; i
++)
424 static void print_sym(Elf_Ehdr
*hdr
, struct sym
*s
)
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
);
432 static void print_usage(char *e
)
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
);
438 int main(int argc
, char **argv
)
440 char *system_map_file
= NULL
;
441 char *vmlinux_file
= NULL
;
442 char *bzimage_file
= NULL
;
443 char *cert_file
= NULL
;
449 char *bzimage
= NULL
;
450 struct zipper
*z
= NULL
;
452 unsigned long *lsize
;
455 Elf_Shdr
*symtab
= NULL
;
456 struct sym cert_sym
, lsize_sym
, used_sym
;
458 while ((opt
= getopt(argc
, argv
, "b:z:c:s:")) != -1) {
461 system_map_file
= optarg
;
464 vmlinux_file
= optarg
;
467 bzimage_file
= optarg
;
478 (!vmlinux_file
&& !bzimage_file
) ||
479 (vmlinux_file
&& bzimage_file
)) {
480 print_usage(argv
[0]);
484 cert
= read_file(cert_file
, &cert_size
);
489 bzimage
= map_file(bzimage_file
, &bzimage_size
);
493 extract_vmlinux(bzimage
, bzimage_size
, &vmlinux_file
, &z
);
498 hdr
= map_file(vmlinux_file
, &vmlinux_size
);
502 if (vmlinux_size
< sizeof(*hdr
)) {
503 err("Invalid ELF file.\n");
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");
515 if (hdr
->e_ident
[EI_CLASS
] != CURRENT_ELFCLASS
) {
516 err("ELF class mismatch.\n");
520 if (hdr
->e_ident
[EI_DATA
] != endianness()) {
521 err("ELF endian mismatch.\n");
525 if (hdr
->e_shoff
> vmlinux_size
) {
526 err("Could not find section header.\n");
530 symtab
= get_symbol_table(hdr
);
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]);
539 system_map
= fopen(system_map_file
, "r");
541 perror(system_map_file
);
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
;
549 info("Symbol table found.\n");
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
);
557 if (!cert_sym
.offset
|| !lsize_sym
.offset
|| !used_sym
.offset
)
560 print_sym(hdr
, &cert_sym
);
561 print_sym(hdr
, &used_sym
);
562 print_sym(hdr
, &lsize_sym
);
564 lsize
= (unsigned long *)lsize_sym
.content
;
565 used
= (int *)used_sym
.content
;
567 if (cert_sym
.size
< cert_size
) {
568 err("Certificate is larger than the reserved area!\n");
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");
580 warn("Replacing previously inserted certificate.\n");
582 memcpy(cert_sym
.content
, cert
, cert_size
);
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
);
589 *lsize
= *lsize
+ cert_size
- *used
;
591 info("Inserted the contents of %s into %lx.\n", cert_file
,
593 info("Used %d bytes out of %d bytes reserved.\n", *used
,
596 if (munmap(hdr
, vmlinux_size
) == -1) {
597 perror(vmlinux_file
);
602 repack_image(bzimage
, bzimage_size
, vmlinux_file
, z
);