diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml
index 707a8d25e8..61906d3ce3 100644
--- a/man/systemd-cryptenroll.xml
+++ b/man/systemd-cryptenroll.xml
@@ -270,6 +270,16 @@
The following options are understood that may be used to unlock the device in preparation of the enrollment operations:
+
+
+
+ Use an empty password/passphrase to unlock the volume, instead of reading one from
+ stdin. This is useful to unlock volumes that are currently protected by an empty password, in order to
+ enroll a different, stronger unlock mechanism.
+
+
+
+
diff --git a/src/cryptenroll/cryptenroll-password.c b/src/cryptenroll/cryptenroll-password.c
index f58321063a..595279c80d 100644
--- a/src/cryptenroll/cryptenroll-password.c
+++ b/src/cryptenroll/cryptenroll-password.c
@@ -14,6 +14,30 @@
#include "string-util.h"
#include "strv.h"
+int load_volume_key_empty(
+ const EnrollContext *c,
+ struct crypt_device *cd,
+ struct iovec *ret_vk) {
+
+ int r;
+
+ assert_se(c);
+ assert_se(cd);
+ assert_se(ret_vk);
+
+ r = sym_crypt_volume_key_get(
+ cd,
+ CRYPT_ANY_SLOT,
+ ret_vk->iov_base,
+ &ret_vk->iov_len,
+ "",
+ 0);
+ if (r < 0)
+ return log_error_errno(r, "Provided empty password did not work: %m");
+
+ return r;
+}
+
int load_volume_key_keyfile(
const EnrollContext *c,
struct crypt_device *cd,
diff --git a/src/cryptenroll/cryptenroll-password.h b/src/cryptenroll/cryptenroll-password.h
index 35ae1760a8..035a1becc5 100644
--- a/src/cryptenroll/cryptenroll-password.h
+++ b/src/cryptenroll/cryptenroll-password.h
@@ -4,6 +4,7 @@
#include "cryptenroll.h"
#include "shared-forward.h"
+int load_volume_key_empty(const EnrollContext *c, struct crypt_device *cd, struct iovec *ret_vk);
int load_volume_key_keyfile(const EnrollContext *c, struct crypt_device *cd, struct iovec *ret_vk);
int load_volume_key_password(const EnrollContext *c, struct crypt_device *cd, struct iovec *ret_vk);
diff --git a/src/cryptenroll/cryptenroll.c b/src/cryptenroll/cryptenroll.c
index ce21f450ba..4b8aa6c241 100644
--- a/src/cryptenroll/cryptenroll.c
+++ b/src/cryptenroll/cryptenroll.c
@@ -334,6 +334,14 @@ static int parse_argv(int argc, char *argv[]) {
OPTION_GROUP("Unlocking"): {}
+ OPTION_LONG("unlock-empty", NULL, "Use an empty password to unlock the volume"):
+ if (arg_unlock_type != UNLOCK_PASSWORD)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Multiple unlock methods specified at once, refusing.");
+
+ arg_unlock_type = UNLOCK_EMPTY;
+ break;
+
OPTION_LONG("unlock-key-file", "PATH",
"Use a file to unlock the volume"):
if (arg_unlock_type != UNLOCK_PASSWORD)
@@ -730,6 +738,10 @@ int prepare_luks(
switch (c->unlock_type) {
+ case UNLOCK_EMPTY:
+ r = load_volume_key_empty(c, cd, &vk);
+ break;
+
case UNLOCK_PASSWORD:
r = load_volume_key_password(c, cd, &vk);
break;
diff --git a/src/cryptenroll/cryptenroll.h b/src/cryptenroll/cryptenroll.h
index 36aa852f42..49fa3b2a35 100644
--- a/src/cryptenroll/cryptenroll.h
+++ b/src/cryptenroll/cryptenroll.h
@@ -19,6 +19,7 @@ typedef enum UnlockType {
UNLOCK_KEYFILE,
UNLOCK_FIDO2,
UNLOCK_TPM2,
+ UNLOCK_EMPTY,
_UNLOCK_TYPE_MAX,
_UNLOCK_TYPE_INVALID = -EINVAL,
} UnlockType;