diff --git a/src/cryptenroll/cryptenroll-recovery.c b/src/cryptenroll/cryptenroll-recovery.c index da7b7a1018..69aa4fca57 100644 --- a/src/cryptenroll/cryptenroll-recovery.c +++ b/src/cryptenroll/cryptenroll-recovery.c @@ -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) diff --git a/src/cryptenroll/cryptenroll-varlink.c b/src/cryptenroll/cryptenroll-varlink.c index d268eb9956..366845569e 100644 --- a/src/cryptenroll/cryptenroll-varlink.c +++ b/src/cryptenroll/cryptenroll-varlink.c @@ -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: diff --git a/src/cryptenroll/cryptenroll.c b/src/cryptenroll/cryptenroll.c index 02f52ee190..208fcc43c6 100644 --- a/src/cryptenroll/cryptenroll.c +++ b/src/cryptenroll/cryptenroll.c @@ -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); diff --git a/src/cryptenroll/cryptenroll.h b/src/cryptenroll/cryptenroll.h index 60073278b2..2915b9c1e8 100644 --- a/src/cryptenroll/cryptenroll.h +++ b/src/cryptenroll/cryptenroll.h @@ -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; diff --git a/src/shared/varlink-io.systemd.CryptEnroll.c b/src/shared/varlink-io.systemd.CryptEnroll.c index 6855299e76..5ff9110437 100644 --- a/src/shared/varlink-io.systemd.CryptEnroll.c +++ b/src/shared/varlink-io.systemd.CryptEnroll.c @@ -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));