## Summary Closes #18280. `systemd-homed` currently writes fscrypt v1 policies, which bind the master key to the calling process's keyring. This means files in a homed-managed directory aren't readable when accessed through a container bind mount, a different mount namespace, or by any process other than the one that first unlocked the home. Reading the file from a context that has the key first warms the page cache, but the underlying problem remains. v2 policies (Linux 5.4+) route the master key through the filesystem keyring, via `FS_IOC_ADD_ENCRYPTION_KEY` / `FS_IOC_REMOVE_ENCRYPTION_KEY`, so the key is visible to every process accessing the filesystem. This PR switches `systemd-homed` to v2 by default and keeps existing v1 homes working. ## Changes **`shared/crypto-util`**: Add `kdf_hkdf_derive` (HKDF, RFC 5869) wrapper around OpenSSL's `EVP_KDF` "HKDF", alongside the existing SSKDF/KBKDF helpers. **`homed/fscrypt`**: - Read the existing policy via `FS_IOC_GET_ENCRYPTION_POLICY_EX`, falling back to the legacy ioctl on pre-5.4 kernels. - `HomeSetup` now carries a full `struct fscrypt_key_specifier` instead of a bare 8-byte descriptor. Slot decryption derives either the v1 descriptor (double SHA-512) or the v2 identifier (HKDF-SHA512 with the kernel's info string) from the unwrapped volume key and compares against the policy. - Install the master key the right way per version: `add_key("logon", ...)` to thread+user keyrings for v1, `FS_IOC_ADD_ENCRYPTION_KEY` for v2. - `home_flush_keyring_fscrypt` opens the image directory, reads the policy version, and either calls `FS_IOC_REMOVE_ENCRYPTION_KEY` (v2) or walks the user keyring (v1). - New homes default to v2; fall back to v1 only when the kernel rejects v2. The on-disk slot xattr format is unchanged - the volume key is the same, only how it binds to the directory changes. There is no v1 -> v2 migration; existing v1 homes continue to unlock, rekey, and deactivate as before.
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
#include "fd-util.h"
|
||||
#include "homework-blob.h"
|
||||
#include "homework-directory.h"
|
||||
#include "homework-fscrypt.h"
|
||||
#include "homework-mount.h"
|
||||
#include "homework-quota.h"
|
||||
#include "log.h"
|
||||
@@ -103,6 +104,11 @@ int home_activate_directory(
|
||||
|
||||
setup->undo_mount = false;
|
||||
|
||||
/* The home is now activated and staying mounted, so it owns any v2 fscrypt master key
|
||||
* home_setup() installed. Disarm the rollback so home_setup_done() doesn't remove the key out
|
||||
* from under the live mount. (No-op for v1 or non-fscrypt storage.) */
|
||||
fscrypt_v2_key_undo_disarm(&setup->fscrypt_v2_key_undo);
|
||||
|
||||
setup->do_drop_caches = false;
|
||||
|
||||
log_info("Everything completed.");
|
||||
|
||||
@@ -9,3 +9,4 @@ typedef enum HomeSetupFlags HomeSetupFlags;
|
||||
|
||||
typedef struct PasswordCache PasswordCache;
|
||||
typedef struct HomeSetup HomeSetup;
|
||||
typedef struct FscryptV2KeyUndo FscryptV2KeyUndo;
|
||||
|
||||
+636
-168
File diff suppressed because it is too large
Load Diff
@@ -3,10 +3,16 @@
|
||||
|
||||
#include "homework-forward.h"
|
||||
|
||||
int home_setup_fscrypt(UserRecord *h, HomeSetup *setup, const PasswordCache *cache);
|
||||
int home_setup_fscrypt(UserRecord *h, HomeSetupFlags flags, HomeSetup *setup, const PasswordCache *cache);
|
||||
|
||||
int home_create_fscrypt(UserRecord *h, HomeSetup *setup, char **effective_passwords, UserRecord **ret_home);
|
||||
|
||||
int home_passwd_fscrypt(UserRecord *h, HomeSetup *setup, const PasswordCache *cache, char **effective_passwords);
|
||||
|
||||
int home_flush_keyring_fscrypt(UserRecord *h);
|
||||
|
||||
/* Fire the pending v2 master-key rollback (remove the key from the filesystem keyring) if armed; called
|
||||
* from home_setup_done() on every teardown/error path. No-op if unarmed or v1. */
|
||||
void fscrypt_v2_key_undo_done(FscryptV2KeyUndo *u);
|
||||
/* Cancel a pending v2 rollback without removing the key: the home is staying active and now owns it. */
|
||||
void fscrypt_v2_key_undo_disarm(FscryptV2KeyUndo *u);
|
||||
|
||||
+6
-1
@@ -459,6 +459,11 @@ int home_setup_done(HomeSetup *setup) {
|
||||
|
||||
setup->key_serial = keyring_unlink(setup->key_serial);
|
||||
|
||||
/* Roll back a v2 fscrypt master key that home_setup_fscrypt() installed but that no activated
|
||||
* home ended up owning (passwd/update/resize of an inactive home, or any error path). On the
|
||||
* activation path home_activate_directory() disarms this first, so the live home keeps its key. */
|
||||
fscrypt_v2_key_undo_done(&setup->fscrypt_v2_key_undo);
|
||||
|
||||
setup->undo_mount = false;
|
||||
setup->undo_dm = false;
|
||||
setup->do_offline_fitrim = false;
|
||||
@@ -514,7 +519,7 @@ int home_setup(
|
||||
break;
|
||||
|
||||
case USER_FSCRYPT:
|
||||
r = home_setup_fscrypt(h, setup, cache);
|
||||
r = home_setup_fscrypt(h, flags, setup, cache);
|
||||
break;
|
||||
|
||||
case USER_CIFS:
|
||||
|
||||
+18
-1
@@ -9,6 +9,21 @@
|
||||
#include "homework-forward.h"
|
||||
#include "user-record-util.h"
|
||||
|
||||
/* RAII rollback state for a v2 fscrypt master-key installation. v2 keys live in the persistent
|
||||
* filesystem keyring (removed only via FS_IOC_REMOVE_ENCRYPTION_KEY), so any setup that installs one
|
||||
* but then tears the home back down instead of activating it must remove the key again, or it lingers
|
||||
* filesystem-wide until the next deactivation or reboot. Armed by home_setup_fscrypt(), fired by
|
||||
* home_setup_done() on every teardown/error path, and disarmed only once a home stays active (see
|
||||
* home_activate_directory()). 'dir_fd' is a dup taken at arm time so the removal target is pinned to
|
||||
* the superblock and unaffected by later rename()/mount changes. (v1 keys go to the per-thread keyring
|
||||
* and die with the homework process, so they need none of this.) */
|
||||
typedef struct FscryptV2KeyUndo {
|
||||
int dir_fd;
|
||||
uint8_t identifier[FSCRYPT_KEY_IDENTIFIER_SIZE];
|
||||
} FscryptV2KeyUndo;
|
||||
|
||||
#define FSCRYPT_V2_KEY_UNDO_INIT { .dir_fd = -EBADF }
|
||||
|
||||
typedef struct HomeSetup {
|
||||
char *dm_name; /* "home-<username>" */
|
||||
char *dm_node; /* "/dev/mapper/home-<username>" */
|
||||
@@ -21,7 +36,8 @@ typedef struct HomeSetup {
|
||||
sd_id128_t found_luks_uuid;
|
||||
sd_id128_t found_fs_uuid;
|
||||
|
||||
uint8_t fscrypt_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
|
||||
struct fscrypt_key_specifier fscrypt_key_spec;
|
||||
FscryptV2KeyUndo fscrypt_v2_key_undo;
|
||||
|
||||
void *volume_key;
|
||||
size_t volume_key_size;
|
||||
@@ -47,6 +63,7 @@ typedef struct HomeSetup {
|
||||
{ \
|
||||
.root_fd = -EBADF, \
|
||||
.image_fd = -EBADF, \
|
||||
.fscrypt_v2_key_undo = FSCRYPT_V2_KEY_UNDO_INIT, \
|
||||
.partition_offset = UINT64_MAX, \
|
||||
.partition_size = UINT64_MAX, \
|
||||
.key_serial = -1, \
|
||||
|
||||
Reference in New Issue
Block a user