• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  
endianness(void)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  
get_offset_from_address(Elf_Ehdr * hdr,unsigned long addr)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  
get_symbol_from_map(Elf_Ehdr * hdr,FILE * f,char * name,struct sym * s)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  
find_elf_symbol(Elf_Ehdr * hdr,Elf_Shdr * symtab,char * name)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  
get_symbol_from_table(Elf_Ehdr * hdr,Elf_Shdr * symtab,char * name,struct sym * s)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  
get_symbol_table(Elf_Ehdr * hdr)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  
map_file(char * file_name,int * size)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  
read_file(char * file_name,int * size)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  
print_sym(Elf_Ehdr * hdr,struct sym * s)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  
print_usage(char * e)268  static void print_usage(char *e)
269  {
270  	printf("Usage %s [-s <System.map>] -b <vmlinux> -c <certfile>\n", e);
271  }
272  
main(int argc,char ** argv)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  }
411