linux/scripts/insert-sys-cert.c
<<
>>
Prefs
   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}
 411