root/scripts/insert-sys-cert.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. endianness
  2. get_offset_from_address
  3. get_symbol_from_map
  4. find_elf_symbol
  5. get_symbol_from_table
  6. get_symbol_table
  7. map_file
  8. read_file
  9. print_sym
  10. print_usage
  11. main

   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 }

/* [<][>][^][v][top][bottom][index][help] */