summaryrefslogtreecommitdiffstats
path: root/src/home/homework-fscrypt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/home/homework-fscrypt.c')
-rw-r--r--src/home/homework-fscrypt.c137
1 files changed, 108 insertions, 29 deletions
diff --git a/src/home/homework-fscrypt.c b/src/home/homework-fscrypt.c
index 6aae1d2..92ce5c3 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 description 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,
@@ -131,7 +224,7 @@ static int fscrypt_slot_try_one(
salt, salt_size,
0xFFFF, EVP_sha512(),
sizeof(derived), derived) != 1)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed");
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed.");
context = EVP_CIPHER_CTX_new();
if (!context)
@@ -212,14 +305,13 @@ static int fscrypt_setup(
r = flistxattr_malloc(setup->root_fd, &xattr_buf);
if (r < 0)
- return log_error_errno(errno, "Failed to retrieve xattr list: %m");
+ return log_error_errno(r, "Failed to retrieve xattr list: %m");
NULSTR_FOREACH(xa, xattr_buf) {
_cleanup_free_ void *salt = NULL, *encrypted = NULL;
_cleanup_free_ char *value = NULL;
size_t salt_size, encrypted_size;
const char *nr, *e;
- char **list;
int n;
/* Check if this xattr has the format 'trusted.fscrypt_slot<nr>' where '<nr>' is a 32-bit unsigned integer */
@@ -237,31 +329,30 @@ static int fscrypt_setup(
e = memchr(value, ':', n);
if (!e)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "xattr %s lacks ':' separator: %m", xa);
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "xattr %s lacks ':' separator.", xa);
- r = unbase64mem(value, e - value, &salt, &salt_size);
+ r = unbase64mem_full(value, e - value, /* secure = */ false, &salt, &salt_size);
if (r < 0)
return log_error_errno(r, "Failed to decode salt of %s: %m", xa);
- r = unbase64mem(e+1, n - (e - value) - 1, &encrypted, &encrypted_size);
+
+ r = unbase64mem_full(e + 1, n - (e - value) - 1, /* secure = */ false, &encrypted, &encrypted_size);
if (r < 0)
return log_error_errno(r, "Failed to decode encrypted key of %s: %m", xa);
r = -ENOANO;
- FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, password) {
+ char **list;
+ FOREACH_ARGUMENT(list, cache->pkcs11_passwords, cache->fido2_passwords, password) {
r = fscrypt_slot_try_many(
list,
salt, salt_size,
encrypted, encrypted_size,
setup->fscrypt_key_descriptor,
ret_volume_key, ret_volume_key_size);
- if (r != -ENOANO)
- break;
- }
- if (r < 0) {
+ if (r >= 0)
+ return 0;
if (r != -ENOANO)
return r;
- } else
- return 0;
+ }
}
return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Failed to set up home directory with provided passwords.");
@@ -319,23 +410,11 @@ int home_setup_fscrypt(
if (r < 0)
return log_error_errno(r, "Failed install encryption key in user's keyring: %m");
if (r == 0) {
- gid_t gid;
-
/* Child */
- gid = user_record_gid(h);
- if (setresgid(gid, gid, gid) < 0) {
- log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
- _exit(EXIT_FAILURE);
- }
-
- if (setgroups(0, NULL) < 0) {
- log_error_errno(errno, "Failed to reset auxiliary groups list: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (setresuid(h->uid, h->uid, h->uid) < 0) {
- log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", h->uid);
+ r = fully_set_uid_gid(h->uid, user_record_gid(h), /* supplementary_gids= */ NULL, /* n_supplementary_gids= */ 0);
+ if (r < 0) {
+ log_error_errno(r, "Failed to change UID/GID to " UID_FMT "/" GID_FMT ": %m", h->uid, user_record_gid(h));
_exit(EXIT_FAILURE);
}
@@ -649,7 +728,7 @@ int home_passwd_fscrypt(
r = flistxattr_malloc(setup->root_fd, &xattr_buf);
if (r < 0)
- return log_error_errno(errno, "Failed to retrieve xattr list: %m");
+ return log_error_errno(r, "Failed to retrieve xattr list: %m");
NULSTR_FOREACH(xa, xattr_buf) {
const char *nr;