aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMehmet Kayaalp <mkayaalp@linux.vnet.ibm.com>2015-11-24 16:18:05 -0500
committerDavid Howells <dhowells@redhat.com>2016-02-26 10:30:20 -0500
commitc4c36105958576fee87d2c75f4b69b6e5bbde772 (patch)
treef4a8451b1471c4f87fab76f8aa613c5dc402ad8c
parent5d06ee20b662a78417245714fc576cba90e6374f (diff)
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>
-rw-r--r--certs/Kconfig16
-rw-r--r--certs/system_certificates.S12
-rw-r--r--scripts/.gitignore1
-rw-r--r--scripts/Makefile1
-rw-r--r--scripts/insert-sys-cert.c410
5 files changed, 440 insertions, 0 deletions
diff --git a/certs/Kconfig b/certs/Kconfig
index b030b9c7ed34..f0f8a4433685 100644
--- a/certs/Kconfig
+++ b/certs/Kconfig
@@ -39,4 +39,20 @@ config SYSTEM_TRUSTED_KEYS
39 form of DER-encoded *.x509 files in the top-level build directory, 39 form of DER-encoded *.x509 files in the top-level build directory,
40 those are no longer used. You will need to set this option instead. 40 those are no longer used. You will need to set this option instead.
41 41
42config 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
50config 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
42endmenu 58endmenu
diff --git a/certs/system_certificates.S b/certs/system_certificates.S
index 9216e8c81764..f82e1b22eac4 100644
--- a/certs/system_certificates.S
+++ b/certs/system_certificates.S
@@ -13,6 +13,18 @@ __cert_list_start:
13 .incbin "certs/x509_certificate_list" 13 .incbin "certs/x509_certificate_list"
14__cert_list_end: 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
19VMLINUX_SYMBOL(system_extra_cert):
20 .fill CONFIG_SYSTEM_EXTRA_CERTIFICATE_SIZE, 1, 0
21
22 .globl VMLINUX_SYMBOL(system_extra_cert_used)
23VMLINUX_SYMBOL(system_extra_cert_used):
24 .int 0
25
26#endif /* CONFIG_SYSTEM_EXTRA_CERTIFICATE */
27
16 .align 8 28 .align 8
17 .globl VMLINUX_SYMBOL(system_certificate_list_size) 29 .globl VMLINUX_SYMBOL(system_certificate_list_size)
18VMLINUX_SYMBOL(system_certificate_list_size): 30VMLINUX_SYMBOL(system_certificate_list_size):
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 1f78169d4254..e063daa3ec4a 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -13,3 +13,4 @@ sortextable
13asn1_compiler 13asn1_compiler
14extract-cert 14extract-cert
15sign-file 15sign-file
16insert-sys-cert
diff --git a/scripts/Makefile b/scripts/Makefile
index fd0d53d4a234..822ab4a6a4aa 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -19,6 +19,7 @@ hostprogs-$(CONFIG_BUILDTIME_EXTABLE_SORT) += sortextable
19hostprogs-$(CONFIG_ASN1) += asn1_compiler 19hostprogs-$(CONFIG_ASN1) += asn1_compiler
20hostprogs-$(CONFIG_MODULE_SIG) += sign-file 20hostprogs-$(CONFIG_MODULE_SIG) += sign-file
21hostprogs-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += extract-cert 21hostprogs-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += extract-cert
22hostprogs-$(CONFIG_SYSTEM_EXTRA_CERTIFICATE) += insert-sys-cert
22 23
23HOSTCFLAGS_sortextable.o = -I$(srctree)/tools/include 24HOSTCFLAGS_sortextable.o = -I$(srctree)/tools/include
24HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include 25HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include
diff --git a/scripts/insert-sys-cert.c b/scripts/insert-sys-cert.c
new file mode 100644
index 000000000000..8902836c2342
--- /dev/null
+++ b/scripts/insert-sys-cert.c
@@ -0,0 +1,410 @@
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
49static 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
60struct sym {
61 char *name;
62 unsigned long address;
63 unsigned long offset;
64 void *content;
65 int size;
66};
67
68static 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
93static 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
133static 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
157static 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
184static 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
201static 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
228static 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
260static 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
268static void print_usage(char *e)
269{
270 printf("Usage %s [-s <System.map>] -b <vmlinux> -c <certfile>\n", e);
271}
272
273int 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}