[PATCH 1/3 v2] KEYS: Support for inserting a certificate into x86 bzImage

From: Mehmet Kayaalp
Date: Wed Jun 22 2016 - 18:38:46 EST


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, a null key is inserted into vmlinux
before creating the bzImage:

make vmlinux
scripts/insert-sys-cert -b vmlinux -c /dev/null
make bzImage

After null key insertion, the script populates the rest of the reserved
space with random bytes, which have poor compression. 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 <mkayaalp@xxxxxxxxxxxxxxxxxx>
Tested-by: Stefan Berger <stefanb@xxxxxxxxxxxxxxxxxx>
Acked-by: Mimi Zohar <zohar@xxxxxxxxxxxxxxxxxx>
---
scripts/insert-sys-cert.c | 210 ++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 203 insertions(+), 7 deletions(-)

diff --git a/scripts/insert-sys-cert.c b/scripts/insert-sys-cert.c
index 8902836..6c6120d 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
@@ -257,6 +258,169 @@ static char *read_file(char *file_name, int *size)
return buf;
}

+static void get_payload_info(char *bzimage, int *offset, int *size)
+{
+ unsigned int system_offset;
+ unsigned char setup_sectors;
+
+ setup_sectors = bzimage[0x1f1] + 1;
+ system_offset = setup_sectors * 512;
+ *offset = system_offset + *((int*)&bzimage[0x248]);
+ *size = *((int*)&bzimage[0x24c]);
+}
+
+static void update_payload_info(char* bzimage, int new_size)
+{
+ int offset, size;
+ get_payload_info(bzimage, &offset, &size);
+ *((int*)&bzimage[0x24c]) = 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;
+}
+
+/*
+ * 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;
+
+ /* TODO: verify that bzImage is supported */
+
+ get_payload_info(bzimage, &offset, &size);
+ z = get_zipper(bzimage + offset);
+ if (z == NULL) {
+ 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;
+
+ 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);
+
+ /* TODO: update CRC */
+
+}
+
+static void fill_random(unsigned char *p, int n) {
+ srand(0);
+ int i;
+ for (i = 0; i < n; i++)
+ p[i] = rand();
+}
+
static void print_sym(Elf_Ehdr *hdr, struct sym *s)
{
info("sym: %s\n", s->name);
@@ -267,18 +431,23 @@ static void print_sym(Elf_Ehdr *hdr, 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;
Elf_Ehdr *hdr;
char *cert;
+ char *bzimage = NULL;
+ struct zipper *z = NULL;
FILE *system_map;
unsigned long *lsize;
int *used;
@@ -286,7 +455,7 @@ int main(int argc, char **argv)
Elf_Shdr *symtab = NULL;
struct sym cert_sym, lsize_sym, used_sym;

- 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;
@@ -294,6 +463,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;
@@ -302,7 +474,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);
}
@@ -311,6 +485,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);
+ }
+
hdr = map_file(vmlinux_file, &vmlinux_size);
if (!hdr)
exit(EXIT_FAILURE);
@@ -386,7 +570,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);
@@ -396,9 +580,11 @@ int main(int argc, char **argv)
warn("Replacing previously inserted certificate.\n");

memcpy(cert_sym.content, cert, cert_size);
+
if (cert_size < cert_sym.size)
- memset(cert_sym.content + cert_size,
- 0, cert_sym.size - cert_size);
+ /* This makes the reserved space incompressable */
+ fill_random(cert_sym.content + cert_size,
+ cert_sym.size - cert_size);

*lsize = *lsize + cert_size - *used;
*used = cert_size;
@@ -406,5 +592,15 @@ int main(int argc, char **argv)
cert_sym.address);
info("Used %d bytes out of %d bytes reserved.\n", *used,
cert_sym.size);
+
+ if (munmap(hdr, vmlinux_size) == -1) {
+ perror(vmlinux_file);
+ exit(EXIT_FAILURE);
+ }
+
+ if (bzimage) {
+ repack_image(bzimage, bzimage_size, vmlinux_file, z);
+ }
+
exit(EXIT_SUCCESS);
}
--
2.7.4