]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
homed: flush fscrypt key on lock/deactivate 33189/head
authorLuca Boccassi <bluca@debian.org>
Tue, 4 Jun 2024 02:17:42 +0000 (03:17 +0100)
committerLuca Boccassi <bluca@debian.org>
Thu, 6 Jun 2024 12:52:26 +0000 (13:52 +0100)
The fscrypt key is added to the user keyring, and needs to be flushed out too.

Fixes https://github.com/systemd/systemd/issues/33138

src/home/homework-fscrypt.c
src/home/homework-fscrypt.h
src/home/homework.c

index 46a77568738223f2af1529db249feb168c91cb2f..125714ba1e16bd2d097456ee4466ddb1219c5038 100644 (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"
 #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,
index 7c2d7aace416b6c3399cfe94e6fc051bf0d4b083..289e9d86fb1eb75544d315faa9b563c9526a97d1 100644 (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);
index b5569046511a387237649b17fd5043d471a2419b..482db23dbcf56039daf0b47d586f8e68b0d9a24f 100644 (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();