cryptenroll-varlink: Allow handing in a recovery key

In some cases we want users to copy the recovery key to a save location
before we enroll it, therefore allow clients to hand in a recovery key.
The next commit will add a varlink method to generate the recovery key.

Closes: https://github.com/systemd/systemd/issues/43676
This commit is contained in:
Julian Sparber
2026-09-23 09:30:28 +02:00
committed by Luca Boccassi
parent 1c168322cf
commit c753849fd6
5 changed files with 35 additions and 4 deletions
+9 -3
View File
@@ -29,9 +29,15 @@ int enroll_recovery(
assert_se(node = sym_crypt_get_device_name(cd));
r = make_recovery_key(&password);
if (r < 0)
return log_error_errno(r, "Failed to generate recovery key: %m");
if (c->recovery_key) {
password = memdup_suffix0(c->recovery_key, c->recovery_key_size);
if (!password)
return log_oom();
} else {
r = make_recovery_key(&password);
if (r < 0)
return log_error_errno(r, "Failed to generate recovery key: %m");
}
r = cryptsetup_set_minimal_pbkdf(cd);
if (r < 0)
+17
View File
@@ -47,6 +47,7 @@ typedef struct MethodEnrollParameters {
char *unlock_fido2_device;
char *unlock_tpm2_device;
char *password;
char *recovery_key;
char *fido2_device;
char *fido2_pin;
int fido2_with_client_pin;
@@ -65,6 +66,7 @@ static void method_enroll_parameters_done(MethodEnrollParameters *p) {
free(p->unlock_fido2_device);
free(p->unlock_tpm2_device);
erase_and_free(p->password);
erase_and_free(p->recovery_key);
free(p->fido2_device);
erase_and_free(p->fido2_pin);
sd_json_variant_unref(p->wipe_slots);
@@ -163,6 +165,7 @@ static int vl_method_enroll(
{ "unlockFido2Device", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(MethodEnrollParameters, unlock_fido2_device), SD_JSON_NULLABLE },
{ "unlockTpm2Device", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(MethodEnrollParameters, unlock_tpm2_device), SD_JSON_NULLABLE },
{ "password", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(MethodEnrollParameters, password), SD_JSON_NULLABLE },
{ "recoveryKey", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(MethodEnrollParameters, recovery_key), SD_JSON_NULLABLE },
{ "fido2Device", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(MethodEnrollParameters, fido2_device), SD_JSON_NULLABLE },
{ "fido2Pin", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(MethodEnrollParameters, fido2_pin), SD_JSON_NULLABLE },
{ "fido2WithClientPin", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MethodEnrollParameters, fido2_with_client_pin), SD_JSON_NULLABLE },
@@ -288,6 +291,20 @@ static int vl_method_enroll(
break;
case ENROLL_RECOVERY:
if (p.recovery_key) {
_cleanup_(erase_and_freep) char *mangled = NULL;
r = normalize_recovery_key(p.recovery_key, &mangled);
if (r == -EINVAL) /* Not properly formatted, rejecting */
return sd_varlink_error_invalid_parameter_name(link, "recoveryKey");
if (r < 0)
return log_error_errno(r, "Failed to normalize recovery key: %m");
if (!streq(p.recovery_key, mangled))
return sd_varlink_error_invalid_parameter_name(link, "recoveryKey");
c.recovery_key = TAKE_PTR(p.recovery_key);
c.recovery_key_size = strlen(c.recovery_key);
}
break;
case ENROLL_FIDO2:
+1
View File
@@ -176,6 +176,7 @@ void enroll_context_done(EnrollContext *c) {
c->unlock_tpm2_device = mfree(c->unlock_tpm2_device);
c->unlock_password = erase_and_free(c->unlock_password);
c->passphrase = erase_and_free(c->passphrase);
c->recovery_key = erase_and_free(c->recovery_key);
c->fido2_device = mfree(c->fido2_device);
c->fido2_salt_file = mfree(c->fido2_salt_file);
c->fido2_pin = erase_and_free(c->fido2_pin);
+5
View File
@@ -66,6 +66,11 @@ typedef struct EnrollContext {
char *passphrase;
size_t passphrase_size;
/* New recovery key to enroll (mechanism == recovery). When NULL the helpers fall back to
* generate a new recovery key. */
char *recovery_key;
size_t recovery_key_size;
/* FIDO2 */
char *fido2_device;
char *fido2_salt_file;
+3 -1
View File
@@ -42,6 +42,8 @@ static SD_VARLINK_DEFINE_METHOD_FULL(
SD_VARLINK_FIELD_COMMENT("The passphrase to enroll, when mechanism is 'password'. Contains key material."),
SD_VARLINK_DEFINE_INPUT(password, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("A recovery key to enroll, when mechanism is 'recovery'. The key must be in the normalized form and have high entropy. Contains key material."),
SD_VARLINK_DEFINE_INPUT(recoveryKey, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Path to the FIDO2 device to enroll, when mechanism is 'fido2'. Leave empty to automatically discover a suitable device."),
SD_VARLINK_DEFINE_INPUT(fido2Device, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
@@ -65,7 +67,7 @@ static SD_VARLINK_DEFINE_METHOD_FULL(
SD_VARLINK_DEFINE_OUTPUT(slot, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The keyslots wiped as part of this call, if any."),
SD_VARLINK_DEFINE_OUTPUT(wipedSlots, SD_VARLINK_INT, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The generated recovery key, when mechanism is 'recoveryKey'. Contains key material."),
SD_VARLINK_FIELD_COMMENT("The generated recovery key or the provided recovery key, when mechanism is 'recovery'. Contains key material."),
SD_VARLINK_DEFINE_OUTPUT(recoveryKey, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Progress indicator, only sent on intermediate replies when 'more' was set. Currently the only value is 'touch', signalling that the FIDO2 token is waiting for a touch. Unset on the terminating reply."),
SD_VARLINK_DEFINE_OUTPUT(state, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));