pcrlock: rework --recovery-pin= to take three different arguments

This reworkds --recovery-pin= from a parameter that takes a boolean to
an enum supporting one of "hide", "show", "query".

If "hide" (default behaviour) we'll generate a recovery pin
automatically, but never show it, and thus just seal it and good.

If "show" we'll generate a recovery pin automatically, but display it in
the output, so the user can write it down.

If "query" we'll ask the user for a recovery pin, and not automatically
generate any.

For compatibility the old boolean behaviour is kept.

With this you can now do "systemd-pcrlock make-policy
--recovery-pin=show" to set up the first policy, write down the recovery
PIN. Later, if the PCR prediction didn't work out one day you can then
do "systemd-pcrlock make-policy --recovery-pin=query" and enter the
recovery key and write a new policy.
This commit is contained in:
Lennart Poettering 2024-04-17 19:04:29 +02:00
parent 0ec4c098dd
commit 43a59b8b86
2 changed files with 41 additions and 14 deletions

View file

@ -504,13 +504,16 @@
<varlistentry>
<term><option>--recovery-pin=</option></term>
<listitem><para>Takes a boolean. Defaults to false. Honoured by <command>make-policy</command>. If
true, will query the user for a PIN to unlock the TPM2 NV index with. If no policy was created before
this PIN is used to protect the newly allocated NV index. If a policy has been created before the PIN
is used to unlock write access to the NV index. If this option is not used a PIN is automatically
generated. Regardless if user supplied or automatically generated, it is stored in encrypted form in
the policy metadata file. The recovery PIN may be used to regain write access to an NV index in case
the access policy became out of date.</para>
<listitem><para>Takes one of <literal>hide</literal>, <literal>show</literal> or
<literal>query</literal>. Defaults to <literal>hide</literal>. Honoured by
<command>make-policy</command>. If <literal>query</literal>, will query the user for a PIN to unlock
the TPM2 NV index with. If no policy was created before, this PIN is used to protect the newly
allocated NV index. If a policy has been created before, the PIN is used to unlock write access to
the NV index. If either <literal>hide</literal> or <literal>show</literal> is used, a PIN is
automatically generated, and — only in case of <literal>show</literal> — displayed on
screen. Regardless if user supplied or automatically generated, it is stored in encrypted form in the
policy metadata file. The recovery PIN may be used to regain write access to an NV index in case the
access policy became out of date.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>

View file

@ -43,6 +43,7 @@
#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"
@ -52,6 +53,14 @@
#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;
@ -62,7 +71,7 @@ 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;
@ -104,6 +113,14 @@ STATIC_DESTRUCTOR_REGISTER(arg_entry_token, 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;
@ -4320,7 +4337,7 @@ static int write_boot_policy_file(const char *json_text) {
return 1;
}
static int make_policy(bool force, bool recovery_pin) {
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
@ -4444,7 +4461,7 @@ static int make_policy(bool force, bool recovery_pin) {
/* Acquire a recovery PIN, either from the user, or create a randomized one */
_cleanup_(erase_and_freep) char *pin = NULL;
if (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");
@ -4476,6 +4493,13 @@ static int make_policy(bool force, bool recovery_pin) {
r = make_recovery_key(&pin);
if (r < 0)
return log_error_errno(r, "Failed to generate a randomized recovery PIN: %m");
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;
@ -5046,9 +5070,9 @@ 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:
@ -5212,7 +5236,7 @@ static int vl_method_make_policy(Varlink *link, JsonVariant *parameters, Varlink
if (r != 0)
return r;
r = make_policy(p.force, /* recovery_key= */ false);
r = make_policy(p.force, /* recovery_key= */ RECOVERY_PIN_HIDE);
if (r < 0)
return r;
if (r == 0)