diff --git a/src/shared/keyring-util.c b/src/basic/keyring-util.c similarity index 53% rename from src/shared/keyring-util.c rename to src/basic/keyring-util.c index fadd90ebcc..c32bd50b6f 100644 --- a/src/shared/keyring-util.c +++ b/src/basic/keyring-util.c @@ -33,3 +33,34 @@ int keyring_read(key_serial_t serial, void **ret, size_t *ret_size) { bufsize = (size_t) n; } } + +int keyring_describe(key_serial_t serial, char **ret) { + _cleanup_free_ char *tuple = NULL; + size_t sz = 64; + int c = -1; /* Workaround for maybe-uninitialized false positive due to missing_syscall indirection */ + + assert(ret); + + for (;;) { + tuple = new(char, sz); + if (!tuple) + return log_oom_debug(); + + c = keyctl(KEYCTL_DESCRIBE, serial, (unsigned long) tuple, c, 0); + if (c < 0) + return log_debug_errno(errno, "Failed to describe key id %d: %m", serial); + + if ((size_t) c <= sz) + break; + + sz = c; + free(tuple); + } + + /* The kernel returns a final NUL in the string, verify that. */ + assert(tuple[c-1] == 0); + + *ret = TAKE_PTR(tuple); + + return 0; +} diff --git a/src/shared/keyring-util.h b/src/basic/keyring-util.h similarity index 85% rename from src/shared/keyring-util.h rename to src/basic/keyring-util.h index c8c53f1be1..6e6e6856ad 100644 --- a/src/shared/keyring-util.h +++ b/src/basic/keyring-util.h @@ -9,3 +9,4 @@ #define TAKE_KEY_SERIAL(key_serial) TAKE_GENERIC(key_serial, key_serial_t, -1) int keyring_read(key_serial_t serial, void **ret, size_t *ret_size); +int keyring_describe(key_serial_t serial, char **ret); diff --git a/src/basic/meson.build b/src/basic/meson.build index d71c9d8bc7..9a214575a5 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -57,6 +57,7 @@ basic_sources = files( 'lock-util.c', 'log.c', 'login-util.c', + 'keyring-util.c', 'memfd-util.c', 'memory-util.c', 'mempool.c', diff --git a/src/home/homework-fscrypt.c b/src/home/homework-fscrypt.c index 46a7756873..125714ba1e 100644 --- a/src/home/homework-fscrypt.c +++ b/src/home/homework-fscrypt.c @@ -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, diff --git a/src/home/homework-fscrypt.h b/src/home/homework-fscrypt.h index 7c2d7aace4..289e9d86fb 100644 --- a/src/home/homework-fscrypt.h +++ b/src/home/homework-fscrypt.h @@ -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); diff --git a/src/home/homework.c b/src/home/homework.c index b556904651..482db23dbc 100644 --- a/src/home/homework.c +++ b/src/home/homework.c @@ -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(); diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c index 62b8aaa347..fc1107b4e8 100644 --- a/src/libsystemd/sd-id128/sd-id128.c +++ b/src/libsystemd/sd-id128/sd-id128.c @@ -13,6 +13,7 @@ #include "hmac.h" #include "id128-util.h" #include "io-util.h" +#include "keyring-util.h" #include "macro.h" #include "missing_syscall.h" #include "missing_threads.h" @@ -202,7 +203,6 @@ static int get_invocation_from_keyring(sd_id128_t *ret) { char *d, *p, *g, *u, *e; unsigned long perms; key_serial_t key; - size_t sz = 256; uid_t uid; gid_t gid; int r, c; @@ -221,24 +221,9 @@ static int get_invocation_from_keyring(sd_id128_t *ret) { return -errno; } - for (;;) { - description = new(char, sz); - if (!description) - return -ENOMEM; - - c = keyctl(KEYCTL_DESCRIBE, key, (unsigned long) description, sz, 0); - if (c < 0) - return -errno; - - if ((size_t) c <= sz) - break; - - sz = c; - free(description); - } - - /* The kernel returns a final NUL in the string, verify that. */ - assert(description[c-1] == 0); + r = keyring_describe(key, &description); + if (r < 0) + return r; /* Chop off the final description string */ d = strrchr(description, ';'); diff --git a/src/shared/meson.build b/src/shared/meson.build index 8fb2b7ec7f..c5106d87d5 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -100,7 +100,6 @@ shared_sources = files( 'kbd-util.c', 'kernel-config.c', 'kernel-image.c', - 'keyring-util.c', 'killall.c', 'label-util.c', 'libarchive-util.c',