summaryrefslogtreecommitdiffstats
path: root/src/pcrlock
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/pcrlock/pcrlock-firmware.c8
-rw-r--r--src/pcrlock/pcrlock.c779
2 files changed, 574 insertions, 213 deletions
diff --git a/src/pcrlock/pcrlock-firmware.c b/src/pcrlock/pcrlock-firmware.c
index 73c68c2..6fd7363 100644
--- a/src/pcrlock/pcrlock-firmware.c
+++ b/src/pcrlock/pcrlock-firmware.c
@@ -100,12 +100,12 @@ int validate_firmware_header(
if (size < (uint64_t) offsetof(TCG_PCClientPCREvent, event) + (uint64_t) h->eventDataSize)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log too short for TCG_PCClientPCREvent events data.");
- if (h->pcrIndex != 0)
- return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header has unexpected PCR index %" PRIu32, h->pcrIndex);
if (h->eventType != EV_NO_ACTION)
- return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header has unexpected event type 0x%" PRIx32, h->eventType);
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header has unexpected event type 0x%08" PRIx32 ". (Probably not a TPM2 event log?)", h->eventType);
+ if (h->pcrIndex != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header has unexpected PCR index %" PRIu32 ". (Probably not a TPM2 event log?)", h->pcrIndex);
if (!memeqzero(h->digest, sizeof(h->digest)))
- return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header has unexpected non-zero digest.");
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header has unexpected non-zero digest. (Probably not a TPM2 event log?)");
if (h->eventDataSize < offsetof(TCG_EfiSpecIDEvent, digestSizes))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Event log header too short for TCG_EfiSpecIdEvent.");
diff --git a/src/pcrlock/pcrlock.c b/src/pcrlock/pcrlock.c
index dde4dd9..1716fb3 100644
--- a/src/pcrlock/pcrlock.c
+++ b/src/pcrlock/pcrlock.c
@@ -9,14 +9,18 @@
#include "ask-password-api.h"
#include "blockdev-util.h"
+#include "boot-entry.h"
#include "build.h"
#include "chase.h"
+#include "color-util.h"
#include "conf-files.h"
+#include "creds-util.h"
#include "efi-api.h"
#include "env-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
+#include "find-esp.h"
#include "format-table.h"
#include "format-util.h"
#include "fs-util.h"
@@ -39,13 +43,24 @@
#include "random-util.h"
#include "recovery-key.h"
#include "sort-util.h"
+#include "string-table.h"
#include "terminal-util.h"
#include "tpm2-util.h"
#include "unaligned.h"
#include "unit-name.h"
#include "utf8.h"
+#include "varlink.h"
+#include "varlink-io.systemd.PCRLock.h"
#include "verbs.h"
+typedef enum RecoveryPinMode {
+ RECOVERY_PIN_HIDE, /* generate a recovery PIN automatically, but don't show it (alias: "no") */
+ RECOVERY_PIN_SHOW, /* generate a recovery PIN automatically, and display it to the user */
+ RECOVERY_PIN_QUERY, /* asks the user for a PIN to use interactively (alias: "yes") */
+ _RECOVERY_PIN_MODE_MAX,
+ _RECOVERY_PIN_MODE_INVALID = -EINVAL,
+} RecoveryPinMode;
+
static PagerFlags arg_pager_flags = 0;
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF|JSON_FORMAT_NEWLINE;
static char **arg_components = NULL;
@@ -56,15 +71,19 @@ static bool arg_raw_description = false;
static char *arg_location_start = NULL;
static char *arg_location_end = NULL;
static TPM2_HANDLE arg_nv_index = 0;
-static bool arg_recovery_pin = false;
+static RecoveryPinMode arg_recovery_pin = RECOVERY_PIN_HIDE;
static char *arg_policy_path = NULL;
static bool arg_force = false;
+static BootEntryTokenType arg_entry_token_type = BOOT_ENTRY_TOKEN_AUTO;
+static char *arg_entry_token = NULL;
+static bool arg_varlink = false;
STATIC_DESTRUCTOR_REGISTER(arg_components, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_pcrlock_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_location_start, freep);
STATIC_DESTRUCTOR_REGISTER(arg_location_end, freep);
STATIC_DESTRUCTOR_REGISTER(arg_policy_path, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep);
#define PCRLOCK_SECUREBOOT_POLICY_PATH "/var/lib/pcrlock.d/240-secureboot-policy.pcrlock.d/generated.pcrlock"
#define PCRLOCK_FIRMWARE_CODE_EARLY_PATH "/var/lib/pcrlock.d/250-firmware-code-early.pcrlock.d/generated.pcrlock"
@@ -94,6 +113,14 @@ STATIC_DESTRUCTOR_REGISTER(arg_policy_path, freep);
(UINT32_C(1) << TPM2_PCR_SHIM_POLICY) | \
(UINT32_C(1) << TPM2_PCR_SYSTEM_IDENTITY))
+static const char* recovery_pin_mode_table[_RECOVERY_PIN_MODE_MAX] = {
+ [RECOVERY_PIN_HIDE] = "hide",
+ [RECOVERY_PIN_SHOW] = "show",
+ [RECOVERY_PIN_QUERY] = "query",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(recovery_pin_mode, RecoveryPinMode, RECOVERY_PIN_QUERY);
+
typedef struct EventLogRecordBank EventLogRecordBank;
typedef struct EventLogRecord EventLogRecord;
typedef struct EventLogRegisterBank EventLogRegisterBank;
@@ -549,7 +576,7 @@ static int event_log_record_extract_firmware_description(EventLogRecord *rec) {
case EV_SEPARATOR: {
if (rec->firmware_payload_size != sizeof(uint32_t)) {
- log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "EFI separator field has wrong size, ignoring.");
+ log_warning("EFI separator field has wrong size, ignoring.");
goto invalid;
}
@@ -567,7 +594,7 @@ static int event_log_record_extract_firmware_description(EventLogRecord *rec) {
break;
default:
- log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "Unexpected separator payload %" PRIu32 ".", val);
+ log_warning("Unexpected separator payload %" PRIu32 ", ignoring.", val);
goto invalid;
}
@@ -585,7 +612,7 @@ static int event_log_record_extract_firmware_description(EventLogRecord *rec) {
return log_error_errno(r, "Failed to make C string from EFI action string: %m");
if (!string_is_safe(d)) {
- log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "Unsafe EFI action string in record, ignoring.");
+ log_warning("Unsafe EFI action string in record, ignoring.");
goto invalid;
}
@@ -597,14 +624,14 @@ static int event_log_record_extract_firmware_description(EventLogRecord *rec) {
case EV_EFI_GPT_EVENT: {
if (rec->firmware_payload_size < sizeof(GptHeader)) {
- log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "GPT measurement too short, ignoring.");
+ log_warning("GPT measurement too short, ignoring.");
goto invalid;
}
const GptHeader *h = rec->firmware_payload;
if (!gpt_header_has_signature(h)) {
- log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "GPT measurement does not cover a GPT partition table header, ignoring.");
+ log_warning("GPT measurement does not cover a GPT partition table header, ignoring.");
goto invalid;
}
@@ -628,7 +655,7 @@ static int event_log_record_extract_firmware_description(EventLogRecord *rec) {
return log_oom();
if (string_has_cc(d, NULL)) {
- log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "Unsafe EFI action string in record, ignoring.");
+ log_warning("Unsafe EFI action string in record, ignoring.");
goto invalid;
}
@@ -644,7 +671,7 @@ static int event_log_record_extract_firmware_description(EventLogRecord *rec) {
size_t left = rec->firmware_payload_size;
if (left == 0) {
- log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "Empty tagged PC client event, ignoring.");
+ log_warning("Empty tagged PC client event, ignoring.");
goto invalid;
}
@@ -652,13 +679,13 @@ static int event_log_record_extract_firmware_description(EventLogRecord *rec) {
uint64_t m;
if (left < offsetof(TCG_PCClientTaggedEvent, taggedEventData)) {
- log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "Tagged PC client event too short, ignoring.");
+ log_warning("Tagged PC client event too short, ignoring.");
goto invalid;
}
m = offsetof(TCG_PCClientTaggedEvent, taggedEventData) + (uint64_t) tag->taggedEventDataSize;
if (left < m) {
- log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "Tagged PC client event data too short, ignoring.");
+ log_warning("Tagged PC client event data too short, ignoring.");
goto invalid;
}
@@ -728,7 +755,7 @@ static int event_log_record_extract_firmware_description(EventLogRecord *rec) {
case EV_EFI_PLATFORM_FIRMWARE_BLOB: {
const UEFI_PLATFORM_FIRMWARE_BLOB *blob;
if (rec->firmware_payload_size != sizeof(UEFI_PLATFORM_FIRMWARE_BLOB)) {
- log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "EV_EFI_PLATFORM_FIRMWARE_BLOB of wrong size, ignoring.");
+ log_warning("EV_EFI_PLATFORM_FIRMWARE_BLOB of wrong size, ignoring.");
goto invalid;
}
@@ -747,14 +774,14 @@ static int event_log_record_extract_firmware_description(EventLogRecord *rec) {
bool end = false;
if (rec->firmware_payload_size < offsetof(UEFI_IMAGE_LOAD_EVENT, devicePath)) {
- log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "Device path too short, ignoring.");
+ log_warning("Device path too short, ignoring.");
goto invalid;
}
load = rec->firmware_payload;
if (load->lengthOfDevicePath !=
rec->firmware_payload_size - offsetof(UEFI_IMAGE_LOAD_EVENT, devicePath)) {
- log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "Device path size does not match, ignoring.");
+ log_warning("Device path size does not match, ignoring.");
goto invalid;
}
@@ -764,7 +791,7 @@ static int event_log_record_extract_firmware_description(EventLogRecord *rec) {
for (;;) {
if (left == 0) {
if (!end) {
- log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "Garbage after device path end, ignoring.");
+ log_warning("Garbage after device path end, ignoring.");
goto invalid;
}
@@ -772,12 +799,12 @@ static int event_log_record_extract_firmware_description(EventLogRecord *rec) {
}
if (end) {
- log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "Garbage after device path end, ignoring.");
+ log_warning("Garbage after device path end, ignoring.");
goto invalid;
}
if (left < offsetof(packed_EFI_DEVICE_PATH, path) || left < dp->length) {
- log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "Device path element too short, ignoring.");
+ log_warning("Device path element too short, ignoring.");
goto invalid;
}
@@ -896,7 +923,7 @@ static int event_log_load_firmware(EventLog *el) {
payload_size == 17 &&
memcmp(payload, "StartupLocality", sizeof("StartupLocality")) == 0) {
if (el->startup_locality_found)
- return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "StartupLocality event found twice!");
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "StartupLocality event found twice.");
el->startup_locality = ((const uint8_t*) payload)[sizeof("StartupLocality")];
el->startup_locality_found = true;
@@ -926,23 +953,30 @@ static int event_log_load_firmware(EventLog *el) {
assert(event->digests.count == n_algorithms);
for (size_t i = 0; i < n_algorithms; i++, ha = ha_next) {
- ha_next = (const uint8_t*) ha + offsetof(TPMT_HA, digest) + algorithms[i].digestSize;
-
/* The TPMT_HA is not aligned in the record, hence read the hashAlg field via an unaligned read */
assert_cc(__builtin_types_compatible_p(uint16_t, typeof(TPMI_ALG_HASH)));
uint16_t hash_alg = unaligned_read_ne16((const uint8_t*) ha + offsetof(TPMT_HA, hashAlg));
- if (hash_alg != algorithms[i].algorithmId)
- return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Hash algorithms in event log record don't match log.");
+ /* On some systems (some HyperV?) the order of hash algorithms announced in the
+ * header does not match the order in the records. Let's hence search for the right
+ * mapping */
+ size_t j;
+ for (j = 0; j < n_algorithms; j++)
+ if (hash_alg == algorithms[j].algorithmId)
+ break;
+ if (j >= n_algorithms)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Hash algorithms in event log record not among those advertised by log header.");
- if (!tpm2_hash_alg_to_string(algorithms[i].algorithmId))
+ ha_next = (const uint8_t*) ha + offsetof(TPMT_HA, digest) + algorithms[j].digestSize;
+
+ if (!tpm2_hash_alg_to_string(hash_alg))
continue;
r = event_log_record_add_bank(
record,
- algorithms[i].algorithmId,
+ hash_alg,
(const uint8_t*) ha + offsetof(TPMT_HA, digest),
- algorithms[i].digestSize,
+ algorithms[j].digestSize,
/* ret= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to add bank to event log record: %m");
@@ -1010,7 +1044,7 @@ static int event_log_record_parse_json(EventLogRecord *record, JsonVariant *j) {
h = json_variant_by_key(k, "digest");
if (!h)
- return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "'digests' field lacks 'digest' field");
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "'digests' field lacks 'digest' field.");
r = json_variant_unhex(h, &hash, &hash_size);
if (r < 0)
@@ -1294,7 +1328,7 @@ static int event_log_calculate_pcrs(EventLog *el) {
rec_b = event_log_record_find_bank(*rr, el->algorithms[i]);
if (!rec_b) {
- log_warning_errno(SYNTHETIC_ERRNO(ENXIO), "Record with missing bank '%s', ignoring.", n);
+ log_warning("Record with missing bank '%s', ignoring.", n);
continue;
}
@@ -1932,40 +1966,6 @@ static int event_log_map_components(EventLog *el) {
return event_log_validate_fully_recognized(el);
}
-static void hsv_to_rgb(
- double h, double s, double v,
- uint8_t* ret_r, uint8_t *ret_g, uint8_t *ret_b) {
-
- double c, x, m, r, g, b;
-
- assert(s >= 0 && s <= 100);
- assert(v >= 0 && v <= 100);
- assert(ret_r);
- assert(ret_g);
- assert(ret_b);
-
- c = (s / 100.0) * (v / 100.0);
- x = c * (1 - fabs(fmod(h / 60.0, 2) - 1));
- m = (v / 100) - c;
-
- if (h >= 0 && h < 60)
- r = c, g = x, b = 0.0;
- else if (h >= 60 && h < 120)
- r = x, g = c, b = 0.0;
- else if (h >= 120 && h < 180)
- r = 0.0, g = c, b = x;
- else if (h >= 180 && h < 240)
- r = 0.0, g = x, b = c;
- else if (h >= 240 && h < 300)
- r = x, g = 0.0, b = c;
- else
- r = c, g = 0.0, b = x;
-
- *ret_r = (uint8_t) ((r + m) * 255);
- *ret_g = (uint8_t) ((g + m) * 255);
- *ret_b = (uint8_t) ((b + m) * 255);
-}
-
#define ANSI_TRUE_COLOR_MAX (7U + 3U + 1U + 3U + 1U + 3U + 2U)
static const char *ansi_true_color(uint8_t r, uint8_t g, uint8_t b, char ret[static ANSI_TRUE_COLOR_MAX]) {
@@ -2341,7 +2341,7 @@ static int event_determine_primary_algorithm(EventLog *el) {
}
FOREACH_ARRAY(alg, el->algorithms, el->n_algorithms) {
- /* If we have SHA256, focus on that that */
+ /* If we have SHA256, focus on that */
if (*alg == TPM2_ALG_SHA256) {
el->primary_algorithm = *alg;
@@ -2439,6 +2439,75 @@ static int verb_show_log(int argc, char *argv[], void *userdata) {
return 0;
}
+static int event_log_record_to_cel(EventLogRecord *record, uint64_t *recnum, JsonVariant **ret) {
+ _cleanup_(json_variant_unrefp) JsonVariant *ja = NULL, *fj = NULL;
+ JsonVariant *cd = NULL;
+ const char *ct = NULL;
+ int r;
+
+ assert(record);
+ assert(recnum);
+ assert(ret);
+
+ LIST_FOREACH(banks, bank, record->banks) {
+ r = json_variant_append_arrayb(
+ &ja, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_STRING("hashAlg", tpm2_hash_alg_to_string(bank->algorithm)),
+ JSON_BUILD_PAIR_HEX("digest", bank->hash.buffer, bank->hash.size)));
+ if (r < 0)
+ return log_error_errno(r, "Failed to append CEL digest entry: %m");
+ }
+
+ if (!ja) {
+ r = json_variant_new_array(&ja, NULL, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate JSON array: %m");
+ }
+
+ if (EVENT_LOG_RECORD_IS_FIRMWARE(record)) {
+ _cleanup_free_ char *et = NULL;
+ const char *z;
+
+ z = tpm2_log_event_type_to_string(record->firmware_event_type);
+ if (z) {
+ _cleanup_free_ char *b = NULL;
+
+ b = strreplace(z, "-", "_");
+ if (!b)
+ return log_oom();
+
+ et = strjoin("EV_", ascii_strupper(b));
+ if (!et)
+ return log_oom();
+ } else if (asprintf(&et, "%" PRIu32, record->firmware_event_type) < 0)
+ return log_oom();
+
+ r = json_build(&fj, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_STRING("event_type", et),
+ JSON_BUILD_PAIR_HEX("event_data", record->firmware_payload, record->firmware_payload_size)));
+ if (r < 0)
+ return log_error_errno(r, "Failed to build firmware event data: %m");
+
+ cd = fj;
+ ct = "pcclient_std";
+ } else if (EVENT_LOG_RECORD_IS_USERSPACE(record)) {
+ cd = record->userspace_content;
+ ct = "systemd";
+ }
+
+ r = json_build(ret,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_UNSIGNED("pcr", record->pcr),
+ JSON_BUILD_PAIR_UNSIGNED("recnum", ++(*recnum)),
+ JSON_BUILD_PAIR_VARIANT("digests", ja),
+ JSON_BUILD_PAIR_CONDITION(ct, "content_type", JSON_BUILD_STRING(ct)),
+ JSON_BUILD_PAIR_CONDITION(cd, "content", JSON_BUILD_VARIANT(cd))));
+ if (r < 0)
+ return log_error_errno(r, "Failed to make CEL record: %m");
+
+ return 0;
+}
+
static int verb_show_cel(int argc, char *argv[], void *userdata) {
_cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
_cleanup_(event_log_freep) EventLog *el = NULL;
@@ -2456,64 +2525,13 @@ static int verb_show_cel(int argc, char *argv[], void *userdata) {
/* Output the event log in TCG CEL-JSON. */
FOREACH_ARRAY(rr, el->records, el->n_records) {
- _cleanup_(json_variant_unrefp) JsonVariant *ja = NULL, *fj = NULL;
- EventLogRecord *record = *rr;
- JsonVariant *cd = NULL;
- const char *ct = NULL;
-
- LIST_FOREACH(banks, bank, record->banks) {
- r = json_variant_append_arrayb(
- &ja, JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR_STRING("hashAlg", tpm2_hash_alg_to_string(bank->algorithm)),
- JSON_BUILD_PAIR_HEX("digest", bank->hash.buffer, bank->hash.size)));
- if (r < 0)
- return log_error_errno(r, "Failed to append CEL digest entry: %m");
- }
-
- if (!ja) {
- r = json_variant_new_array(&ja, NULL, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to allocate JSON array: %m");
- }
-
- if (EVENT_LOG_RECORD_IS_FIRMWARE(record)) {
- _cleanup_free_ char *et = NULL;
- const char *z;
-
- z = tpm2_log_event_type_to_string(record->firmware_event_type);
- if (z) {
- _cleanup_free_ char *b = NULL;
-
- b = strreplace(z, "-", "_");
- if (!b)
- return log_oom();
-
- et = strjoin("EV_", ascii_strupper(b));
- if (!et)
- return log_oom();
- } else if (asprintf(&et, "%" PRIu32, record->firmware_event_type) < 0)
- return log_oom();
+ _cleanup_(json_variant_unrefp) JsonVariant *cel = NULL;
- r = json_build(&fj, JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR_STRING("event_type", et),
- JSON_BUILD_PAIR_HEX("event_data", record->firmware_payload, record->firmware_payload_size)));
- if (r < 0)
- return log_error_errno(r, "Failed to build firmware event data: %m");
-
- cd = fj;
- ct = "pcclient_std";
- } else if (EVENT_LOG_RECORD_IS_USERSPACE(record)) {
- cd = record->userspace_content;
- ct = "systemd";
- }
+ r = event_log_record_to_cel(*rr, &recnum, &cel);
+ if (r < 0)
+ return r;
- r = json_variant_append_arrayb(&array,
- JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR_UNSIGNED("pcr", record->pcr),
- JSON_BUILD_PAIR_UNSIGNED("recnum", ++recnum),
- JSON_BUILD_PAIR_VARIANT("digests", ja),
- JSON_BUILD_PAIR_CONDITION(ct, "content_type", JSON_BUILD_STRING(ct)),
- JSON_BUILD_PAIR_CONDITION(cd, "content", JSON_BUILD_VARIANT(cd))));
+ r = json_variant_append_array(&array, cel);
if (r < 0)
return log_error_errno(r, "Failed to append CEL record: %m");
}
@@ -2603,17 +2621,17 @@ static int verb_list_components(int argc, char *argv[], void *userdata) {
}
}
- if (table_get_rows(table) > 1 || !FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
+ if (!table_isempty(table) || !FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, /* show_header= */ true);
if (r < 0)
return log_error_errno(r, "Failed to output table: %m");
}
if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
- if (table_get_rows(table) > 1)
- printf("\n%zu components listed.\n", table_get_rows(table) - 1);
- else
+ if (table_isempty(table))
printf("No components defined.\n");
+ else
+ printf("\n%zu components listed.\n", table_get_rows(table) - 1);
}
return 0;
@@ -2661,7 +2679,7 @@ static int make_pcrlock_record(
assert_se(a = tpm2_hash_alg_to_string(*pa));
assert_se(md = EVP_get_digestbyname(a));
hash_ssize = EVP_MD_size(md);
- assert_se(hash_ssize > 0);
+ assert(hash_ssize > 0);
hash_usize = hash_ssize;
hash = malloc(hash_usize);
@@ -2690,6 +2708,101 @@ static int make_pcrlock_record(
return 0;
}
+static void evp_md_ctx_free_all(EVP_MD_CTX *(*md)[TPM2_N_HASH_ALGORITHMS]) {
+ assert(md);
+ FOREACH_ARRAY(alg, *md, TPM2_N_HASH_ALGORITHMS)
+ if (*alg)
+ EVP_MD_CTX_free(*alg);
+}
+
+static int make_pcrlock_record_from_stream(
+ uint32_t pcr_mask,
+ FILE *f,
+ JsonVariant **ret_records) {
+
+ _cleanup_(json_variant_unrefp) JsonVariant *digests = NULL;
+ _cleanup_(evp_md_ctx_free_all) EVP_MD_CTX *mdctx[TPM2_N_HASH_ALGORITHMS] = {};
+ int r;
+
+ assert(f);
+ assert(ret_records);
+
+ for (size_t i = 0; i < TPM2_N_HASH_ALGORITHMS; i++) {
+ const char *a;
+ const EVP_MD *md;
+
+ assert_se(a = tpm2_hash_alg_to_string(tpm2_hash_algorithms[i]));
+ assert_se(md = EVP_get_digestbyname(a));
+
+ mdctx[i] = EVP_MD_CTX_new();
+ if (!mdctx[i])
+ return log_oom();
+
+ if (EVP_DigestInit_ex(mdctx[i], md, NULL) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to initialize message digest for %s.", a);
+ }
+
+ for (;;) {
+ uint8_t buffer[64*1024];
+ size_t n;
+
+ n = fread(buffer, 1, sizeof(buffer), f);
+ if (ferror(f))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to read file.");
+ if (n == 0 && feof(f))
+ break;
+
+ for (size_t i = 0; i < TPM2_N_HASH_ALGORITHMS; i++)
+ if (EVP_DigestUpdate(mdctx[i], buffer, n) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to hash data.");
+ }
+
+ for (size_t i = 0; i < TPM2_N_HASH_ALGORITHMS; i++) {
+ const char *a;
+ int hash_ssize;
+ unsigned hash_usize;
+
+ assert_se(a = tpm2_hash_alg_to_string(tpm2_hash_algorithms[i]));
+ hash_ssize = EVP_MD_CTX_size(mdctx[i]);
+ assert(hash_ssize > 0 && hash_ssize <= EVP_MAX_MD_SIZE);
+ hash_usize = hash_ssize;
+ unsigned char hash[hash_usize];
+
+ if (EVP_DigestFinal_ex(mdctx[i], hash, &hash_usize) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to finalize hash context for algorithn '%s'.", a);
+
+ r = json_variant_append_arrayb(
+ &digests,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(a)),
+ JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(hash, hash_usize))));
+ if (r < 0)
+ return log_error_errno(r, "Failed to build JSON digest object: %m");
+ }
+
+ for (uint32_t i = 0; i < TPM2_PCRS_MAX; i++) {
+ _cleanup_(json_variant_unrefp) JsonVariant *record = NULL;
+
+ if (!FLAGS_SET(pcr_mask, UINT32_C(1) << i))
+ continue;
+
+ r = json_build(&record,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(i)),
+ JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(digests))));
+ if (r < 0)
+ return log_error_errno(r, "Failed to build record object: %m");
+
+ r = json_variant_append_array(ret_records, record);
+ if (r < 0)
+ return log_error_errno(r, "Failed to append to JSON array: %m");
+ }
+
+ return 0;
+}
+
static const char *pcrlock_path(const char *default_pcrlock_path) {
return arg_pcrlock_path ?: arg_pcrlock_auto ? default_pcrlock_path : NULL;
}
@@ -2753,10 +2866,8 @@ static int unlink_pcrlock(const char *default_pcrlock_path) {
}
static int verb_lock_raw(int argc, char *argv[], void *userdata) {
- _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
- _cleanup_free_ char *data = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *records = NULL;
_cleanup_fclose_ FILE *f = NULL;
- size_t size;
int r;
if (arg_pcr_mask == 0)
@@ -2768,26 +2879,11 @@ static int verb_lock_raw(int argc, char *argv[], void *userdata) {
return log_error_errno(errno, "Failed to open '%s': %m", argv[1]);
}
- r = read_full_stream(f ?: stdin, &data, &size);
+ r = make_pcrlock_record_from_stream(arg_pcr_mask, f ?: stdin, &records);
if (r < 0)
- return log_error_errno(r, "Failed to read data from stdin: %m");
-
- for (uint32_t i = 0; i < TPM2_PCRS_MAX; i++) {
- _cleanup_(json_variant_unrefp) JsonVariant *record = NULL;
-
- if (!FLAGS_SET(arg_pcr_mask, UINT32_C(1) << i))
- continue;
-
- r = make_pcrlock_record(i, data, size, &record);
- if (r < 0)
- return r;
-
- r = json_variant_append_array(&array, record);
- if (r < 0)
- return log_error_errno(r, "Failed to append to JSON array: %m");
- }
+ return r;
- return write_pcrlock(array, NULL);
+ return write_pcrlock(records, NULL);
}
static int verb_unlock_simple(int argc, char *argv[], void *userdata) {
@@ -2815,7 +2911,7 @@ static int verb_lock_secureboot_policy(int argc, char *argv[], void *userdata) {
/* Generates expected records from the current SecureBoot state, as readable in the EFI variables
* right now. */
- FOREACH_ARRAY(vv, variables, ELEMENTSOF(variables)) {
+ FOREACH_ELEMENT(vv, variables) {
_cleanup_(json_variant_unrefp) JsonVariant *record = NULL;
_cleanup_free_ char *name = NULL;
@@ -3099,7 +3195,7 @@ static int verb_lock_gpt(int argc, char *argv[], void *userdata) {
if (n < 0)
return log_error_errno(errno, "Failed to read GPT header of block device: %m");
if ((size_t) n != sizeof(h))
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read trying to read GPT header: %m");
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read trying to read GPT header.");
/* Try a couple of sector sizes */
for (size_t sz = 512; sz <= 4096; sz <<= 1) {
@@ -3152,7 +3248,7 @@ static int verb_lock_gpt(int argc, char *argv[], void *userdata) {
if (n < 0)
return log_error_errno(errno, "Failed to read GPT partition table entries: %m");
if ((size_t) n != member_bufsz)
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading GPT partition table entries: %m");
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading GPT partition table entries.");
size_t vdata_size = le32toh(p->header_size) + sizeof(le64_t) + member_size * n_members;
_cleanup_free_ void *vdata = malloc0(vdata_size);
@@ -3795,10 +3891,9 @@ static int verb_unlock_kernel_cmdline(int argc, char *argv[], void *userdata) {
}
static int verb_lock_kernel_initrd(int argc, char *argv[], void *userdata) {
- _cleanup_(json_variant_unrefp) JsonVariant *record = NULL, *array = NULL;
- _cleanup_free_ void *data = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *records = NULL;
_cleanup_fclose_ FILE *f = NULL;
- size_t size;
+ uint32_t pcr_mask = UINT32_C(1) << TPM2_PCR_KERNEL_INITRD;
int r;
if (argc >= 2) {
@@ -3807,19 +3902,11 @@ static int verb_lock_kernel_initrd(int argc, char *argv[], void *userdata) {
return log_error_errno(errno, "Failed to open '%s': %m", argv[1]);
}
- r = read_full_stream(f ?: stdin, (char**) &data, &size);
- if (r < 0)
- return log_error_errno(r, "Failed to read data from stdin: %m");
-
- r = make_pcrlock_record(TPM2_PCR_KERNEL_INITRD /* = 9 */, data, size, &record);
+ r = make_pcrlock_record_from_stream(pcr_mask, f ?: stdin, &records);
if (r < 0)
return r;
- r = json_variant_new_array(&array, &record, 1);
- if (r < 0)
- return log_error_errno(r, "Failed to create record array: %m");
-
- r = write_pcrlock(array, PCRLOCK_KERNEL_INITRD_PATH);
+ r = write_pcrlock(records, PCRLOCK_KERNEL_INITRD_PATH);
if (r < 0)
return r;
@@ -4197,7 +4284,129 @@ static int remove_policy_file(const char *path) {
return 1;
}
-static int verb_make_policy(int argc, char *argv[], void *userdata) {
+static int determine_boot_policy_file(char **ret) {
+ _cleanup_free_ char *path = NULL, *fn = NULL, *joined = NULL;
+ sd_id128_t machine_id;
+ int r;
+
+ assert(ret);
+
+ r = find_xbootldr_and_warn(
+ /* root= */ NULL,
+ /* path= */ NULL,
+ /* unprivileged_mode= */ false,
+ &path,
+ /* ret_uuid= */ NULL,
+ /* ret_devid= */ NULL);
+ if (r < 0) {
+ if (r != -ENOKEY)
+ return log_error_errno(r, "Failed to find XBOOTLDR partition: %m");
+
+ r = find_esp_and_warn(
+ /* root= */ NULL,
+ /* path= */ NULL,
+ /* unprivileged_mode= */ false,
+ &path,
+ /* ret_part= */ NULL,
+ /* ret_pstart= */ NULL,
+ /* ret_psize= */ NULL,
+ /* ret_uuid= */ NULL,
+ /* ret_devid= */ NULL);
+ if (r < 0) {
+ if (r != -ENOKEY)
+ return log_error_errno(r, "Failed to find ESP partition: %m");
+
+ *ret = NULL;
+ return 0; /* not found! */
+ }
+ }
+
+ r = sd_id128_get_machine(&machine_id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read machine ID: %m");
+
+ r = boot_entry_token_ensure(
+ /* root= */ NULL,
+ "/etc/kernel",
+ machine_id,
+ /* machine_id_is_random = */ false,
+ &arg_entry_token_type,
+ &arg_entry_token);
+ if (r < 0)
+ return r;
+
+ fn = strjoin("pcrlock.", arg_entry_token, ".cred");
+ if (!fn)
+ return log_oom();
+
+ if (!filename_is_valid(fn))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential name '%s' would not be a valid file name, refusing.", fn);
+
+ joined = path_join(path, "loader/credentials", fn);
+ if (!joined)
+ return log_oom();
+
+ *ret = TAKE_PTR(joined);
+ return 1; /* found! */
+}
+
+static int write_boot_policy_file(const char *json_text) {
+ _cleanup_free_ char *boot_policy_file = NULL;
+ int r;
+
+ assert(json_text);
+
+ r = determine_boot_policy_file(&boot_policy_file);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ log_info("Did not find XBOOTLDR/ESP partition, not writing boot policy file.");
+ return 0;
+ }
+
+ _cleanup_free_ char *c = NULL;
+ r = path_extract_filename(boot_policy_file, &c);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract file name from %s: %m", boot_policy_file);
+
+ ascii_strlower(c); /* lowercase this file, no matter what, since stored on VFAT, and we don't want to
+ * run into case change incompatibilities */
+
+ _cleanup_(iovec_done) struct iovec encoded = {};
+ r = encrypt_credential_and_warn(
+ CRED_AES256_GCM_BY_NULL,
+ c,
+ now(CLOCK_REALTIME),
+ /* not_after= */ USEC_INFINITY,
+ /* tpm2_device= */ NULL,
+ /* tpm2_hash_pcr_mask= */ 0,
+ /* tpm2_pubkey_path= */ NULL,
+ /* tpm2_pubkey_path_mask= */ 0,
+ UID_INVALID,
+ &IOVEC_MAKE_STRING(json_text),
+ CREDENTIAL_ALLOW_NULL,
+ &encoded);
+ if (r < 0)
+ return log_error_errno(r, "Failed to encode policy as credential: %m");
+
+ _cleanup_free_ char *base64_buf = NULL;
+ ssize_t base64_size;
+ base64_size = base64mem_full(encoded.iov_base, encoded.iov_len, 79, &base64_buf);
+ if (base64_size < 0)
+ return base64_size;
+
+ r = write_string_file(
+ boot_policy_file,
+ base64_buf,
+ WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_MKDIR_0755);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write boot policy file to '%s': %m", boot_policy_file);
+
+ log_info("Written new boot policy to '%s'.", boot_policy_file);
+ return 1;
+}
+
+static int make_policy(bool force, RecoveryPinMode recovery_pin_mode) {
int r;
/* Here's how this all works: after predicting all possible PCR values for next boot (with
@@ -4207,7 +4416,7 @@ static int verb_make_policy(int argc, char *argv[], void *userdata) {
* policies).
*
* Whenever we want to lock an encrypted object (for example FDE) against this policy, we'll use a
- * PolicyAuthorizeNV epxression that pins the NV index in the policy, and permits access to any
+ * PolicyAuthorizeNV expression that pins the NV index in the policy, and permits access to any
* policies matching the current NV index contents.
*
* We grant world-readable read access to the NV index. Write access is controlled by a PIN (which we
@@ -4272,11 +4481,11 @@ static int verb_make_policy(int argc, char *argv[], void *userdata) {
if (arg_nv_index != 0 && old_policy.nv_index != arg_nv_index)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Stored policy references different NV index (0x%x) than specified (0x%x), refusing.", old_policy.nv_index, arg_nv_index);
- if (!arg_force &&
+ if (!force &&
old_policy.algorithm == el->primary_algorithm &&
tpm2_pcr_prediction_equal(&old_policy.prediction, &new_prediction, el->primary_algorithm)) {
log_info("Prediction is identical to current policy, skipping update.");
- return EXIT_SUCCESS;
+ return 0; /* NOP */
}
}
@@ -4321,19 +4530,21 @@ static int verb_make_policy(int argc, char *argv[], void *userdata) {
/* Acquire a recovery PIN, either from the user, or create a randomized one */
_cleanup_(erase_and_freep) char *pin = NULL;
- if (arg_recovery_pin) {
+ if (recovery_pin_mode == RECOVERY_PIN_QUERY) {
r = getenv_steal_erase("PIN", &pin);
if (r < 0)
return log_error_errno(r, "Failed to acquire PIN from environment: %m");
if (r == 0) {
_cleanup_(strv_free_erasep) char **l = NULL;
+ AskPasswordRequest req = {
+ .message = "Recovery PIN",
+ .id = "pcrlock-recovery-pin",
+ .credential = "pcrlock.recovery-pin",
+ };
+
r = ask_password_auto(
- "Recovery PIN",
- /* icon= */ NULL,
- /* id= */ "pcrlock-recovery-pin",
- /* key_name= */ NULL,
- /* credential_name= */ "systemd-pcrlock.recovery-pin",
+ &req,
/* until= */ 0,
/* flags= */ 0,
&l);
@@ -4348,16 +4559,16 @@ static int verb_make_policy(int argc, char *argv[], void *userdata) {
}
} else if (!have_old_policy) {
- char rnd[256];
-
- r = crypto_random_bytes(rnd, sizeof(rnd));
+ r = make_recovery_key(&pin);
if (r < 0)
return log_error_errno(r, "Failed to generate a randomized recovery PIN: %m");
- (void) base64mem(rnd, sizeof(rnd), &pin);
- explicit_bzero_safe(rnd, sizeof(rnd));
- if (!pin)
- return log_oom();
+ if (recovery_pin_mode == RECOVERY_PIN_SHOW)
+ printf("%s Selected recovery PIN is: %s%s%s\n",
+ special_glyph(SPECIAL_GLYPH_LOCK_AND_KEY),
+ ansi_highlight_cyan(),
+ pin,
+ ansi_normal());
}
_cleanup_(tpm2_handle_freep) Tpm2Handle *nv_handle = NULL;
@@ -4375,7 +4586,7 @@ static int verb_make_policy(int argc, char *argv[], void *userdata) {
CLEANUP_ERASE(auth);
if (pin) {
- r = tpm2_get_pin_auth(TPM2_ALG_SHA256, pin, &auth);
+ r = tpm2_auth_value_from_pin(TPM2_ALG_SHA256, pin, &auth);
if (r < 0)
return log_error_errno(r, "Failed to hash PIN: %m");
} else {
@@ -4442,15 +4653,28 @@ static int verb_make_policy(int argc, char *argv[], void *userdata) {
log_info("Retrieved PIN from TPM2 in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC), pin_start_usec), 1));
}
- TPM2B_NV_PUBLIC nv_public = {};
+ /* Now convert the PIN into an HMAC-SHA256 key that we can use in PolicySigned to protect access to the nvindex with */
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *pin_handle = NULL;
+ r = tpm2_hmac_key_from_pin(tc, encryption_session, &auth, &pin_handle);
+ if (r < 0)
+ return r;
+ TPM2B_NV_PUBLIC nv_public = {};
usec_t nv_index_start_usec = now(CLOCK_MONOTONIC);
if (!iovec_is_set(&nv_blob)) {
+ _cleanup_(Esys_Freep) TPM2B_NAME *pin_name = NULL;
+ r = tpm2_get_name(
+ tc,
+ pin_handle,
+ &pin_name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get name of PIN from TPM2: %m");
+
TPM2B_DIGEST recovery_policy_digest = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
- r = tpm2_calculate_policy_auth_value(&recovery_policy_digest);
+ r = tpm2_calculate_policy_signed(&recovery_policy_digest, pin_name);
if (r < 0)
- return log_error_errno(r, "Failed to calculate authentication value policy: %m");
+ return log_error_errno(r, "Failed to calculate PolicySigned policy: %m");
log_debug("Allocating NV index to write PCR policy to...");
r = tpm2_define_policy_nv_index(
@@ -4458,8 +4682,6 @@ static int verb_make_policy(int argc, char *argv[], void *userdata) {
encryption_session,
arg_nv_index,
&recovery_policy_digest,
- pin,
- &auth,
&nv_index,
&nv_handle,
&nv_public);
@@ -4469,10 +4691,6 @@ static int verb_make_policy(int argc, char *argv[], void *userdata) {
return log_error_errno(r, "Failed to allocate NV index: %m");
}
- r = tpm2_set_auth_binary(tc, nv_handle, &auth);
- if (r < 0)
- return log_error_errno(r, "Failed to set authentication value on NV index: %m");
-
_cleanup_(tpm2_handle_freep) Tpm2Handle *policy_session = NULL;
r = tpm2_make_policy_session(
tc,
@@ -4482,9 +4700,11 @@ static int verb_make_policy(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to allocate policy session: %m");
- r = tpm2_policy_auth_value(
+ r = tpm2_policy_signed_hmac_sha256(
tc,
policy_session,
+ pin_handle,
+ &IOVEC_MAKE(auth.buffer, auth.size),
/* ret_policy_digest= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to submit authentication value policy: %m");
@@ -4595,9 +4815,15 @@ static int verb_make_policy(int argc, char *argv[], void *userdata) {
log_info("Written new policy to '%s' and digest to TPM2 NV index 0x%x.", path, nv_index);
+ (void) write_boot_policy_file(text);
+
log_info("Overall time spent: %s", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC), start_usec), 1));
- return 0;
+ return 1; /* installed new policy */
+}
+
+static int verb_make_policy(int argc, char *argv[], void *userdata) {
+ return make_policy(arg_force, arg_recovery_pin);
}
static int undefine_policy_nv_index(
@@ -4653,8 +4879,8 @@ static int undefine_policy_nv_index(
return 0;
}
-static int verb_remove_policy(int argc, char *argv[], void *userdata) {
- int r;
+static int remove_policy(void) {
+ int ret = 0, r;
_cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy policy = {};
r = tpm2_pcrlock_policy_load(arg_policy_path, &policy);
@@ -4669,22 +4895,31 @@ static int verb_remove_policy(int argc, char *argv[], void *userdata) {
r = undefine_policy_nv_index(policy.nv_index, &policy.nv_handle, &policy.srk_handle);
if (r < 0)
log_notice("Failed to remove NV index, assuming data out of date, removing policy file.");
- }
- if (arg_policy_path) {
- r = remove_policy_file(arg_policy_path);
- if (r < 0)
- return r;
-
- return 0;
- } else {
- int ret = 0;
+ RET_GATHER(ret, r);
+ }
+ if (arg_policy_path)
+ RET_GATHER(ret, remove_policy_file(arg_policy_path));
+ else {
RET_GATHER(ret, remove_policy_file("/var/lib/systemd/pcrlock.json"));
RET_GATHER(ret, remove_policy_file("/run/systemd/pcrlock.json"));
-
- return ret;
}
+
+ _cleanup_free_ char *boot_policy_file = NULL;
+ r = determine_boot_policy_file(&boot_policy_file);
+ if (r == 0)
+ log_info("Did not find XBOOTLDR/ESP partition, not removing boot policy file.");
+ else if (r > 0) {
+ RET_GATHER(ret, remove_policy_file(boot_policy_file));
+ } else
+ RET_GATHER(ret, r);
+
+ return ret;
+}
+
+static int verb_remove_policy(int argc, char *argv[], void *userdata) {
+ return remove_policy();
}
static int help(int argc, char *argv[], void *userdata) {
@@ -4744,6 +4979,8 @@ static int help(int argc, char *argv[], void *userdata) {
" --pcrlock=PATH .pcrlock file to write expected PCR measurement to\n"
" --policy=PATH JSON file to write policy output to\n"
" --force Write policy even if it matches existing policy\n"
+ " --entry-token=machine-id|os-id|os-image-id|auto|literal:…\n"
+ " Boot entry token to use for this installation\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
@@ -4769,6 +5006,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_PCRLOCK,
ARG_POLICY,
ARG_FORCE,
+ ARG_ENTRY_TOKEN,
};
static const struct option options[] = {
@@ -4785,6 +5023,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "pcrlock", required_argument, NULL, ARG_PCRLOCK },
{ "policy", required_argument, NULL, ARG_POLICY },
{ "force", no_argument, NULL, ARG_FORCE },
+ { "entry-token", required_argument, NULL, ARG_ENTRY_TOKEN },
{}
};
@@ -4900,13 +5139,13 @@ static int parse_argv(int argc, char *argv[]) {
}
case ARG_RECOVERY_PIN:
- r = parse_boolean_argument("--recovery-pin", optarg, &arg_recovery_pin);
- if (r < 0)
- return r;
+ arg_recovery_pin = recovery_pin_mode_from_string(optarg);
+ if (arg_recovery_pin < 0)
+ return log_error_errno(arg_recovery_pin, "Failed to parse --recovery-pin= mode: %s", optarg);
break;
case ARG_PCRLOCK:
- if (isempty(optarg) || streq(optarg, "-"))
+ if (empty_or_dash(optarg))
arg_pcrlock_path = mfree(arg_pcrlock_path);
else {
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_pcrlock_path);
@@ -4918,7 +5157,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_POLICY:
- if (isempty(optarg) || streq(optarg, "-"))
+ if (empty_or_dash(optarg))
arg_policy_path = mfree(arg_policy_path);
else {
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_policy_path);
@@ -4932,6 +5171,12 @@ static int parse_argv(int argc, char *argv[]) {
arg_force = true;
break;
+ case ARG_ENTRY_TOKEN:
+ r = parse_boot_entry_token_type(optarg, &arg_entry_token_type, &arg_entry_token);
+ if (r < 0)
+ return r;
+ break;
+
case '?':
return -EINVAL;
@@ -4952,6 +5197,14 @@ static int parse_argv(int argc, char *argv[]) {
return log_oom();
}
+ r = varlink_invocation(VARLINK_ALLOW_ACCEPT);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
+ if (r > 0) {
+ arg_varlink = true;
+ arg_pager_flags |= PAGER_DISABLE;
+ }
+
return 1;
}
@@ -4994,17 +5247,125 @@ static int pcrlock_main(int argc, char *argv[]) {
return dispatch_verb(argc, argv, verbs, NULL);
}
+static int vl_method_read_event_log(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+ _cleanup_(event_log_freep) EventLog *el = NULL;
+ uint64_t recnum = 0;
+ int r;
+
+ assert(link);
+
+ if (json_variant_elements(parameters) > 0)
+ return varlink_error_invalid_parameter(link, parameters);
+
+ el = event_log_new();
+ if (!el)
+ return log_oom();
+
+ r = event_log_load(el);
+ if (r < 0)
+ return r;
+
+ _cleanup_(json_variant_unrefp) JsonVariant *rec_cel = NULL;
+
+ FOREACH_ARRAY(rr, el->records, el->n_records) {
+
+ if (rec_cel) {
+ r = varlink_notifyb(link,
+ JSON_BUILD_OBJECT(JSON_BUILD_PAIR_VARIANT("record", rec_cel)));
+ if (r < 0)
+ return r;
+
+ rec_cel = json_variant_unref(rec_cel);
+ }
+
+ r = event_log_record_to_cel(*rr, &recnum, &rec_cel);
+ if (r < 0)
+ return r;
+ }
+
+ return varlink_replyb(link,
+ JSON_BUILD_OBJECT(JSON_BUILD_PAIR_CONDITION(rec_cel, "record", JSON_BUILD_VARIANT(rec_cel))));
+}
+
+typedef struct MethodMakePolicyParameters {
+ bool force;
+} MethodMakePolicyParameters;
+
+static int vl_method_make_policy(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+ static const JsonDispatch dispatch_table[] = {
+ { "force", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(MethodMakePolicyParameters, force), 0 },
+ {}
+ };
+ MethodMakePolicyParameters p = {};
+ int r;
+
+ assert(link);
+
+ r = varlink_dispatch(link, parameters, dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ r = make_policy(p.force, /* recovery_key= */ RECOVERY_PIN_HIDE);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return varlink_error(link, "io.systemd.PCRLock.NoChange", NULL);
+
+ return varlink_reply(link, NULL);
+}
+
+static int vl_method_remove_policy(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+ int r;
+
+ assert(link);
+
+ if (json_variant_elements(parameters) > 0)
+ return varlink_error_invalid_parameter(link, parameters);
+
+ r = remove_policy();
+ if (r < 0)
+ return r;
+
+ return varlink_reply(link, NULL);
+}
+
static int run(int argc, char *argv[]) {
int r;
- log_show_color(true);
- log_parse_environment();
- log_open();
+ log_setup();
r = parse_argv(argc, argv);
if (r <= 0)
return r;
+ if (arg_varlink) {
+ _cleanup_(varlink_server_unrefp) VarlinkServer *varlink_server = NULL;
+
+ /* Invocation as Varlink service */
+
+ r = varlink_server_new(&varlink_server, VARLINK_SERVER_ROOT_ONLY);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate Varlink server: %m");
+
+ r = varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_PCRLock);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add Varlink interface: %m");
+
+ r = varlink_server_bind_method_many(
+ varlink_server,
+ "io.systemd.PCRLock.ReadEventLog", vl_method_read_event_log,
+ "io.systemd.PCRLock.MakePolicy", vl_method_make_policy,
+ "io.systemd.PCRLock.RemovePolicy", vl_method_remove_policy);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind Varlink methods: %m");
+
+ r = varlink_server_loop_auto(varlink_server);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run Varlink event loop: %m");
+
+ return EXIT_SUCCESS;
+ }
+
return pcrlock_main(argc, argv);
}