Skip to content

Commit c4b5de7

Browse files
committed
homed: flush fscrypt key on lock/deactivate
The fscrypt key is added to the user keyring, and needs to be flushed out too. Fixes systemd/systemd#33138
1 parent 67dfbe0 commit c4b5de7

File tree

3 files changed

+98
-0
lines changed

3 files changed

+98
-0
lines changed

src/home/homework-fscrypt.c

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "homework-fscrypt.h"
1313
#include "homework-mount.h"
1414
#include "homework-quota.h"
15+
#include "keyring-util.h"
1516
#include "memory-util.h"
1617
#include "missing_keyctl.h"
1718
#include "missing_syscall.h"
@@ -29,6 +30,98 @@
2930
#include "user-util.h"
3031
#include "xattr-util.h"
3132

33+
static int fscrypt_unlink_key(UserRecord *h) {
34+
_cleanup_free_ void *keyring = NULL;
35+
size_t keyring_size = 0, n_keys = 0;
36+
int r;
37+
38+
assert(h);
39+
assert(user_record_storage(h) == USER_FSCRYPT);
40+
41+
r = fully_set_uid_gid(
42+
h->uid,
43+
user_record_gid(h),
44+
/* supplementary_gids= */ NULL,
45+
/* n_supplementary_gids= */ 0);
46+
if (r < 0)
47+
return log_error_errno(r, "Failed to change UID/GID to " UID_FMT "/" GID_FMT ": %m",
48+
h->uid, user_record_gid(h));
49+
50+
r = keyring_read(KEY_SPEC_USER_KEYRING, &keyring, &keyring_size);
51+
if (r < 0)
52+
return log_error_errno(r, "Failed to read the keyring of user " UID_FMT ": %m", h->uid);
53+
54+
n_keys = keyring_size / sizeof(key_serial_t);
55+
assert(keyring_size % sizeof(key_serial_t) == 0);
56+
57+
/* Find any key with a description starting with 'fscrypt:' and unlink it. We need to iterate as we
58+
* store the key with a description that uses the hash of the secret key, that we do not have when
59+
* we are deactivating. */
60+
FOREACH_ARRAY(key, ((key_serial_t *) keyring), n_keys) {
61+
_cleanup_free_ char *description = NULL;
62+
char *d;
63+
64+
r = keyring_describe(*key, &description);
65+
if (r < 0) {
66+
if (r == -ENOKEY) /* Something else deleted it already, that's ok. */
67+
continue;
68+
69+
return log_error_errno(r, "Failed to describe key id %d: %m", *key);
70+
}
71+
72+
/* The decription is the final element as per manpage. */
73+
d = strrchr(description, ';');
74+
if (!d)
75+
return log_error_errno(
76+
SYNTHETIC_ERRNO(EINVAL),
77+
"Failed to parse description of key id %d: %s",
78+
*key,
79+
description);
80+
81+
if (!startswith(d + 1, "fscrypt:"))
82+
continue;
83+
84+
r = keyctl(KEYCTL_UNLINK, *key, KEY_SPEC_USER_KEYRING, 0, 0);
85+
if (r < 0) {
86+
if (errno == ENOKEY) /* Something else deleted it already, that's ok. */
87+
continue;
88+
89+
return log_error_errno(
90+
errno,
91+
"Failed to delete encryption key with id '%d' from the keyring of user " UID_FMT ": %m",
92+
*key,
93+
h->uid);
94+
}
95+
96+
log_debug("Deleted encryption key with id '%d' from the keyring of user " UID_FMT ".", *key, h->uid);
97+
}
98+
99+
return 0;
100+
}
101+
102+
int home_flush_keyring_fscrypt(UserRecord *h) {
103+
int r;
104+
105+
assert(h);
106+
assert(user_record_storage(h) == USER_FSCRYPT);
107+
108+
if (!uid_is_valid(h->uid))
109+
return 0;
110+
111+
r = safe_fork("(sd-delkey)",
112+
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT|FORK_REOPEN_LOG,
113+
NULL);
114+
if (r < 0)
115+
return r;
116+
if (r == 0) {
117+
if (fscrypt_unlink_key(h) < 0)
118+
_exit(EXIT_FAILURE);
119+
_exit(EXIT_SUCCESS);
120+
}
121+
122+
return 0;
123+
}
124+
32125
static int fscrypt_upload_volume_key(
33126
const uint8_t key_descriptor[static FS_KEY_DESCRIPTOR_SIZE],
34127
const void *volume_key,

src/home/homework-fscrypt.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ int home_setup_fscrypt(UserRecord *h, HomeSetup *setup, const PasswordCache *cac
99
int home_create_fscrypt(UserRecord *h, HomeSetup *setup, char **effective_passwords, UserRecord **ret_home);
1010

1111
int home_passwd_fscrypt(UserRecord *h, HomeSetup *setup, const PasswordCache *cache, char **effective_passwords);
12+
13+
int home_flush_keyring_fscrypt(UserRecord *h);

src/home/homework.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,9 @@ static int keyring_flush(UserRecord *h) {
379379

380380
assert(h);
381381

382+
if (user_record_storage(h) == USER_FSCRYPT)
383+
(void) home_flush_keyring_fscrypt(h);
384+
382385
name = strjoin("homework-user-", h->user_name);
383386
if (!name)
384387
return log_oom();

0 commit comments

Comments
 (0)