homed: flush fscrypt key on lock/deactivate

The fscrypt key is added to the user keyring, and needs to be flushed out too.

Fixes https://github.com/systemd/systemd/issues/33138
This commit is contained in:
Luca Boccassi
2024-06-06 13:52:26 +01:00
parent 67dfbe021c
commit c4b5de7eff
3 changed files with 98 additions and 0 deletions
+93
View File
@@ -12,6 +12,7 @@
#include "homework-fscrypt.h"
#include "homework-mount.h"
#include "homework-quota.h"
#include "keyring-util.h"
#include "memory-util.h"
#include "missing_keyctl.h"
#include "missing_syscall.h"
@@ -29,6 +30,98 @@
#include "user-util.h"
#include "xattr-util.h"
static int fscrypt_unlink_key(UserRecord *h) {
_cleanup_free_ void *keyring = NULL;
size_t keyring_size = 0, n_keys = 0;
int r;
assert(h);
assert(user_record_storage(h) == USER_FSCRYPT);
r = fully_set_uid_gid(
h->uid,
user_record_gid(h),
/* supplementary_gids= */ NULL,
/* n_supplementary_gids= */ 0);
if (r < 0)
return log_error_errno(r, "Failed to change UID/GID to " UID_FMT "/" GID_FMT ": %m",
h->uid, user_record_gid(h));
r = keyring_read(KEY_SPEC_USER_KEYRING, &keyring, &keyring_size);
if (r < 0)
return log_error_errno(r, "Failed to read the keyring of user " UID_FMT ": %m", h->uid);
n_keys = keyring_size / sizeof(key_serial_t);
assert(keyring_size % sizeof(key_serial_t) == 0);
/* Find any key with a description starting with 'fscrypt:' and unlink it. We need to iterate as we
* store the key with a description that uses the hash of the secret key, that we do not have when
* we are deactivating. */
FOREACH_ARRAY(key, ((key_serial_t *) keyring), n_keys) {
_cleanup_free_ char *description = NULL;
char *d;
r = keyring_describe(*key, &description);
if (r < 0) {
if (r == -ENOKEY) /* Something else deleted it already, that's ok. */
continue;
return log_error_errno(r, "Failed to describe key id %d: %m", *key);
}
/* The decription is the final element as per manpage. */
d = strrchr(description, ';');
if (!d)
return log_error_errno(
SYNTHETIC_ERRNO(EINVAL),
"Failed to parse description of key id %d: %s",
*key,
description);
if (!startswith(d + 1, "fscrypt:"))
continue;
r = keyctl(KEYCTL_UNLINK, *key, KEY_SPEC_USER_KEYRING, 0, 0);
if (r < 0) {
if (errno == ENOKEY) /* Something else deleted it already, that's ok. */
continue;
return log_error_errno(
errno,
"Failed to delete encryption key with id '%d' from the keyring of user " UID_FMT ": %m",
*key,
h->uid);
}
log_debug("Deleted encryption key with id '%d' from the keyring of user " UID_FMT ".", *key, h->uid);
}
return 0;
}
int home_flush_keyring_fscrypt(UserRecord *h) {
int r;
assert(h);
assert(user_record_storage(h) == USER_FSCRYPT);
if (!uid_is_valid(h->uid))
return 0;
r = safe_fork("(sd-delkey)",
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT|FORK_REOPEN_LOG,
NULL);
if (r < 0)
return r;
if (r == 0) {
if (fscrypt_unlink_key(h) < 0)
_exit(EXIT_FAILURE);
_exit(EXIT_SUCCESS);
}
return 0;
}
static int fscrypt_upload_volume_key(
const uint8_t key_descriptor[static FS_KEY_DESCRIPTOR_SIZE],
const void *volume_key,
+2
View File
@@ -9,3 +9,5 @@ int home_setup_fscrypt(UserRecord *h, HomeSetup *setup, const PasswordCache *cac
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);
+3
View File
@@ -379,6 +379,9 @@ static int keyring_flush(UserRecord *h) {
assert(h);
if (user_record_storage(h) == USER_FSCRYPT)
(void) home_flush_keyring_fscrypt(h);
name = strjoin("homework-user-", h->user_name);
if (!name)
return log_oom();