diff --git a/_service b/_service index 99a13d5..6dac258 100644 --- a/_service +++ b/_service @@ -7,7 +7,7 @@ https://github.com/okirch/pcr-oracle.git pcr-oracle @PARENT_TAG@ - refs/tags/0.5.2 + refs/tags/0.5.3 pcr-oracle*.tar diff --git a/boot_entry.patch b/boot_entry.patch new file mode 100644 index 0000000..f7430a1 --- /dev/null +++ b/boot_entry.patch @@ -0,0 +1,2093 @@ +From 0be3db467f042cc030f9a4a840854ce36a0f8317 Mon Sep 17 00:00:00 2001 +From: Olaf Kirch +Date: Mon, 27 Nov 2023 16:48:28 +0100 +Subject: [PATCH 01/13] Remove obsolete pcr_policy_sign_systemd() + +Signed-off-by: Olaf Kirch +--- + src/pcr-policy.c | 51 ------------------------------------------------ + src/pcr.h | 3 --- + 2 files changed, 54 deletions(-) + +diff --git a/src/pcr-policy.c b/src/pcr-policy.c +index 95a4299..8f2c42c 100644 +--- a/src/pcr-policy.c ++++ b/src/pcr-policy.c +@@ -1539,57 +1539,6 @@ pcr_unseal_secret(const target_platform_t *platform, + return platform->unseal_secret(input_path, output_path, pcr_selection, signed_policy_path, public_key_file); + } + +-bool +-pcr_policy_sign_systemd(const tpm_pcr_bank_t *bank, +- const stored_key_t *private_key_file, +- const char *output_path) +-{ +- bool ok = false; +- FILE *fp = NULL; +- tpm_rsa_key_t *rsa_key = NULL; +- const tpm_evdigest_t *digest; +- ESYS_CONTEXT *esys_context = tss_esys_context(); +- TPM2B_DIGEST *pcr_policy = NULL; +- TPMT_SIGNATURE *signed_policy = NULL; +- +- if (!(fp = fopen(output_path, "w"))) { +- error("Cannot open systemd JSON file %s: %m\n", output_path); +- goto out; +- } +- +- if (!(rsa_key = stored_key_read_rsa_private(private_key_file))) +- goto out; +- digest = tpm_rsa_key_public_digest(rsa_key); +- +- if (!(pcr_policy = __pcr_policy_make(esys_context, bank))) +- goto out; +- +- if (!__pcr_policy_sign(rsa_key, pcr_policy, &signed_policy)) +- goto out; +- +- fprintf(fp, "{\n"); +- fprintf(fp, "\t\"%s\": [\n", bank->algo_name); +- fprintf(fp, "\t\t\{\n"); +- fprintf(fp, "\t\t\t\"pcrs\": [\n"); +- fprintf(fp, "\t\t\t\t%s\n", print_pcr_mask(bank->pcr_mask)); +- fprintf(fp, "\t\t\t],\n"); +- fprintf(fp, "\t\t\t\"pkfp\": \"%s\",\n", print_hex_string(digest->data, digest->size)); +- fprintf(fp, "\t\t\t\"pol\": \"%s\",\n", print_hex_string(pcr_policy->buffer, pcr_policy->size)); +- fprintf(fp, "\t\t\t\"sig\": \"%s\"\n", print_base64_value(signed_policy->signature.rsassa.sig.buffer, signed_policy->signature.rsassa.sig.size)); +- fprintf(fp, "\t\t}\n"); +- fprintf(fp, "\t]\n"); +- fprintf(fp, "}\n"); +- +- ok = true; +- +-out: +- if (rsa_key) +- tpm_rsa_key_free(rsa_key); +- +- fclose(fp); +- return ok; +-} +- + /* + * Depending on the target platform, sealed data, authorized policies etc are + * written to different types of files. +diff --git a/src/pcr.h b/src/pcr.h +index 1b0f544..4d8f816 100644 +--- a/src/pcr.h ++++ b/src/pcr.h +@@ -65,9 +65,6 @@ extern bool pcr_policy_sign(const target_platform_t *platform, const tpm_pcr_ba + const stored_key_t *private_key_file, + const char *input_path, + const char *output_path, const char *policy_name); +-extern bool pcr_policy_sign_systemd(const tpm_pcr_bank_t *bank, +- const stored_key_t *private_key_file, +- const char *output_path); + extern bool pcr_authorized_policy_seal_secret(const target_platform_t *platform, + const char *authorized_policy, const char *input_path, + const char *output_path); + +From 3a66b6e1dc953ac95a715a4b1123e772024fba2c Mon Sep 17 00:00:00 2001 +From: Olaf Kirch +Date: Mon, 27 Nov 2023 17:18:14 +0100 +Subject: [PATCH 02/13] Introduce read_single_line_file() and use it in + sd-boot.c + +Signed-off-by: Olaf Kirch +--- + src/sd-boot.c | 30 ++---------------------------- + src/util.c | 19 +++++++++++++++++++ + src/util.h | 1 + + 3 files changed, 22 insertions(+), 28 deletions(-) + +diff --git a/src/sd-boot.c b/src/sd-boot.c +index 3eea1a1..bbdb498 100644 +--- a/src/sd-boot.c ++++ b/src/sd-boot.c +@@ -37,21 +37,8 @@ static const char * + read_entry_token(void) + { + static char id[SDB_LINE_MAX]; +- FILE *fp; +- +- if (!(fp = fopen("/etc/kernel/entry-token", "r"))) { +- debug("Cannot open /etc/kernel/entry-token\n"); +- goto fail; +- } +- +- if (fgets(id, SDB_LINE_MAX, fp)) +- id[strcspn(id, "\n")] = 0; + +- fclose(fp); +- return id; +- +-fail: +- return NULL; ++ return read_single_line_file("/etc/kernel/entry-token", id, sizeof(id)); + } + + static const char * +@@ -109,21 +96,8 @@ static const char * + read_machine_id(void) + { + static char id[SDB_LINE_MAX]; +- FILE *fp; +- +- if (!(fp = fopen("/etc/machine-id", "r"))) { +- error("Cannot open /etc/machine_id: %m\n"); +- goto fail; +- } +- +- if (fgets(id, SDB_LINE_MAX, fp)) +- id[strcspn(id, "\n")] = 0; + +- fclose(fp); +- return id; +- +-fail: +- return NULL; ++ return read_single_line_file("/etc/machine-id", id, sizeof(id)); + } + + static bool +diff --git a/src/util.c b/src/util.c +index 1f2640f..644d138 100644 +--- a/src/util.c ++++ b/src/util.c +@@ -514,3 +514,22 @@ path_has_file_extension(const char *path, const char *suffix) + + return !strcasecmp(path + n, suffix); + } ++ ++const char * ++read_single_line_file(const char *path, char *buffer, size_t size) ++{ ++ FILE *fp; ++ ++ if (!(fp = fopen(path, "r"))) { ++ debug("Cannot open %s: %m\n", path); ++ return NULL; ++ } ++ ++ if (fgets(buffer, size, fp) != NULL) ++ buffer[strcspn(buffer, "\n")] = 0; ++ else ++ buffer[0] = '\0'; ++ ++ fclose(fp); ++ return buffer; ++} +diff --git a/src/util.h b/src/util.h +index c98017f..e63ef16 100644 +--- a/src/util.h ++++ b/src/util.h +@@ -142,6 +142,7 @@ extern double timing_since(double); + extern const char * path_unix2dos(const char *path); + extern const char * path_dos2unix(const char *path); + extern bool path_has_file_extension(const char *path, const char *suffix); ++extern const char * read_single_line_file(const char *path, char *buffer, size_t size); + + extern int version_string_compare(const char *, const char *); + + +From 32680d9efa0b866bd78d7ad5bd362aca511efc05 Mon Sep 17 00:00:00 2001 +From: Olaf Kirch +Date: Mon, 27 Nov 2023 18:26:36 +0100 +Subject: [PATCH 03/13] tpm_event_get_digest: changed second argument from + algo_name to an algo_info pointer + +Signed-off-by: Olaf Kirch +--- + src/efi-application.c | 2 +- + src/efi-variable.c | 6 +++--- + src/eventlog.c | 6 +----- + src/eventlog.h | 2 +- + src/oracle.c | 2 +- + 5 files changed, 7 insertions(+), 11 deletions(-) + +diff --git a/src/efi-application.c b/src/efi-application.c +index 14a2450..06ac9ff 100644 +--- a/src/efi-application.c ++++ b/src/efi-application.c +@@ -287,7 +287,7 @@ __tpm_event_efi_bsa_rehash(const tpm_event_t *ev, const tpm_parsed_event_t *pars + */ + if (!evspec->efi_application) { + debug("Unable to locate boot service application - probably not a file\n"); +- return tpm_event_get_digest(ev, ctx->algo->openssl_name); ++ return tpm_event_get_digest(ev, ctx->algo); + } + + /* The next boot can have a different kernel */ +diff --git a/src/efi-variable.c b/src/efi-variable.c +index 7e9e38b..6c08cde 100644 +--- a/src/efi-variable.c ++++ b/src/efi-variable.c +@@ -141,7 +141,7 @@ __tpm_event_efi_variable_detect_hash_strategy(const tpm_event_t *ev, const tpm_p + { + const tpm_evdigest_t *md, *old_md; + +- old_md = tpm_event_get_digest(ev, algo->openssl_name); ++ old_md = tpm_event_get_digest(ev, algo); + if (old_md == NULL) { + debug("Event does not provide a digest for algorithm %s\n", algo->openssl_name); + return -1; +@@ -199,7 +199,7 @@ __tpm_event_efi_variable_rehash(const tpm_event_t *ev, const tpm_parsed_event_t + * For the time being, just pretend these cannot be changed from + * within the running system. + */ +- md = tpm_event_get_digest(ev, algo->openssl_name); ++ md = tpm_event_get_digest(ev, algo); + goto out; + } + } else { +@@ -214,7 +214,7 @@ __tpm_event_efi_variable_rehash(const tpm_event_t *ev, const tpm_parsed_event_t + /* The content of the variable doesn't exist during the measurement + * and is also not available at runtime. Let's skip this event. + */ +- md = tpm_event_get_digest(ev, algo->openssl_name); ++ md = tpm_event_get_digest(ev, algo); + } + goto out; + } +diff --git a/src/eventlog.c b/src/eventlog.c +index e99a20f..32cc75c 100644 +--- a/src/eventlog.c ++++ b/src/eventlog.c +@@ -406,14 +406,10 @@ tpm_event_type_to_string(unsigned int event_type) + } + + const tpm_evdigest_t * +-tpm_event_get_digest(const tpm_event_t *ev, const char *algo_name) ++tpm_event_get_digest(const tpm_event_t *ev, const tpm_algo_info_t *algo_info) + { +- const tpm_algo_info_t *algo_info; + unsigned int i; + +- if ((algo_info = digest_by_name(algo_name)) < 0) +- fatal("Unknown algo name \"%s\"\n", algo_name); +- + for (i = 0; i < ev->pcr_count; ++i) { + const tpm_evdigest_t *md = &ev->pcr_values[i]; + +diff --git a/src/eventlog.h b/src/eventlog.h +index 8951765..bd1106f 100644 +--- a/src/eventlog.h ++++ b/src/eventlog.h +@@ -291,7 +291,7 @@ extern void tpm_event_log_scan_ctx_init(tpm_event_log_scan_ctx_t *); + extern void tpm_event_log_scan_ctx_destroy(tpm_event_log_scan_ctx_t *); + extern tpm_parsed_event_t * tpm_event_parse(tpm_event_t *ev, tpm_event_log_scan_ctx_t *); + extern const char * tpm_event_type_to_string(unsigned int event_type); +-extern const tpm_evdigest_t * tpm_event_get_digest(const tpm_event_t *ev, const char *algo_name); ++extern const tpm_evdigest_t * tpm_event_get_digest(const tpm_event_t *ev, const tpm_algo_info_t *algo_info); + extern void tpm_parsed_event_print(tpm_parsed_event_t *parsed, + tpm_event_bit_printer *); + extern const char * tpm_parsed_event_describe(tpm_parsed_event_t *parsed); +diff --git a/src/oracle.c b/src/oracle.c +index 52fe261..c4e4873 100644 +--- a/src/oracle.c ++++ b/src/oracle.c +@@ -670,7 +670,7 @@ predictor_update_eventlog(struct predictor *pred) + debug("\n"); + __tpm_event_print(ev, debug); + +- if (!(old_digest = tpm_event_get_digest(ev, pred->algo))) ++ if (!(old_digest = tpm_event_get_digest(ev, pred->algo_info))) + fatal("Event log lacks a hash for digest algorithm %s\n", pred->algo); + + if (false) { + +From 4d88450e9da882c759a68ba8c15267b22a377cdc Mon Sep 17 00:00:00 2001 +From: Olaf Kirch +Date: Mon, 27 Nov 2023 18:31:42 +0100 +Subject: [PATCH 04/13] Add uapi_ types and functions for dealing with UAPI + boot entries + +Signed-off-by: Olaf Kirch +--- + Makefile.in | 3 +- + src/types.h | 1 + + src/uapi.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + src/uapi.h | 43 +++++++ + 4 files changed, 369 insertions(+), 1 deletion(-) + create mode 100644 src/uapi.c + create mode 100644 src/uapi.h + +diff --git a/Makefile.in b/Makefile.in +index a0508e9..02a915b 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -33,7 +33,8 @@ ORACLE_SRCS = oracle.c \ + bufparser.c \ + store.c \ + util.c \ +- sd-boot.c ++ sd-boot.c \ ++ uapi.c + ORACLE_OBJS = $(addprefix build/,$(patsubst %.c,%.o,$(ORACLE_SRCS))) + + all: $(TOOLS) $(MANPAGES) +diff --git a/src/types.h b/src/types.h +index e76903a..5b01079 100644 +--- a/src/types.h ++++ b/src/types.h +@@ -35,6 +35,7 @@ typedef struct pecoff_image_info pecoff_image_info_t; + typedef struct testcase testcase_t; + typedef struct stored_key stored_key_t; + typedef struct target_platform target_platform_t; ++typedef struct uapi_boot_entry uapi_boot_entry_t; + + #endif /* TYPES_H */ + +diff --git a/src/uapi.c b/src/uapi.c +new file mode 100644 +index 0000000..bbcaea9 +--- /dev/null ++++ b/src/uapi.c +@@ -0,0 +1,323 @@ ++/* ++ * Copyright (C) 2022, 2023 SUSE LLC ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Written by Alberto Planas , Olaf Kirch ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "uapi.h" ++#include "util.h" ++ ++#define UAPI_LINE_MAX 1024 ++ ++ ++static int vercmp(const void *va, const void *vb); ++ ++static uapi_boot_entry_t * ++uapi_boot_entry_new(void) ++{ ++ uapi_boot_entry_t *ube; ++ ++ ube = calloc(1, sizeof(*ube)); ++ return ube; ++} ++ ++static void ++drop_boot_entry(uapi_boot_entry_t **entry_p) ++{ ++ if (*entry_p) { ++ uapi_boot_entry_free(*entry_p); ++ *entry_p = NULL; ++ } ++} ++ ++static uapi_boot_entry_t * ++uapi_boot_entry_load(const char *path) ++{ ++ uapi_boot_entry_t *result = NULL; ++ char line[UAPI_LINE_MAX]; ++ FILE *fp; ++ ++ if (!(fp = fopen(path, "r"))) { ++ error("Unable to open %s: %m\n", path); ++ return NULL; ++ } ++ ++ result = uapi_boot_entry_new(); ++ while (fgets(line, sizeof(line), fp)) { ++ char *key, *value; ++ unsigned int i; ++ ++ if (!isalpha(line[0])) ++ continue; ++ ++ /* strip white space off the end */ ++ i = strlen(line); ++ while (i > 0 && isspace(line[i-1])) ++ line[--i] = '\0'; ++ ++ key = line; ++ for (i = 0; line[i]; ++i) { ++ if (isspace(line[i])) { ++ line[i++] = '\0'; ++ break; ++ } ++ } ++ ++ while (isspace(line[i])) ++ ++i; ++ value = line + i; ++ if (*value == '\0') ++ value = NULL; ++ ++ if (!strcmp("sort-key", key)) ++ assign_string(&result->sort_key, value); ++ else ++ if (!strcmp("machine-id", key)) ++ assign_string(&result->machine_id, value); ++ else ++ if (!strcmp("version", key)) ++ assign_string(&result->version, value); ++ else ++ if (!strcmp("options", key)) ++ assign_string(&result->options, value); ++ else ++ if (!strcmp("linux", key)) ++ assign_string(&result->image_path, value); ++ else ++ if (!strcmp("initrd", key)) ++ assign_string(&result->initrd_path, value); ++ } ++ ++ fclose(fp); ++ return result; ++} ++ ++static bool ++uapi_boot_entry_applies(const uapi_boot_entry_t *entry, const char *machine_id, const char *architecture) ++{ ++ if (entry->machine_id && machine_id && strcmp(entry->machine_id, machine_id)) ++ return false; ++ if (entry->architecture && architecture && strcmp(entry->architecture, architecture)) ++ return false; ++ /* We don't check the efi key because it can only express "entry is EFI only"; it cannot express ++ * "this is a legacy BIOS entry" */ ++ return true; ++} ++ ++/* ++ * Returns true iff entry_a is "more recent" or "better" than entry_b ++ */ ++static bool ++uapi_boot_entry_more_recent(const uapi_boot_entry_t *entry_a, const uapi_boot_entry_t *entry_b) ++{ ++ int r = 0; ++ ++ /* having a sort key is better than not having one. */ ++ r = strcmp(entry_a->sort_key? : "", entry_b->sort_key? : ""); ++ ++ if (r == 0) ++ r = vercmp(entry_a->version? : "", entry_b->version? : ""); ++ ++ return r > 0; ++} ++ ++/* ++ * We pass in a best_ret pointer in case we need to extend this ++ * to search more than on directory ++ */ ++uapi_boot_entry_t * ++uapi_find_matching_boot_entry(const char *dir_path, ++ const char *entry_id, const char *machine_id, const char *architecture, ++ uapi_boot_entry_t **best_ret) ++{ ++ uapi_boot_entry_t *best = *best_ret; ++ unsigned int entry_id_len = 0; ++ struct dirent *d; ++ DIR *dir; ++ ++ if (!(dir = opendir(dir_path))) { ++ if (errno != ENOENT) ++ error("Cannot open %s for reading: %m\n", dir_path); ++ return NULL; ++ } ++ ++ if (entry_id) ++ entry_id_len = strlen(entry_id); ++ ++ while ((d = readdir(dir)) != NULL) { ++ char config_path[PATH_MAX]; ++ uapi_boot_entry_t *entry; ++ ++ if (d->d_type != DT_REG) ++ continue; ++ ++ if (entry_id && strncmp(d->d_name, entry_id, entry_id_len)) ++ continue; ++ ++ snprintf(config_path, sizeof(config_path), "%s/%s", dir_path, d->d_name); ++ if (!(entry = uapi_boot_entry_load(config_path))) { ++ warning("Unable to process UAPI boot entry file at \"%s\"\n", config_path); ++ continue; ++ } ++ ++ if (uapi_boot_entry_applies(entry, machine_id, architecture)) { ++ if (best == NULL || uapi_boot_entry_more_recent(entry, best)) { ++ /* swap best and entry */ ++ uapi_boot_entry_t *tmp = best; ++ best = entry; ++ entry = tmp; ++ } ++ } ++ ++ drop_boot_entry(&entry); ++ } ++ ++ closedir(dir); ++ ++ *best_ret = best; ++ return best; ++} ++ ++uapi_boot_entry_t * ++uapi_find_boot_entry(const char *id, const char *machine_id) ++{ ++ uapi_boot_entry_t *best = NULL; ++ struct utsname uts; ++ const char *architecture; ++ ++ if (uname(&uts) >= 0) ++ architecture = uts.machine; ++ ++ return uapi_find_matching_boot_entry(UAPI_BOOT_DIRECTORY, ++ id, machine_id, architecture, ++ &best); ++} ++ ++void ++uapi_boot_entry_free(uapi_boot_entry_t *ube) ++{ ++ drop_string(&ube->title); ++ drop_string(&ube->version); ++ drop_string(&ube->machine_id); ++ drop_string(&ube->image_path); ++ drop_string(&ube->initrd_path); ++ drop_string(&ube->options); ++ free(ube); ++} ++ ++/* ++ * Version comparison ++ */ ++static int ++cmp(int a, int b) ++{ ++ return a - b; ++} ++ ++static bool ++isvalid(char a) ++{ ++ return isalnum(a) || a == '~' || a == '-' || a == '^' || a == '.'; ++} ++ ++static int ++natoi(const char *a, unsigned int n) ++{ ++ char line[UAPI_LINE_MAX]; ++ ++ strncpy(line, a, MIN(UAPI_LINE_MAX, n)); ++ return atoi(line); ++} ++ ++static int ++vercmp(const void *va, const void *vb) ++{ ++ /* https://uapi-group.org/specifications/specs/version_format_specification/ */ ++ /* This code is based on strverscmp_improved from systemd */ ++ ++ const char *a = va; ++ const char *b = vb; ++ const char *sep = "~-^."; ++ ++ assert(a != NULL); ++ assert(b != NULL); ++ ++ for(;;) { ++ const char *aa, *bb; ++ int r; ++ ++ while (*a != '\0' && !isvalid(*a)) ++ a++; ++ while (*b != '\0' && !isvalid(*b)) ++ b++; ++ ++ /* The longer string is considered new */ ++ if (*a == '\0' || *b == '\0') ++ return cmp(*a, *b); ++ ++ for (int i = 0; i < strlen(sep); i++) { ++ char s = sep[i]; ++ ++ if (*a == s || *b == s) { ++ r = cmp(*a != s, *b != s); ++ if (r != 0) ++ return r; ++ ++ a++; ++ b++; ++ } ++ } ++ ++ if (isdigit(*a) || isdigit(*b)) { ++ for (aa = a; isdigit(*aa); aa++); ++ for (bb = b; isdigit(*bb); bb++); ++ ++ r = cmp(a != aa, b != bb); ++ if (r != 0) ++ return r; ++ ++ r = cmp(natoi(a, aa - a), natoi(b, bb - b)); ++ if (r != 0) ++ return r; ++ } else { ++ for (aa = a; isalpha(*aa); aa++); ++ for (bb = b; isalpha(*bb); bb++); ++ ++ r = cmp(strncmp(a, b, MIN(aa - a, bb - b)), 0); ++ if (r != 0) ++ return r; ++ ++ r = cmp(aa - a, bb - b); ++ if (r != 0) ++ return r; ++ } ++ ++ a = aa; ++ b = bb; ++ } ++} ++ +diff --git a/src/uapi.h b/src/uapi.h +new file mode 100644 +index 0000000..ba1147e +--- /dev/null ++++ b/src/uapi.h +@@ -0,0 +1,43 @@ ++/* ++ * Copyright (C) 2022, 2023 SUSE LLC ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Written by Olaf Kirch ++ */ ++ ++#ifndef UAPI_H ++#define UAPI_H ++ ++#include "types.h" ++ ++struct uapi_boot_entry { ++ char * title; ++ bool efi; ++ char * sort_key; ++ char * version; ++ char * machine_id; ++ char * architecture; ++ char * image_path; ++ char * initrd_path; ++ char * options; ++}; ++ ++#define UAPI_BOOT_DIRECTORY "/boot/efi/loader/entries" ++ ++extern uapi_boot_entry_t * uapi_find_boot_entry(const char *id, const char *machine_id); ++extern void uapi_boot_entry_free(uapi_boot_entry_t *); ++ ++#endif /* UAPI_H */ + +From a7a4c854c9668900c8eb9ea9c99d51b6e822f376 Mon Sep 17 00:00:00 2001 +From: Olaf Kirch +Date: Mon, 27 Nov 2023 18:33:38 +0100 +Subject: [PATCH 05/13] Add --next-kernel command line option; use uapi to + locate next kernel when given + +Signed-off-by: Olaf Kirch +--- + man/pcr-oracle.8.in | 12 ++++++++++++ + src/eventlog.h | 3 +++ + src/oracle.c | 23 +++++++++++++++++++++-- + src/sd-boot.c | 19 +++++++++++++++++++ + src/sd-boot.h | 3 +++ + 5 files changed, 58 insertions(+), 2 deletions(-) + +diff --git a/man/pcr-oracle.8.in b/man/pcr-oracle.8.in +index eda3cfb..4bd60ba 100644 +--- a/man/pcr-oracle.8.in ++++ b/man/pcr-oracle.8.in +@@ -495,6 +495,18 @@ events (tracked in PCR9). In this case, it would be totally sufficient + to stop processing after grub loaded \fPgrub.cfg\fP from the EFI + System Partition. + .TP ++.BI --next-kernel " id ++In a systemd-boot environment, the values of PCR registers also depend on ++the contents of the kernel and initrd image, as well as the kernel command line ++options. After a kernel update or an initrd rebuild, these values will change. ++So when predicting PCR values in this case, \fBpcr-oracle\fP needs to be ++instructed to refer to the future kernel rather than the current one. ++.IP ++This is what the \fB--next-kernel\fP option is for. It takes one argument, which ++is either an ID (in the sense of systemd-boot IDs), or \fBauto\fP. When ++the latter is given, \fBpcr-oracle\fP will make a best guess as to what ++kernel image will be used on next boot. ++.TP + .BI --authorized-policy " path + Specify the location of the authorized policy. In conjunction with + the \fBcreate-authorized-policy\fP action, the newly created policy +diff --git a/src/eventlog.h b/src/eventlog.h +index bd1106f..65e5322 100644 +--- a/src/eventlog.h ++++ b/src/eventlog.h +@@ -200,6 +200,9 @@ typedef struct tpm_event_log_rehash_ctx { + bool use_pesign; /* compute authenticode FP using external pesign application */ + + const pecoff_image_info_t *next_stage_img; ++ ++ /* This get set when the user specifies --next-kernel */ ++ uapi_boot_entry_t * next_kernel; + } tpm_event_log_rehash_ctx_t; + + #define GRUB_COMMAND_ARGV_MAX 32 +diff --git a/src/oracle.c b/src/oracle.c +index c4e4873..2cc53c1 100644 +--- a/src/oracle.c ++++ b/src/oracle.c +@@ -34,6 +34,7 @@ + #include "rsa.h" + #include "store.h" + #include "testcase.h" ++#include "sd-boot.h" + + enum { + ACTION_NONE, +@@ -58,6 +59,7 @@ struct predictor { + const char * initial_source; + + const char * tpm_event_log_path; ++ const char * next_kernel_id; + + const char * algo; + const tpm_algo_info_t * algo_info; +@@ -98,6 +100,7 @@ enum { + OPT_POLICY_NAME, + OPT_POLICY_FORMAT, + OPT_TARGET_PLATFORM, ++ OPT_NEXT_KERNEL, + }; + + static struct option options[] = { +@@ -114,6 +117,7 @@ static struct option options[] = { + { "before", no_argument, 0, OPT_BEFORE }, + { "verify", required_argument, 0, OPT_VERIFY }, + { "use-pesign", no_argument, 0, OPT_USE_PESIGN }, ++ { "next-kernel", required_argument, 0, OPT_NEXT_KERNEL }, + { "create-testcase", required_argument, 0, OPT_CREATE_TESTCASE }, + { "replay-testcase", required_argument, 0, OPT_REPLAY_TESTCASE }, + +@@ -129,6 +133,7 @@ static struct option options[] = { + { "policy-name", required_argument, 0, OPT_POLICY_NAME }, + { "policy-format", required_argument, 0, OPT_POLICY_FORMAT }, + { "target-platform", required_argument, 0, OPT_TARGET_PLATFORM }, ++ { "next-kernel", required_argument, 0, OPT_NEXT_KERNEL }, + + { NULL } + }; +@@ -250,7 +255,8 @@ predictor_load_eventlog(struct predictor *pred) + static struct predictor * + predictor_new(const tpm_pcr_selection_t *pcr_selection, const char *source, + const char *tpm_eventlog_path, +- const char *output_format) ++ const char *output_format, ++ const char *next_kernel_id) + { + struct predictor *pred; + +@@ -260,6 +266,7 @@ predictor_new(const tpm_pcr_selection_t *pcr_selection, const char *source, + pred = calloc(1, sizeof(*pred)); + pred->pcr_mask = pcr_selection->pcr_mask; + pred->initial_source = source; ++ pred->next_kernel_id = next_kernel_id; + + pred->algo = pcr_selection->algo_info->openssl_name; + pred->algo_info = pcr_selection->algo_info; +@@ -651,6 +658,14 @@ predictor_update_eventlog(struct predictor *pred) + tpm_event_log_rehash_ctx_init(&rehash_ctx, pred->algo_info); + rehash_ctx.use_pesign = opt_use_pesign; + ++ /* The argument given to --next-kernel will be either "auto" or the ++ * systemd ID of the next kernel entry to be booted. ++ * FIXME: we should probably hide this behind a target_platform function. ++ */ ++ if (pred->next_kernel_id != NULL ++ && !(rehash_ctx.next_kernel = sdb_identify_next_kernel(pred->next_kernel_id))) ++ fatal("unable to identify next kernel \"%s\"\n", pred->next_kernel_id); ++ + for (ev = pred->event_log; ev; ev = ev->next) { + tpm_evdigest_t *pcr; + bool stop = false; +@@ -1021,6 +1036,7 @@ main(int argc, char **argv) + char *opt_rsa_bits = NULL; + char *opt_policy_name = NULL; + char *opt_target_platform = NULL; ++ char *opt_next_kernel = NULL; + const target_platform_t *target; + unsigned int action_flags = 0; + unsigned int rsa_bits = 2048; +@@ -1055,6 +1071,9 @@ main(int argc, char **argv) + case OPT_USE_PESIGN: + opt_use_pesign = 1; + break; ++ case OPT_NEXT_KERNEL: ++ opt_next_kernel = optarg; ++ break; + case OPT_STOP_EVENT: + opt_stop_event = optarg; + break; +@@ -1296,7 +1315,7 @@ main(int argc, char **argv) + fatal("BUG: action %u should have parsed a PCR selection argument", action); + + pred = predictor_new(pcr_selection, opt_from, opt_eventlog_path, +- opt_output_format); ++ opt_output_format, opt_next_kernel); + + if (opt_stop_event) + predictor_set_stop_event(pred, opt_stop_event, !opt_stop_before); +diff --git a/src/sd-boot.c b/src/sd-boot.c +index bbdb498..2e7ec61 100644 +--- a/src/sd-boot.c ++++ b/src/sd-boot.c +@@ -387,6 +387,25 @@ sdb_get_next_kernel(void) + return NULL; + } + ++/* ++ * Identify the next kernel and initrd given an ID ++ */ ++uapi_boot_entry_t * ++sdb_identify_next_kernel(const char *id) ++{ ++ const char *machine_id; ++ ++ if (id == NULL || !strcasecmp(id, "default")) { ++ if (!(id = get_token_id())) ++ return NULL; ++ } ++ ++ if (!(machine_id = read_machine_id())) ++ return NULL; ++ ++ return uapi_find_boot_entry(id, machine_id); ++} ++ + /* + * Update the systemd json file + */ +diff --git a/src/sd-boot.h b/src/sd-boot.h +index ef36ce7..fa56751 100644 +--- a/src/sd-boot.h ++++ b/src/sd-boot.h +@@ -21,6 +21,8 @@ + #define SD_BOOT_H + + #include ++#include "uapi.h" ++#include "types.h" + + #define SDB_MAX_ENTRIES 16 + #define SDB_LINE_MAX 512 +@@ -40,6 +42,7 @@ typedef struct sdb_entry_list { + sdb_entry_data_t entries[SDB_MAX_ENTRIES]; + } sdb_entry_list_t; + ++extern uapi_boot_entry_t * sdb_identify_next_kernel(const char *id); + extern bool sdb_get_entry_list(sdb_entry_list_t *result); + extern bool sdb_is_kernel(const char *application); + extern const char * sdb_get_next_kernel(void); + +From dcfc751ac78400d5bca53b560768f6a3603443f7 Mon Sep 17 00:00:00 2001 +From: Olaf Kirch +Date: Mon, 27 Nov 2023 18:41:26 +0100 +Subject: [PATCH 06/13] Adjust EFI BSA and systemd event rehashing to use + rehash_ctx->next_kernel + +Signed-off-by: Olaf Kirch +--- + src/efi-application.c | 4 ++-- + src/eventlog.c | 38 +++++++++++++++++++++----------------- + 2 files changed, 23 insertions(+), 19 deletions(-) + +diff --git a/src/efi-application.c b/src/efi-application.c +index 06ac9ff..9d7e531 100644 +--- a/src/efi-application.c ++++ b/src/efi-application.c +@@ -291,8 +291,8 @@ __tpm_event_efi_bsa_rehash(const tpm_event_t *ev, const tpm_parsed_event_t *pars + } + + /* The next boot can have a different kernel */ +- if (sdb_is_kernel(evspec->efi_application)) { +- new_application = sdb_get_next_kernel(); ++ if (sdb_is_kernel(evspec->efi_application) && ctx->next_kernel) { ++ new_application = ctx->next_kernel->image_path; + if (new_application) { + evspec_clone = *evspec; + evspec_clone.efi_application = strdup(new_application); +diff --git a/src/eventlog.c b/src/eventlog.c +index 32cc75c..3498790 100644 +--- a/src/eventlog.c ++++ b/src/eventlog.c +@@ -32,7 +32,7 @@ + #include "runtime.h" + #include "digest.h" + #include "util.h" +-#include "sd-boot.h" ++#include "uapi.h" + + #define TPM_EVENT_LOG_MAX_ALGOS 64 + +@@ -789,21 +789,24 @@ __tpm_event_systemd_describe(const tpm_parsed_event_t *parsed) + static const tpm_evdigest_t * + __tpm_event_systemd_rehash(const tpm_event_t *ev, const tpm_parsed_event_t *parsed, tpm_event_log_rehash_ctx_t *ctx) + { +- sdb_entry_list_t entry_list; ++ const uapi_boot_entry_t *next_kernel = ctx->next_kernel; + char initrd[2048]; + char initrd_utf16[4096]; + unsigned int len; + +- memset(&entry_list, 0, sizeof(entry_list)); +- if (!sdb_get_entry_list(&entry_list)) { +- error("Error generating the list of boot entries\n"); ++ /* If no --next-kernel option was given, do not rehash anything */ ++ if (next_kernel == NULL) ++ return tpm_event_get_digest(ev, ctx->algo); ++ ++ if (!next_kernel->image_path) { ++ error("Unable to identify the next kernel\n"); + return NULL; + } + +- debug("Next boot entry expected from: %s\n", entry_list.entries[0].path); ++ debug("Next boot entry expected from: %s %s\n", next_kernel->title, next_kernel->version? : ""); + snprintf(initrd, sizeof(initrd), "initrd=%s %s", +- path_unix2dos(entry_list.entries[0].path), +- entry_list.entries[0].options); ++ path_unix2dos(next_kernel->initrd_path), ++ next_kernel->options? : ""); + + len = (strlen(initrd) + 1) << 1; + assert(len <= sizeof(initrd_utf16)); +@@ -860,19 +863,20 @@ __tpm_event_tag_initrd_describe(const tpm_parsed_event_t *parsed) + static const tpm_evdigest_t * + __tpm_event_tag_initrd_rehash(const tpm_event_t *ev, const tpm_parsed_event_t *parsed, tpm_event_log_rehash_ctx_t *ctx) + { +- sdb_entry_list_t entry_list; +- const tpm_evdigest_t *md = NULL; ++ const uapi_boot_entry_t *next_kernel = ctx->next_kernel; ++ ++ /* If no --next-kernel option was given, do not rehash anything */ ++ if (next_kernel == NULL) ++ return tpm_event_get_digest(ev, ctx->algo); + +- memset(&entry_list, 0, sizeof(entry_list)); +- if (!sdb_get_entry_list(&entry_list)) { +- error("Error generating the list of boot entries\n"); ++ if (!next_kernel->initrd_path) { ++ /* Can this happen eg when going from a split kernel to a unified kernel? */ ++ error("Unable to identify the next initrd\n"); + return NULL; + } + +- debug("Next boot entry expected from: %s\n", entry_list.entries[0].path); +- md = runtime_digest_efi_file(ctx->algo, entry_list.entries[0].initrd); +- +- return md; ++ debug("Next boot entry expected from: %s %s\n", next_kernel->title, next_kernel->version? : ""); ++ return runtime_digest_efi_file(ctx->algo, next_kernel->initrd_path); + } + + /* + +From 13f67d840ffadccbd0daf4fb133f9471a5b1a439 Mon Sep 17 00:00:00 2001 +From: Olaf Kirch +Date: Mon, 27 Nov 2023 18:44:05 +0100 +Subject: [PATCH 07/13] Remove old code from sd-boot.* + +Signed-off-by: Olaf Kirch +--- + src/sd-boot.c | 222 +------------------------------------------------- + src/sd-boot.h | 2 - + 2 files changed, 3 insertions(+), 221 deletions(-) + +diff --git a/src/sd-boot.c b/src/sd-boot.c +index 2e7ec61..c878f6c 100644 +--- a/src/sd-boot.c ++++ b/src/sd-boot.c +@@ -100,163 +100,6 @@ read_machine_id(void) + return read_single_line_file("/etc/machine-id", id, sizeof(id)); + } + +-static bool +-read_entry(sdb_entry_data_t *result) +-{ +- FILE *fp; +- char line[SDB_LINE_MAX]; +- +- if (!(fp = fopen(result->path, "r"))) { +- error("Cannot open %s: %m\n", result->path); +- goto fail; +- } +- +- while (fgets(line, SDB_LINE_MAX, fp)) { +- char *dest = NULL; +- +- if (!strncmp("sort-key", line, strlen("sort-key"))) +- dest = result->sort_key; +- else +- if (!strncmp("machine-id", line, strlen("machine-id"))) +- dest = result->machine_id; +- else +- if (!strncmp("version", line, strlen("version"))) +- dest = result->version; +- else +- if (!strncmp("options", line, strlen("options"))) +- dest = result->options; +- else +- if (!strncmp("linux", line, strlen("linux"))) +- dest = result->image; +- else +- if (!strncmp("initrd", line, strlen("initrd"))) +- dest = result->initrd; +- else +- continue; +- +- /* Position the index on the value section of the line */ +- unsigned int index = 0; +- while (line[++index] != ' '); +- while (line[++index] == ' '); +- strncpy(dest, &line[index], strlen(&line[index]) - 1); +- } +- +- fclose(fp); +- return true; +- +-fail: +- return false; +-} +- +-static int +-cmp(int a, int b) +-{ +- return a - b; +-} +- +-static bool +-isvalid(char a) +-{ +- return isalnum(a) || a == '~' || a == '-' || a == '^' || a == '.'; +-} +- +-static int +-natoi(const char *a, unsigned int n) +-{ +- char line[SDB_LINE_MAX]; +- +- strncpy(line, a, MIN(SDB_LINE_MAX, n)); +- return atoi(line); +-} +- +-static int +-vercmp(const void *va, const void *vb) +-{ +- /* https://uapi-group.org/specifications/specs/version_format_specification/ */ +- /* This code is based on strverscmp_improved from systemd */ +- +- const char *a = va; +- const char *b = vb; +- const char *sep = "~-^."; +- +- assert(a != NULL); +- assert(b != NULL); +- +- for(;;) { +- const char *aa, *bb; +- int r; +- +- while (*a != '\0' && !isvalid(*a)) +- a++; +- while (*b != '\0' && !isvalid(*b)) +- b++; +- +- /* The longer string is considered new */ +- if (*a == '\0' || *b == '\0') +- return cmp(*a, *b); +- +- for (int i = 0; i < strlen(sep); i++) { +- char s = sep[i]; +- +- if (*a == s || *b == s) { +- r = cmp(*a != s, *b != s); +- if (r != 0) +- return r; +- +- a++; +- b++; +- } +- } +- +- if (isdigit(*a) || isdigit(*b)) { +- for (aa = a; isdigit(*aa); aa++); +- for (bb = b; isdigit(*bb); bb++); +- +- r = cmp(a != aa, b != bb); +- if (r != 0) +- return r; +- +- r = cmp(natoi(a, aa - a), natoi(b, bb - b)); +- if (r != 0) +- return r; +- } else { +- for (aa = a; isalpha(*aa); aa++); +- for (bb = b; isalpha(*bb); bb++); +- +- r = cmp(strncmp(a, b, MIN(aa - a, bb - b)), 0); +- if (r != 0) +- return r; +- +- r = cmp(aa - a, bb - b); +- if (r != 0) +- return r; +- } +- +- a = aa; +- b = bb; +- } +-} +- +-static int +-entrycmp(const void *va, const void *vb) +-{ +- /* https://uapi-group.org/specifications/specs/boot_loader_specification/#sorting */ +- int result; +- const sdb_entry_data_t *a = va; +- const sdb_entry_data_t *b = vb; +- +- result = strcmp(a->sort_key, b->sort_key); +- +- if (result == 0) +- result = strcmp(a->machine_id, b->machine_id); +- +- if (result == 0) +- result = vercmp(a->version, b->version); +- +- /* Reverse the order, so new kernels appears first */ +- return -result; +-} +- + static bool + exists_efi_dir(const char *path) + { +@@ -302,49 +145,9 @@ get_token_id(void) + return token_id; + } + +-bool +-sdb_get_entry_list(sdb_entry_list_t *result) +-{ +- const char *token_id = NULL; +- DIR *d = NULL; +- struct dirent *dir; +- char *path = "/boot/efi/loader/entries"; +- +- if (!(token_id = get_token_id())) +- goto fail; +- +- if (!(d = opendir(path))) { +- error("Cannot read directory contents from /boot/efi/loader/entries: %m\n"); +- goto fail; +- } +- +- while ((dir = readdir(d)) != NULL) { +- if (result->num_entries >= SDB_MAX_ENTRIES) +- break; +- +- if (strncmp(token_id, dir->d_name, strlen(token_id))) +- continue; +- +- debug("Bootloader entry %s\n", dir->d_name); +- +- snprintf(result->entries[result->num_entries].path, PATH_MAX, "%s/%s", path, dir->d_name); +- if (!read_entry(&result->entries[result->num_entries])) { +- error("Cannot read bootloader entry %s\n", dir->d_name); +- continue; +- } +- +- result->num_entries++; +- } +- +- qsort(result->entries, result->num_entries, sizeof(result->entries[0]), entrycmp); +- +- closedir(d); +- return true; +- +-fail: +- return false; +-} +- ++/* ++ * This should probably use UAPI boot entry logic as well ++ */ + bool + sdb_is_kernel(const char *application) + { +@@ -368,25 +171,6 @@ sdb_is_kernel(const char *application) + return false; + } + +-const char * +-sdb_get_next_kernel(void) +-{ +- static char result[SDB_LINE_MAX]; +- sdb_entry_list_t entry_list; +- +- memset(&entry_list, 0, sizeof(entry_list)); +- if (!sdb_get_entry_list(&entry_list)) { +- error("Error generating the list of boot entries\n"); +- goto fail; +- } +- +- strncpy(result, entry_list.entries[0].image, SDB_LINE_MAX); +- return result; +- +-fail: +- return NULL; +-} +- + /* + * Identify the next kernel and initrd given an ID + */ +diff --git a/src/sd-boot.h b/src/sd-boot.h +index fa56751..2b11dc1 100644 +--- a/src/sd-boot.h ++++ b/src/sd-boot.h +@@ -43,9 +43,7 @@ typedef struct sdb_entry_list { + } sdb_entry_list_t; + + extern uapi_boot_entry_t * sdb_identify_next_kernel(const char *id); +-extern bool sdb_get_entry_list(sdb_entry_list_t *result); + extern bool sdb_is_kernel(const char *application); +-extern const char * sdb_get_next_kernel(void); + + /* This will have to update the systemd json file, and add a new entry. */ + extern bool sdb_policy_file_add_entry(const char *filename, + +From 737d8d561cfbd1dd03e7c4d37d7c515b709cce11 Mon Sep 17 00:00:00 2001 +From: Olaf Kirch +Date: Tue, 28 Nov 2023 10:30:10 +0100 +Subject: [PATCH 08/13] Manpage fixes + +Signed-off-by: Olaf Kirch +--- + man/pcr-oracle.8.in | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +diff --git a/man/pcr-oracle.8.in b/man/pcr-oracle.8.in +index 4bd60ba..3682afa 100644 +--- a/man/pcr-oracle.8.in ++++ b/man/pcr-oracle.8.in +@@ -82,7 +82,7 @@ object in a native format defined in the TPM specification. + Finally, the systemd-boot implementation for unlocking disk partitions + stores policies in a JSON formatted file, which can be fed to the + \fBsystemd-cryptenroll\fP tool. This file will contain any number of +-signed policies that we want \fBsystemd-cryptsetup\fB to recognize during ++signed policies that we want \fBsystemd-cryptsetup\fP to recognize during + boot time. Each entry contains the list of PCRs that compose the PCR policy, a + finger print of the public key, the hash of the policy and the signature (in + base64) of the PCR policy. +@@ -366,7 +366,7 @@ that. The main difference is in the way the signed policy is stored: + --from eventlog \\ + --output tpm2-pcr-signature.json \\ + --policy-format systemd \\ +- sign 0,2,4,7,9,11 ++ sign 0,2,4,7,9,12 + .fi + .P + This will predict a set of PCR values, sign them using the specified +@@ -384,9 +384,8 @@ These policies can then be enrolled for future system boots using + # systemd-cryptenroll \\ + --tpm2-device=auto \\ + --tpm2-public-key=public-key.pem \\ +- --tpm2-public-key-pcrs="0,2,4,7,9,11" \ ++ --tpm2-public-key-pcrs="0,2,4,7,9,12" \ + --tpm2-signature=tpm2-pcr-signature.json \\ +- --policy-format systemd \\ + /dev/sda3 + .fi + .\" ################################################################## +@@ -510,7 +509,7 @@ kernel image will be used on next boot. + .BI --authorized-policy " path + Specify the location of the authorized policy. In conjunction with + the \fBcreate-authorized-policy\fP action, the newly created policy +-is written to this location. For subsequent actions, such as \fBseal\fB, ++is written to this location. For subsequent actions, such as \fBseal\fP, + the policy will be read from this location. + .TP + .BI --private-key " path + +From c0011884d8685d4f59523e45f69bd77d2c113838 Mon Sep 17 00:00:00 2001 +From: Olaf Kirch +Date: Tue, 28 Nov 2023 10:42:12 +0100 +Subject: [PATCH 09/13] Several fixes resulting from review by alberto + +Signed-off-by: Olaf Kirch +--- + src/uapi.c | 4 +--- + src/util.c | 2 +- + 2 files changed, 2 insertions(+), 4 deletions(-) + +diff --git a/src/uapi.c b/src/uapi.c +index bbcaea9..a12ad21 100644 +--- a/src/uapi.c ++++ b/src/uapi.c +@@ -123,8 +123,6 @@ uapi_boot_entry_applies(const uapi_boot_entry_t *entry, const char *machine_id, + return false; + if (entry->architecture && architecture && strcmp(entry->architecture, architecture)) + return false; +- /* We don't check the efi key because it can only express "entry is EFI only"; it cannot express +- * "this is a legacy BIOS entry" */ + return true; + } + +@@ -147,7 +145,7 @@ uapi_boot_entry_more_recent(const uapi_boot_entry_t *entry_a, const uapi_boot_en + + /* + * We pass in a best_ret pointer in case we need to extend this +- * to search more than on directory ++ * to search more than one directory + */ + uapi_boot_entry_t * + uapi_find_matching_boot_entry(const char *dir_path, +diff --git a/src/util.c b/src/util.c +index 644d138..7343ffb 100644 +--- a/src/util.c ++++ b/src/util.c +@@ -526,7 +526,7 @@ read_single_line_file(const char *path, char *buffer, size_t size) + } + + if (fgets(buffer, size, fp) != NULL) +- buffer[strcspn(buffer, "\n")] = 0; ++ buffer[strcspn(buffer, "\n")] = '\0'; + else + buffer[0] = '\0'; + + +From f0c09557aa72794c7149529056e6fc9ea1cd2763 Mon Sep 17 00:00:00 2001 +From: Olaf Kirch +Date: Tue, 28 Nov 2023 10:45:15 +0100 +Subject: [PATCH 10/13] When matching UAPI boot entries, check against all + possible entry-tokens + +Signed-off-by: Olaf Kirch +--- + src/sd-boot.c | 87 +++++++++++++++++++++++++++++++++++++-------------- + src/uapi.c | 50 ++++++++++++++++++++++++----- + src/uapi.h | 11 ++++++- + 3 files changed, 115 insertions(+), 33 deletions(-) + +diff --git a/src/sd-boot.c b/src/sd-boot.c +index c878f6c..8dc9bf1 100644 +--- a/src/sd-boot.c ++++ b/src/sd-boot.c +@@ -32,7 +32,6 @@ + #include "sd-boot.h" + #include "util.h" + +- + static const char * + read_entry_token(void) + { +@@ -100,6 +99,34 @@ read_machine_id(void) + return read_single_line_file("/etc/machine-id", id, sizeof(id)); + } + ++/* ++ * Kernels installed by kernel-install can use a variety of IDs as entry-token. ++ * Try to cater to all of them. ++ */ ++static const uapi_kernel_entry_tokens_t * ++get_valid_kernel_entry_tokens(void) ++{ ++ static uapi_kernel_entry_tokens_t valid_tokens; ++ const char *token; ++ ++ if (valid_tokens.count != 0) ++ return &valid_tokens; /* I've been here before */ ++ ++ if ((token = read_entry_token()) != NULL) ++ uapi_kernel_entry_tokens_add(&valid_tokens, token); ++ ++ if ((token = read_machine_id()) != NULL) ++ uapi_kernel_entry_tokens_add(&valid_tokens, token); ++ ++ if ((token = read_os_release("ID")) != NULL) ++ uapi_kernel_entry_tokens_add(&valid_tokens, token); ++ ++ if ((token = read_os_release("IMAGE_ID")) != NULL) ++ uapi_kernel_entry_tokens_add(&valid_tokens, token); ++ ++ return &valid_tokens; ++} ++ + static bool + exists_efi_dir(const char *path) + { +@@ -151,24 +178,28 @@ get_token_id(void) + bool + sdb_is_kernel(const char *application) + { +- const char *token_id; +- const char *prefix = "linux-"; +- char path[PATH_MAX]; +- +- if (!(token_id = get_token_id())) +- goto fail; +- +- snprintf(path, PATH_MAX, "/%s/", token_id); +- if (strncmp(path, application, strlen(path))) +- goto fail; +- +- strncpy(path, application, PATH_MAX); +- for (char *ptr = strtok(path, "/"); ptr; ptr = strtok(NULL, "/")) +- if (!strncmp(ptr, prefix, strlen(prefix))) +- return true; ++ static const char prefix[] = "linux-"; ++ const uapi_kernel_entry_tokens_t *match; ++ char *path_copy; ++ int found = 0; ++ ++ match = get_valid_kernel_entry_tokens(); ++ path_copy = strdup(application); ++ ++ for (char *ptr = strtok(path_copy, "/"); ptr; ptr = strtok(NULL, "/")) { ++ unsigned int i; ++ for (i = 0; i < match->count; ++i) { ++ const char *token = match->entry_token[i]; ++ ++ if (!strcmp(ptr, token)) ++ found |= 1; ++ else if (!strncmp(ptr, prefix, sizeof(prefix) - 1)) ++ found |= 2; ++ } ++ } + +-fail: +- return false; ++ free(path_copy); ++ return (found == 3); + } + + /* +@@ -177,17 +208,25 @@ sdb_is_kernel(const char *application) + uapi_boot_entry_t * + sdb_identify_next_kernel(const char *id) + { ++ uapi_kernel_entry_tokens_t id_match = { 0 }; ++ const uapi_kernel_entry_tokens_t *match; + const char *machine_id; ++ uapi_boot_entry_t *result = NULL; + +- if (id == NULL || !strcasecmp(id, "default")) { +- if (!(id = get_token_id())) +- return NULL; ++ if (id == NULL || !strcasecmp(id, "auto")) { ++ match = get_valid_kernel_entry_tokens(); ++ } else { ++ uapi_kernel_entry_tokens_add(&id_match, id); ++ match = &id_match; + } + +- if (!(machine_id = read_machine_id())) +- return NULL; ++ machine_id = read_machine_id(); ++ ++ if (machine_id != NULL) ++ result = uapi_find_boot_entry(match, machine_id); + +- return uapi_find_boot_entry(id, machine_id); ++ uapi_kernel_entry_tokens_destroy(&id_match); ++ return result; + } + + /* +diff --git a/src/uapi.c b/src/uapi.c +index a12ad21..acba37a 100644 +--- a/src/uapi.c ++++ b/src/uapi.c +@@ -149,11 +149,10 @@ uapi_boot_entry_more_recent(const uapi_boot_entry_t *entry_a, const uapi_boot_en + */ + uapi_boot_entry_t * + uapi_find_matching_boot_entry(const char *dir_path, +- const char *entry_id, const char *machine_id, const char *architecture, ++ const uapi_kernel_entry_tokens_t *match, const char *machine_id, const char *architecture, + uapi_boot_entry_t **best_ret) + { + uapi_boot_entry_t *best = *best_ret; +- unsigned int entry_id_len = 0; + struct dirent *d; + DIR *dir; + +@@ -163,9 +162,6 @@ uapi_find_matching_boot_entry(const char *dir_path, + return NULL; + } + +- if (entry_id) +- entry_id_len = strlen(entry_id); +- + while ((d = readdir(dir)) != NULL) { + char config_path[PATH_MAX]; + uapi_boot_entry_t *entry; +@@ -173,7 +169,7 @@ uapi_find_matching_boot_entry(const char *dir_path, + if (d->d_type != DT_REG) + continue; + +- if (entry_id && strncmp(d->d_name, entry_id, entry_id_len)) ++ if (match && !uapi_kernel_entry_tokens_match_filename(match, d->d_name)) + continue; + + snprintf(config_path, sizeof(config_path), "%s/%s", dir_path, d->d_name); +@@ -201,7 +197,7 @@ uapi_find_matching_boot_entry(const char *dir_path, + } + + uapi_boot_entry_t * +-uapi_find_boot_entry(const char *id, const char *machine_id) ++uapi_find_boot_entry(const uapi_kernel_entry_tokens_t *match, const char *machine_id) + { + uapi_boot_entry_t *best = NULL; + struct utsname uts; +@@ -211,7 +207,7 @@ uapi_find_boot_entry(const char *id, const char *machine_id) + architecture = uts.machine; + + return uapi_find_matching_boot_entry(UAPI_BOOT_DIRECTORY, +- id, machine_id, architecture, ++ match, machine_id, architecture, + &best); + } + +@@ -227,6 +223,44 @@ uapi_boot_entry_free(uapi_boot_entry_t *ube) + free(ube); + } + ++/* ++ * Manage list of valid entry-tokens ++ */ ++void ++uapi_kernel_entry_tokens_add(uapi_kernel_entry_tokens_t *match, const char *id) ++{ ++ if (id == NULL) ++ return; ++ ++ if (match->count >= UAPI_MAX_ENTRY_TOKENS) ++ fatal("%s: too many tokens\n", __func__); ++ ++ match->entry_token[match->count++] = strdup(id); ++} ++ ++void ++uapi_kernel_entry_tokens_destroy(uapi_kernel_entry_tokens_t *match) ++{ ++ while (match->count) ++ drop_string(&match->entry_token[--(match->count)]); ++} ++ ++bool ++uapi_kernel_entry_tokens_match_filename(const uapi_kernel_entry_tokens_t *token_list, const char *filename) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < token_list->count; ++i) { ++ const char *token = token_list->entry_token[i]; ++ int len = strlen(token); ++ ++ if (!strncmp(filename, token, len) && filename[len] == '-') ++ return true; ++ } ++ ++ return false; ++} ++ + /* + * Version comparison + */ +diff --git a/src/uapi.h b/src/uapi.h +index ba1147e..cc38395 100644 +--- a/src/uapi.h ++++ b/src/uapi.h +@@ -35,9 +35,18 @@ struct uapi_boot_entry { + char * options; + }; + ++#define UAPI_MAX_ENTRY_TOKENS 8 ++typedef struct uapi_kernel_entry_tokens { ++ unsigned int count; ++ char * entry_token[UAPI_MAX_ENTRY_TOKENS]; ++} uapi_kernel_entry_tokens_t; ++ + #define UAPI_BOOT_DIRECTORY "/boot/efi/loader/entries" + +-extern uapi_boot_entry_t * uapi_find_boot_entry(const char *id, const char *machine_id); ++extern uapi_boot_entry_t * uapi_find_boot_entry(const uapi_kernel_entry_tokens_t *match, const char *machine_id); + extern void uapi_boot_entry_free(uapi_boot_entry_t *); ++extern void uapi_kernel_entry_tokens_add(uapi_kernel_entry_tokens_t *, const char *); ++extern void uapi_kernel_entry_tokens_destroy(uapi_kernel_entry_tokens_t *); ++extern bool uapi_kernel_entry_tokens_match_filename(const uapi_kernel_entry_tokens_t *, const char *filename); + + #endif /* UAPI_H */ + +From 8a0d1e8050340034e4690123c205a8b98abd7d24 Mon Sep 17 00:00:00 2001 +From: Olaf Kirch +Date: Tue, 28 Nov 2023 10:46:20 +0100 +Subject: [PATCH 11/13] Remove code that's been obsoleted by + uapi_kernel_entry_tokens stuff + +Signed-off-by: Olaf Kirch +--- + src/sd-boot.c | 45 --------------------------------------------- + 1 file changed, 45 deletions(-) + +diff --git a/src/sd-boot.c b/src/sd-boot.c +index 8dc9bf1..3b72e37 100644 +--- a/src/sd-boot.c ++++ b/src/sd-boot.c +@@ -127,51 +127,6 @@ get_valid_kernel_entry_tokens(void) + return &valid_tokens; + } + +-static bool +-exists_efi_dir(const char *path) +-{ +- DIR *d = NULL; +- char full_path[PATH_MAX]; +- +- if (path == NULL) +- return false; +- +- snprintf(full_path, PATH_MAX, "/boot/efi/%s", path); +- if (!(d = opendir(full_path))) +- return false; +- +- closedir(d); +- return true; +-} +- +-static const char * +-get_token_id(void) +-{ +- static const char *token_id = NULL; +- const char *id = NULL; +- const char *image_id = NULL; +- const char *machine_id = NULL; +- +- /* All IDs are optional (cannot be present), except machine_id */ +- token_id = read_entry_token(); +- id = read_os_release("ID"); +- image_id = read_os_release("IMAGE_ID"); +- if (!(machine_id = read_machine_id())) +- return NULL; +- +- /* The order is not correct, and it is using some heuristics +- * to find the correct prefix. Other tools like sdbootutil +- * seems to use parameters to decide */ +- if (token_id == NULL && exists_efi_dir(id)) +- token_id = id; +- if (token_id == NULL && exists_efi_dir(image_id)) +- token_id = id; +- if (token_id == NULL && exists_efi_dir(machine_id)) +- token_id = machine_id; +- +- return token_id; +-} +- + /* + * This should probably use UAPI boot entry logic as well + */ + +From 07203a598a95a11afb4cf4a6f50bb92c7d4ae981 Mon Sep 17 00:00:00 2001 +From: Olaf Kirch +Date: Tue, 28 Nov 2023 14:01:40 +0100 +Subject: [PATCH 12/13] Fix an issue with not being able to find an entry + referenced exactly by the boot id + +Signed-off-by: Olaf Kirch +--- + src/sd-boot.c | 8 ++++++++ + src/uapi.c | 31 ++++++++++++++++++++++++++++++- + src/uapi.h | 1 + + 3 files changed, 39 insertions(+), 1 deletion(-) + +diff --git a/src/sd-boot.c b/src/sd-boot.c +index 3b72e37..e3e0568 100644 +--- a/src/sd-boot.c ++++ b/src/sd-boot.c +@@ -171,6 +171,13 @@ sdb_identify_next_kernel(const char *id) + if (id == NULL || !strcasecmp(id, "auto")) { + match = get_valid_kernel_entry_tokens(); + } else { ++ /* Try to load the entry referenced _exactly_ by the ++ * given id. */ ++ result = uapi_get_boot_entry(id); ++ if (result != NULL) ++ goto done; ++ ++ /* No cigar, revert to a prefix based search */ + uapi_kernel_entry_tokens_add(&id_match, id); + match = &id_match; + } +@@ -180,6 +187,7 @@ sdb_identify_next_kernel(const char *id) + if (machine_id != NULL) + result = uapi_find_boot_entry(match, machine_id); + ++done: + uapi_kernel_entry_tokens_destroy(&id_match); + return result; + } +diff --git a/src/uapi.c b/src/uapi.c +index acba37a..f0db62a 100644 +--- a/src/uapi.c ++++ b/src/uapi.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -196,6 +197,29 @@ uapi_find_matching_boot_entry(const char *dir_path, + return best; + } + ++/* ++ * Get the exact boot entry identified by id. ++ * This is mainly for iterating over entries like ++ * for name in "/boot/efi/loader/entries/"*.conf; do ++ * pcr-oracle --boot-entry $(basename $name .conf) ... ++ * done ++ */ ++uapi_boot_entry_t * ++uapi_get_boot_entry(const char *id) ++{ ++ char path[PATH_MAX]; ++ ++ if (path_has_file_extension(id, ".conf")) ++ snprintf(path, sizeof(path), "%s/%s", UAPI_BOOT_DIRECTORY, id); ++ else ++ snprintf(path, sizeof(path), "%s/%s.conf", UAPI_BOOT_DIRECTORY, id); ++ ++ if (access(path, R_OK) < 0) ++ return NULL; ++ ++ return uapi_boot_entry_load(path); ++} ++ + uapi_boot_entry_t * + uapi_find_boot_entry(const uapi_kernel_entry_tokens_t *match, const char *machine_id) + { +@@ -253,8 +277,13 @@ uapi_kernel_entry_tokens_match_filename(const uapi_kernel_entry_tokens_t *token_ + for (i = 0; i < token_list->count; ++i) { + const char *token = token_list->entry_token[i]; + int len = strlen(token); ++ char cc; ++ ++ if (strncmp(filename, token, len)) ++ continue; + +- if (!strncmp(filename, token, len) && filename[len] == '-') ++ cc = filename[len]; ++ if (cc == '\0' || cc == '-' || cc == '.') + return true; + } + +diff --git a/src/uapi.h b/src/uapi.h +index cc38395..96ca7ed 100644 +--- a/src/uapi.h ++++ b/src/uapi.h +@@ -43,6 +43,7 @@ typedef struct uapi_kernel_entry_tokens { + + #define UAPI_BOOT_DIRECTORY "/boot/efi/loader/entries" + ++extern uapi_boot_entry_t * uapi_get_boot_entry(const char *id); + extern uapi_boot_entry_t * uapi_find_boot_entry(const uapi_kernel_entry_tokens_t *match, const char *machine_id); + extern void uapi_boot_entry_free(uapi_boot_entry_t *); + extern void uapi_kernel_entry_tokens_add(uapi_kernel_entry_tokens_t *, const char *); + +From 0db6dae5c065944506255b92033314c97b45e4e4 Mon Sep 17 00:00:00 2001 +From: Olaf Kirch +Date: Tue, 28 Nov 2023 14:04:43 +0100 +Subject: [PATCH 13/13] Rename --next-kernel to --boot-entry + +Signed-off-by: Olaf Kirch +--- + man/pcr-oracle.8.in | 4 ++-- + src/efi-application.c | 4 ++-- + src/eventlog.c | 22 +++++++++++----------- + src/eventlog.h | 2 +- + src/oracle.c | 26 +++++++++++++------------- + src/sd-boot.c | 2 +- + src/sd-boot.h | 2 +- + 7 files changed, 31 insertions(+), 31 deletions(-) + +diff --git a/man/pcr-oracle.8.in b/man/pcr-oracle.8.in +index 3682afa..86ac275 100644 +--- a/man/pcr-oracle.8.in ++++ b/man/pcr-oracle.8.in +@@ -494,14 +494,14 @@ events (tracked in PCR9). In this case, it would be totally sufficient + to stop processing after grub loaded \fPgrub.cfg\fP from the EFI + System Partition. + .TP +-.BI --next-kernel " id ++.BI --boot-entry " id + In a systemd-boot environment, the values of PCR registers also depend on + the contents of the kernel and initrd image, as well as the kernel command line + options. After a kernel update or an initrd rebuild, these values will change. + So when predicting PCR values in this case, \fBpcr-oracle\fP needs to be + instructed to refer to the future kernel rather than the current one. + .IP +-This is what the \fB--next-kernel\fP option is for. It takes one argument, which ++This is what the \fB--boot-entry\fP option is for. It takes one argument, which + is either an ID (in the sense of systemd-boot IDs), or \fBauto\fP. When + the latter is given, \fBpcr-oracle\fP will make a best guess as to what + kernel image will be used on next boot. +diff --git a/src/efi-application.c b/src/efi-application.c +index 9d7e531..3e80083 100644 +--- a/src/efi-application.c ++++ b/src/efi-application.c +@@ -291,8 +291,8 @@ __tpm_event_efi_bsa_rehash(const tpm_event_t *ev, const tpm_parsed_event_t *pars + } + + /* The next boot can have a different kernel */ +- if (sdb_is_kernel(evspec->efi_application) && ctx->next_kernel) { +- new_application = ctx->next_kernel->image_path; ++ if (sdb_is_kernel(evspec->efi_application) && ctx->boot_entry) { ++ new_application = ctx->boot_entry->image_path; + if (new_application) { + evspec_clone = *evspec; + evspec_clone.efi_application = strdup(new_application); +diff --git a/src/eventlog.c b/src/eventlog.c +index 3498790..4277d42 100644 +--- a/src/eventlog.c ++++ b/src/eventlog.c +@@ -789,24 +789,24 @@ __tpm_event_systemd_describe(const tpm_parsed_event_t *parsed) + static const tpm_evdigest_t * + __tpm_event_systemd_rehash(const tpm_event_t *ev, const tpm_parsed_event_t *parsed, tpm_event_log_rehash_ctx_t *ctx) + { +- const uapi_boot_entry_t *next_kernel = ctx->next_kernel; ++ const uapi_boot_entry_t *boot_entry = ctx->boot_entry; + char initrd[2048]; + char initrd_utf16[4096]; + unsigned int len; + + /* If no --next-kernel option was given, do not rehash anything */ +- if (next_kernel == NULL) ++ if (boot_entry == NULL) + return tpm_event_get_digest(ev, ctx->algo); + +- if (!next_kernel->image_path) { ++ if (!boot_entry->image_path) { + error("Unable to identify the next kernel\n"); + return NULL; + } + +- debug("Next boot entry expected from: %s %s\n", next_kernel->title, next_kernel->version? : ""); ++ debug("Next boot entry expected from: %s %s\n", boot_entry->title, boot_entry->version? : ""); + snprintf(initrd, sizeof(initrd), "initrd=%s %s", +- path_unix2dos(next_kernel->initrd_path), +- next_kernel->options? : ""); ++ path_unix2dos(boot_entry->initrd_path), ++ boot_entry->options? : ""); + + len = (strlen(initrd) + 1) << 1; + assert(len <= sizeof(initrd_utf16)); +@@ -863,20 +863,20 @@ __tpm_event_tag_initrd_describe(const tpm_parsed_event_t *parsed) + static const tpm_evdigest_t * + __tpm_event_tag_initrd_rehash(const tpm_event_t *ev, const tpm_parsed_event_t *parsed, tpm_event_log_rehash_ctx_t *ctx) + { +- const uapi_boot_entry_t *next_kernel = ctx->next_kernel; ++ const uapi_boot_entry_t *boot_entry = ctx->boot_entry; + + /* If no --next-kernel option was given, do not rehash anything */ +- if (next_kernel == NULL) ++ if (boot_entry == NULL) + return tpm_event_get_digest(ev, ctx->algo); + +- if (!next_kernel->initrd_path) { ++ if (!boot_entry->initrd_path) { + /* Can this happen eg when going from a split kernel to a unified kernel? */ + error("Unable to identify the next initrd\n"); + return NULL; + } + +- debug("Next boot entry expected from: %s %s\n", next_kernel->title, next_kernel->version? : ""); +- return runtime_digest_efi_file(ctx->algo, next_kernel->initrd_path); ++ debug("Next boot entry expected from: %s %s\n", boot_entry->title, boot_entry->version? : ""); ++ return runtime_digest_efi_file(ctx->algo, boot_entry->initrd_path); + } + + /* +diff --git a/src/eventlog.h b/src/eventlog.h +index 65e5322..3741b58 100644 +--- a/src/eventlog.h ++++ b/src/eventlog.h +@@ -202,7 +202,7 @@ typedef struct tpm_event_log_rehash_ctx { + const pecoff_image_info_t *next_stage_img; + + /* This get set when the user specifies --next-kernel */ +- uapi_boot_entry_t * next_kernel; ++ uapi_boot_entry_t * boot_entry; + } tpm_event_log_rehash_ctx_t; + + #define GRUB_COMMAND_ARGV_MAX 32 +diff --git a/src/oracle.c b/src/oracle.c +index 2cc53c1..1cafafc 100644 +--- a/src/oracle.c ++++ b/src/oracle.c +@@ -59,7 +59,7 @@ struct predictor { + const char * initial_source; + + const char * tpm_event_log_path; +- const char * next_kernel_id; ++ const char * boot_entry_id; + + const char * algo; + const tpm_algo_info_t * algo_info; +@@ -100,7 +100,7 @@ enum { + OPT_POLICY_NAME, + OPT_POLICY_FORMAT, + OPT_TARGET_PLATFORM, +- OPT_NEXT_KERNEL, ++ OPT_BOOT_ENTRY, + }; + + static struct option options[] = { +@@ -117,7 +117,7 @@ static struct option options[] = { + { "before", no_argument, 0, OPT_BEFORE }, + { "verify", required_argument, 0, OPT_VERIFY }, + { "use-pesign", no_argument, 0, OPT_USE_PESIGN }, +- { "next-kernel", required_argument, 0, OPT_NEXT_KERNEL }, ++ { "boot-entry", required_argument, 0, OPT_BOOT_ENTRY }, + { "create-testcase", required_argument, 0, OPT_CREATE_TESTCASE }, + { "replay-testcase", required_argument, 0, OPT_REPLAY_TESTCASE }, + +@@ -133,7 +133,7 @@ static struct option options[] = { + { "policy-name", required_argument, 0, OPT_POLICY_NAME }, + { "policy-format", required_argument, 0, OPT_POLICY_FORMAT }, + { "target-platform", required_argument, 0, OPT_TARGET_PLATFORM }, +- { "next-kernel", required_argument, 0, OPT_NEXT_KERNEL }, ++ { "next-kernel", required_argument, 0, OPT_BOOT_ENTRY }, + + { NULL } + }; +@@ -256,7 +256,7 @@ static struct predictor * + predictor_new(const tpm_pcr_selection_t *pcr_selection, const char *source, + const char *tpm_eventlog_path, + const char *output_format, +- const char *next_kernel_id) ++ const char *boot_entry_id) + { + struct predictor *pred; + +@@ -266,7 +266,7 @@ predictor_new(const tpm_pcr_selection_t *pcr_selection, const char *source, + pred = calloc(1, sizeof(*pred)); + pred->pcr_mask = pcr_selection->pcr_mask; + pred->initial_source = source; +- pred->next_kernel_id = next_kernel_id; ++ pred->boot_entry_id = boot_entry_id; + + pred->algo = pcr_selection->algo_info->openssl_name; + pred->algo_info = pcr_selection->algo_info; +@@ -662,9 +662,9 @@ predictor_update_eventlog(struct predictor *pred) + * systemd ID of the next kernel entry to be booted. + * FIXME: we should probably hide this behind a target_platform function. + */ +- if (pred->next_kernel_id != NULL +- && !(rehash_ctx.next_kernel = sdb_identify_next_kernel(pred->next_kernel_id))) +- fatal("unable to identify next kernel \"%s\"\n", pred->next_kernel_id); ++ if (pred->boot_entry_id != NULL ++ && !(rehash_ctx.boot_entry = sdb_identify_boot_entry(pred->boot_entry_id))) ++ fatal("unable to identify next kernel \"%s\"\n", pred->boot_entry_id); + + for (ev = pred->event_log; ev; ev = ev->next) { + tpm_evdigest_t *pcr; +@@ -1036,7 +1036,7 @@ main(int argc, char **argv) + char *opt_rsa_bits = NULL; + char *opt_policy_name = NULL; + char *opt_target_platform = NULL; +- char *opt_next_kernel = NULL; ++ char *opt_boot_entry = NULL; + const target_platform_t *target; + unsigned int action_flags = 0; + unsigned int rsa_bits = 2048; +@@ -1071,8 +1071,8 @@ main(int argc, char **argv) + case OPT_USE_PESIGN: + opt_use_pesign = 1; + break; +- case OPT_NEXT_KERNEL: +- opt_next_kernel = optarg; ++ case OPT_BOOT_ENTRY: ++ opt_boot_entry = optarg; + break; + case OPT_STOP_EVENT: + opt_stop_event = optarg; +@@ -1315,7 +1315,7 @@ main(int argc, char **argv) + fatal("BUG: action %u should have parsed a PCR selection argument", action); + + pred = predictor_new(pcr_selection, opt_from, opt_eventlog_path, +- opt_output_format, opt_next_kernel); ++ opt_output_format, opt_boot_entry); + + if (opt_stop_event) + predictor_set_stop_event(pred, opt_stop_event, !opt_stop_before); +diff --git a/src/sd-boot.c b/src/sd-boot.c +index e3e0568..ad13617 100644 +--- a/src/sd-boot.c ++++ b/src/sd-boot.c +@@ -161,7 +161,7 @@ sdb_is_kernel(const char *application) + * Identify the next kernel and initrd given an ID + */ + uapi_boot_entry_t * +-sdb_identify_next_kernel(const char *id) ++sdb_identify_boot_entry(const char *id) + { + uapi_kernel_entry_tokens_t id_match = { 0 }; + const uapi_kernel_entry_tokens_t *match; +diff --git a/src/sd-boot.h b/src/sd-boot.h +index 2b11dc1..0472320 100644 +--- a/src/sd-boot.h ++++ b/src/sd-boot.h +@@ -42,7 +42,7 @@ typedef struct sdb_entry_list { + sdb_entry_data_t entries[SDB_MAX_ENTRIES]; + } sdb_entry_list_t; + +-extern uapi_boot_entry_t * sdb_identify_next_kernel(const char *id); ++extern uapi_boot_entry_t * sdb_identify_boot_entry(const char *id); + extern bool sdb_is_kernel(const char *application); + + /* This will have to update the systemd json file, and add a new entry. */ diff --git a/fix_pcr_index.patch b/fix_pcr_index.patch new file mode 100644 index 0000000..739b86e --- /dev/null +++ b/fix_pcr_index.patch @@ -0,0 +1,32 @@ +From 152f97b1b49ca12d1de1df67c892b1c35140c056 Mon Sep 17 00:00:00 2001 +From: Alberto Planas +Date: Thu, 30 Nov 2023 13:20:54 +0100 +Subject: [PATCH 1/3] Remove old systemd parameter + +Signed-off-by: Alberto Planas +--- + man/pcr-oracle.8.in | 1 - + 1 file changed, 1 deletion(-) + +Index: pcr-oracle-0.5.3/src/sd-boot.c +=================================================================== +--- pcr-oracle-0.5.3.orig/src/sd-boot.c ++++ pcr-oracle-0.5.3/src/sd-boot.c +@@ -233,7 +233,7 @@ sdb_policy_entry_set_pcr_mask(struct jso + pcrs = json_object_new_array(); + json_object_object_add(entry, "pcrs", pcrs); + +- for (pcr_index = 1; pcr_mask; pcr_index++, pcr_mask >>= 1) { ++ for (pcr_index = 0; pcr_mask; pcr_index++, pcr_mask >>= 1) { + if (pcr_mask & 1) + json_object_array_add(pcrs, json_object_new_int(pcr_index)); + } +@@ -315,7 +315,7 @@ sdb_policy_file_add_entry(const char *fi + goto out; + + sdb_policy_entry_set_pcr_mask(entry, pcr_mask); +- json_object_object_add(entry, "pfkp", ++ json_object_object_add(entry, "pkfp", + json_object_new_string(print_hex_string(fingerprint, fingerprint_len))); + json_object_object_add(entry, "sig", + json_object_new_string(print_base64_value(signature, signature_len))); diff --git a/fix_rsa.patch b/fix_rsa.patch deleted file mode 100644 index 2757095..0000000 --- a/fix_rsa.patch +++ /dev/null @@ -1,224 +0,0 @@ -From bba8e4aa53d7c75ad3a153418c6c8ece19d8049b Mon Sep 17 00:00:00 2001 -From: Alberto Planas -Date: Fri, 17 Nov 2023 08:40:39 +0100 -Subject: [PATCH 1/2] Add rsa-public-pem paramenter - -This parameter will instruct store-public-key to store the public part -in PEM format. - -Signed-off-by: Alberto Planas ---- - man/pcr-oracle.8.in | 23 +++++++++++++++++++ - src/oracle.c | 16 ++++++++++++-- - src/rsa.c | 54 +++++++++++++++++++++++++++++++++------------ - src/rsa.h | 2 ++ - 4 files changed, 79 insertions(+), 16 deletions(-) - -diff --git a/man/pcr-oracle.8.in b/man/pcr-oracle.8.in -index 8fed99e..bc210c5 100644 ---- a/man/pcr-oracle.8.in -+++ b/man/pcr-oracle.8.in -@@ -199,6 +199,29 @@ supports this via its \fBstore-public-key\fP subcommand: - This command will read the RSA private key from the PEM file, - and write the public key as a \fBTPM2B_PUBLIC\fP object to - the indicated output file \fBpolicy-pubkey\fP. -+.P -+In other cases it is convenient to generate a private key and store -+the public and the private components without using \fBopenssl\fP, but -+using more conventional formats like PEM. -+.P -+.nf -+.in +2 -+# pcr-oracle \\ -+.br -+ --rsa-generate-key \\ -+.br -+ --rsa-public-pem \\ -+.br -+ --private-key policy-key.pem \\ -+.br -+ --public-key policy-pubkey \\ -+.br -+ store-public-key -+.fi -+.P -+This command will read the RSA private key from the PEM file, -+and write the public key as a \fBTPM2B_PUBLIC\fP object to -+the indicated output file \fBpolicy-pubkey\fP. - .\" ################################################################## - .\" # New key format - .\" ################################################################## -diff --git a/src/oracle.c b/src/oracle.c -index 0238110..726c11d 100644 ---- a/src/oracle.c -+++ b/src/oracle.c -@@ -89,6 +89,7 @@ enum { - OPT_RSA_PUBLIC_KEY, - OPT_RSA_GENERATE_KEY, - OPT_RSA_BITS, -+ OPT_RSA_PUBLIC_PEM, - OPT_INPUT, - OPT_OUTPUT, - OPT_AUTHORIZED_POLICY, -@@ -119,6 +120,7 @@ static struct option options[] = { - { "public-key", required_argument, 0, OPT_RSA_PUBLIC_KEY }, - { "rsa-generate-key", no_argument, 0, OPT_RSA_GENERATE_KEY }, - { "rsa-bits", required_argument, 0, OPT_RSA_BITS }, -+ { "rsa-public-pem", no_argument, 0, OPT_RSA_PUBLIC_PEM }, - { "input", required_argument, 0, OPT_INPUT }, - { "output", required_argument, 0, OPT_OUTPUT }, - { "authorized-policy", required_argument, 0, OPT_AUTHORIZED_POLICY }, -@@ -1016,6 +1018,7 @@ main(int argc, char **argv) - char *opt_rsa_public_key = NULL; - bool opt_rsa_generate = false; - char *opt_rsa_bits = NULL; -+ bool opt_rsa_public_pem = false; - char *opt_key_format = NULL; - char *opt_policy_name = NULL; - char *opt_policy_format = NULL; -@@ -1086,6 +1089,9 @@ main(int argc, char **argv) - case OPT_RSA_BITS: - opt_rsa_bits = optarg; - break; -+ case OPT_RSA_PUBLIC_PEM: -+ opt_rsa_public_pem = true; -+ break; - case OPT_INPUT: - opt_input = optarg; - break; -@@ -1267,8 +1273,14 @@ main(int argc, char **argv) - } - - if (action == ACTION_STORE_PUBLIC_KEY) { -- if (!pcr_store_public_key(opt_rsa_private_key, opt_rsa_public_key)) -- return 1; -+ if (opt_rsa_public_pem) { -+ tpm_rsa_key_t *key = tpm_rsa_key_read_private(opt_rsa_private_key); -+ if (!key || !tpm_rsa_key_write_public(opt_rsa_public_key, key)) -+ return 1; -+ } -+ else -+ if (!pcr_store_public_key(opt_rsa_private_key, opt_rsa_public_key)) -+ return 1; - return 0; - } - -diff --git a/src/rsa.c b/src/rsa.c -index f3672b1..5385441 100644 ---- a/src/rsa.c -+++ b/src/rsa.c -@@ -95,36 +95,27 @@ tpm_rsa_key_read_public(const char *pathname) - } - - /* -- * Write a private key to a PEM file. -- * Pass phrases currently not supported. -+ * Write a public key to a PEM file. - */ - bool --tpm_rsa_key_write_private(const char *pathname, const tpm_rsa_key_t *key) -+tpm_rsa_key_write_public(const char *pathname, const tpm_rsa_key_t *key) - { - bool ok = false; -- mode_t omask; - FILE *fp; - -- /* Turn off group and other rw bits to make the private key mode 600 -- * right from the start. */ -- omask = umask(077); -- - if (!(fp = fopen(pathname, "w"))) { -- error("Cannot open RSA private key file %s: %m\n", pathname); -+ error("Cannot open RSA public key file %s: %m\n", pathname); - goto fail; - } - -- if (!PEM_write_PrivateKey(fp, key->pkey, NULL, NULL, 0, 0, NULL)) { -- error("Unable to write private key to %s\n", pathname); -+ if (!PEM_write_PUBKEY(fp, key->pkey)) { -+ error("Unable to write public key to %s\n", pathname); - goto fail; - } - - ok = true; - - fail: -- /* Reset the umask */ -- umask(omask); -- - fclose(fp); - return ok; - } -@@ -164,6 +155,41 @@ tpm_rsa_key_read_private(const char *pathname) - return NULL; - } - -+/* -+ * Write a private key to a PEM file. -+ * Pass phrases currently not supported. -+ */ -+bool -+tpm_rsa_key_write_private(const char *pathname, const tpm_rsa_key_t *key) -+{ -+ bool ok = false; -+ mode_t omask; -+ FILE *fp; -+ -+ /* Turn off group and other rw bits to make the private key mode 600 -+ * right from the start. */ -+ omask = umask(077); -+ -+ if (!(fp = fopen(pathname, "w"))) { -+ error("Cannot open RSA private key file %s: %m\n", pathname); -+ goto fail; -+ } -+ -+ if (!PEM_write_PrivateKey(fp, key->pkey, NULL, NULL, 0, 0, NULL)) { -+ error("Unable to write private key to %s\n", pathname); -+ goto fail; -+ } -+ -+ ok = true; -+ -+fail: -+ /* Reset the umask */ -+ umask(omask); -+ -+ fclose(fp); -+ return ok; -+} -+ - tpm_rsa_key_t * - tpm_rsa_generate(unsigned int bits) - { -diff --git a/src/rsa.h b/src/rsa.h -index 49c0bb4..7b8362f 100644 ---- a/src/rsa.h -+++ b/src/rsa.h -@@ -26,6 +26,8 @@ - typedef struct tpm_rsa_key tpm_rsa_key_t; - - extern tpm_rsa_key_t * tpm_rsa_key_read_public(const char *pathname); -+extern bool tpm_rsa_key_write_public(const char *pathname, -+ const tpm_rsa_key_t *key); - extern tpm_rsa_key_t * tpm_rsa_key_read_private(const char *pathname); - extern bool tpm_rsa_key_write_private(const char *pathname, - const tpm_rsa_key_t *key); - -From ddd92b8f58d0f3bb89aada4adeb71d6ba9d1573a Mon Sep 17 00:00:00 2001 -From: Alberto Planas -Date: Fri, 17 Nov 2023 08:43:47 +0100 -Subject: [PATCH 2/2] Update version 0.5.3 - -Signed-off-by: Alberto Planas ---- - microconf/version | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/microconf/version b/microconf/version -index a486208..c4f2939 100644 ---- a/microconf/version -+++ b/microconf/version -@@ -1 +1 @@ --uc_version=0.5.2 -+uc_version=0.5.3 diff --git a/pcr-oracle-0.5.2.tar.xz b/pcr-oracle-0.5.2.tar.xz deleted file mode 100644 index ecb4482..0000000 --- a/pcr-oracle-0.5.2.tar.xz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fcf10282e1dd989dd638231682ef5fe1fc141ca82a55fb27224bf4d77fd85d64 -size 76060 diff --git a/pcr-oracle-0.5.3.tar.xz b/pcr-oracle-0.5.3.tar.xz new file mode 100644 index 0000000..fb394b1 --- /dev/null +++ b/pcr-oracle-0.5.3.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc53ad505c90d59675f87bd00769d7be8a6c9a3fbfa30c8013dc26d2f9a96bf7 +size 80368 diff --git a/pcr-oracle.changes b/pcr-oracle.changes index deea280..3c7c3d0 100644 --- a/pcr-oracle.changes +++ b/pcr-oracle.changes @@ -1,3 +1,17 @@ +------------------------------------------------------------------- +Wed Nov 29 15:56:39 UTC 2023 - Alberto Planas Dominguez + +- Update to 0.5.3 + - Improve documentation + - Detect key format store via extension + - Replace --key-format and --policy-format options with a single + --target-platform option + - The json file can contain multiple predictions +- Remove fix_rsa.patch as is already upstream +- Add boot_entry.patch to add new parameter to point to a new systemd + boot entry +- Add fix_pcr_index.patch to fix the PCR index number in the JSON file + ------------------------------------------------------------------- Mon Nov 20 10:24:32 UTC 2023 - Alberto Planas Dominguez diff --git a/pcr-oracle.spec b/pcr-oracle.spec index 1a7ac77..d44ca61 100644 --- a/pcr-oracle.spec +++ b/pcr-oracle.spec @@ -18,15 +18,17 @@ Name: pcr-oracle -Version: 0.5.2 +Version: 0.5.3 Release: 0 Summary: Predict TPM PCR values License: GPL-2.0-only Group: System/Boot URL: https://github.com/okirch/pcr-oracle Source: %{name}-%{version}.tar.xz -# PATCH-FEATURE-UPSTREAM fix_rsa.patch gh#okirch/pcr-oracle#37 -Patch: fix_rsa.patch +# PATCH-FEATURE-UPSTREAM boot_entry.patch gh#okirch/pcr-oracle#40 +Patch1: boot_entry.patch +# PATCH-FEATURE-UPSTREAM boot_entry.patch gh#okirch/pcr-oracle#44 +Patch2: fix_pcr_index.patch BuildRequires: libopenssl-devel >= 0.9.8 BuildRequires: tpm2-0-tss-devel >= 2.4.0 Requires: libtss2-tcti-device0