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