Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KEYS: Reserve an extra certificate symbol for inserting without recompiling

Place a system_extra_cert buffer of configurable size, right after the
system_certificate_list, so that inserted keys can be readily processed by
the existing mechanism. Added script takes a key file and a kernel image
and inserts its contents to the reserved area. The
system_certificate_list_size is also adjusted accordingly.

Call the script as:

scripts/insert-sys-cert -b <vmlinux> -c <certfile>

If vmlinux has no symbol table, supply System.map file with -s flag.
Subsequent runs replace the previously inserted key, instead of appending
the new one.

Signed-off-by: Mehmet Kayaalp <mkayaalp@linux.vnet.ibm.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Mimi Zohar <zohar@linux.vnet.ibm.com>

authored by

Mehmet Kayaalp and committed by
David Howells
c4c36105 5d06ee20

+440
+16
certs/Kconfig
··· 39 39 form of DER-encoded *.x509 files in the top-level build directory, 40 40 those are no longer used. You will need to set this option instead. 41 41 42 + config SYSTEM_EXTRA_CERTIFICATE 43 + bool "Reserve area for inserting a certificate without recompiling" 44 + depends on SYSTEM_TRUSTED_KEYRING 45 + help 46 + If set, space for an extra certificate will be reserved in the kernel 47 + image. This allows introducing a trusted certificate to the default 48 + system keyring without recompiling the kernel. 49 + 50 + config SYSTEM_EXTRA_CERTIFICATE_SIZE 51 + int "Number of bytes to reserve for the extra certificate" 52 + depends on SYSTEM_EXTRA_CERTIFICATE 53 + default 4096 54 + help 55 + This is the number of bytes reserved in the kernel image for a 56 + certificate to be inserted. 57 + 42 58 endmenu
+12
certs/system_certificates.S
··· 13 13 .incbin "certs/x509_certificate_list" 14 14 __cert_list_end: 15 15 16 + #ifdef CONFIG_SYSTEM_EXTRA_CERTIFICATE 17 + .globl VMLINUX_SYMBOL(system_extra_cert) 18 + .size system_extra_cert, CONFIG_SYSTEM_EXTRA_CERTIFICATE_SIZE 19 + VMLINUX_SYMBOL(system_extra_cert): 20 + .fill CONFIG_SYSTEM_EXTRA_CERTIFICATE_SIZE, 1, 0 21 + 22 + .globl VMLINUX_SYMBOL(system_extra_cert_used) 23 + VMLINUX_SYMBOL(system_extra_cert_used): 24 + .int 0 25 + 26 + #endif /* CONFIG_SYSTEM_EXTRA_CERTIFICATE */ 27 + 16 28 .align 8 17 29 .globl VMLINUX_SYMBOL(system_certificate_list_size) 18 30 VMLINUX_SYMBOL(system_certificate_list_size):
+1
scripts/.gitignore
··· 13 13 asn1_compiler 14 14 extract-cert 15 15 sign-file 16 + insert-sys-cert
+1
scripts/Makefile
··· 19 19 hostprogs-$(CONFIG_ASN1) += asn1_compiler 20 20 hostprogs-$(CONFIG_MODULE_SIG) += sign-file 21 21 hostprogs-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += extract-cert 22 + hostprogs-$(CONFIG_SYSTEM_EXTRA_CERTIFICATE) += insert-sys-cert 22 23 23 24 HOSTCFLAGS_sortextable.o = -I$(srctree)/tools/include 24 25 HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include
+410
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 + */ 12 + 13 + #define _GNU_SOURCE 14 + #include <stdio.h> 15 + #include <ctype.h> 16 + #include <string.h> 17 + #include <limits.h> 18 + #include <stdbool.h> 19 + #include <errno.h> 20 + #include <stdlib.h> 21 + #include <stdarg.h> 22 + #include <sys/types.h> 23 + #include <sys/stat.h> 24 + #include <sys/mman.h> 25 + #include <fcntl.h> 26 + #include <unistd.h> 27 + #include <elf.h> 28 + 29 + #define CERT_SYM "system_extra_cert" 30 + #define USED_SYM "system_extra_cert_used" 31 + #define LSIZE_SYM "system_certificate_list_size" 32 + 33 + #define info(format, args...) fprintf(stderr, "INFO: " format, ## args) 34 + #define warn(format, args...) fprintf(stdout, "WARNING: " format, ## args) 35 + #define err(format, args...) fprintf(stderr, "ERROR: " format, ## args) 36 + 37 + #if UINTPTR_MAX == 0xffffffff 38 + #define CURRENT_ELFCLASS ELFCLASS32 39 + #define Elf_Ehdr Elf32_Ehdr 40 + #define Elf_Shdr Elf32_Shdr 41 + #define Elf_Sym Elf32_Sym 42 + #else 43 + #define CURRENT_ELFCLASS ELFCLASS64 44 + #define Elf_Ehdr Elf64_Ehdr 45 + #define Elf_Shdr Elf64_Shdr 46 + #define Elf_Sym Elf64_Sym 47 + #endif 48 + 49 + static unsigned char endianness(void) 50 + { 51 + uint16_t two_byte = 0x00FF; 52 + uint8_t low_address = *((uint8_t *)&two_byte); 53 + 54 + if (low_address == 0) 55 + return ELFDATA2MSB; 56 + else 57 + return ELFDATA2LSB; 58 + } 59 + 60 + struct sym { 61 + char *name; 62 + unsigned long address; 63 + unsigned long offset; 64 + void *content; 65 + int size; 66 + }; 67 + 68 + static unsigned long get_offset_from_address(Elf_Ehdr *hdr, unsigned long addr) 69 + { 70 + Elf_Shdr *x; 71 + unsigned int i, num_sections; 72 + 73 + x = (void *)hdr + hdr->e_shoff; 74 + if (hdr->e_shnum == SHN_UNDEF) 75 + num_sections = x[0].sh_size; 76 + else 77 + num_sections = hdr->e_shnum; 78 + 79 + for (i = 1; i < num_sections; i++) { 80 + unsigned long start = x[i].sh_addr; 81 + unsigned long end = start + x[i].sh_size; 82 + unsigned long offset = x[i].sh_offset; 83 + 84 + if (addr >= start && addr <= end) 85 + return addr - start + offset; 86 + } 87 + return 0; 88 + } 89 + 90 + 91 + #define LINE_SIZE 100 92 + 93 + static void get_symbol_from_map(Elf_Ehdr *hdr, FILE *f, char *name, 94 + struct sym *s) 95 + { 96 + char l[LINE_SIZE]; 97 + char *w, *p, *n; 98 + 99 + s->size = 0; 100 + s->address = 0; 101 + s->offset = 0; 102 + if (fseek(f, 0, SEEK_SET) != 0) { 103 + perror("File seek failed"); 104 + exit(EXIT_FAILURE); 105 + } 106 + while (fgets(l, LINE_SIZE, f)) { 107 + p = strchr(l, '\n'); 108 + if (!p) { 109 + err("Missing line ending.\n"); 110 + return; 111 + } 112 + n = strstr(l, name); 113 + if (n) 114 + break; 115 + } 116 + if (!n) { 117 + err("Unable to find symbol: %s\n", name); 118 + return; 119 + } 120 + w = strchr(l, ' '); 121 + if (!w) 122 + return; 123 + 124 + *w = '\0'; 125 + s->address = strtoul(l, NULL, 16); 126 + if (s->address == 0) 127 + return; 128 + s->offset = get_offset_from_address(hdr, s->address); 129 + s->name = name; 130 + s->content = (void *)hdr + s->offset; 131 + } 132 + 133 + static Elf_Sym *find_elf_symbol(Elf_Ehdr *hdr, Elf_Shdr *symtab, char *name) 134 + { 135 + Elf_Sym *sym, *symtab_start; 136 + char *strtab, *symname; 137 + unsigned int link; 138 + Elf_Shdr *x; 139 + int i, n; 140 + 141 + x = (void *)hdr + hdr->e_shoff; 142 + link = symtab->sh_link; 143 + symtab_start = (void *)hdr + symtab->sh_offset; 144 + n = symtab->sh_size / symtab->sh_entsize; 145 + strtab = (void *)hdr + x[link].sh_offset; 146 + 147 + for (i = 0; i < n; i++) { 148 + sym = &symtab_start[i]; 149 + symname = strtab + sym->st_name; 150 + if (strcmp(symname, name) == 0) 151 + return sym; 152 + } 153 + err("Unable to find symbol: %s\n", name); 154 + return NULL; 155 + } 156 + 157 + static void get_symbol_from_table(Elf_Ehdr *hdr, Elf_Shdr *symtab, 158 + char *name, struct sym *s) 159 + { 160 + Elf_Shdr *sec; 161 + int secndx; 162 + Elf_Sym *elf_sym; 163 + Elf_Shdr *x; 164 + 165 + x = (void *)hdr + hdr->e_shoff; 166 + s->size = 0; 167 + s->address = 0; 168 + s->offset = 0; 169 + elf_sym = find_elf_symbol(hdr, symtab, name); 170 + if (!elf_sym) 171 + return; 172 + secndx = elf_sym->st_shndx; 173 + if (!secndx) 174 + return; 175 + sec = &x[secndx]; 176 + s->size = elf_sym->st_size; 177 + s->address = elf_sym->st_value; 178 + s->offset = s->address - sec->sh_addr 179 + + sec->sh_offset; 180 + s->name = name; 181 + s->content = (void *)hdr + s->offset; 182 + } 183 + 184 + static Elf_Shdr *get_symbol_table(Elf_Ehdr *hdr) 185 + { 186 + Elf_Shdr *x; 187 + unsigned int i, num_sections; 188 + 189 + x = (void *)hdr + hdr->e_shoff; 190 + if (hdr->e_shnum == SHN_UNDEF) 191 + num_sections = x[0].sh_size; 192 + else 193 + num_sections = hdr->e_shnum; 194 + 195 + for (i = 1; i < num_sections; i++) 196 + if (x[i].sh_type == SHT_SYMTAB) 197 + return &x[i]; 198 + return NULL; 199 + } 200 + 201 + static void *map_file(char *file_name, int *size) 202 + { 203 + struct stat st; 204 + void *map; 205 + int fd; 206 + 207 + fd = open(file_name, O_RDWR); 208 + if (fd < 0) { 209 + perror(file_name); 210 + return NULL; 211 + } 212 + if (fstat(fd, &st)) { 213 + perror("Could not determine file size"); 214 + close(fd); 215 + return NULL; 216 + } 217 + *size = st.st_size; 218 + map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 219 + if (map == MAP_FAILED) { 220 + perror("Mapping to memory failed"); 221 + close(fd); 222 + return NULL; 223 + } 224 + close(fd); 225 + return map; 226 + } 227 + 228 + static char *read_file(char *file_name, int *size) 229 + { 230 + struct stat st; 231 + char *buf; 232 + int fd; 233 + 234 + fd = open(file_name, O_RDONLY); 235 + if (fd < 0) { 236 + perror(file_name); 237 + return NULL; 238 + } 239 + if (fstat(fd, &st)) { 240 + perror("Could not determine file size"); 241 + close(fd); 242 + return NULL; 243 + } 244 + *size = st.st_size; 245 + buf = malloc(*size); 246 + if (!buf) { 247 + perror("Allocating memory failed"); 248 + close(fd); 249 + return NULL; 250 + } 251 + if (read(fd, buf, *size) != *size) { 252 + perror("File read failed"); 253 + close(fd); 254 + return NULL; 255 + } 256 + close(fd); 257 + return buf; 258 + } 259 + 260 + static void print_sym(Elf_Ehdr *hdr, struct sym *s) 261 + { 262 + info("sym: %s\n", s->name); 263 + info("addr: 0x%lx\n", s->address); 264 + info("size: %d\n", s->size); 265 + info("offset: 0x%lx\n", (unsigned long)s->offset); 266 + } 267 + 268 + static void print_usage(char *e) 269 + { 270 + printf("Usage %s [-s <System.map>] -b <vmlinux> -c <certfile>\n", e); 271 + } 272 + 273 + int main(int argc, char **argv) 274 + { 275 + char *system_map_file = NULL; 276 + char *vmlinux_file = NULL; 277 + char *cert_file = NULL; 278 + int vmlinux_size; 279 + int cert_size; 280 + Elf_Ehdr *hdr; 281 + char *cert; 282 + FILE *system_map; 283 + unsigned long *lsize; 284 + int *used; 285 + int opt; 286 + Elf_Shdr *symtab = NULL; 287 + struct sym cert_sym, lsize_sym, used_sym; 288 + 289 + while ((opt = getopt(argc, argv, "b:c:s:")) != -1) { 290 + switch (opt) { 291 + case 's': 292 + system_map_file = optarg; 293 + break; 294 + case 'b': 295 + vmlinux_file = optarg; 296 + break; 297 + case 'c': 298 + cert_file = optarg; 299 + break; 300 + default: 301 + break; 302 + } 303 + } 304 + 305 + if (!vmlinux_file || !cert_file) { 306 + print_usage(argv[0]); 307 + exit(EXIT_FAILURE); 308 + } 309 + 310 + cert = read_file(cert_file, &cert_size); 311 + if (!cert) 312 + exit(EXIT_FAILURE); 313 + 314 + hdr = map_file(vmlinux_file, &vmlinux_size); 315 + if (!hdr) 316 + exit(EXIT_FAILURE); 317 + 318 + if (vmlinux_size < sizeof(*hdr)) { 319 + err("Invalid ELF file.\n"); 320 + exit(EXIT_FAILURE); 321 + } 322 + 323 + if ((hdr->e_ident[EI_MAG0] != ELFMAG0) || 324 + (hdr->e_ident[EI_MAG1] != ELFMAG1) || 325 + (hdr->e_ident[EI_MAG2] != ELFMAG2) || 326 + (hdr->e_ident[EI_MAG3] != ELFMAG3)) { 327 + err("Invalid ELF magic.\n"); 328 + exit(EXIT_FAILURE); 329 + } 330 + 331 + if (hdr->e_ident[EI_CLASS] != CURRENT_ELFCLASS) { 332 + err("ELF class mismatch.\n"); 333 + exit(EXIT_FAILURE); 334 + } 335 + 336 + if (hdr->e_ident[EI_DATA] != endianness()) { 337 + err("ELF endian mismatch.\n"); 338 + exit(EXIT_FAILURE); 339 + } 340 + 341 + if (hdr->e_shoff > vmlinux_size) { 342 + err("Could not find section header.\n"); 343 + exit(EXIT_FAILURE); 344 + } 345 + 346 + symtab = get_symbol_table(hdr); 347 + if (!symtab) { 348 + warn("Could not find the symbol table.\n"); 349 + if (!system_map_file) { 350 + err("Please provide a System.map file.\n"); 351 + print_usage(argv[0]); 352 + exit(EXIT_FAILURE); 353 + } 354 + 355 + system_map = fopen(system_map_file, "r"); 356 + if (!system_map) { 357 + perror(system_map_file); 358 + exit(EXIT_FAILURE); 359 + } 360 + get_symbol_from_map(hdr, system_map, CERT_SYM, &cert_sym); 361 + get_symbol_from_map(hdr, system_map, USED_SYM, &used_sym); 362 + get_symbol_from_map(hdr, system_map, LSIZE_SYM, &lsize_sym); 363 + cert_sym.size = used_sym.address - cert_sym.address; 364 + } else { 365 + info("Symbol table found.\n"); 366 + if (system_map_file) 367 + warn("System.map is ignored.\n"); 368 + get_symbol_from_table(hdr, symtab, CERT_SYM, &cert_sym); 369 + get_symbol_from_table(hdr, symtab, USED_SYM, &used_sym); 370 + get_symbol_from_table(hdr, symtab, LSIZE_SYM, &lsize_sym); 371 + } 372 + 373 + if (!cert_sym.offset || !lsize_sym.offset || !used_sym.offset) 374 + exit(EXIT_FAILURE); 375 + 376 + print_sym(hdr, &cert_sym); 377 + print_sym(hdr, &used_sym); 378 + print_sym(hdr, &lsize_sym); 379 + 380 + lsize = (unsigned long *)lsize_sym.content; 381 + used = (int *)used_sym.content; 382 + 383 + if (cert_sym.size < cert_size) { 384 + err("Certificate is larger than the reserved area!\n"); 385 + exit(EXIT_FAILURE); 386 + } 387 + 388 + /* If the existing cert is the same, don't overwrite */ 389 + if (cert_size == *used && 390 + strncmp(cert_sym.content, cert, cert_size) == 0) { 391 + warn("Certificate was already inserted.\n"); 392 + exit(EXIT_SUCCESS); 393 + } 394 + 395 + if (*used > 0) 396 + warn("Replacing previously inserted certificate.\n"); 397 + 398 + memcpy(cert_sym.content, cert, cert_size); 399 + if (cert_size < cert_sym.size) 400 + memset(cert_sym.content + cert_size, 401 + 0, cert_sym.size - cert_size); 402 + 403 + *lsize = *lsize + cert_size - *used; 404 + *used = cert_size; 405 + info("Inserted the contents of %s into %lx.\n", cert_file, 406 + cert_sym.address); 407 + info("Used %d bytes out of %d bytes reserved.\n", *used, 408 + cert_sym.size); 409 + exit(EXIT_SUCCESS); 410 + }