2018-05-02 23:10:11

by Mehmet Kayaalp

[permalink] [raw]
Subject: [PATCH v6 0/4] Certificate insertion support for x86 bzImages

These patches add support for modifying the reserved space for extra
certificates in a compressed bzImage in x86. This allows separating the
system keyring certificate from the kernel build process. After the kernel
image is distributed, the insert-sys-cert script can be used to insert the
certificate for x86.

Changes:

v6:
* Removed libelf dependency (2/4)

v5:
* Added CRC fixing (3/4)

v4:
* Applied checkpatch.pl suggestions (2/4, 3/4)
* Cleaned up the commit messages (1/4, 2/4)
* Added the build file to .gitignore (1/4)

v3:
* Rewrote 1/4 to insert incompressible bytes are at build time. Previous
solution required changes to <arch>/boot/Makefile's for modifying the
vmlinux file after linking, and did not work well with cross compilation.
* Added 2/4 for ELF class-independent processing of vmlinux file, in case
the script was compiled for 64-bit and the kernel was compiled for 32-bit.
* Reordered 3/4, added x86 bzImage boot version (>=2.08) verification.

v2:
* Rebased arch/boot/x86/Makefile patch (removed in v3)

Mehmet Kayaalp (4):
KEYS: Insert incompressible bytes to reserve space in bzImage
KEYS: Add ELF class-independent certificate insertion support
KEYS: Support for inserting a certificate into x86 bzImage
KEYS: Print insert-sys-cert information to stdout instead of stderr

certs/.gitignore | 1 +
certs/Makefile | 21 +-
certs/system_certificates.S | 2 +-
scripts/insert-sys-cert.c | 584 +++++++++++++++++++++++++++++-------
4 files changed, 488 insertions(+), 120 deletions(-)

--
2.17.0



2018-05-02 23:09:25

by Mehmet Kayaalp

[permalink] [raw]
Subject: [PATCH v6 2/4] KEYS: Add ELF class-independent certificate insertion support

This patch adds support for handling either 32-bit or 64-bit ELF files
by moving ELF class checks to runtime. This makes it possible to compile
the script for 64-bit, and the kernel for 32-bit (e.g. make ARCH=i386 on
x86-64). Gelf/libelf is purposely not used to avoid the build dependency.

Signed-off-by: Mehmet Kayaalp <[email protected]>
---
scripts/insert-sys-cert.c | 331 +++++++++++++++++++++++++-------------
1 file changed, 218 insertions(+), 113 deletions(-)

diff --git a/scripts/insert-sys-cert.c b/scripts/insert-sys-cert.c
index 8902836c2342..10a17504dc87 100644
--- a/scripts/insert-sys-cert.c
+++ b/scripts/insert-sys-cert.c
@@ -25,6 +25,7 @@
#include <fcntl.h>
#include <unistd.h>
#include <elf.h>
+#include <linux/types.h>

#define CERT_SYM "system_extra_cert"
#define USED_SYM "system_extra_cert_used"
@@ -57,6 +58,18 @@ static unsigned char endianness(void)
return ELFDATA2LSB;
}

+struct elf {
+ unsigned char *base;
+ int size;
+ unsigned char eclass;
+ unsigned char data;
+ unsigned long shoff;
+ unsigned long shnum;
+ unsigned long shentsize;
+ unsigned char *shstart;
+ unsigned long shsize;
+};
+
struct sym {
char *name;
unsigned long address;
@@ -65,24 +78,92 @@ struct sym {
int size;
};

-static unsigned long get_offset_from_address(Elf_Ehdr *hdr, unsigned long addr)
+static int read_elf_data(struct elf *elf, char *base, int size)
{
- Elf_Shdr *x;
- unsigned int i, num_sections;
+ unsigned char *e_ident;
+ Elf32_Ehdr *e32 = (Elf32_Ehdr *)base;
+ Elf64_Ehdr *e64 = (Elf64_Ehdr *)base;

- x = (void *)hdr + hdr->e_shoff;
- if (hdr->e_shnum == SHN_UNDEF)
- num_sections = x[0].sh_size;
- else
- num_sections = hdr->e_shnum;
+ elf->base = (unsigned char *)base;
+ elf->size = size;
+
+ if (size < EI_NIDENT) {
+ err("Invalid file size for ELF format.\n");
+ return -1;
+ }
+ e_ident = (unsigned char *)base;
+ if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 ||
+ e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) {
+ err("Invalid ELF magic.\n");
+ return -1;
+ }
+ elf->eclass = e_ident[EI_CLASS];
+ if (elf->eclass != ELFCLASS32 && elf->eclass != ELFCLASS64) {
+ err("Invalid ELF class.\n");
+ return -1;
+ }
+ elf->data = e_ident[EI_DATA];
+ if (elf->data != endianness()) {
+ err("ELF endian mismatch.\n");
+ return -1;
+ }
+ if (e_ident[EI_VERSION] != EV_CURRENT) {
+ err("Invalid ELF file version.\n");
+ return -1;
+ }
+ if (elf->eclass == ELFCLASS32) {
+ elf->shoff = e32->e_shoff;
+ elf->shnum = e32->e_shnum;
+ elf->shentsize = e32->e_shentsize;
+ } else {
+ elf->shoff = e64->e_shoff;
+ elf->shnum = e64->e_shnum;
+ elf->shentsize = e64->e_shentsize;
+ }
+ if (elf->shoff == 0 || elf->shoff > size) {
+ err("Could not find section header.\n");
+ exit(EXIT_FAILURE);
+ }
+ elf->shstart = elf->base + elf->shoff;
+ elf->shsize = elf->shnum * elf->shentsize;
+ return 0;
+}

- for (i = 1; i < num_sections; i++) {
- unsigned long start = x[i].sh_addr;
- unsigned long end = start + x[i].sh_size;
- unsigned long offset = x[i].sh_offset;
+static char *get_elf_string(struct elf *elf, unsigned long link,
+ unsigned long namendx)
+{
+ unsigned char *p = elf->shstart + link * elf->shentsize;
+ Elf32_Shdr *s32 = (Elf32_Shdr *)p;
+ Elf64_Shdr *s64 = (Elf64_Shdr *)p;

+ if (elf->eclass == ELFCLASS32)
+ return (char *)elf->base + s32->sh_offset + namendx;
+ else
+ return (char *)elf->base + s64->sh_offset + namendx;
+}
+
+static unsigned long get_offset_from_address(struct elf *elf,
+ unsigned long addr)
+{
+ unsigned long start, end, offset;
+ unsigned char *p = elf->shstart;
+
+ while (p < elf->shstart + elf->shsize) {
+ Elf32_Shdr *s32 = (Elf32_Shdr *)p;
+ Elf64_Shdr *s64 = (Elf64_Shdr *)p;
+
+ if (elf->eclass == ELFCLASS32) {
+ start = s32->sh_addr;
+ end = start + s32->sh_size;
+ offset = s32->sh_offset;
+ } else {
+ start = s64->sh_addr;
+ end = start + s64->sh_size;
+ offset = s64->sh_offset;
+ }
if (addr >= start && addr <= end)
return addr - start + offset;
+ p += elf->shentsize;
}
return 0;
}
@@ -90,7 +171,7 @@ static unsigned long get_offset_from_address(Elf_Ehdr *hdr, unsigned long addr)

#define LINE_SIZE 100

-static void get_symbol_from_map(Elf_Ehdr *hdr, FILE *f, char *name,
+static void get_symbol_from_map(struct elf *elf, FILE *f, char *name,
struct sym *s)
{
char l[LINE_SIZE];
@@ -125,76 +206,108 @@ static void get_symbol_from_map(Elf_Ehdr *hdr, FILE *f, char *name,
s->address = strtoul(l, NULL, 16);
if (s->address == 0)
return;
- s->offset = get_offset_from_address(hdr, s->address);
+ s->offset = get_offset_from_address(elf, s->address);
s->name = name;
- s->content = (void *)hdr + s->offset;
+ s->content = elf->base + s->offset;
}

-static Elf_Sym *find_elf_symbol(Elf_Ehdr *hdr, Elf_Shdr *symtab, char *name)
-{
- Elf_Sym *sym, *symtab_start;
- char *strtab, *symname;
- unsigned int link;
- Elf_Shdr *x;
- int i, n;
-
- x = (void *)hdr + hdr->e_shoff;
- link = symtab->sh_link;
- symtab_start = (void *)hdr + symtab->sh_offset;
- n = symtab->sh_size / symtab->sh_entsize;
- strtab = (void *)hdr + x[link].sh_offset;
-
- for (i = 0; i < n; i++) {
- sym = &symtab_start[i];
- symname = strtab + sym->st_name;
- if (strcmp(symname, name) == 0)
- return sym;
- }
- err("Unable to find symbol: %s\n", name);
- return NULL;
-}
-
-static void get_symbol_from_table(Elf_Ehdr *hdr, Elf_Shdr *symtab,
+static void get_symbol_from_table(struct elf *elf, unsigned char *symtab,
char *name, struct sym *s)
{
- Elf_Shdr *sec;
- int secndx;
- Elf_Sym *elf_sym;
- Elf_Shdr *x;
+ Elf32_Shdr *s32 = (Elf32_Shdr *)symtab;
+ Elf64_Shdr *s64 = (Elf64_Shdr *)symtab;
+ unsigned char *p;
+ unsigned long address;
+ unsigned long offset;
+ unsigned long size;
+ unsigned long entsize;
+ unsigned long link;
+ unsigned long shndx;
+ char *symname;
+ int found = 0;

- x = (void *)hdr + hdr->e_shoff;
s->size = 0;
s->address = 0;
s->offset = 0;
- elf_sym = find_elf_symbol(hdr, symtab, name);
- if (!elf_sym)
- return;
- secndx = elf_sym->st_shndx;
- if (!secndx)
+
+ if (elf->eclass == ELFCLASS32) {
+ offset = s32->sh_offset;
+ size = s32->sh_size;
+ entsize = s32->sh_entsize;
+ link = s32->sh_link;
+ } else {
+ offset = s64->sh_offset;
+ size = s64->sh_size;
+ entsize = s64->sh_entsize;
+ link = s64->sh_link;
+ }
+
+ p = elf->base + offset;
+ while (p < elf->base + offset + size) {
+ Elf32_Sym *sym32 = (Elf32_Sym *)p;
+ Elf64_Sym *sym64 = (Elf64_Sym *)p;
+ unsigned long namendx;
+
+ if (elf->eclass == ELFCLASS32) {
+ namendx = sym32->st_name;
+ shndx = sym32->st_shndx;
+ } else {
+ namendx = sym64->st_name;
+ shndx = sym64->st_shndx;
+ }
+
+ symname = get_elf_string(elf, link, namendx);
+ if (strcmp(symname, name) == 0 && shndx) {
+ found = 1;
+ if (elf->eclass == ELFCLASS32) {
+ s->size = sym32->st_size;
+ s->address = sym32->st_value;
+ } else {
+ s->size = sym64->st_size;
+ s->address = sym64->st_value;
+ }
+ break;
+ }
+ p += entsize;
+ }
+
+ if (!found)
return;
- sec = &x[secndx];
- s->size = elf_sym->st_size;
- s->address = elf_sym->st_value;
- s->offset = s->address - sec->sh_addr
- + sec->sh_offset;
+
+ p = elf->shstart + shndx * elf->shentsize;
+ s32 = (Elf32_Shdr *)p;
+ s64 = (Elf64_Shdr *)p;
+
+ if (elf->eclass == ELFCLASS32) {
+ offset = s32->sh_offset;
+ address = s32->sh_addr;
+ } else {
+ offset = s64->sh_offset;
+ address = s64->sh_addr;
+ }
+
+ s->offset = s->address - address + offset;
s->name = name;
- s->content = (void *)hdr + s->offset;
+ s->content = elf->base + s->offset;
}

-static Elf_Shdr *get_symbol_table(Elf_Ehdr *hdr)
+static unsigned char *get_symbol_table(struct elf *elf)
{
- Elf_Shdr *x;
- unsigned int i, num_sections;
-
- x = (void *)hdr + hdr->e_shoff;
- if (hdr->e_shnum == SHN_UNDEF)
- num_sections = x[0].sh_size;
- else
- num_sections = hdr->e_shnum;
-
- for (i = 1; i < num_sections; i++)
- if (x[i].sh_type == SHT_SYMTAB)
- return &x[i];
+ unsigned char *p = elf->shstart;
+
+ while (p < elf->shstart + elf->shsize) {
+ Elf32_Shdr *s32 = (Elf32_Shdr *)p;
+ Elf64_Shdr *s64 = (Elf64_Shdr *)p;
+
+ if (elf->eclass == ELFCLASS32) {
+ if (s32->sh_type == SHT_SYMTAB)
+ return p;
+ } else {
+ if (s64->sh_type == SHT_SYMTAB)
+ return p;
+ }
+ p += elf->shentsize;
+ }
return NULL;
}

@@ -257,7 +370,7 @@ static char *read_file(char *file_name, int *size)
return buf;
}

-static void print_sym(Elf_Ehdr *hdr, struct sym *s)
+static void print_sym(struct sym *s)
{
info("sym: %s\n", s->name);
info("addr: 0x%lx\n", s->address);
@@ -277,14 +390,14 @@ int main(int argc, char **argv)
char *cert_file = NULL;
int vmlinux_size;
int cert_size;
- Elf_Ehdr *hdr;
+ char *vmlinux;
char *cert;
FILE *system_map;
- unsigned long *lsize;
int *used;
int opt;
- Elf_Shdr *symtab = NULL;
struct sym cert_sym, lsize_sym, used_sym;
+ struct elf elf;
+ unsigned char *symtab = NULL;

while ((opt = getopt(argc, argv, "b:c:s:")) != -1) {
switch (opt) {
@@ -311,39 +424,16 @@ int main(int argc, char **argv)
if (!cert)
exit(EXIT_FAILURE);

- hdr = map_file(vmlinux_file, &vmlinux_size);
- if (!hdr)
+ vmlinux = map_file(vmlinux_file, &vmlinux_size);
+ if (!vmlinux)
exit(EXIT_FAILURE);

- if (vmlinux_size < sizeof(*hdr)) {
- err("Invalid ELF file.\n");
- exit(EXIT_FAILURE);
- }
-
- if ((hdr->e_ident[EI_MAG0] != ELFMAG0) ||
- (hdr->e_ident[EI_MAG1] != ELFMAG1) ||
- (hdr->e_ident[EI_MAG2] != ELFMAG2) ||
- (hdr->e_ident[EI_MAG3] != ELFMAG3)) {
- err("Invalid ELF magic.\n");
+ if (read_elf_data(&elf, vmlinux, vmlinux_size)) {
+ err("Unable to read ELF file.\n");
exit(EXIT_FAILURE);
}

- if (hdr->e_ident[EI_CLASS] != CURRENT_ELFCLASS) {
- err("ELF class mismatch.\n");
- exit(EXIT_FAILURE);
- }
-
- if (hdr->e_ident[EI_DATA] != endianness()) {
- err("ELF endian mismatch.\n");
- exit(EXIT_FAILURE);
- }
-
- if (hdr->e_shoff > vmlinux_size) {
- err("Could not find section header.\n");
- exit(EXIT_FAILURE);
- }
-
- symtab = get_symbol_table(hdr);
+ symtab = get_symbol_table(&elf);
if (!symtab) {
warn("Could not find the symbol table.\n");
if (!system_map_file) {
@@ -357,27 +447,28 @@ int main(int argc, char **argv)
perror(system_map_file);
exit(EXIT_FAILURE);
}
- get_symbol_from_map(hdr, system_map, CERT_SYM, &cert_sym);
- get_symbol_from_map(hdr, system_map, USED_SYM, &used_sym);
- get_symbol_from_map(hdr, system_map, LSIZE_SYM, &lsize_sym);
+ get_symbol_from_map(&elf, system_map, CERT_SYM, &cert_sym);
+ get_symbol_from_map(&elf, system_map, USED_SYM, &used_sym);
+ get_symbol_from_map(&elf, system_map, LSIZE_SYM, &lsize_sym);
cert_sym.size = used_sym.address - cert_sym.address;
} else {
info("Symbol table found.\n");
if (system_map_file)
warn("System.map is ignored.\n");
- get_symbol_from_table(hdr, symtab, CERT_SYM, &cert_sym);
- get_symbol_from_table(hdr, symtab, USED_SYM, &used_sym);
- get_symbol_from_table(hdr, symtab, LSIZE_SYM, &lsize_sym);
+ get_symbol_from_table(&elf, symtab, CERT_SYM, &cert_sym);
+ get_symbol_from_table(&elf, symtab, USED_SYM, &used_sym);
+ get_symbol_from_table(&elf, symtab, LSIZE_SYM, &lsize_sym);
}

- if (!cert_sym.offset || !lsize_sym.offset || !used_sym.offset)
+ if (!cert_sym.offset || !lsize_sym.offset || !used_sym.offset) {
+ err("Unable to find symbols.\n");
exit(EXIT_FAILURE);
+ }

- print_sym(hdr, &cert_sym);
- print_sym(hdr, &used_sym);
- print_sym(hdr, &lsize_sym);
+ print_sym(&cert_sym);
+ print_sym(&used_sym);
+ print_sym(&lsize_sym);

- lsize = (unsigned long *)lsize_sym.content;
used = (int *)used_sym.content;

if (cert_sym.size < cert_size) {
@@ -400,11 +491,25 @@ int main(int argc, char **argv)
memset(cert_sym.content + cert_size,
0, cert_sym.size - cert_size);

- *lsize = *lsize + cert_size - *used;
+ if (elf.eclass == ELFCLASS64) {
+ u64 *lsize;
+
+ lsize = (u64 *)lsize_sym.content;
+ *lsize = *lsize + cert_size - *used;
+ } else {
+ u32 *lsize;
+
+ lsize = (u32 *)lsize_sym.content;
+ *lsize = *lsize + cert_size - *used;
+ }
*used = cert_size;
info("Inserted the contents of %s into %lx.\n", cert_file,
cert_sym.address);
info("Used %d bytes out of %d bytes reserved.\n", *used,
cert_sym.size);
+ if (munmap(vmlinux, vmlinux_size) == -1) {
+ perror(vmlinux_file);
+ exit(EXIT_FAILURE);
+ }
exit(EXIT_SUCCESS);
}
--
2.17.0


2018-05-02 23:10:19

by Mehmet Kayaalp

[permalink] [raw]
Subject: [PATCH v6 3/4] KEYS: Support for inserting a certificate into x86 bzImage

The config option SYSTEM_EXTRA_CERTIFICATE (introduced in c4c361059585)
reserves space in vmlinux file, which is compressed to create the
self-extracting bzImage. This patch adds the capability of extracting the
vmlinux, inserting the certificate, and repackaging the result into a
bzImage.

It only works if the resulting compressed vmlinux is smaller than the
original. Otherwise re-linking would be required. To make the reserved
space allocate actual space in bzImage, incompressible bytes are
inserted into the vmlinux as a placeholder for the extra certificate.
After receiving a bzImage that is created this way, the actual
certificate can be inserted into the bzImage:

scripts/insert-sys-cert -s <System.map> -z <bzImage> -c <certfile>

Signed-off-by: Mehmet Kayaalp <[email protected]>
---
scripts/insert-sys-cert.c | 257 +++++++++++++++++++++++++++++++++++++-
1 file changed, 252 insertions(+), 5 deletions(-)

diff --git a/scripts/insert-sys-cert.c b/scripts/insert-sys-cert.c
index 10a17504dc87..a3bd7ea8a436 100644
--- a/scripts/insert-sys-cert.c
+++ b/scripts/insert-sys-cert.c
@@ -7,7 +7,8 @@
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
- * Usage: insert-sys-cert [-s <System.map> -b <vmlinux> -c <certfile>
+ * Usage: insert-sys-cert [-s <System.map>] -b <vmlinux> -c <certfile>
+ * [-s <System.map>] -z <bzImage> -c <certfile>
*/

#define _GNU_SOURCE
@@ -370,6 +371,228 @@ static char *read_file(char *file_name, int *size)
return buf;
}

+#define BOOT_FLAG 0xAA55
+#define MAGIC 0x53726448
+
+#define BOOT_FLAG_O 0x1FE
+#define MAGIC_O 0x202
+#define VERSION_O 0x206
+#define SETUP_SECTS_O 0x1F1
+#define PAYLOAD_OFFSET_O 0x248
+#define PAYLOAD_LENGTH_O 0x24C
+
+static int image_supported(char *bzimage, int bzimage_size)
+{
+ u16 boot_flag;
+ u32 magic;
+ u16 version;
+
+ if (bzimage_size < 1024) {
+ err("Invalid bzImage: File is too small\n");
+ return 0;
+ }
+
+ boot_flag = *((u16 *)&bzimage[BOOT_FLAG_O]);
+ magic = *((u32 *)&bzimage[MAGIC_O]);
+ version = *((u16 *)&bzimage[VERSION_O]);
+
+ if (boot_flag != BOOT_FLAG || magic != MAGIC) {
+ err("Invalid bzImage: Magic mismatch\n");
+ return 0;
+ }
+
+ if (version < 0x208) {
+ err("Invalid bzImage: Boot version <2.08 not supported\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static void get_payload_info(char *bzimage, int *offset, int *size)
+{
+ unsigned int system_offset;
+ unsigned char setup_sectors;
+
+ setup_sectors = bzimage[SETUP_SECTS_O] + 1;
+ system_offset = setup_sectors * 512;
+ *offset = system_offset + *((int *)&bzimage[PAYLOAD_OFFSET_O]);
+ *size = *((int *)&bzimage[PAYLOAD_LENGTH_O]);
+}
+
+static void update_payload_info(char *bzimage, int new_size)
+{
+ int offset, size;
+
+ get_payload_info(bzimage, &offset, &size);
+ *((int *)&bzimage[PAYLOAD_LENGTH_O]) = new_size;
+ if (new_size < size)
+ memset(bzimage + offset + new_size, 0, size - new_size);
+}
+
+struct zipper {
+ unsigned char pattern[10];
+ int length;
+ char *command;
+ char *compress;
+};
+
+struct zipper zippers[] = {
+ {{0x7F, 'E', 'L', 'F'},
+ 4, "cat", "cat"},
+ {{0x1F, 0x8B},
+ 2, "gunzip", "gzip -n -f -9"},
+ {{0xFD, '7', 'z', 'X', 'Z', 0},
+ 6, "unxz", "xz"},
+ {{'B', 'Z', 'h'},
+ 3, "bunzip2", "bzip2 -9"},
+ {{0xFF, 'L', 'Z', 'M', 'A', 0},
+ 6, "unlzma", "lzma -9"},
+ {{0xD3, 'L', 'Z', 'O', 0, '\r', '\n', 0x20, '\n'},
+ 9, "lzop -d", "lzop -9"}
+};
+
+static struct zipper *get_zipper(char *p)
+{
+ int i;
+
+ for (i = 0; i < sizeof(zippers) / sizeof(struct zipper); i++) {
+ if (memcmp(p, zippers[i].pattern, zippers[i].length) == 0)
+ return &zippers[i];
+ }
+ return NULL;
+}
+
+static u32 crc32(u32 seed, const char *buffer, int size)
+{
+ int i, j;
+ u32 byte, crc, mask;
+
+ crc = seed;
+ for (i = 0; i < size; i++) {
+ byte = buffer[i];
+ crc = crc ^ byte;
+ for (j = 7; j >= 0; j--) {
+ mask = -(crc & 1);
+ crc = (crc >> 1) ^ (0xEDB88320 & mask);
+ }
+ }
+ return crc;
+}
+
+/*
+ * This only works for x86 bzImage
+ */
+static void extract_vmlinux(char *bzimage, int bzimage_size,
+ char **file, struct zipper **zipper)
+{
+ int r;
+ char src[15] = "vmlinux-XXXXXX";
+ char dest[15] = "vmlinux-XXXXXX";
+ char cmd[100];
+ int src_fd, dest_fd;
+ int offset, size;
+ struct zipper *z;
+
+ if (!image_supported(bzimage, bzimage_size))
+ return;
+
+ get_payload_info(bzimage, &offset, &size);
+ z = get_zipper(bzimage + offset);
+ if (!z) {
+ err("Unable to determine the compression of vmlinux\n");
+ return;
+ }
+
+ src_fd = mkstemp(src);
+ if (src_fd == -1) {
+ perror("Could not create temp file");
+ return;
+ }
+
+ r = write(src_fd, bzimage + offset, size);
+ if (r != size) {
+ perror("Could not write vmlinux");
+ return;
+ }
+ dest_fd = mkstemp(dest);
+ if (dest_fd == -1) {
+ perror("Could not create temp file");
+ return;
+ }
+
+ snprintf(cmd, sizeof(cmd), "%s <%s >%s", z->command, src, dest);
+ info("Executing: %s\n", cmd);
+ r = system(cmd);
+ if (r != 0)
+ warn("Possible errors when extracting\n");
+
+ r = remove(src);
+ if (r != 0)
+ perror(src);
+
+ *file = strdup(dest);
+ *zipper = z;
+}
+
+static void repack_image(char *bzimage, int bzimage_size,
+ char *vmlinux_file, struct zipper *z)
+{
+ char tmp[15] = "vmlinux-XXXXXX";
+ char cmd[100];
+ int fd;
+ struct stat st;
+ int new_size;
+ int r;
+ int offset, size;
+ u32 *crc;
+
+ get_payload_info(bzimage, &offset, &size);
+
+ fd = mkstemp(tmp);
+ if (fd == -1) {
+ perror("Could not create temp file");
+ return;
+ }
+ snprintf(cmd, sizeof(cmd), "%s <%s >%s",
+ z->compress, vmlinux_file, tmp);
+
+ info("Executing: %s\n", cmd);
+ r = system(cmd);
+ if (r != 0)
+ warn("Possible errors when compressing\n");
+
+ r = remove(vmlinux_file);
+ if (r != 0)
+ perror(vmlinux_file);
+
+ if (fstat(fd, &st)) {
+ perror("Could not determine file size");
+ close(fd);
+ }
+ new_size = st.st_size;
+ if (new_size > size) {
+ err("Increase in compressed size is not supported.\n");
+ err("Old size was %d, new size is %d\n", size, new_size);
+ exit(EXIT_FAILURE);
+ }
+
+ r = read(fd, bzimage + offset, new_size);
+ if (r != new_size)
+ perror(tmp);
+
+ r = remove(tmp);
+ if (r != 0)
+ perror(tmp);
+
+ /* x86 specific patching of bzimage */
+ update_payload_info(bzimage, new_size);
+
+ /* update CRC */
+ crc = (u32 *)(bzimage + bzimage_size - 4);
+ *crc = crc32(~0, bzimage, bzimage_size);
+}
+
static void print_sym(struct sym *s)
{
info("sym: %s\n", s->name);
@@ -380,18 +603,23 @@ static void print_sym(struct sym *s)

static void print_usage(char *e)
{
- printf("Usage %s [-s <System.map>] -b <vmlinux> -c <certfile>\n", e);
+ printf("Usage: %s [-s <System.map>] -b <vmlinux> -c <certfile>\n", e);
+ printf(" %s [-s <System.map>] -z <bzImage> -c <certfile>\n", e);
}

int main(int argc, char **argv)
{
char *system_map_file = NULL;
char *vmlinux_file = NULL;
+ char *bzimage_file = NULL;
char *cert_file = NULL;
int vmlinux_size;
+ int bzimage_size;
int cert_size;
char *vmlinux;
char *cert;
+ char *bzimage = NULL;
+ struct zipper *z = NULL;
FILE *system_map;
int *used;
int opt;
@@ -399,7 +627,7 @@ int main(int argc, char **argv)
struct elf elf;
unsigned char *symtab = NULL;

- while ((opt = getopt(argc, argv, "b:c:s:")) != -1) {
+ while ((opt = getopt(argc, argv, "b:z:c:s:")) != -1) {
switch (opt) {
case 's':
system_map_file = optarg;
@@ -407,6 +635,9 @@ int main(int argc, char **argv)
case 'b':
vmlinux_file = optarg;
break;
+ case 'z':
+ bzimage_file = optarg;
+ break;
case 'c':
cert_file = optarg;
break;
@@ -415,7 +646,9 @@ int main(int argc, char **argv)
}
}

- if (!vmlinux_file || !cert_file) {
+ if (!cert_file ||
+ (!vmlinux_file && !bzimage_file) ||
+ (vmlinux_file && bzimage_file)) {
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
@@ -424,6 +657,16 @@ int main(int argc, char **argv)
if (!cert)
exit(EXIT_FAILURE);

+ if (bzimage_file) {
+ bzimage = map_file(bzimage_file, &bzimage_size);
+ if (!bzimage)
+ exit(EXIT_FAILURE);
+
+ extract_vmlinux(bzimage, bzimage_size, &vmlinux_file, &z);
+ if (!vmlinux_file)
+ exit(EXIT_FAILURE);
+ }
+
vmlinux = map_file(vmlinux_file, &vmlinux_size);
if (!vmlinux)
exit(EXIT_FAILURE);
@@ -477,7 +720,7 @@ int main(int argc, char **argv)
}

/* If the existing cert is the same, don't overwrite */
- if (cert_size == *used &&
+ if (cert_size > 0 && cert_size == *used &&
strncmp(cert_sym.content, cert, cert_size) == 0) {
warn("Certificate was already inserted.\n");
exit(EXIT_SUCCESS);
@@ -511,5 +754,9 @@ int main(int argc, char **argv)
perror(vmlinux_file);
exit(EXIT_FAILURE);
}
+
+ if (bzimage)
+ repack_image(bzimage, bzimage_size, vmlinux_file, z);
+
exit(EXIT_SUCCESS);
}
--
2.17.0


2018-05-02 23:11:26

by Mehmet Kayaalp

[permalink] [raw]
Subject: [PATCH v6 1/4] KEYS: Insert incompressible bytes to reserve space in bzImage

Include a random filled binary in vmlinux at the space reserved with
CONFIG_SYSTEM_EXTRA_CERTIFICATE. This results in an uncompressed reserved
area inside the bzImage as well, so that it can be replaced with an actual
certificate later (after the bzImage is distributed).

The bzImage contains a stripped ELF file with one section containing the
compressed vmlinux. If the reserved space is initially filled with zeros,
certificate insertion will cause a size increase in the compressed vmlinux.
In that case, reconstructing the bzImage would require relocation. To avoid
this situation, the reserved space is initially filled with random bytes.
Since a certificate contains some compressible bytes, after insertion the
vmlinux will hopefully be compressed to a smaller size.

Signed-off-by: Mehmet Kayaalp <[email protected]>
---
certs/.gitignore | 1 +
certs/Makefile | 21 ++++++++++++++++++---
certs/system_certificates.S | 2 +-
3 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/certs/.gitignore b/certs/.gitignore
index f51aea4a71ec..4ecc8dd7559d 100644
--- a/certs/.gitignore
+++ b/certs/.gitignore
@@ -2,3 +2,4 @@
# Generated files
#
x509_certificate_list
+extra_cert_placeholder
diff --git a/certs/Makefile b/certs/Makefile
index 5d0999b9e21b..a284c0c72ce5 100644
--- a/certs/Makefile
+++ b/certs/Makefile
@@ -16,7 +16,12 @@ ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y)
$(eval $(call config_filename,SYSTEM_TRUSTED_KEYS))

# GCC doesn't include .incbin files in -MD generated dependencies (PR#66871)
-$(obj)/system_certificates.o: $(obj)/x509_certificate_list
+ifeq ($(CONFIG_SYSTEM_EXTRA_CERTIFICATE),y)
+system_certs_incbin = $(obj)/x509_certificate_list $(obj)/extra_cert_placeholder
+else
+system_certs_incbin = $(obj)/x509_certificate_list
+endif
+$(obj)/system_certificates.o: $(system_certs_incbin)

# Cope with signing_key.x509 existing in $(srctree) not $(objtree)
AFLAGS_system_certificates.o := -I$(srctree)
@@ -24,12 +29,22 @@ AFLAGS_system_certificates.o := -I$(srctree)
quiet_cmd_extract_certs = EXTRACT_CERTS $(patsubst "%",%,$(2))
cmd_extract_certs = scripts/extract-cert $(2) $@ || ( rm $@; exit 1)

-targets += x509_certificate_list
+targets += $(system_certs_incbin)
$(obj)/x509_certificate_list: scripts/extract-cert $(SYSTEM_TRUSTED_KEYS_SRCPREFIX)$(SYSTEM_TRUSTED_KEYS_FILENAME) FORCE
$(call if_changed,extract_certs,$(SYSTEM_TRUSTED_KEYS_SRCPREFIX)$(CONFIG_SYSTEM_TRUSTED_KEYS))
+
+ifeq ($(CONFIG_SYSTEM_EXTRA_CERTIFICATE),y)
+# Generate incompressible bytes. Use seed to make it reproducible
+quiet_cmd_placeholder = EXTRA_CERT_PLACEHOLDER
+ cmd_placeholder = perl -e 'srand(0); printf("%c", int(rand(256))) for (1..$(2))' > $@
+
+$(obj)/extra_cert_placeholder: FORCE
+ $(call if_changed,placeholder,$(CONFIG_SYSTEM_EXTRA_CERTIFICATE_SIZE))
+endif # CONFIG_SYSTEM_EXTRA_CERTIFICATE
+
endif # CONFIG_SYSTEM_TRUSTED_KEYRING

-clean-files := x509_certificate_list .x509.list
+clean-files := $(system_certs_incbin) .x509.list

ifeq ($(CONFIG_MODULE_SIG),y)
###############################################################################
diff --git a/certs/system_certificates.S b/certs/system_certificates.S
index 3918ff7235ed..e23de70c1a30 100644
--- a/certs/system_certificates.S
+++ b/certs/system_certificates.S
@@ -18,7 +18,7 @@ __cert_list_end:
.globl VMLINUX_SYMBOL(system_extra_cert)
.size system_extra_cert, CONFIG_SYSTEM_EXTRA_CERTIFICATE_SIZE
VMLINUX_SYMBOL(system_extra_cert):
- .fill CONFIG_SYSTEM_EXTRA_CERTIFICATE_SIZE, 1, 0
+ .incbin "certs/extra_cert_placeholder"

.align 4
.globl VMLINUX_SYMBOL(system_extra_cert_used)
--
2.17.0


2018-05-02 23:12:53

by Mehmet Kayaalp

[permalink] [raw]
Subject: [PATCH v6 4/4] KEYS: Print insert-sys-cert information to stdout instead of stderr

Detailed INFO output should go to stdout instead of stderr.

Signed-off-by: Mehmet Kayaalp <[email protected]>
---
scripts/insert-sys-cert.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/insert-sys-cert.c b/scripts/insert-sys-cert.c
index a3bd7ea8a436..caa446fe91b8 100644
--- a/scripts/insert-sys-cert.c
+++ b/scripts/insert-sys-cert.c
@@ -32,7 +32,7 @@
#define USED_SYM "system_extra_cert_used"
#define LSIZE_SYM "system_certificate_list_size"

-#define info(format, args...) fprintf(stderr, "INFO: " format, ## args)
+#define info(format, args...) fprintf(stdout, "INFO: " format, ## args)
#define warn(format, args...) fprintf(stdout, "WARNING: " format, ## args)
#define err(format, args...) fprintf(stderr, "ERROR: " format, ## args)

--
2.17.0


2018-05-03 17:13:34

by James Morris

[permalink] [raw]
Subject: Re: [PATCH v6 0/4] Certificate insertion support for x86 bzImages

On Wed, 2 May 2018, Mehmet Kayaalp wrote:

> These patches add support for modifying the reserved space for extra
> certificates in a compressed bzImage in x86. This allows separating the
> system keyring certificate from the kernel build process. After the kernel
> image is distributed, the insert-sys-cert script can be used to insert the
> certificate for x86.

Can you provide more explanation of how this is useful and who would use
it?

--
James Morris
<[email protected]>


2018-05-03 21:42:36

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH v6 0/4] Certificate insertion support for x86 bzImages

On Fri, 2018-05-04 at 03:11 +1000, James Morris wrote:
> On Wed, 2 May 2018, Mehmet Kayaalp wrote:
>
> > These patches add support for modifying the reserved space for extra
> > certificates in a compressed bzImage in x86. This allows separating the
> > system keyring certificate from the kernel build process. After the kernel
> > image is distributed, the insert-sys-cert script can be used to insert the
> > certificate for x86.
>
> Can you provide more explanation of how this is useful and who would use
> it?

I'm involved in a number projects that rely on a kernel build group to
actually build kernels for their systems.  Reserving memory for
additional public keys, allows product groups to insert public keys
post build.  Initially the product groups might insert development
keys, but eventually they would insert the product's public key.

Mimi


2018-05-04 01:21:28

by Mehmet Kayaalp

[permalink] [raw]
Subject: Re: [PATCH v6 0/4] Certificate insertion support for x86 bzImages


> On May 3, 2018, at 5:42 PM, Mimi Zohar <[email protected]> wrote:
>
> On Fri, 2018-05-04 at 03:11 +1000, James Morris wrote:
>> On Wed, 2 May 2018, Mehmet Kayaalp wrote:
>>
>>> These patches add support for modifying the reserved space for extra
>>> certificates in a compressed bzImage in x86. This allows separating the
>>> system keyring certificate from the kernel build process. After the kernel
>>> image is distributed, the insert-sys-cert script can be used to insert the
>>> certificate for x86.
>>
>> Can you provide more explanation of how this is useful and who would use
>> it?
>
> I'm involved in a number projects that rely on a kernel build group to
> actually build kernels for their systems. Reserving memory for
> additional public keys, allows product groups to insert public keys
> post build. Initially the product groups might insert development
> keys, but eventually they would insert the product's public key.
>
> Mimi

With CONFIG_SYSTEM_TRUSTED_KEYRING, we have a system keyring populated
with keys that we trust. Initial keys are compiled-in:
-The module signing key, as specified by CONFIG_MODULE_SIG_KEY,
-Additional keys, as specified by CONFIG_SYSTEM_TRUSTED_KEYS.

In userspace, we can add more keys to the system keyring, only if they can be
verified by keys already in the keyring.

In kernel or modules, we can bypass this restriction by using the
KEY_ALLOC_BYPASS_RESTRICTION flag. As far as I can tell, only the compiled-in
keys are loaded this way in the upstream kernel.

Other asymmetric keyrings can specify the same restriction using the
"builtin_trusted" option. Currently, CONFIG_INTEGRITY_TRUSTED_KEYRING creates
".ima" and ".evm" keyrings with this restriction. As a result, in order to
add a key to an integrity keyring, either that key needs to be compiled-in,
or it must be signed with a key that is in the system keyring, which again
needs to be compiled-in.

If a user is not compiling their own kernel, and has no access to the module
signing key (or any other compiled-in key), then how can they add their own
keys to the integrity keyrings?

This patchset allows the user to take the kernel from a distro, insert their
own key, sign the resulting image as required for their secure boot method,
and when booted, the system keyring will include their key, which can be used
to control the integrity keys.

An alternative way is to allow the "secondary" trusted keyring to verify the
IMA keys with CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY, but
it is the same problem. Some distros carry patches that populate the
secondary keyring with UEFI keys during kernel initialization, which would
mean having access to a UEFI key (which is also needed with this patchset,
for signing the resulting image), would be enough to add integrity keys. But
that is much more permissive, since ANY key that goes into the secondary
keyring, coming from UEFI, could then be used to control the integrity keys.

Mehmet