Back to home page

LXR

 
 

    


0001 /* Write the contents of the <certfile> into kernel symbol system_extra_cert
0002  *
0003  * Copyright (C) IBM Corporation, 2015
0004  *
0005  * Author: Mehmet Kayaalp <mkayaalp@linux.vnet.ibm.com>
0006  *
0007  * This software may be used and distributed according to the terms
0008  * of the GNU General Public License, incorporated herein by reference.
0009  *
0010  * Usage: insert-sys-cert [-s <System.map> -b <vmlinux> -c <certfile>
0011  */
0012 
0013 #define _GNU_SOURCE
0014 #include <stdio.h>
0015 #include <ctype.h>
0016 #include <string.h>
0017 #include <limits.h>
0018 #include <stdbool.h>
0019 #include <errno.h>
0020 #include <stdlib.h>
0021 #include <stdarg.h>
0022 #include <sys/types.h>
0023 #include <sys/stat.h>
0024 #include <sys/mman.h>
0025 #include <fcntl.h>
0026 #include <unistd.h>
0027 #include <elf.h>
0028 
0029 #define CERT_SYM  "system_extra_cert"
0030 #define USED_SYM  "system_extra_cert_used"
0031 #define LSIZE_SYM "system_certificate_list_size"
0032 
0033 #define info(format, args...) fprintf(stderr, "INFO:    " format, ## args)
0034 #define warn(format, args...) fprintf(stdout, "WARNING: " format, ## args)
0035 #define  err(format, args...) fprintf(stderr, "ERROR:   " format, ## args)
0036 
0037 #if UINTPTR_MAX == 0xffffffff
0038 #define CURRENT_ELFCLASS ELFCLASS32
0039 #define Elf_Ehdr    Elf32_Ehdr
0040 #define Elf_Shdr    Elf32_Shdr
0041 #define Elf_Sym     Elf32_Sym
0042 #else
0043 #define CURRENT_ELFCLASS ELFCLASS64
0044 #define Elf_Ehdr    Elf64_Ehdr
0045 #define Elf_Shdr    Elf64_Shdr
0046 #define Elf_Sym     Elf64_Sym
0047 #endif
0048 
0049 static unsigned char endianness(void)
0050 {
0051     uint16_t two_byte = 0x00FF;
0052     uint8_t low_address = *((uint8_t *)&two_byte);
0053 
0054     if (low_address == 0)
0055         return ELFDATA2MSB;
0056     else
0057         return ELFDATA2LSB;
0058 }
0059 
0060 struct sym {
0061     char *name;
0062     unsigned long address;
0063     unsigned long offset;
0064     void *content;
0065     int size;
0066 };
0067 
0068 static unsigned long get_offset_from_address(Elf_Ehdr *hdr, unsigned long addr)
0069 {
0070     Elf_Shdr *x;
0071     unsigned int i, num_sections;
0072 
0073     x = (void *)hdr + hdr->e_shoff;
0074     if (hdr->e_shnum == SHN_UNDEF)
0075         num_sections = x[0].sh_size;
0076     else
0077         num_sections = hdr->e_shnum;
0078 
0079     for (i = 1; i < num_sections; i++) {
0080         unsigned long start = x[i].sh_addr;
0081         unsigned long end = start + x[i].sh_size;
0082         unsigned long offset = x[i].sh_offset;
0083 
0084         if (addr >= start && addr <= end)
0085             return addr - start + offset;
0086     }
0087     return 0;
0088 }
0089 
0090 
0091 #define LINE_SIZE 100
0092 
0093 static void get_symbol_from_map(Elf_Ehdr *hdr, FILE *f, char *name,
0094                 struct sym *s)
0095 {
0096     char l[LINE_SIZE];
0097     char *w, *p, *n;
0098 
0099     s->size = 0;
0100     s->address = 0;
0101     s->offset = 0;
0102     if (fseek(f, 0, SEEK_SET) != 0) {
0103         perror("File seek failed");
0104         exit(EXIT_FAILURE);
0105     }
0106     while (fgets(l, LINE_SIZE, f)) {
0107         p = strchr(l, '\n');
0108         if (!p) {
0109             err("Missing line ending.\n");
0110             return;
0111         }
0112         n = strstr(l, name);
0113         if (n)
0114             break;
0115     }
0116     if (!n) {
0117         err("Unable to find symbol: %s\n", name);
0118         return;
0119     }
0120     w = strchr(l, ' ');
0121     if (!w)
0122         return;
0123 
0124     *w = '\0';
0125     s->address = strtoul(l, NULL, 16);
0126     if (s->address == 0)
0127         return;
0128     s->offset = get_offset_from_address(hdr, s->address);
0129     s->name = name;
0130     s->content = (void *)hdr + s->offset;
0131 }
0132 
0133 static Elf_Sym *find_elf_symbol(Elf_Ehdr *hdr, Elf_Shdr *symtab, char *name)
0134 {
0135     Elf_Sym *sym, *symtab_start;
0136     char *strtab, *symname;
0137     unsigned int link;
0138     Elf_Shdr *x;
0139     int i, n;
0140 
0141     x = (void *)hdr + hdr->e_shoff;
0142     link = symtab->sh_link;
0143     symtab_start = (void *)hdr + symtab->sh_offset;
0144     n = symtab->sh_size / symtab->sh_entsize;
0145     strtab = (void *)hdr + x[link].sh_offset;
0146 
0147     for (i = 0; i < n; i++) {
0148         sym = &symtab_start[i];
0149         symname = strtab + sym->st_name;
0150         if (strcmp(symname, name) == 0)
0151             return sym;
0152     }
0153     err("Unable to find symbol: %s\n", name);
0154     return NULL;
0155 }
0156 
0157 static void get_symbol_from_table(Elf_Ehdr *hdr, Elf_Shdr *symtab,
0158                   char *name, struct sym *s)
0159 {
0160     Elf_Shdr *sec;
0161     int secndx;
0162     Elf_Sym *elf_sym;
0163     Elf_Shdr *x;
0164 
0165     x = (void *)hdr + hdr->e_shoff;
0166     s->size = 0;
0167     s->address = 0;
0168     s->offset = 0;
0169     elf_sym = find_elf_symbol(hdr, symtab, name);
0170     if (!elf_sym)
0171         return;
0172     secndx = elf_sym->st_shndx;
0173     if (!secndx)
0174         return;
0175     sec = &x[secndx];
0176     s->size = elf_sym->st_size;
0177     s->address = elf_sym->st_value;
0178     s->offset = s->address - sec->sh_addr
0179                    + sec->sh_offset;
0180     s->name = name;
0181     s->content = (void *)hdr + s->offset;
0182 }
0183 
0184 static Elf_Shdr *get_symbol_table(Elf_Ehdr *hdr)
0185 {
0186     Elf_Shdr *x;
0187     unsigned int i, num_sections;
0188 
0189     x = (void *)hdr + hdr->e_shoff;
0190     if (hdr->e_shnum == SHN_UNDEF)
0191         num_sections = x[0].sh_size;
0192     else
0193         num_sections = hdr->e_shnum;
0194 
0195     for (i = 1; i < num_sections; i++)
0196         if (x[i].sh_type == SHT_SYMTAB)
0197             return &x[i];
0198     return NULL;
0199 }
0200 
0201 static void *map_file(char *file_name, int *size)
0202 {
0203     struct stat st;
0204     void *map;
0205     int fd;
0206 
0207     fd = open(file_name, O_RDWR);
0208     if (fd < 0) {
0209         perror(file_name);
0210         return NULL;
0211     }
0212     if (fstat(fd, &st)) {
0213         perror("Could not determine file size");
0214         close(fd);
0215         return NULL;
0216     }
0217     *size = st.st_size;
0218     map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
0219     if (map == MAP_FAILED) {
0220         perror("Mapping to memory failed");
0221         close(fd);
0222         return NULL;
0223     }
0224     close(fd);
0225     return map;
0226 }
0227 
0228 static char *read_file(char *file_name, int *size)
0229 {
0230     struct stat st;
0231     char *buf;
0232     int fd;
0233 
0234     fd = open(file_name, O_RDONLY);
0235     if (fd < 0) {
0236         perror(file_name);
0237         return NULL;
0238     }
0239     if (fstat(fd, &st)) {
0240         perror("Could not determine file size");
0241         close(fd);
0242         return NULL;
0243     }
0244     *size = st.st_size;
0245     buf = malloc(*size);
0246     if (!buf) {
0247         perror("Allocating memory failed");
0248         close(fd);
0249         return NULL;
0250     }
0251     if (read(fd, buf, *size) != *size) {
0252         perror("File read failed");
0253         close(fd);
0254         return NULL;
0255     }
0256     close(fd);
0257     return buf;
0258 }
0259 
0260 static void print_sym(Elf_Ehdr *hdr, struct sym *s)
0261 {
0262     info("sym:    %s\n", s->name);
0263     info("addr:   0x%lx\n", s->address);
0264     info("size:   %d\n", s->size);
0265     info("offset: 0x%lx\n", (unsigned long)s->offset);
0266 }
0267 
0268 static void print_usage(char *e)
0269 {
0270     printf("Usage %s [-s <System.map>] -b <vmlinux> -c <certfile>\n", e);
0271 }
0272 
0273 int main(int argc, char **argv)
0274 {
0275     char *system_map_file = NULL;
0276     char *vmlinux_file = NULL;
0277     char *cert_file = NULL;
0278     int vmlinux_size;
0279     int cert_size;
0280     Elf_Ehdr *hdr;
0281     char *cert;
0282     FILE *system_map;
0283     unsigned long *lsize;
0284     int *used;
0285     int opt;
0286     Elf_Shdr *symtab = NULL;
0287     struct sym cert_sym, lsize_sym, used_sym;
0288 
0289     while ((opt = getopt(argc, argv, "b:c:s:")) != -1) {
0290         switch (opt) {
0291         case 's':
0292             system_map_file = optarg;
0293             break;
0294         case 'b':
0295             vmlinux_file = optarg;
0296             break;
0297         case 'c':
0298             cert_file = optarg;
0299             break;
0300         default:
0301             break;
0302         }
0303     }
0304 
0305     if (!vmlinux_file || !cert_file) {
0306         print_usage(argv[0]);
0307         exit(EXIT_FAILURE);
0308     }
0309 
0310     cert = read_file(cert_file, &cert_size);
0311     if (!cert)
0312         exit(EXIT_FAILURE);
0313 
0314     hdr = map_file(vmlinux_file, &vmlinux_size);
0315     if (!hdr)
0316         exit(EXIT_FAILURE);
0317 
0318     if (vmlinux_size < sizeof(*hdr)) {
0319         err("Invalid ELF file.\n");
0320         exit(EXIT_FAILURE);
0321     }
0322 
0323     if ((hdr->e_ident[EI_MAG0] != ELFMAG0) ||
0324         (hdr->e_ident[EI_MAG1] != ELFMAG1) ||
0325         (hdr->e_ident[EI_MAG2] != ELFMAG2) ||
0326         (hdr->e_ident[EI_MAG3] != ELFMAG3)) {
0327         err("Invalid ELF magic.\n");
0328         exit(EXIT_FAILURE);
0329     }
0330 
0331     if (hdr->e_ident[EI_CLASS] != CURRENT_ELFCLASS) {
0332         err("ELF class mismatch.\n");
0333         exit(EXIT_FAILURE);
0334     }
0335 
0336     if (hdr->e_ident[EI_DATA] != endianness()) {
0337         err("ELF endian mismatch.\n");
0338         exit(EXIT_FAILURE);
0339     }
0340 
0341     if (hdr->e_shoff > vmlinux_size) {
0342         err("Could not find section header.\n");
0343         exit(EXIT_FAILURE);
0344     }
0345 
0346     symtab = get_symbol_table(hdr);
0347     if (!symtab) {
0348         warn("Could not find the symbol table.\n");
0349         if (!system_map_file) {
0350             err("Please provide a System.map file.\n");
0351             print_usage(argv[0]);
0352             exit(EXIT_FAILURE);
0353         }
0354 
0355         system_map = fopen(system_map_file, "r");
0356         if (!system_map) {
0357             perror(system_map_file);
0358             exit(EXIT_FAILURE);
0359         }
0360         get_symbol_from_map(hdr, system_map, CERT_SYM, &cert_sym);
0361         get_symbol_from_map(hdr, system_map, USED_SYM, &used_sym);
0362         get_symbol_from_map(hdr, system_map, LSIZE_SYM, &lsize_sym);
0363         cert_sym.size = used_sym.address - cert_sym.address;
0364     } else {
0365         info("Symbol table found.\n");
0366         if (system_map_file)
0367             warn("System.map is ignored.\n");
0368         get_symbol_from_table(hdr, symtab, CERT_SYM, &cert_sym);
0369         get_symbol_from_table(hdr, symtab, USED_SYM, &used_sym);
0370         get_symbol_from_table(hdr, symtab, LSIZE_SYM, &lsize_sym);
0371     }
0372 
0373     if (!cert_sym.offset || !lsize_sym.offset || !used_sym.offset)
0374         exit(EXIT_FAILURE);
0375 
0376     print_sym(hdr, &cert_sym);
0377     print_sym(hdr, &used_sym);
0378     print_sym(hdr, &lsize_sym);
0379 
0380     lsize = (unsigned long *)lsize_sym.content;
0381     used = (int *)used_sym.content;
0382 
0383     if (cert_sym.size < cert_size) {
0384         err("Certificate is larger than the reserved area!\n");
0385         exit(EXIT_FAILURE);
0386     }
0387 
0388     /* If the existing cert is the same, don't overwrite */
0389     if (cert_size == *used &&
0390         strncmp(cert_sym.content, cert, cert_size) == 0) {
0391         warn("Certificate was already inserted.\n");
0392         exit(EXIT_SUCCESS);
0393     }
0394 
0395     if (*used > 0)
0396         warn("Replacing previously inserted certificate.\n");
0397 
0398     memcpy(cert_sym.content, cert, cert_size);
0399     if (cert_size < cert_sym.size)
0400         memset(cert_sym.content + cert_size,
0401             0, cert_sym.size - cert_size);
0402 
0403     *lsize = *lsize + cert_size - *used;
0404     *used = cert_size;
0405     info("Inserted the contents of %s into %lx.\n", cert_file,
0406                         cert_sym.address);
0407     info("Used %d bytes out of %d bytes reserved.\n", *used,
0408                          cert_sym.size);
0409     exit(EXIT_SUCCESS);
0410 }