Marcus Meissner
07fa94a1ef
- 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 OBS-URL: https://build.opensuse.org/request/show/1130043 OBS-URL: https://build.opensuse.org/package/show/Base:System/pcr-oracle?expand=0&rev=17
2094 lines
61 KiB
Diff
2094 lines
61 KiB
Diff
From 0be3db467f042cc030f9a4a840854ce36a0f8317 Mon Sep 17 00:00:00 2001
|
|
From: Olaf Kirch <okir@suse.com>
|
|
Date: Mon, 27 Nov 2023 16:48:28 +0100
|
|
Subject: [PATCH 01/13] Remove obsolete pcr_policy_sign_systemd()
|
|
|
|
Signed-off-by: Olaf Kirch <okir@suse.com>
|
|
---
|
|
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 <okir@suse.com>
|
|
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 <okir@suse.com>
|
|
---
|
|
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 <okir@suse.com>
|
|
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 <okir@suse.com>
|
|
---
|
|
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 <okir@suse.com>
|
|
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 <okir@suse.com>
|
|
---
|
|
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 <aplanas@suse.com>, Olaf Kirch <okir@suse.com>
|
|
+ */
|
|
+
|
|
+
|
|
+#include <stdlib.h>
|
|
+#include <sys/dir.h>
|
|
+#include <sys/param.h>
|
|
+#include <sys/utsname.h>
|
|
+#include <limits.h>
|
|
+#include <assert.h>
|
|
+#include <ctype.h>
|
|
+#include <errno.h>
|
|
+
|
|
+#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 <okir@suse.com>
|
|
+ */
|
|
+
|
|
+#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 <okir@suse.com>
|
|
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 <okir@suse.com>
|
|
---
|
|
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 <limits.h>
|
|
+#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 <okir@suse.com>
|
|
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 <okir@suse.com>
|
|
---
|
|
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 <okir@suse.com>
|
|
Date: Mon, 27 Nov 2023 18:44:05 +0100
|
|
Subject: [PATCH 07/13] Remove old code from sd-boot.*
|
|
|
|
Signed-off-by: Olaf Kirch <okir@suse.com>
|
|
---
|
|
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 <okir@suse.com>
|
|
Date: Tue, 28 Nov 2023 10:30:10 +0100
|
|
Subject: [PATCH 08/13] Manpage fixes
|
|
|
|
Signed-off-by: Olaf Kirch <okir@suse.com>
|
|
---
|
|
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 <okir@suse.com>
|
|
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 <okir@suse.com>
|
|
---
|
|
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 <okir@suse.com>
|
|
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 <okir@suse.com>
|
|
---
|
|
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 <okir@suse.com>
|
|
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 <okir@suse.com>
|
|
---
|
|
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 <okir@suse.com>
|
|
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 <okir@suse.com>
|
|
---
|
|
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 <sys/param.h>
|
|
#include <sys/utsname.h>
|
|
#include <limits.h>
|
|
+#include <unistd.h>
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
@@ -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 <okir@suse.com>
|
|
Date: Tue, 28 Nov 2023 14:04:43 +0100
|
|
Subject: [PATCH 13/13] Rename --next-kernel to --boot-entry
|
|
|
|
Signed-off-by: Olaf Kirch <okir@suse.com>
|
|
---
|
|
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. */
|