1092 lines
30 KiB
Diff
1092 lines
30 KiB
Diff
|
From 0baab1fc20a34e298466e4f87ad23701b46e6096 Mon Sep 17 00:00:00 2001
|
||
|
From: Alberto Planas <aplanas@suse.com>
|
||
|
Date: Wed, 20 Sep 2023 10:12:21 +0200
|
||
|
Subject: [PATCH 01/10] testcase: iterate over s instead
|
||
|
|
||
|
Signed-off-by: Alberto Planas <aplanas@suse.com>
|
||
|
---
|
||
|
src/testcase.c | 2 +-
|
||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||
|
|
||
|
diff --git a/src/testcase.c b/src/testcase.c
|
||
|
index bae3a69..f74238b 100644
|
||
|
--- a/src/testcase.c
|
||
|
+++ b/src/testcase.c
|
||
|
@@ -453,7 +453,7 @@ canon_path(const char *path)
|
||
|
++path;
|
||
|
|
||
|
save_path = strdup(path);
|
||
|
- for (s = save_path; *comp; ) {
|
||
|
+ for (s = save_path; *s; ) {
|
||
|
comp = s;
|
||
|
while (*s) {
|
||
|
if (*s == '/') {
|
||
|
|
||
|
From 2aec1cc9b0fb095f4be28f0895fb3fc2f73935d4 Mon Sep 17 00:00:00 2001
|
||
|
From: Alberto Planas <aplanas@suse.com>
|
||
|
Date: Thu, 21 Sep 2023 18:38:12 +0200
|
||
|
Subject: [PATCH 02/10] util: add print_base64_value function
|
||
|
|
||
|
Signed-off-by: Alberto Planas <aplanas@suse.com>
|
||
|
---
|
||
|
src/util.c | 37 +++++++++++++++++++++++++++++++++++++
|
||
|
src/util.h | 2 ++
|
||
|
2 files changed, 39 insertions(+)
|
||
|
|
||
|
diff --git a/src/util.c b/src/util.c
|
||
|
index 04d53f4..2c90315 100644
|
||
|
--- a/src/util.c
|
||
|
+++ b/src/util.c
|
||
|
@@ -22,6 +22,7 @@
|
||
|
#include <stdlib.h>
|
||
|
#include <ctype.h>
|
||
|
#include <iconv.h>
|
||
|
+#include <assert.h>
|
||
|
|
||
|
#include "util.h"
|
||
|
#include "digest.h"
|
||
|
@@ -188,6 +189,42 @@ print_octet_string(const unsigned char *data, unsigned int len)
|
||
|
|
||
|
}
|
||
|
|
||
|
+const char *
|
||
|
+print_base64_value(const unsigned char *data, unsigned int len)
|
||
|
+{
|
||
|
+ static char buffer[2048];
|
||
|
+ static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||
|
+ unsigned int b64_len, i;
|
||
|
+ char *b;
|
||
|
+
|
||
|
+ b64_len = 4 * ((len + 2) / 3) + 1;
|
||
|
+ assert(b64_len < 2048);
|
||
|
+
|
||
|
+ b = buffer;
|
||
|
+ for (i = 0; (i + 2) < len; i += 3) {
|
||
|
+ *b++ = table[(data[i] >> 2) & 63];
|
||
|
+ *b++ = table[((data[i] & 3) << 4 | data[i + 1] >> 4) & 63];
|
||
|
+ *b++ = table[((data[i + 1] & 15) << 2 | data[i + 2] >> 6) & 63];
|
||
|
+ *b++ = table[data[i + 2] & 63];
|
||
|
+ }
|
||
|
+
|
||
|
+ if ((i + 2) == len) {
|
||
|
+ *b++ = table[(data[i] >> 2) & 63];
|
||
|
+ *b++ = table[((data[i] & 3) << 4 | data[i + 1] >> 4) & 63];
|
||
|
+ *b++ = table[((data[i + 1] & 15) << 2) & 63];
|
||
|
+ *b++ = '=';
|
||
|
+ } else if ((i + 1) == len) {
|
||
|
+ *b++ = table[(data[i] >> 2) & 63];
|
||
|
+ *b++ = table[((data[i] & 3) << 4) & 63];
|
||
|
+ *b++ = '=';
|
||
|
+ *b++ = '=';
|
||
|
+ }
|
||
|
+
|
||
|
+ *b = 0;
|
||
|
+
|
||
|
+ return buffer;
|
||
|
+}
|
||
|
+
|
||
|
const tpm_evdigest_t *
|
||
|
parse_digest(const char *string, const char *algo)
|
||
|
{
|
||
|
diff --git a/src/util.h b/src/util.h
|
||
|
index 6f89f4b..79f54cb 100644
|
||
|
--- a/src/util.h
|
||
|
+++ b/src/util.h
|
||
|
@@ -129,6 +129,8 @@ extern const tpm_evdigest_t *parse_digest(const char *string, const char *algo);
|
||
|
extern void hexdump(const void *data, size_t size, void (*)(const char *, ...), unsigned int indent);
|
||
|
extern const char * print_octet_string(const unsigned char *data, unsigned int len);
|
||
|
|
||
|
+extern const char * print_base64_value(const unsigned char *data, unsigned int len);
|
||
|
+
|
||
|
extern bool __convert_from_utf16le(char *in_string, size_t in_bytes, char *out_string, size_t out_bytes);
|
||
|
extern bool __convert_to_utf16le(char *in_string, size_t in_bytes, char *out_string, size_t out_bytes);
|
||
|
|
||
|
|
||
|
From 5841fd76d1483af24a71ce022495f6961947eec8 Mon Sep 17 00:00:00 2001
|
||
|
From: Alberto Planas <aplanas@suse.com>
|
||
|
Date: Mon, 25 Sep 2023 12:52:44 +0200
|
||
|
Subject: [PATCH 03/10] util: add print_hex_string function
|
||
|
|
||
|
Signed-off-by: Alberto Planas <aplanas@suse.com>
|
||
|
---
|
||
|
src/util.c | 22 ++++++++++++++++++++++
|
||
|
src/util.h | 4 +++-
|
||
|
2 files changed, 25 insertions(+), 1 deletion(-)
|
||
|
|
||
|
diff --git a/src/util.c b/src/util.c
|
||
|
index 2c90315..4d95b30 100644
|
||
|
--- a/src/util.c
|
||
|
+++ b/src/util.c
|
||
|
@@ -189,6 +189,28 @@ print_octet_string(const unsigned char *data, unsigned int len)
|
||
|
|
||
|
}
|
||
|
|
||
|
+const char *
|
||
|
+print_hex_string(const unsigned char *data, unsigned int len)
|
||
|
+{
|
||
|
+ static char buffer[2 * 64 + 1];
|
||
|
+
|
||
|
+ if (len <= 64) {
|
||
|
+ unsigned int i;
|
||
|
+ char *s;
|
||
|
+
|
||
|
+ s = buffer;
|
||
|
+ for (i = 0; i < len; ++i) {
|
||
|
+ sprintf(s, "%02x", data[i]);
|
||
|
+ s += 2;
|
||
|
+ }
|
||
|
+ *s = '\0';
|
||
|
+ } else {
|
||
|
+ snprintf(buffer, sizeof(buffer), "<%u bytes of data>", len);
|
||
|
+ }
|
||
|
+
|
||
|
+ return buffer;
|
||
|
+}
|
||
|
+
|
||
|
const char *
|
||
|
print_base64_value(const unsigned char *data, unsigned int len)
|
||
|
{
|
||
|
diff --git a/src/util.h b/src/util.h
|
||
|
index 79f54cb..5168f19 100644
|
||
|
--- a/src/util.h
|
||
|
+++ b/src/util.h
|
||
|
@@ -129,7 +129,9 @@ extern const tpm_evdigest_t *parse_digest(const char *string, const char *algo);
|
||
|
extern void hexdump(const void *data, size_t size, void (*)(const char *, ...), unsigned int indent);
|
||
|
extern const char * print_octet_string(const unsigned char *data, unsigned int len);
|
||
|
|
||
|
-extern const char * print_base64_value(const unsigned char *data, unsigned int len);
|
||
|
+extern const char * print_hex_string(const unsigned char *data, unsigned int len);
|
||
|
+
|
||
|
+extern const char * print_base64_value(const unsigned char *data, unsigned int len);
|
||
|
|
||
|
extern bool __convert_from_utf16le(char *in_string, size_t in_bytes, char *out_string, size_t out_bytes);
|
||
|
extern bool __convert_to_utf16le(char *in_string, size_t in_bytes, char *out_string, size_t out_bytes);
|
||
|
|
||
|
From 683473448c70ce8fcc87caff5d13647ab264ecbe Mon Sep 17 00:00:00 2001
|
||
|
From: Alberto Planas <aplanas@suse.com>
|
||
|
Date: Mon, 25 Sep 2023 13:06:04 +0200
|
||
|
Subject: [PATCH 04/10] rsa: add tpm_rsa_key_public_digest function
|
||
|
|
||
|
This function convert the public key into DER format and calculate the
|
||
|
SHA256 hash of it.
|
||
|
|
||
|
Signed-off-by: Alberto Planas <aplanas@suse.com>
|
||
|
---
|
||
|
src/rsa.c | 32 ++++++++++++++++++++++++++++++++
|
||
|
src/rsa.h | 1 +
|
||
|
2 files changed, 33 insertions(+)
|
||
|
|
||
|
diff --git a/src/rsa.c b/src/rsa.c
|
||
|
index 4d3883e..f3f694f 100644
|
||
|
--- a/src/rsa.c
|
||
|
+++ b/src/rsa.c
|
||
|
@@ -32,6 +32,7 @@
|
||
|
|
||
|
#include "util.h"
|
||
|
#include "rsa.h"
|
||
|
+#include "digest.h"
|
||
|
|
||
|
struct tpm_rsa_key {
|
||
|
bool is_private;
|
||
|
@@ -324,3 +325,34 @@ tpm_rsa_key_to_tss2(const tpm_rsa_key_t *key)
|
||
|
return rsa_pubkey_alloc(n, e, key->path);
|
||
|
}
|
||
|
|
||
|
+const tpm_evdigest_t *
|
||
|
+tpm_rsa_key_public_digest(tpm_rsa_key_t *pubkey) {
|
||
|
+ unsigned int der_size;
|
||
|
+ unsigned char *der, *bder = NULL;
|
||
|
+ const tpm_algo_info_t *algo;
|
||
|
+ const tpm_evdigest_t *digest = NULL;
|
||
|
+
|
||
|
+ /* Convert the public key into DER format */
|
||
|
+ der_size = i2d_PublicKey(pubkey->pkey, NULL);
|
||
|
+ if (der_size < 0) {
|
||
|
+ error("%s: cannot convert public key into DER format", pubkey->path);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ der = bder = malloc(der_size);
|
||
|
+ der_size = i2d_PublicKey(pubkey->pkey, &der);
|
||
|
+ if (der_size < 0) {
|
||
|
+ error("%s: cannot convert public key into DER format", pubkey->path);
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Hash the public key */
|
||
|
+ algo = digest_by_name("sha256");
|
||
|
+ digest = digest_compute(algo, bder, der_size);
|
||
|
+
|
||
|
+ out:
|
||
|
+ if (bder)
|
||
|
+ free(bder);
|
||
|
+
|
||
|
+ return digest;
|
||
|
+}
|
||
|
diff --git a/src/rsa.h b/src/rsa.h
|
||
|
index 0d73ee8..49c0bb4 100644
|
||
|
--- a/src/rsa.h
|
||
|
+++ b/src/rsa.h
|
||
|
@@ -37,4 +37,5 @@ extern int tpm_rsa_sign(const tpm_rsa_key_t *,
|
||
|
|
||
|
extern TPM2B_PUBLIC * tpm_rsa_key_to_tss2(const tpm_rsa_key_t *key);
|
||
|
|
||
|
+extern const tpm_evdigest_t * tpm_rsa_key_public_digest(tpm_rsa_key_t *pubkey);
|
||
|
#endif /* RSA_H */
|
||
|
|
||
|
From 0b0c0d310cdbf779bbfae72329fe0ffbc9c5b2b7 Mon Sep 17 00:00:00 2001
|
||
|
From: Alberto Planas <aplanas@suse.com>
|
||
|
Date: Wed, 27 Sep 2023 15:09:31 +0200
|
||
|
Subject: [PATCH 05/10] util: remove duplicate code
|
||
|
|
||
|
Signed-off-by: Alberto Planas <aplanas@suse.com>
|
||
|
---
|
||
|
src/util.c | 35 +++++++++++++----------------------
|
||
|
1 file changed, 13 insertions(+), 22 deletions(-)
|
||
|
|
||
|
diff --git a/src/util.c b/src/util.c
|
||
|
index 4d95b30..aab9f9d 100644
|
||
|
--- a/src/util.c
|
||
|
+++ b/src/util.c
|
||
|
@@ -310,15 +310,13 @@ hexdump(const void *data, size_t size, void (*print_fn)(const char *, ...), unsi
|
||
|
}
|
||
|
}
|
||
|
|
||
|
-/*
|
||
|
- * Conversion between UTF-8 and UTF-16LE for EFI event log
|
||
|
- */
|
||
|
+
|
||
|
bool
|
||
|
-__convert_from_utf16le(char *in_string, size_t in_bytes, char *out_string, size_t out_bytes)
|
||
|
+__convert(const char *tocode, const char *fromcode, char *in_string, size_t in_bytes, char *out_string, size_t out_bytes)
|
||
|
{
|
||
|
iconv_t *ctx;
|
||
|
|
||
|
- ctx = iconv_open("utf8", "utf16le");
|
||
|
+ ctx = iconv_open(tocode, fromcode);
|
||
|
|
||
|
while (in_bytes) {
|
||
|
size_t converted;
|
||
|
@@ -336,26 +334,19 @@ __convert_from_utf16le(char *in_string, size_t in_bytes, char *out_string, size_
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
+/*
|
||
|
+ * Conversion between UTF-8 and UTF-16LE for EFI event log
|
||
|
+ */
|
||
|
bool
|
||
|
-__convert_to_utf16le(char *in_string, size_t in_bytes, char *out_string, size_t out_bytes)
|
||
|
+__convert_from_utf16le(char *in_string, size_t in_bytes, char *out_string, size_t out_bytes)
|
||
|
{
|
||
|
- iconv_t *ctx;
|
||
|
-
|
||
|
- ctx = iconv_open("utf16le", "utf8");
|
||
|
-
|
||
|
- while (in_bytes) {
|
||
|
- size_t converted;
|
||
|
-
|
||
|
- converted = iconv(ctx,
|
||
|
- &in_string, &in_bytes,
|
||
|
- &out_string, &out_bytes);
|
||
|
- if (converted == (size_t) -1) {
|
||
|
- perror("iconv");
|
||
|
- return false;
|
||
|
- }
|
||
|
- }
|
||
|
+ return __convert("utf8", "utf16le", in_string, in_bytes, out_string, out_bytes);
|
||
|
+}
|
||
|
|
||
|
- return true;
|
||
|
+bool
|
||
|
+__convert_to_utf16le(char *in_string, size_t in_bytes, char *out_string, size_t out_bytes)
|
||
|
+{
|
||
|
+ return __convert("utf16le", "utf8", in_string, in_bytes, out_string, out_bytes);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
|
||
|
From 37edd2b4fdac5b037b68215dfd1546478c359cd5 Mon Sep 17 00:00:00 2001
|
||
|
From: Alberto Planas <aplanas@suse.com>
|
||
|
Date: Thu, 28 Sep 2023 16:19:51 +0200
|
||
|
Subject: [PATCH 06/10] eventlog: parse sd-boot events for PCR12
|
||
|
|
||
|
Systemd boot can extend PCR12 with the initrd line. This is stored in
|
||
|
UTF16, that also includes a double 0x00 at the end.
|
||
|
|
||
|
Signed-off-by: Alberto Planas <aplanas@suse.com>
|
||
|
---
|
||
|
src/eventlog.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++-
|
||
|
src/eventlog.h | 6 +++++
|
||
|
2 files changed, 64 insertions(+), 1 deletion(-)
|
||
|
|
||
|
diff --git a/src/eventlog.c b/src/eventlog.c
|
||
|
index 4f4ed44..4f2d3f4 100644
|
||
|
--- a/src/eventlog.c
|
||
|
+++ b/src/eventlog.c
|
||
|
@@ -551,7 +551,7 @@ tpm_event_decode_uuid(const unsigned char *data)
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
- * Handle IPL events, which grub2 uses to hide its stuff in
|
||
|
+ * Handle IPL events, which grub2 and sd-boot uses to hide its stuff in
|
||
|
*/
|
||
|
static void
|
||
|
__tpm_event_grub_file_destroy(tpm_parsed_event_t *parsed)
|
||
|
@@ -764,6 +764,60 @@ __tpm_event_shim_event_parse(tpm_event_t *ev, tpm_parsed_event_t *parsed, const
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
+static void
|
||
|
+__tpm_event_systemd_destroy(tpm_parsed_event_t *parsed)
|
||
|
+{
|
||
|
+ drop_string(&parsed->systemd_event.string);
|
||
|
+}
|
||
|
+
|
||
|
+static const char *
|
||
|
+__tpm_event_systemd_describe(const tpm_parsed_event_t *parsed)
|
||
|
+{
|
||
|
+ static char buffer[1024];
|
||
|
+ char data[768];
|
||
|
+ unsigned int len;
|
||
|
+
|
||
|
+ /* It is in UTF16, and also include two '\0' at the end */
|
||
|
+ len = parsed->systemd_event.len >> 1;
|
||
|
+ if (len > sizeof(data))
|
||
|
+ len = sizeof(data);
|
||
|
+ __convert_from_utf16le(parsed->systemd_event.string, parsed->systemd_event.len, data, len);
|
||
|
+ data[len] = '\0';
|
||
|
+
|
||
|
+ snprintf(buffer, sizeof(buffer), "systemd boot event %s", data);
|
||
|
+ return buffer;
|
||
|
+}
|
||
|
+
|
||
|
+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)
|
||
|
+{
|
||
|
+ if (parsed->systemd_event.string == NULL)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ /* TODO: The hashed string (UTF16) should be the new initrd command */
|
||
|
+ return digest_compute(ctx->algo, parsed->systemd_event.string, parsed->systemd_event.len);
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * This event holds stuff like
|
||
|
+ * initrd = ....
|
||
|
+ */
|
||
|
+static bool
|
||
|
+__tpm_event_systemd_event_parse(tpm_event_t *ev, tpm_parsed_event_t *parsed, const char *value, unsigned int len)
|
||
|
+{
|
||
|
+ struct systemd_event *evspec = &parsed->systemd_event;
|
||
|
+
|
||
|
+ evspec->len = len;
|
||
|
+ evspec->string = malloc(len);
|
||
|
+ memcpy(evspec->string, value, len);
|
||
|
+
|
||
|
+ parsed->event_subtype = SYSTEMD_EVENT_VARIABLE;
|
||
|
+ parsed->destroy = __tpm_event_systemd_destroy;
|
||
|
+ parsed->rehash = __tpm_event_systemd_rehash;
|
||
|
+ parsed->describe = __tpm_event_systemd_describe;
|
||
|
+
|
||
|
+ return true;
|
||
|
+}
|
||
|
|
||
|
static bool
|
||
|
__tpm_event_parse_ipl(tpm_event_t *ev, tpm_parsed_event_t *parsed, buffer_t *bp)
|
||
|
@@ -788,6 +842,9 @@ __tpm_event_parse_ipl(tpm_event_t *ev, tpm_parsed_event_t *parsed, buffer_t *bp)
|
||
|
if (ev->pcr_index == 9)
|
||
|
return __tpm_event_grub_file_event_parse(ev, parsed, value);
|
||
|
|
||
|
+ if (ev->pcr_index == 12)
|
||
|
+ return __tpm_event_systemd_event_parse(ev, parsed, value, len);
|
||
|
+
|
||
|
if (ev->pcr_index == 14)
|
||
|
return __tpm_event_shim_event_parse(ev, parsed, value);
|
||
|
|
||
|
diff --git a/src/eventlog.h b/src/eventlog.h
|
||
|
index 9acbac4..514fee2 100644
|
||
|
--- a/src/eventlog.h
|
||
|
+++ b/src/eventlog.h
|
||
|
@@ -92,6 +92,7 @@ enum {
|
||
|
GRUB_EVENT_FILE = 0x0002,
|
||
|
GRUB_EVENT_KERNEL_CMDLINE = 0x0003,
|
||
|
SHIM_EVENT_VARIABLE = 0x0004,
|
||
|
+ SYSTEMD_EVENT_VARIABLE = 0x0005,
|
||
|
};
|
||
|
|
||
|
#define EFI_DEVICE_PATH_MAX 16
|
||
|
@@ -254,6 +255,11 @@ typedef struct tpm_parsed_event {
|
||
|
char * efi_partition;
|
||
|
char * disk_device;
|
||
|
} efi_gpt_event;
|
||
|
+
|
||
|
+ struct systemd_event {
|
||
|
+ unsigned int len;
|
||
|
+ char * string;
|
||
|
+ } systemd_event;
|
||
|
};
|
||
|
} tpm_parsed_event_t;
|
||
|
|
||
|
|
||
|
From c032131e7d7e45f31aef85ce1fee76c336963101 Mon Sep 17 00:00:00 2001
|
||
|
From: Alberto Planas <aplanas@suse.com>
|
||
|
Date: Thu, 28 Sep 2023 16:21:47 +0200
|
||
|
Subject: [PATCH 07/10] Support measurement of empty EFI vars
|
||
|
|
||
|
If secure boot is disabled, the PCR7 is still extended with serialized
|
||
|
event structs, but without associated data.
|
||
|
|
||
|
This patch support the rehash of emtpy EFI vars if they are associated
|
||
|
with PCR7.
|
||
|
|
||
|
Signed-off-by: Alberto Planas <aplanas@suse.com>
|
||
|
---
|
||
|
src/bufparser.h | 19 ++++++++++++++-----
|
||
|
src/efi-variable.c | 5 ++++-
|
||
|
src/runtime.c | 2 +-
|
||
|
3 files changed, 19 insertions(+), 7 deletions(-)
|
||
|
|
||
|
diff --git a/src/bufparser.h b/src/bufparser.h
|
||
|
index 0eba6da..3f7c5a6 100644
|
||
|
--- a/src/bufparser.h
|
||
|
+++ b/src/bufparser.h
|
||
|
@@ -55,13 +55,19 @@ buffer_skip(buffer_t *bp, unsigned int count)
|
||
|
static inline const void *
|
||
|
buffer_read_pointer(const buffer_t *bp)
|
||
|
{
|
||
|
- return bp->data + bp->rpos;
|
||
|
+ if (bp)
|
||
|
+ return bp->data + bp->rpos;
|
||
|
+ else
|
||
|
+ return NULL;
|
||
|
}
|
||
|
|
||
|
static inline unsigned int
|
||
|
buffer_available(const buffer_t *bp)
|
||
|
{
|
||
|
- return bp->wpos - bp->rpos;
|
||
|
+ if (bp)
|
||
|
+ return bp->wpos - bp->rpos;
|
||
|
+ else
|
||
|
+ return 0;
|
||
|
}
|
||
|
|
||
|
static inline bool
|
||
|
@@ -206,14 +212,17 @@ buffer_alloc_write(unsigned long size)
|
||
|
static inline void
|
||
|
buffer_free(buffer_t *bp)
|
||
|
{
|
||
|
- free(bp);
|
||
|
+ if (bp)
|
||
|
+ free(bp);
|
||
|
}
|
||
|
|
||
|
static inline void
|
||
|
buffer_free_secret(buffer_t *bp)
|
||
|
{
|
||
|
- memset(bp->data, 0, bp->size);
|
||
|
- free(bp);
|
||
|
+ if (bp) {
|
||
|
+ memset(bp->data, 0, bp->size);
|
||
|
+ free(bp);
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
static inline void *
|
||
|
diff --git a/src/efi-variable.c b/src/efi-variable.c
|
||
|
index 0188273..7e9e38b 100644
|
||
|
--- a/src/efi-variable.c
|
||
|
+++ b/src/efi-variable.c
|
||
|
@@ -206,7 +206,10 @@ __tpm_event_efi_variable_rehash(const tpm_event_t *ev, const tpm_parsed_event_t
|
||
|
file_data = runtime_read_efi_variable(var_name);
|
||
|
}
|
||
|
|
||
|
- if (file_data == NULL) {
|
||
|
+ /* The PCR 7 is always expanded, even if the data is empty */
|
||
|
+ if (file_data == NULL
|
||
|
+ && ev->event_type != TPM2_EFI_VARIABLE_DRIVER_CONFIG
|
||
|
+ && ev->pcr_index != 7) {
|
||
|
if (parsed->efi_variable_event.len == 0) {
|
||
|
/* The content of the variable doesn't exist during the measurement
|
||
|
* and is also not available at runtime. Let's skip this event.
|
||
|
diff --git a/src/runtime.c b/src/runtime.c
|
||
|
index f2ae90f..39acb25 100644
|
||
|
--- a/src/runtime.c
|
||
|
+++ b/src/runtime.c
|
||
|
@@ -157,7 +157,7 @@ __system_read_efi_variable(const char *var_name)
|
||
|
}
|
||
|
|
||
|
if (result == NULL)
|
||
|
- error("Unable to read EFI variable \"%s\"\n", var_name);
|
||
|
+ debug("Unable to read EFI variable \"%s\"\n", var_name);
|
||
|
else if (testcase_recording)
|
||
|
testcase_record_efi_variable(testcase_recording, var_name, result);
|
||
|
|
||
|
|
||
|
From 45a8f1f8becabfe8aa24d933ad71a6e8e4f481ed Mon Sep 17 00:00:00 2001
|
||
|
From: Alberto Planas <aplanas@suse.com>
|
||
|
Date: Fri, 6 Oct 2023 09:53:56 +0200
|
||
|
Subject: [PATCH 08/10] Add pcr_policy_sign_systemd
|
||
|
|
||
|
Similar to pcr_policy_sign, the new function will create a pcr policy
|
||
|
and sign it, but the output will be a JSON file expected by
|
||
|
systemd-cryptenroll.
|
||
|
|
||
|
Signed-off-by: Alberto Planas <aplanas@suse.com>
|
||
|
---
|
||
|
src/pcr-policy.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++
|
||
|
src/pcr.h | 2 ++
|
||
|
2 files changed, 52 insertions(+)
|
||
|
|
||
|
diff --git a/src/pcr-policy.c b/src/pcr-policy.c
|
||
|
index bc882da..dd54ad0 100644
|
||
|
--- a/src/pcr-policy.c
|
||
|
+++ b/src/pcr-policy.c
|
||
|
@@ -1538,3 +1538,53 @@ pcr_policy_unseal_tpm2key(const char *input_path, const char *output_path)
|
||
|
|
||
|
return okay;
|
||
|
}
|
||
|
+
|
||
|
+bool
|
||
|
+pcr_policy_sign_systemd(const tpm_pcr_bank_t *bank, const char *rsakey_path,
|
||
|
+ 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 = tpm_rsa_key_read_private(rsakey_path)))
|
||
|
+ 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;
|
||
|
+}
|
||
|
diff --git a/src/pcr.h b/src/pcr.h
|
||
|
index 5138e54..cc0fb0f 100644
|
||
|
--- a/src/pcr.h
|
||
|
+++ b/src/pcr.h
|
||
|
@@ -61,6 +61,8 @@ extern bool pcr_store_public_key(const char *rsakey_path, const char *output_pa
|
||
|
extern bool pcr_policy_sign(const bool tpm2key_fmt, const tpm_pcr_bank_t *bank,
|
||
|
const char *rsakey_path, 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 char *rsakey_path,
|
||
|
+ const char *output_path);
|
||
|
extern bool pcr_authorized_policy_seal_secret(const bool tpm2key_fmt,
|
||
|
const char *authorized_policy, const char *input_path,
|
||
|
const char *output_path);
|
||
|
|
||
|
From 2d135060fee1eff8da74e756ac19dc128691a1f2 Mon Sep 17 00:00:00 2001
|
||
|
From: Alberto Planas <aplanas@suse.com>
|
||
|
Date: Fri, 6 Oct 2023 10:01:08 +0200
|
||
|
Subject: [PATCH 09/10] Add --policy-format parameter
|
||
|
|
||
|
Signed-off-by: Alberto Planas <aplanas@suse.com>
|
||
|
---
|
||
|
src/oracle.c | 33 +++++++++++++++++++++++++++------
|
||
|
1 file changed, 27 insertions(+), 6 deletions(-)
|
||
|
|
||
|
diff --git a/src/oracle.c b/src/oracle.c
|
||
|
index 0a7668e..1db8285 100644
|
||
|
--- a/src/oracle.c
|
||
|
+++ b/src/oracle.c
|
||
|
@@ -93,6 +93,7 @@ enum {
|
||
|
OPT_PCR_POLICY,
|
||
|
OPT_KEY_FORMAT,
|
||
|
OPT_POLICY_NAME,
|
||
|
+ OPT_POLICY_FORMAT,
|
||
|
};
|
||
|
|
||
|
static struct option options[] = {
|
||
|
@@ -121,6 +122,7 @@ static struct option options[] = {
|
||
|
{ "pcr-policy", required_argument, 0, OPT_PCR_POLICY },
|
||
|
{ "key-format", required_argument, 0, OPT_KEY_FORMAT },
|
||
|
{ "policy-name", required_argument, 0, OPT_POLICY_NAME },
|
||
|
+ { "policy-format", required_argument, 0, OPT_POLICY_FORMAT },
|
||
|
|
||
|
{ NULL }
|
||
|
};
|
||
|
@@ -1008,7 +1010,9 @@ main(int argc, char **argv)
|
||
|
bool opt_rsa_generate = false;
|
||
|
char *opt_key_format = NULL;
|
||
|
char *opt_policy_name = NULL;
|
||
|
+ char *opt_policy_format = NULL;
|
||
|
bool tpm2key_fmt = false;
|
||
|
+ int systemd_json = false;
|
||
|
int c, exit_code = 0;
|
||
|
|
||
|
while ((c = getopt_long(argc, argv, "dhA:CF:LSZ", options, NULL)) != EOF) {
|
||
|
@@ -1088,6 +1092,9 @@ main(int argc, char **argv)
|
||
|
case OPT_POLICY_NAME:
|
||
|
opt_policy_name = optarg;
|
||
|
break;
|
||
|
+ case OPT_POLICY_FORMAT:
|
||
|
+ opt_policy_format = optarg;
|
||
|
+ break;
|
||
|
case 'h':
|
||
|
usage(0, NULL);
|
||
|
default:
|
||
|
@@ -1114,6 +1121,14 @@ main(int argc, char **argv)
|
||
|
else
|
||
|
fatal("Unsupported key format \"%s\"\n", opt_key_format);
|
||
|
|
||
|
+ if (!opt_policy_format || !strcasecmp(opt_policy_format, "grub2"))
|
||
|
+ systemd_json = false;
|
||
|
+ else
|
||
|
+ if (!strcasecmp(opt_policy_format, "systemd"))
|
||
|
+ systemd_json = true;
|
||
|
+ else
|
||
|
+ fatal("Unsupported policy format \"%s\"\n", opt_policy_format);
|
||
|
+
|
||
|
/* Validate options */
|
||
|
switch (action) {
|
||
|
case ACTION_PREDICT:
|
||
|
@@ -1163,10 +1178,11 @@ main(int argc, char **argv)
|
||
|
case ACTION_SIGN:
|
||
|
if (opt_rsa_private_key == NULL)
|
||
|
usage(1, "You need to specify the --private-key option when signing a policy\n");
|
||
|
- if (tpm2key_fmt) {
|
||
|
- if (opt_input == NULL)
|
||
|
- usage(1, "You need to specify the --input option when signing a policy into a TPM 2.0 Key file\n");
|
||
|
- }
|
||
|
+ if (systemd_json && opt_output == NULL)
|
||
|
+ usage(1, "You need to specify the --output option when signing a systemd policy\n");
|
||
|
+ if (tpm2key_fmt && opt_input == NULL)
|
||
|
+ usage(1, "You need to specify the --input option when signing a policy into a TPM 2.0 Key file\n");
|
||
|
+
|
||
|
pcr_selection = get_pcr_selection_argument(argc, argv, opt_algo);
|
||
|
end_arguments(argc, argv);
|
||
|
break;
|
||
|
@@ -1268,8 +1284,13 @@ main(int argc, char **argv)
|
||
|
return 1;
|
||
|
} else
|
||
|
if (action == ACTION_SIGN) {
|
||
|
- if (!pcr_policy_sign(tpm2key_fmt, &pred->prediction, opt_rsa_private_key, opt_input, opt_output, opt_policy_name))
|
||
|
- return 1;
|
||
|
+ if (systemd_json) {
|
||
|
+ if (!pcr_policy_sign_systemd(&pred->prediction, opt_rsa_private_key, opt_output))
|
||
|
+ return 1;
|
||
|
+ } else {
|
||
|
+ if (!pcr_policy_sign(tpm2key_fmt, &pred->prediction, opt_rsa_private_key, opt_input, opt_output, opt_policy_name))
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
return exit_code;
|
||
|
|
||
|
From 822ae935ada56b11a8e0b6a44d393f24311c91af Mon Sep 17 00:00:00 2001
|
||
|
From: Alberto Planas <aplanas@suse.com>
|
||
|
Date: Wed, 18 Oct 2023 15:45:47 +0200
|
||
|
Subject: [PATCH 10/10] Predict PCR12 for systemd-boot
|
||
|
|
||
|
Generate the list of boot entries, sorted using the BLS algorithm (that
|
||
|
will select the latest kernel). From this, compose the initrd entry
|
||
|
that will be used to exted the PCR12 register.
|
||
|
|
||
|
Signed-off-by: Alberto Planas <aplanas@suse.com>
|
||
|
---
|
||
|
Makefile.in | 3 +-
|
||
|
src/eventlog.c | 21 ++++-
|
||
|
src/sd-boot.c | 250 +++++++++++++++++++++++++++++++++++++++++++++++++
|
||
|
src/sd-boot.h | 44 +++++++++
|
||
|
4 files changed, 315 insertions(+), 3 deletions(-)
|
||
|
create mode 100644 src/sd-boot.c
|
||
|
create mode 100644 src/sd-boot.h
|
||
|
|
||
|
diff --git a/Makefile.in b/Makefile.in
|
||
|
index bfcb3f3..cf7b141 100644
|
||
|
--- a/Makefile.in
|
||
|
+++ b/Makefile.in
|
||
|
@@ -30,7 +30,8 @@ ORACLE_SRCS = oracle.c \
|
||
|
platform.c \
|
||
|
testcase.c \
|
||
|
bufparser.c \
|
||
|
- util.c
|
||
|
+ util.c \
|
||
|
+ sd-boot.c
|
||
|
ORACLE_OBJS = $(addprefix build/,$(patsubst %.c,%.o,$(ORACLE_SRCS)))
|
||
|
|
||
|
all: $(TOOLS) $(MANPAGES)
|
||
|
diff --git a/src/eventlog.c b/src/eventlog.c
|
||
|
index 4f2d3f4..7de9cd6 100644
|
||
|
--- a/src/eventlog.c
|
||
|
+++ b/src/eventlog.c
|
||
|
@@ -31,6 +31,7 @@
|
||
|
#include "runtime.h"
|
||
|
#include "digest.h"
|
||
|
#include "util.h"
|
||
|
+#include "sd-boot.h"
|
||
|
|
||
|
#define TPM_EVENT_LOG_MAX_ALGOS 64
|
||
|
|
||
|
@@ -791,11 +792,27 @@ __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;
|
||
|
+ char initrd[1024];
|
||
|
+ char initrd_utf16[2048];
|
||
|
+ unsigned int len;
|
||
|
+
|
||
|
if (parsed->systemd_event.string == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
- /* TODO: The hashed string (UTF16) should be the new initrd command */
|
||
|
- return digest_compute(ctx->algo, parsed->systemd_event.string, parsed->systemd_event.len);
|
||
|
+ if (!sdb_get_entry_list(&entry_list)) {
|
||
|
+ error("Error generating the list of boot entries\n");
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ debug("Next boot entry expected from: %s\n", entry_list.entries[0].path);
|
||
|
+ snprintf(initrd, sizeof(initrd), "initrd=%s %s",
|
||
|
+ entry_list.entries[0].initrd, entry_list.entries[0].options);
|
||
|
+
|
||
|
+ len = (strlen(initrd) + 1) << 2;
|
||
|
+ __convert_to_utf16le(initrd, strlen(initrd) + 1, initrd_utf16, len);
|
||
|
+
|
||
|
+ return digest_compute(ctx->algo, initrd_utf16, len);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
diff --git a/src/sd-boot.c b/src/sd-boot.c
|
||
|
new file mode 100644
|
||
|
index 0000000..56a4257
|
||
|
--- /dev/null
|
||
|
+++ b/src/sd-boot.c
|
||
|
@@ -0,0 +1,250 @@
|
||
|
+/*
|
||
|
+ * Copyright (C) 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.
|
||
|
+ *
|
||
|
+ */
|
||
|
+
|
||
|
+#include <assert.h>
|
||
|
+#include <dirent.h>
|
||
|
+#include <ctype.h>
|
||
|
+#include <errno.h>
|
||
|
+#include <stdbool.h>
|
||
|
+#include <stdio.h>
|
||
|
+#include <stdlib.h>
|
||
|
+#include <sys/param.h>
|
||
|
+
|
||
|
+#include "sd-boot.h"
|
||
|
+#include "util.h"
|
||
|
+
|
||
|
+static char *
|
||
|
+machine_id()
|
||
|
+{
|
||
|
+ static char id[33];
|
||
|
+ FILE *fp;
|
||
|
+
|
||
|
+ if (!(fp = fopen("/etc/machine-id", "r"))) {
|
||
|
+ error("Cannot read /etc/machine_id: %m\n");
|
||
|
+ goto fail;
|
||
|
+ }
|
||
|
+ if (fread(id, 32, 1, fp) != 1) {
|
||
|
+ error("Cannot read /etc/machine_id: %m\n");
|
||
|
+ goto fail;
|
||
|
+ }
|
||
|
+
|
||
|
+ id[32] = '\0';
|
||
|
+ return id;
|
||
|
+
|
||
|
+fail:
|
||
|
+ fclose(fp);
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+
|
||
|
+static bool
|
||
|
+read_entry(sdb_entry_data_t *result)
|
||
|
+{
|
||
|
+ FILE *fp;
|
||
|
+ char line[SDB_LINE_MAX];
|
||
|
+
|
||
|
+ if (!(fp = fopen(result->path, "r"))) {
|
||
|
+ error("Cannot read %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("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);
|
||
|
+ }
|
||
|
+
|
||
|
+ return true;
|
||
|
+
|
||
|
+fail:
|
||
|
+ fclose(fp);
|
||
|
+ 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;
|
||
|
+}
|
||
|
+
|
||
|
+bool
|
||
|
+sdb_get_entry_list(sdb_entry_list_t *result)
|
||
|
+{
|
||
|
+ char *id = NULL;
|
||
|
+ DIR *d = NULL;
|
||
|
+ struct dirent *dir;
|
||
|
+ char *path = "/boot/efi/loader/entries";
|
||
|
+
|
||
|
+ memset(result, 0, sizeof(*result));
|
||
|
+
|
||
|
+ if (!(id = machine_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 (strncmp(id, dir->d_name, strlen(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);
|
||
|
+
|
||
|
+ return true;
|
||
|
+
|
||
|
+fail:
|
||
|
+ closedir(d);
|
||
|
+ return false;
|
||
|
+}
|
||
|
+
|
||
|
diff --git a/src/sd-boot.h b/src/sd-boot.h
|
||
|
new file mode 100644
|
||
|
index 0000000..80f21d6
|
||
|
--- /dev/null
|
||
|
+++ b/src/sd-boot.h
|
||
|
@@ -0,0 +1,44 @@
|
||
|
+/*
|
||
|
+ * Copyright (C) 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.
|
||
|
+ *
|
||
|
+ */
|
||
|
+
|
||
|
+#ifndef SD_BOOT_H
|
||
|
+#define SD_BOOT_H
|
||
|
+
|
||
|
+#include <limits.h>
|
||
|
+
|
||
|
+#define SDB_MAX_ENTRIES 16
|
||
|
+#define SDB_LINE_MAX 512
|
||
|
+
|
||
|
+typedef struct sdb_entry_data {
|
||
|
+ char path[PATH_MAX];
|
||
|
+ char sort_key[SDB_LINE_MAX];
|
||
|
+ char machine_id[SDB_LINE_MAX];
|
||
|
+ char version[SDB_LINE_MAX];
|
||
|
+ char options[SDB_LINE_MAX];
|
||
|
+ char initrd[SDB_LINE_MAX];
|
||
|
+} sdb_entry_data_t;
|
||
|
+
|
||
|
+typedef struct sdb_entry_list {
|
||
|
+ unsigned int num_entries;
|
||
|
+ sdb_entry_data_t entries[SDB_MAX_ENTRIES];
|
||
|
+} sdb_entry_list_t;
|
||
|
+
|
||
|
+extern bool sdb_get_entry_list(sdb_entry_list_t *result);
|
||
|
+
|
||
|
+#endif /* SD_BOOT_H */
|