aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/insert-sys-cert.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-03-17 14:33:45 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-03-17 14:33:45 -0400
commitbb7aeae3d680c2c777f54274b0270ced0599f33d (patch)
tree4801a103c2b157b5019cf38a19dc67d54bf38453 /scripts/insert-sys-cert.c
parent70477371dc350746d10431d74f0f213a8d59924c (diff)
parent88a1b564a20e371e6be41b39b85673e9c1959491 (diff)
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security layer updates from James Morris: "There are a bunch of fixes to the TPM, IMA, and Keys code, with minor fixes scattered across the subsystem. IMA now requires signed policy, and that policy is also now measured and appraised" * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (67 commits) X.509: Make algo identifiers text instead of enum akcipher: Move the RSA DER encoding check to the crypto layer crypto: Add hash param to pkcs1pad sign-file: fix build with CMS support disabled MAINTAINERS: update tpmdd urls MODSIGN: linux/string.h should be #included to get memcpy() certs: Fix misaligned data in extra certificate list X.509: Handle midnight alternative notation in GeneralizedTime X.509: Support leap seconds Handle ISO 8601 leap seconds and encodings of midnight in mktime64() X.509: Fix leap year handling again PKCS#7: fix unitialized boolean 'want' firmware: change kernel read fail to dev_dbg() KEYS: Use the symbol value for list size, updated by scripts/insert-sys-cert KEYS: Reserve an extra certificate symbol for inserting without recompiling modsign: hide openssl output in silent builds tpm_tis: fix build warning with tpm_tis_resume ima: require signed IMA policy ima: measure and appraise the IMA policy itself ima: load policy using path ...
Diffstat (limited to 'scripts/insert-sys-cert.c')
-rw-r--r--scripts/insert-sys-cert.c410
1 files changed, 410 insertions, 0 deletions
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}