mirror of
https://github.com/Dasharo/systemd.git
synced 2026-03-06 15:02:31 -08:00
Merge pull request #27925 from DaanDeMeyer/repart-encrypt
repart: Do online encryption when loop devices are available
This commit is contained in:
@@ -3155,29 +3155,85 @@ static int context_wipe_and_discard(Context *context) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct DecryptedPartitionTarget {
|
||||
int fd;
|
||||
char *volume;
|
||||
struct crypt_device *device;
|
||||
} DecryptedPartitionTarget;
|
||||
|
||||
static DecryptedPartitionTarget* decrypted_partition_target_free(DecryptedPartitionTarget *t) {
|
||||
#ifdef HAVE_LIBCRYPTSETUP
|
||||
_cleanup_free_ char *name = NULL;
|
||||
int r;
|
||||
|
||||
if (!t)
|
||||
return NULL;
|
||||
|
||||
r = path_extract_filename(t->volume, &name);
|
||||
if (r < 0) {
|
||||
assert(r == -ENOMEM);
|
||||
log_oom();
|
||||
}
|
||||
|
||||
safe_close(t->fd);
|
||||
|
||||
if (name) {
|
||||
/* udev or so might access out block device in the background while we are done. Let's hence
|
||||
* force detach the volume. We sync'ed before, hence this should be safe. */
|
||||
r = sym_crypt_deactivate_by_name(t->device, name, CRYPT_DEACTIVATE_FORCE);
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Failed to deactivate LUKS device: %m");
|
||||
}
|
||||
|
||||
sym_crypt_free(t->device);
|
||||
free(t->volume);
|
||||
free(t);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
LoopDevice *loop;
|
||||
int fd;
|
||||
char *path;
|
||||
int whole_fd;
|
||||
DecryptedPartitionTarget *decrypted;
|
||||
} PartitionTarget;
|
||||
|
||||
static int partition_target_fd(PartitionTarget *t) {
|
||||
assert(t);
|
||||
assert(t->loop || t->fd >= 0 || t->whole_fd >= 0);
|
||||
return t->loop ? t->loop->fd : t->fd >= 0 ? t->fd : t->whole_fd;
|
||||
|
||||
if (t->decrypted)
|
||||
return t->decrypted->fd;
|
||||
|
||||
if (t->loop)
|
||||
return t->loop->fd;
|
||||
|
||||
if (t->fd >= 0)
|
||||
return t->fd;
|
||||
|
||||
return t->whole_fd;
|
||||
}
|
||||
|
||||
static const char* partition_target_path(PartitionTarget *t) {
|
||||
assert(t);
|
||||
assert(t->loop || t->path);
|
||||
return t->loop ? t->loop->node : t->path;
|
||||
|
||||
if (t->decrypted)
|
||||
return t->decrypted->volume;
|
||||
|
||||
if (t->loop)
|
||||
return t->loop->node;
|
||||
|
||||
return t->path;
|
||||
}
|
||||
|
||||
static PartitionTarget *partition_target_free(PartitionTarget *t) {
|
||||
if (!t)
|
||||
return NULL;
|
||||
|
||||
decrypted_partition_target_free(t->decrypted);
|
||||
loop_device_unref(t->loop);
|
||||
safe_close(t->fd);
|
||||
unlink_and_free(t->path);
|
||||
@@ -3285,6 +3341,7 @@ static int partition_target_grow(PartitionTarget *t, uint64_t size) {
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
assert(!t->decrypted);
|
||||
|
||||
if (t->loop) {
|
||||
r = loop_device_refresh_size(t->loop, UINT64_MAX, size);
|
||||
@@ -3308,6 +3365,9 @@ static int partition_target_sync(Context *context, Partition *p, PartitionTarget
|
||||
|
||||
assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
|
||||
|
||||
if (t->decrypted && fsync(t->decrypted->fd) < 0)
|
||||
return log_error_errno(errno, "Failed to sync changes to '%s': %m", t->decrypted->volume);
|
||||
|
||||
if (t->loop) {
|
||||
r = loop_device_sync(t->loop);
|
||||
if (r < 0)
|
||||
@@ -3340,12 +3400,13 @@ static int partition_target_sync(Context *context, Partition *p, PartitionTarget
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int partition_encrypt(Context *context, Partition *p, const char *node) {
|
||||
static int partition_encrypt(Context *context, Partition *p, PartitionTarget *target, bool offline) {
|
||||
#if HAVE_LIBCRYPTSETUP && HAVE_CRYPT_SET_DATA_OFFSET && HAVE_CRYPT_REENCRYPT_INIT_BY_PASSPHRASE && HAVE_CRYPT_REENCRYPT
|
||||
const char *node = partition_target_path(target);
|
||||
struct crypt_params_luks2 luks_params = {
|
||||
.label = strempty(ASSERT_PTR(p)->new_label),
|
||||
.sector_size = ASSERT_PTR(context)->sector_size,
|
||||
.data_device = node,
|
||||
.data_device = offline ? node : NULL,
|
||||
};
|
||||
struct crypt_params_reencrypt reencrypt_params = {
|
||||
.mode = CRYPT_REENCRYPT_ENCRYPT,
|
||||
@@ -3358,7 +3419,7 @@ static int partition_encrypt(Context *context, Partition *p, const char *node) {
|
||||
_cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
|
||||
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
|
||||
_cleanup_fclose_ FILE *h = NULL;
|
||||
_cleanup_free_ char *hp = NULL;
|
||||
_cleanup_free_ char *hp = NULL, *vol = NULL, *dm_name = NULL;
|
||||
const char *passphrase = NULL;
|
||||
size_t passphrase_size = 0;
|
||||
const char *vt;
|
||||
@@ -3374,39 +3435,51 @@ static int partition_encrypt(Context *context, Partition *p, const char *node) {
|
||||
|
||||
log_info("Encrypting future partition %" PRIu64 "...", p->partno);
|
||||
|
||||
r = var_tmp_dir(&vt);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine temporary files directory: %m");
|
||||
if (offline) {
|
||||
r = var_tmp_dir(&vt);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine temporary files directory: %m");
|
||||
|
||||
r = fopen_temporary_child(vt, &h, &hp);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create temporary LUKS header file: %m");
|
||||
r = fopen_temporary_child(vt, &h, &hp);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create temporary LUKS header file: %m");
|
||||
|
||||
/* Weird cryptsetup requirement which requires the header file to be the size of at least one sector. */
|
||||
r = ftruncate(fileno(h), context->sector_size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to grow temporary LUKS header file: %m");
|
||||
/* Weird cryptsetup requirement which requires the header file to be the size of at least one
|
||||
* sector. */
|
||||
r = ftruncate(fileno(h), context->sector_size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to grow temporary LUKS header file: %m");
|
||||
} else {
|
||||
if (asprintf(&dm_name, "luks-repart-%08" PRIx64, random_u64()) < 0)
|
||||
return log_oom();
|
||||
|
||||
r = sym_crypt_init(&cd, hp);
|
||||
vol = path_join("/dev/mapper/", dm_name);
|
||||
if (!vol)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
r = sym_crypt_init(&cd, offline ? hp : node);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", hp);
|
||||
|
||||
cryptsetup_enable_logging(cd);
|
||||
|
||||
/* Disable kernel keyring usage by libcryptsetup as a workaround for
|
||||
* https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/273. This makes sure that we can do
|
||||
* offline encryption even when repart is running in a container. */
|
||||
r = sym_crypt_volume_key_keyring(cd, false);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to disable kernel keyring: %m");
|
||||
if (offline) {
|
||||
/* Disable kernel keyring usage by libcryptsetup as a workaround for
|
||||
* https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/273. This makes sure that we can
|
||||
* do offline encryption even when repart is running in a container. */
|
||||
r = sym_crypt_volume_key_keyring(cd, false);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to disable kernel keyring: %m");
|
||||
|
||||
r = sym_crypt_metadata_locking(cd, false);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to disable metadata locking: %m");
|
||||
r = sym_crypt_metadata_locking(cd, false);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to disable metadata locking: %m");
|
||||
|
||||
r = sym_crypt_set_data_offset(cd, LUKS2_METADATA_SIZE / 512);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set data offset: %m");
|
||||
r = sym_crypt_set_data_offset(cd, LUKS2_METADATA_SIZE / 512);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set data offset: %m");
|
||||
}
|
||||
|
||||
r = sym_crypt_format(cd,
|
||||
CRYPT_LUKS2,
|
||||
@@ -3517,51 +3590,84 @@ static int partition_encrypt(Context *context, Partition *p, const char *node) {
|
||||
#endif
|
||||
}
|
||||
|
||||
r = sym_crypt_reencrypt_init_by_passphrase(
|
||||
cd,
|
||||
NULL,
|
||||
passphrase,
|
||||
passphrase_size,
|
||||
CRYPT_ANY_SLOT,
|
||||
0,
|
||||
sym_crypt_get_cipher(cd),
|
||||
sym_crypt_get_cipher_mode(cd),
|
||||
&reencrypt_params);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to prepare for reencryption: %m");
|
||||
if (offline) {
|
||||
r = sym_crypt_reencrypt_init_by_passphrase(
|
||||
cd,
|
||||
NULL,
|
||||
passphrase,
|
||||
passphrase_size,
|
||||
CRYPT_ANY_SLOT,
|
||||
0,
|
||||
sym_crypt_get_cipher(cd),
|
||||
sym_crypt_get_cipher_mode(cd),
|
||||
&reencrypt_params);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to prepare for reencryption: %m");
|
||||
|
||||
/* crypt_reencrypt_init_by_passphrase() doesn't actually put the LUKS header at the front, we have
|
||||
* to do that ourselves. */
|
||||
/* crypt_reencrypt_init_by_passphrase() doesn't actually put the LUKS header at the front, we
|
||||
* have to do that ourselves. */
|
||||
|
||||
sym_crypt_free(cd);
|
||||
cd = NULL;
|
||||
sym_crypt_free(cd);
|
||||
cd = NULL;
|
||||
|
||||
r = sym_crypt_init(&cd, node);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", node);
|
||||
r = sym_crypt_init(&cd, node);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", node);
|
||||
|
||||
r = sym_crypt_header_restore(cd, CRYPT_LUKS2, hp);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to place new LUKS header at head of %s: %m", node);
|
||||
r = sym_crypt_header_restore(cd, CRYPT_LUKS2, hp);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to place new LUKS header at head of %s: %m", node);
|
||||
|
||||
reencrypt_params.flags &= ~CRYPT_REENCRYPT_INITIALIZE_ONLY;
|
||||
reencrypt_params.flags &= ~CRYPT_REENCRYPT_INITIALIZE_ONLY;
|
||||
|
||||
r = sym_crypt_reencrypt_init_by_passphrase(
|
||||
cd,
|
||||
NULL,
|
||||
passphrase,
|
||||
passphrase_size,
|
||||
CRYPT_ANY_SLOT,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
&reencrypt_params);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to load reencryption context: %m");
|
||||
r = sym_crypt_reencrypt_init_by_passphrase(
|
||||
cd,
|
||||
NULL,
|
||||
passphrase,
|
||||
passphrase_size,
|
||||
CRYPT_ANY_SLOT,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
&reencrypt_params);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to load reencryption context: %m");
|
||||
|
||||
r = sym_crypt_reencrypt(cd, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to encrypt %s: %m", node);
|
||||
r = sym_crypt_reencrypt(cd, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to encrypt %s: %m", node);
|
||||
} else {
|
||||
_cleanup_free_ DecryptedPartitionTarget *t = NULL;
|
||||
_cleanup_close_ int dev_fd = -1;
|
||||
|
||||
r = sym_crypt_activate_by_volume_key(
|
||||
cd,
|
||||
dm_name,
|
||||
NULL,
|
||||
VOLUME_KEY_SIZE,
|
||||
arg_discard ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to activate LUKS superblock: %m");
|
||||
|
||||
dev_fd = open(vol, O_RDWR|O_CLOEXEC|O_NOCTTY);
|
||||
if (dev_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open LUKS volume '%s': %m", vol);
|
||||
|
||||
if (flock(dev_fd, LOCK_EX) < 0)
|
||||
return log_error_errno(errno, "Failed to lock '%s': %m", vol);
|
||||
|
||||
t = new(DecryptedPartitionTarget, 1);
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
*t = (DecryptedPartitionTarget) {
|
||||
.fd = TAKE_FD(dev_fd),
|
||||
.volume = TAKE_PTR(vol),
|
||||
.device = TAKE_PTR(cd),
|
||||
};
|
||||
|
||||
target->decrypted = TAKE_PTR(t);
|
||||
}
|
||||
|
||||
log_info("Successfully encrypted future partition %" PRIu64 ".", p->partno);
|
||||
|
||||
@@ -3833,6 +3939,12 @@ static int context_copy_blocks(Context *context) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (p->encrypt != ENCRYPT_OFF && t->loop) {
|
||||
r = partition_encrypt(context, p, t, /* offline = */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
log_info("Copying in '%s' (%s) on block level into future partition %" PRIu64 ".",
|
||||
p->copy_blocks_path, FORMAT_BYTES(p->copy_blocks_size), p->partno);
|
||||
|
||||
@@ -3840,8 +3952,10 @@ static int context_copy_blocks(Context *context) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy in data from '%s': %m", p->copy_blocks_path);
|
||||
|
||||
if (p->encrypt != ENCRYPT_OFF) {
|
||||
r = partition_encrypt(context, p, partition_target_path(t));
|
||||
log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path);
|
||||
|
||||
if (p->encrypt != ENCRYPT_OFF && !t->loop) {
|
||||
r = partition_encrypt(context, p, t, /* offline = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@@ -3850,8 +3964,6 @@ static int context_copy_blocks(Context *context) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path);
|
||||
|
||||
if (p->siblings[VERITY_HASH] && !partition_defer(p->siblings[VERITY_HASH])) {
|
||||
r = partition_format_verity_hash(context, p->siblings[VERITY_HASH],
|
||||
/* node = */ NULL, partition_target_path(t));
|
||||
@@ -4270,6 +4382,16 @@ static int context_mkfs(Context *context) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (p->encrypt != ENCRYPT_OFF && t->loop) {
|
||||
r = partition_target_grow(t, p->new_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = partition_encrypt(context, p, t, /* offline = */ false);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to encrypt device: %m");
|
||||
}
|
||||
|
||||
log_info("Formatting future partition %" PRIu64 ".", p->partno);
|
||||
|
||||
/* If we're not writing to a loop device or if we're populating a read-only filesystem, we
|
||||
@@ -4305,17 +4427,17 @@ static int context_mkfs(Context *context) {
|
||||
if (partition_needs_populate(p) && !root) {
|
||||
assert(t->loop);
|
||||
|
||||
r = partition_populate_filesystem(context, p, t->loop->node);
|
||||
r = partition_populate_filesystem(context, p, partition_target_path(t));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (p->encrypt != ENCRYPT_OFF) {
|
||||
if (p->encrypt != ENCRYPT_OFF && !t->loop) {
|
||||
r = partition_target_grow(t, p->new_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = partition_encrypt(context, p, partition_target_path(t));
|
||||
r = partition_encrypt(context, p, t, /* offline = */ true);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to encrypt device: %m");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user