From fca4a51b9aac712b3adc5b6b187cc31a8391bcf6 Mon Sep 17 00:00:00 2001 From: Alberto Planas Date: Fri, 22 Mar 2024 22:44:41 +0100 Subject: [PATCH] GRUB: predict cmdline, linux and initrd lines Signed-off-by: Alberto Planas --- src/eventlog.c | 195 ++++++++++++++++++++++++++++++++++++++----------- src/eventlog.h | 26 ++++--- 2 files changed, 169 insertions(+), 52 deletions(-) diff --git a/src/eventlog.c b/src/eventlog.c index db18f41..727f6a9 100644 --- a/src/eventlog.c +++ b/src/eventlog.c @@ -549,14 +549,66 @@ tpm_event_decode_uuid(const unsigned char *data) return uuid; } +/* + * For files residing on the EFI partition, grub usually formats these as + * (hdX,gptY)/EFI/BOOT/some.file + * Once it has determined the final root device, the device part will be + * omitted (eg for kernel and initrd). + */ +static bool +__grub_file_parse(grub_file_t *grub_file, const char *value) +{ + if (value[0] == '/') { + grub_file->device = NULL; + grub_file->path = strdup(value); + } else if (value[0] == '(') { + char *copy = strdup(value); + char *path; + + if ((path = strchr(copy, ')')) == NULL) { + free(copy); + return false; + } + + *path++ = '\0'; + + grub_file->device = strdup(copy + 1); + grub_file->path = strdup(path); + free(copy); + } else { + return false; + } + + return true; +} + +static const char * +__grub_file_join(grub_file_t grub_file) +{ + static char path[PATH_MAX]; + + if (grub_file.device == NULL) + snprintf(path, sizeof(path), "%s", grub_file.path); + else + snprintf(path, sizeof(path), "(%s)%s", grub_file.device, grub_file.path); + + return path; +} + +static void +__grub_file_destroy(grub_file_t *grub_file) +{ + drop_string(&grub_file->device); + drop_string(&grub_file->path); +} + /* * Handle IPL events, which grub2 and sd-boot uses to hide its stuff in */ static void __tpm_event_grub_file_destroy(tpm_parsed_event_t *parsed) { - drop_string(&parsed->grub_file.device); - drop_string(&parsed->grub_file.path); + __grub_file_destroy(&parsed->grub_file); } const char * @@ -564,10 +616,7 @@ __tpm_event_grub_file_describe(const tpm_parsed_event_t *parsed) { static char buffer[1024]; - if (parsed->grub_file.device == NULL) - snprintf(buffer, sizeof(buffer), "grub2 file load from %s", parsed->grub_file.path); - else - snprintf(buffer, sizeof(buffer), "grub2 file load from (%s)%s", parsed->grub_file.device, parsed->grub_file.path); + snprintf(buffer, sizeof(buffer), "grub2 file load from %s", __grub_file_join(parsed->grub_file)); return buffer; } @@ -575,7 +624,7 @@ __tpm_event_grub_file_describe(const tpm_parsed_event_t *parsed) static const tpm_evdigest_t * __tpm_event_grub_file_rehash(const tpm_event_t *ev, const tpm_parsed_event_t *parsed, tpm_event_log_rehash_ctx_t *ctx) { - const struct grub_file_event *evspec = &parsed->grub_file; + const grub_file_event *evspec = &parsed->grub_file; const tpm_evdigest_t *md = NULL; debug(" re-hashing %s\n", __tpm_event_grub_file_describe(parsed)); @@ -606,35 +655,11 @@ __tpm_event_grub_file_rehash(const tpm_event_t *ev, const tpm_parsed_event_t *pa return md; } -/* - * For files residing on the EFI partition, grub usually formats these as - * (hdX,gptY)/EFI/BOOT/some.file - * Once it has determined the final root device, the device part will be - * omitted (eg for kernel and initrd). - */ static bool __tpm_event_grub_file_event_parse(tpm_event_t *ev, tpm_parsed_event_t *parsed, const char *value) { - if (value[0] == '/') { - parsed->grub_file.device = NULL; - parsed->grub_file.path = strdup(value); - } else if (value[0] == '(') { - char *copy = strdup(value); - char *path; - - if ((path = strchr(copy, ')')) == NULL) { - free(copy); - return false; - } - - *path++ = '\0'; - - parsed->grub_file.device = strdup(copy + 1); - parsed->grub_file.path = strdup(path); - free(copy); - } else { + if (!__grub_file_parse(&parsed->grub_file, value)) return false; - } parsed->event_subtype = GRUB_EVENT_FILE; parsed->destroy = __tpm_event_grub_file_destroy; @@ -658,21 +683,87 @@ static const char * __tpm_event_grub_command_describe(const tpm_parsed_event_t *parsed) { static char buffer[128]; + static char *topic = NULL; + + switch (parsed->event_subtype) { + case GRUB_EVENT_COMMAND: + topic = "grub2 command"; + break; + case GRUB_EVENT_COMMAND_LINUX: + topic = "grub2 linux command"; + break; + case GRUB_EVENT_COMMAND_INITRD: + topic = "grub2 initrd command"; + break; + case GRUB_EVENT_KERNEL_CMDLINE: + topic = "grub2 kernel cmdline"; + break; + } + + snprintf(buffer, sizeof(buffer), "%s \"%s\"", topic, parsed->grub_command.string); - if (parsed->event_subtype == GRUB_EVENT_COMMAND) - snprintf(buffer, sizeof(buffer), "grub2 command \"%s\"", parsed->grub_command.string); - else - snprintf(buffer, sizeof(buffer), "grub2 kernel cmdline \"%s\"", parsed->grub_command.string); return buffer; } static const tpm_evdigest_t * __tpm_event_grub_command_rehash(const tpm_event_t *ev, const tpm_parsed_event_t *parsed, tpm_event_log_rehash_ctx_t *ctx) { - if (parsed->grub_command.string == NULL) - return NULL; + char *str = NULL; + size_t sz = 0; + const tpm_evdigest_t *digest = NULL; + grub_file_t file; + + switch (parsed->event_subtype) { + case GRUB_EVENT_COMMAND: + str = strdup(parsed->grub_command.string); + break; + case GRUB_EVENT_COMMAND_LINUX: + if (ctx->boot_entry && parsed->grub_command.file.path) { + file = (grub_file_t) { + .device = parsed->grub_command.file.device, + .path = ctx->boot_entry->image_path, + }; + sz = snprintf(NULL, 0, "linux %s %s", __grub_file_join(file), ctx->boot_entry->options); + str = malloc(sz + 1); + snprintf(str, sz + 1, "linux %s %s", __grub_file_join(file), ctx->boot_entry->options); + debug("Hashed linux command: %s\n", str); + } else + str = strdup(parsed->grub_command.string); + break; + case GRUB_EVENT_COMMAND_INITRD: + if (ctx->boot_entry && parsed->grub_command.file.path) { + file = (grub_file_t) { + .device = parsed->grub_command.file.device, + .path = ctx->boot_entry->initrd_path, + }; + sz = snprintf(NULL, 0, "initrd %s", __grub_file_join(file)); + str = malloc(sz + 1); + snprintf(str, sz + 1, "initrd %s", __grub_file_join(file)); + debug("Hashed initrd command: %s\n", str); + } else + str = strdup(parsed->grub_command.string); + break; + case GRUB_EVENT_KERNEL_CMDLINE: + if (ctx->boot_entry && parsed->grub_command.file.path) { + file = (grub_file_t) { + .device = parsed->grub_command.file.device, + .path = ctx->boot_entry->image_path, + }; + sz = snprintf(NULL, 0, "%s %s", __grub_file_join(file), ctx->boot_entry->options); + str = malloc(sz + 1); + snprintf(str, sz + 1, "%s %s", __grub_file_join(file), ctx->boot_entry->options); + debug("Hashed kernel cmdline: %s\n", str); + } else + str = strdup(parsed->grub_command.string); + break; + } + + if (str) { + digest = digest_compute(ctx->algo, str, strlen(str)); + free(str); + } - return digest_compute(ctx->algo, parsed->grub_command.string, strlen(parsed->grub_command.string)); + return digest; } /* @@ -703,15 +794,29 @@ __tpm_event_grub_command_event_parse(tpm_event_t *ev, tpm_parsed_event_t *parsed keyword = copy; arg = copy + wordlen; + if (!strcmp(keyword, "grub_cmd") && !strncmp(arg, "linux", strlen("linux"))) { + for (wordlen = 0; (cc = arg[wordlen]) && (cc != ' '); ++wordlen) + ; + if (arg[wordlen] == ' ' && !__grub_file_parse(&parsed->grub_command.file, arg + wordlen + 1)) + goto failed; + parsed->event_subtype = GRUB_EVENT_COMMAND_LINUX; + } else + if (!strcmp(keyword, "grub_cmd") && !strncmp(arg, "initrd", strlen("initrd"))) { + for (wordlen = 0; (cc = arg[wordlen]) && (cc != ' '); ++wordlen) + ; + if (arg[wordlen] == ' ' && !__grub_file_parse(&parsed->grub_command.file, arg + wordlen + 1)) + goto failed; + parsed->event_subtype = GRUB_EVENT_COMMAND_INITRD; + } else if (!strcmp(keyword, "grub_cmd")) { parsed->event_subtype = GRUB_EVENT_COMMAND; } else if (!strcmp(keyword, "kernel_cmdline")) { + if (!__grub_file_parse(&parsed->grub_command.file, arg)) + goto failed; parsed->event_subtype = GRUB_EVENT_KERNEL_CMDLINE; - } else { - free(copy); - return false; - } + } else + goto failed; parsed->grub_command.string = strdup(arg); for (argc = 0, s = strtok(arg, " \t"); s && argc < GRUB_COMMAND_ARGV_MAX - 1; s = strtok(NULL, " \t")) { @@ -725,6 +830,10 @@ __tpm_event_grub_command_event_parse(tpm_event_t *ev, tpm_parsed_event_t *parsed free(copy); return true; + +failed: + free(copy); + return false; } static void diff --git a/src/eventlog.h b/src/eventlog.h index d142744..6a8c3a4 100644 --- a/src/eventlog.h +++ b/src/eventlog.h @@ -89,10 +89,12 @@ enum { enum { /* IPL subtypes for grub */ GRUB_EVENT_COMMAND = 0x0001, - GRUB_EVENT_FILE = 0x0002, - GRUB_EVENT_KERNEL_CMDLINE = 0x0003, - SHIM_EVENT_VARIABLE = 0x0004, - SYSTEMD_EVENT_VARIABLE = 0x0005, + GRUB_EVENT_COMMAND_LINUX = 0x0002, + GRUB_EVENT_COMMAND_INITRD = 0x0003, + GRUB_EVENT_FILE = 0x0004, + GRUB_EVENT_KERNEL_CMDLINE = 0x0005, + SHIM_EVENT_VARIABLE = 0x0006, + SYSTEMD_EVENT_VARIABLE = 0x0007, }; enum { @@ -208,6 +210,13 @@ typedef struct tpm_event_log_rehash_ctx { #define GRUB_COMMAND_ARGV_MAX 32 +typedef struct grub_file { + char * device; + char * path; +} grub_file_t; + +typedef grub_file_t grub_file_event; + /* * Parsed event types */ @@ -247,13 +256,12 @@ typedef struct tpm_parsed_event { /* for GRUB_COMMAND, GRUB_KERNEL_CMDLINE */ struct grub_command_event { char * string; - char * argv[GRUB_COMMAND_ARGV_MAX]; + char * argv[GRUB_COMMAND_ARGV_MAX]; + grub_file_t file; } grub_command; - struct grub_file_event { - char * device; - char * path; - } grub_file; + /* for GRUB_FILE */ + grub_file_event grub_file; struct shim_event { char * string;