#include "copy.h"
#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "home-util.h"
#include "homework-cifs.h"
#include "homework-directory.h"
+#include "homework-fido2.h"
#include "homework-fscrypt.h"
#include "homework-luks.h"
#include "homework-mount.h"
#include "missing_magic.h"
#include "mount-util.h"
#include "path-util.h"
-#include "pkcs11-util.h"
#include "rm-rf.h"
#include "stat-util.h"
#include "strv.h"
/* Make sure a bad password always results in a 3s delay, no matter what */
#define BAD_PASSWORD_DELAY_USEC (3 * USEC_PER_SEC)
+void password_cache_free(PasswordCache *cache) {
+ if (!cache)
+ return;
+
+ cache->pkcs11_passwords = strv_free_erase(cache->pkcs11_passwords);
+ cache->fido2_passwords = strv_free_erase(cache->fido2_passwords);
+}
+
int user_record_authenticate(
UserRecord *h,
UserRecord *secret,
- char ***pkcs11_decrypted_passwords) {
+ PasswordCache *cache,
+ bool strict_verify) {
- bool need_password = false, need_token = false, need_pin = false, need_protected_authentication_path_permitted = false,
- pin_locked = false, pin_incorrect = false, pin_incorrect_few_tries_left = false, pin_incorrect_one_try_left = false;
- size_t n;
+ bool need_password = false, need_token = false, need_pin = false, need_protected_authentication_path_permitted = false, need_user_presence_permitted = false,
+ pin_locked = false, pin_incorrect = false, pin_incorrect_few_tries_left = false, pin_incorrect_one_try_left = false, token_action_timeout = false;
int r;
assert(h);
/* Tries to authenticate a user record with the supplied secrets. i.e. checks whether at least one
* supplied plaintext passwords matches a hashed password field of the user record. Or if a
- * configured PKCS#11 token is around and can unlock the record.
+ * configured PKCS#11 or FIDO2 token is around and can unlock the record.
*
- * Note that the pkcs11_decrypted_passwords parameter is both an input and and output parameter: it
- * is a list of configured, decrypted PKCS#11 passwords. We typically have to call this function
- * multiple times over the course of an operation (think: on login we authenticate the host user
- * record, the record embedded in the LUKS record and the one embedded in $HOME). Hence we keep a
- * list of passwords we already decrypted, so that we don't have to do the (slow an potentially
- * interactive) PKCS#11 dance for the relevant token again and again. */
+ * Note that the 'cache' parameter is both an input and output parameter: it contains lists of
+ * configured, decrypted PKCS#11/FIDO2 passwords. We typically have to call this function multiple
+ * times over the course of an operation (think: on login we authenticate the host user record, the
+ * record embedded in the LUKS record and the one embedded in $HOME). Hence we keep a list of
+ * passwords we already decrypted, so that we don't have to do the (slow and potentially interactive)
+ * PKCS#11/FIDO2 dance for the relevant token again and again. */
/* First, let's see if the supplied plain-text passwords work? */
r = user_record_test_secret(h, secret);
return log_error_errno(r, "Failed to validate password of record: %m");
else {
log_info("Provided password unlocks user record.");
- return 0;
+ return 1;
}
- /* Second, let's see if any of the PKCS#11 security tokens are plugged in and help us */
- for (n = 0; n < h->n_pkcs11_encrypted_key; n++) {
-#if HAVE_P11KIT
- _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {
- .user_record = h,
- .secret = secret,
- .encrypted_key = h->pkcs11_encrypted_key + n,
- };
+ /* Second, test cached PKCS#11 passwords */
+ for (size_t n = 0; n < h->n_pkcs11_encrypted_key; n++) {
char **pp;
- /* See if any of the previously calculated passwords work */
- STRV_FOREACH(pp, *pkcs11_decrypted_passwords) {
- r = test_password_one(data.encrypted_key->hashed_password, *pp);
+ STRV_FOREACH(pp, cache->pkcs11_passwords) {
+ r = test_password_one(h->pkcs11_encrypted_key[n].hashed_password, *pp);
if (r < 0)
return log_error_errno(r, "Failed to check supplied PKCS#11 password: %m");
if (r > 0) {
log_info("Previously acquired PKCS#11 password unlocks user record.");
+ return 1;
+ }
+ }
+ }
+
+ /* Third, test cached FIDO2 passwords */
+ for (size_t n = 0; n < h->n_fido2_hmac_salt; n++) {
+ char **pp;
+
+ /* See if any of the previously calculated passwords work */
+ STRV_FOREACH(pp, cache->fido2_passwords) {
+ r = test_password_one(h->fido2_hmac_salt[n].hashed_password, *pp);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check supplied FIDO2 password: %m");
+ if (r > 0) {
+ log_info("Previously acquired FIDO2 password unlocks user record.");
return 0;
}
}
+ }
+
+ /* Fourth, let's see if any of the PKCS#11 security tokens are plugged in and help us */
+ for (size_t n = 0; n < h->n_pkcs11_encrypted_key; n++) {
+#if HAVE_P11KIT
+ _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {
+ .user_record = h,
+ .secret = secret,
+ .encrypted_key = h->pkcs11_encrypted_key + n,
+ };
r = pkcs11_find_token(data.encrypted_key->uri, pkcs11_callback, &data);
switch (r) {
log_info("Decrypted password from PKCS#11 security token %s unlocks user record.", data.encrypted_key->uri);
- r = strv_extend(pkcs11_decrypted_passwords, data.decrypted_password);
+ r = strv_extend(&cache->pkcs11_passwords, data.decrypted_password);
if (r < 0)
return log_oom();
#endif
}
+ /* Fifth, let's see if any of the FIDO2 security tokens are plugged in and help us */
+ for (size_t n = 0; n < h->n_fido2_hmac_salt; n++) {
+#if HAVE_LIBFIDO2
+ _cleanup_(erase_and_freep) char *decrypted_password = NULL;
+
+ r = fido2_use_token(h, secret, h->fido2_hmac_salt + n, &decrypted_password);
+ switch (r) {
+ case -EAGAIN:
+ need_token = true;
+ break;
+ case -ENOANO:
+ need_pin = true;
+ break;
+ case -EOWNERDEAD:
+ pin_locked = true;
+ break;
+ case -ENOLCK:
+ pin_incorrect = true;
+ break;
+ case -EMEDIUMTYPE:
+ need_user_presence_permitted = true;
+ break;
+ case -ENOSTR:
+ token_action_timeout = true;
+ break;
+ default:
+ if (r < 0)
+ return r;
+
+ r = test_password_one(h->fido2_hmac_salt[n].hashed_password, decrypted_password);
+ if (r < 0)
+ return log_error_errno(r, "Failed to test FIDO2 password: %m");
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Configured FIDO2 security token does not decrypt encrypted key correctly.");
+
+ log_info("Decrypted password from FIDO2 security token unlocks user record.");
+
+ r = strv_extend(&cache->fido2_passwords, decrypted_password);
+ if (r < 0)
+ return log_oom();
+
+ return 1;
+ }
+#else
+ need_token = true;
+ break;
+#endif
+ }
+
/* Ordered by "relevance", i.e. the most "important" or "interesting" error condition is returned. */
if (pin_incorrect_one_try_left)
return -EUCLEAN;
return -ENOLCK;
if (pin_locked)
return -EOWNERDEAD;
+ if (token_action_timeout)
+ return -ENOSTR;
if (need_protected_authentication_path_permitted)
return -ERFKILL;
+ if (need_user_presence_permitted)
+ return -EMEDIUMTYPE;
if (need_pin)
return -ENOANO;
if (need_token)
if (need_password)
return -ENOKEY;
- /* Hmm, this means neither PCKS#11 nor classic hashed passwords were supplied, we cannot authenticate this reasonably */
- return log_debug_errno(SYNTHETIC_ERRNO(EKEYREVOKED), "No hashed passwords and no PKCS#11 tokens defined, cannot authenticate user record.");
+ /* Hmm, this means neither PCKS#11/FIDO2 nor classic hashed passwords were supplied, we cannot
+ * authenticate this reasonably */
+ if (strict_verify)
+ return log_debug_errno(SYNTHETIC_ERRNO(EKEYREVOKED),
+ "No hashed passwords and no PKCS#11/FIDO2 tokens defined, cannot authenticate user record, refusing.");
+
+ /* If strict verification is off this means we are possibly in the case where we encountered an
+ * unfixated record, i.e. a synthetic one that accordingly lacks any authentication data. In this
+ * case, allow the authentication to pass for now, so that the second (or third) authentication level
+ * (the ones of the user record in the LUKS header or inside the home directory) will then catch
+ * invalid passwords. The second/third authentication always runs in strict verification mode. */
+ log_debug("No hashed passwords and no PKCS#11 tokens defined in record, cannot authenticate user record. "
+ "Deferring to embedded user record.");
+ return 0;
}
int home_setup_undo(HomeSetup *setup) {
assert(setup);
- setup->root_fd = safe_close(setup->root_fd);
+ if (setup->root_fd >= 0) {
+ if (setup->do_offline_fitrim) {
+ q = run_fitrim(setup->root_fd);
+ if (q < 0)
+ r = q;
+ }
+
+ setup->root_fd = safe_close(setup->root_fd);
+ }
if (setup->undo_mount) {
q = umount_verbose("/run/systemd/user-home-mount");
r = q;
}
+ if (setup->image_fd >= 0) {
+ if (setup->do_offline_fallocate) {
+ q = run_fallocate(setup->image_fd, NULL);
+ if (q < 0)
+ r = q;
+ }
+
+ setup->image_fd = safe_close(setup->image_fd);
+ }
+
setup->undo_mount = false;
setup->undo_dm = false;
+ setup->do_offline_fitrim = false;
+ setup->do_offline_fallocate = false;
setup->dm_name = mfree(setup->dm_name);
setup->dm_node = mfree(setup->dm_node);
int home_prepare(
UserRecord *h,
bool already_activated,
- char ***pkcs11_decrypted_passwords,
+ PasswordCache *cache,
HomeSetup *setup,
UserRecord **ret_header_home) {
switch (user_record_storage(h)) {
case USER_LUKS:
- return home_prepare_luks(h, already_activated, NULL, pkcs11_decrypted_passwords, setup, ret_header_home);
+ return home_prepare_luks(h, already_activated, NULL, cache, setup, ret_header_home);
case USER_SUBVOLUME:
case USER_DIRECTORY:
break;
case USER_FSCRYPT:
- r = home_prepare_fscrypt(h, already_activated, pkcs11_decrypted_passwords, setup);
+ r = home_prepare_fscrypt(h, already_activated, cache, setup);
break;
case USER_CIFS:
if (r < 0)
return log_error_errno(r, "Embedded identity file is not a regular file, refusing: %m");
- identity_file = fdopen(identity_fd, "r");
+ identity_file = take_fdopen(&identity_fd, "r");
if (!identity_file)
return log_oom();
- identity_fd = -1;
-
r = json_parse_file(identity_file, ".identity", JSON_PARSE_SENSITIVE, ret, &line, &column);
if (r < 0)
return log_error_errno(r, "[.identity:%u:%u] Failed to parse JSON data: %m", line, column);
if (identity_fd < 0)
return log_error_errno(errno, "Failed to create .identity file in home directory: %m");
- identity_file = fdopen(identity_fd, "w");
+ identity_file = take_fdopen(&identity_fd, "w");
if (!identity_file) {
r = log_oom();
goto fail;
}
- identity_fd = -1;
-
json_variant_dump(normalized, JSON_FORMAT_PRETTY, identity_file, NULL);
r = fflush_and_check(identity_file);
int root_fd,
UserRecord *header_home,
UserReconcileMode mode,
- char ***pkcs11_decrypted_passwords,
+ PasswordCache *cache,
UserRecord **ret_embedded_home,
UserRecord **ret_new_home) {
return log_error_errno(SYNTHETIC_ERRNO(EREMCHG), "Embedded home record not compatible with host record, refusing.");
/* Insist that credentials the user supplies also unlocks any embedded records. */
- r = user_record_authenticate(embedded_home, h, pkcs11_decrypted_passwords);
+ r = user_record_authenticate(embedded_home, h, cache, /* strict_verify= */ true);
if (r < 0)
return r;
+ assert(r > 0); /* Insist that a password was verified */
/* At this point we have three records to deal with:
*
UserRecord *h,
HomeSetup *setup,
UserRecord *header_home,
- char ***pkcs11_decrypted_passwords,
+ PasswordCache *cache,
struct statfs *ret_statfs,
UserRecord **ret_new_home) {
/* When activating a home directory, does the identity work: loads the identity from the $HOME
* directory, reconciles it with our idea, chown()s everything. */
- r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_ANY, pkcs11_decrypted_passwords, &embedded_home, &new_home);
+ r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_ANY, cache, &embedded_home, &new_home);
if (r < 0)
return r;
}
static int home_activate(UserRecord *h, UserRecord **ret_home) {
- _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+ _cleanup_(password_cache_free) PasswordCache cache = {};
_cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
int r;
if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Activating home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
- r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+ r = user_record_authenticate(h, h, &cache, /* strict_verify= */ false);
if (r < 0)
return r;
switch (user_record_storage(h)) {
case USER_LUKS:
- r = home_activate_luks(h, &pkcs11_decrypted_passwords, &new_home);
+ r = home_activate_luks(h, &cache, &new_home);
if (r < 0)
return r;
case USER_SUBVOLUME:
case USER_DIRECTORY:
case USER_FSCRYPT:
- r = home_activate_directory(h, &pkcs11_decrypted_passwords, &new_home);
+ r = home_activate_directory(h, &cache, &new_home);
if (r < 0)
return r;
break;
case USER_CIFS:
- r = home_activate_cifs(h, &pkcs11_decrypted_passwords, &new_home);
+ r = home_activate_cifs(h, &cache, &new_home);
if (r < 0)
return r;
if (r < 0)
return r;
if (r == USER_TEST_MOUNTED) {
+ if (user_record_storage(h) == USER_LUKS) {
+ r = home_trim_luks(h);
+ if (r < 0)
+ return r;
+ }
+
if (umount2(user_record_home_directory(h), UMOUNT_NOFOLLOW | (force ? MNT_FORCE|MNT_DETACH : 0)) < 0)
return log_error_errno(errno, "Failed to unmount %s: %m", user_record_home_directory(h));
static int user_record_compile_effective_passwords(
UserRecord *h,
- char ***ret_effective_passwords,
- char ***ret_pkcs11_decrypted_passwords) {
+ PasswordCache *cache,
+ char ***ret_effective_passwords) {
- _cleanup_(strv_free_erasep) char **effective = NULL, **pkcs11_passwords = NULL;
+ _cleanup_(strv_free_erasep) char **effective = NULL;
size_t n;
char **i;
int r;
assert(h);
+ assert(cache);
/* We insist on at least one classic hashed password to be defined in addition to any PKCS#11 one, as
* a safe fallback, but also to simplify the password changing algorithm: there we require providing
return log_oom();
}
- if (ret_pkcs11_decrypted_passwords) {
- r = strv_extend(&pkcs11_passwords, data.decrypted_password);
+ r = strv_extend(&cache->pkcs11_passwords, data.decrypted_password);
+ if (r < 0)
+ return log_oom();
+#else
+ return -EBADSLT;
+#endif
+ }
+
+ for (n = 0; n < h->n_fido2_hmac_salt; n++) {
+#if HAVE_LIBFIDO2
+ _cleanup_(erase_and_freep) char *decrypted_password = NULL;
+
+ r = fido2_use_token(h, h, h->fido2_hmac_salt + n, &decrypted_password);
+ if (r < 0)
+ return r;
+
+ r = test_password_one(h->fido2_hmac_salt[n].hashed_password, decrypted_password);
+ if (r < 0)
+ return log_error_errno(r, "Failed to test FIDO2 password: %m");
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Decrypted password from token is not correct, refusing.");
+
+ if (ret_effective_passwords) {
+ r = strv_extend(&effective, decrypted_password);
if (r < 0)
return log_oom();
}
+
+ r = strv_extend(&cache->fido2_passwords, decrypted_password);
+ if (r < 0)
+ return log_oom();
#else
return -EBADSLT;
#endif
if (ret_effective_passwords)
*ret_effective_passwords = TAKE_PTR(effective);
- if (ret_pkcs11_decrypted_passwords)
- *ret_pkcs11_decrypted_passwords = TAKE_PTR(pkcs11_passwords);
+
+ return 0;
+}
+
+static int determine_default_storage(UserStorage *ret) {
+ UserStorage storage = _USER_STORAGE_INVALID;
+ const char *e;
+ int r;
+
+ assert(ret);
+
+ /* homed tells us via an environment variable which default storage to use */
+ e = getenv("SYSTEMD_HOME_DEFAULT_STORAGE");
+ if (e) {
+ storage = user_storage_from_string(e);
+ if (storage < 0)
+ log_warning("$SYSTEMD_HOME_DEFAULT_STORAGE set to invalid storage type, ignoring: %s", e);
+ else {
+ log_info("Using configured default storage '%s'.", user_storage_to_string(storage));
+ *ret = storage;
+ return 0;
+ }
+ }
+
+ /* When neither user nor admin specified the storage type to use, fix it to be LUKS — unless we run
+ * in a container where loopback devices and LUKS/DM are not available. Also, if /home is encrypted
+ * anyway, let's avoid duplicate encryption. Note that we typically default to the assumption of
+ * "classic" storage for most operations. However, if we create a new home, then let's user LUKS if
+ * nothing is specified. */
+
+ r = detect_container();
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether we are in a container: %m");
+ if (r == 0) {
+ r = path_is_encrypted("/home");
+ if (r < 0)
+ log_warning_errno(r, "Failed to determine if /home is encrypted, ignoring: %m");
+ if (r <= 0) {
+ log_info("Using automatic default storage of '%s'.", user_storage_to_string(USER_LUKS));
+ *ret = USER_LUKS;
+ return 0;
+ }
+
+ log_info("/home is encrypted, not using '%s' storage, in order to avoid double encryption.", user_storage_to_string(USER_LUKS));
+ } else
+ log_info("Running in container, not using '%s' storage.", user_storage_to_string(USER_LUKS));
+
+ r = path_is_fs_type("/home", BTRFS_SUPER_MAGIC);
+ if (r < 0)
+ log_warning_errno(r, "Failed to determine file system of /home, ignoring: %m");
+ if (r > 0) {
+ log_info("/home is on btrfs, using '%s' as storage.", user_storage_to_string(USER_SUBVOLUME));
+ *ret = USER_SUBVOLUME;
+ } else {
+ log_info("/home is on simple file system, using '%s' as storage.", user_storage_to_string(USER_DIRECTORY));
+ *ret = USER_DIRECTORY;
+ }
return 0;
}
static int home_create(UserRecord *h, UserRecord **ret_home) {
- _cleanup_(strv_free_erasep) char **effective_passwords = NULL, **pkcs11_decrypted_passwords = NULL;
+ _cleanup_(strv_free_erasep) char **effective_passwords = NULL;
_cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
+ _cleanup_(password_cache_free) PasswordCache cache = {};
+ UserStorage new_storage = _USER_STORAGE_INVALID;
+ const char *new_fs = NULL;
int r;
assert(h);
if (!uid_is_valid(h->uid))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks UID, refusing.");
- r = user_record_compile_effective_passwords(h, &effective_passwords, &pkcs11_decrypted_passwords);
+ r = user_record_compile_effective_passwords(h, &cache, &effective_passwords);
if (r < 0)
return r;
if (r != USER_TEST_ABSENT)
return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Home directory %s already exists, refusing.", user_record_home_directory(h));
- /* When the user didn't specify the storage type to use, fix it to be LUKS -- unless we run in a
- * container where loopback devices and LUKS/DM are not available. Note that we typically default to
- * the assumption of "classic" storage for most operations. However, if we create a new home, then
- * let's user LUKS if nothing is specified. */
if (h->storage < 0) {
- UserStorage new_storage;
-
- r = detect_container();
+ r = determine_default_storage(&new_storage);
if (r < 0)
- return log_error_errno(r, "Failed to determine whether we are in a container: %m");
- if (r > 0) {
- new_storage = USER_DIRECTORY;
-
- r = path_is_fs_type("/home", BTRFS_SUPER_MAGIC);
- if (r < 0)
- log_debug_errno(r, "Failed to determine file system of /home, ignoring: %m");
+ return r;
+ }
- new_storage = r > 0 ? USER_SUBVOLUME : USER_DIRECTORY;
- } else
- new_storage = USER_LUKS;
+ if ((h->storage == USER_LUKS ||
+ (h->storage < 0 && new_storage == USER_LUKS)) &&
+ !h->file_system_type)
+ new_fs = getenv("SYSTEMD_HOME_DEFAULT_FILE_SYSTEM_TYPE");
+ if (new_storage >= 0 || new_fs) {
r = user_record_add_binding(
h,
new_storage,
NULL,
NULL,
UINT64_MAX,
- NULL,
+ new_fs,
NULL,
UID_INVALID,
GID_INVALID);
if (r < 0)
return log_error_errno(r, "Failed to change storage type to LUKS: %m");
-
- if (!h->image_path_auto) {
- h->image_path_auto = strjoin("/home/", user_record_user_name_and_realm(h), new_storage == USER_LUKS ? ".home" : ".homedir");
- if (!h->image_path_auto)
- return log_oom();
- }
}
r = user_record_test_image_path_and_warn(h);
switch (user_record_storage(h)) {
case USER_LUKS:
- r = home_create_luks(h, pkcs11_decrypted_passwords, effective_passwords, &new_home);
+ r = home_create_luks(h, &cache, effective_passwords, &new_home);
break;
case USER_DIRECTORY:
assert(ip);
if (stat(ip, &st) < 0) {
- if (errno != -ENOENT)
+ if (errno != ENOENT)
return log_error_errno(errno, "Failed to stat() %s: %m", ip);
} else {
if (deleted)
log_info("Everything completed.");
- else {
- log_notice("Nothing to remove.");
- return -EALREADY;
- }
+ else
+ return log_notice_errno(SYNTHETIC_ERRNO(EALREADY),
+ "Nothing to remove.");
return 0;
}
static int home_update(UserRecord *h, UserRecord **ret) {
_cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *header_home = NULL, *embedded_home = NULL;
- _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
_cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+ _cleanup_(password_cache_free) PasswordCache cache = {};
bool already_activated = false;
int r;
assert(h);
assert(ret);
- r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+ r = user_record_authenticate(h, h, &cache, /* strict_verify= */ true);
if (r < 0)
return r;
+ assert(r > 0); /* Insist that a password was verified */
r = home_validate_update(h, &setup);
if (r < 0)
already_activated = r > 0;
- r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &setup, &header_home);
+ r = home_prepare(h, already_activated, &cache, &setup, &header_home);
if (r < 0)
return r;
- r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER, &pkcs11_decrypted_passwords, &embedded_home, &new_home);
+ r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER, &cache, &embedded_home, &new_home);
if (r < 0)
return r;
static int home_resize(UserRecord *h, UserRecord **ret) {
_cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
- _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+ _cleanup_(password_cache_free) PasswordCache cache = {};
bool already_activated = false;
int r;
if (h->disk_size == UINT64_MAX)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No target size specified, refusing.");
- r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+ r = user_record_authenticate(h, h, &cache, /* strict_verify= */ true);
if (r < 0)
return r;
+ assert(r > 0); /* Insist that a password was verified */
r = home_validate_update(h, &setup);
if (r < 0)
switch (user_record_storage(h)) {
case USER_LUKS:
- return home_resize_luks(h, already_activated, &pkcs11_decrypted_passwords, &setup, ret);
+ return home_resize_luks(h, already_activated, &cache, &setup, ret);
case USER_DIRECTORY:
case USER_SUBVOLUME:
case USER_FSCRYPT:
- return home_resize_directory(h, already_activated, &pkcs11_decrypted_passwords, &setup, ret);
+ return home_resize_directory(h, already_activated, &cache, &setup, ret);
default:
return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Resizing home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
static int home_passwd(UserRecord *h, UserRecord **ret_home) {
_cleanup_(user_record_unrefp) UserRecord *header_home = NULL, *embedded_home = NULL, *new_home = NULL;
- _cleanup_(strv_free_erasep) char **effective_passwords = NULL, **pkcs11_decrypted_passwords = NULL;
+ _cleanup_(strv_free_erasep) char **effective_passwords = NULL;
_cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+ _cleanup_(password_cache_free) PasswordCache cache = {};
bool already_activated = false;
int r;
if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT))
return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Changing password of home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
- r = user_record_compile_effective_passwords(h, &effective_passwords, &pkcs11_decrypted_passwords);
+ r = user_record_compile_effective_passwords(h, &cache, &effective_passwords);
if (r < 0)
return r;
already_activated = r > 0;
- r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &setup, &header_home);
+ r = home_prepare(h, already_activated, &cache, &setup, &header_home);
if (r < 0)
return r;
- r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, &pkcs11_decrypted_passwords, &embedded_home, &new_home);
+ r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, &cache, &embedded_home, &new_home);
if (r < 0)
return r;
switch (user_record_storage(h)) {
case USER_LUKS:
- r = home_passwd_luks(h, &setup, pkcs11_decrypted_passwords, effective_passwords);
+ r = home_passwd_luks(h, &setup, &cache, effective_passwords);
if (r < 0)
return r;
break;
case USER_FSCRYPT:
- r = home_passwd_fscrypt(h, &setup, pkcs11_decrypted_passwords, effective_passwords);
+ r = home_passwd_fscrypt(h, &setup, &cache, effective_passwords);
if (r < 0)
return r;
break;
static int home_inspect(UserRecord *h, UserRecord **ret_home) {
_cleanup_(user_record_unrefp) UserRecord *header_home = NULL, *new_home = NULL;
_cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
- _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+ _cleanup_(password_cache_free) PasswordCache cache = {};
bool already_activated = false;
int r;
assert(h);
assert(ret_home);
- r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+ r = user_record_authenticate(h, h, &cache, /* strict_verify= */ false);
if (r < 0)
return r;
already_activated = r > 0;
- r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &setup, &header_home);
+ r = home_prepare(h, already_activated, &cache, &setup, &header_home);
if (r < 0)
return r;
- r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_ANY, &pkcs11_decrypted_passwords, NULL, &new_home);
+ r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_ANY, &cache, NULL, &new_home);
if (r < 0)
return r;
}
static int home_unlock(UserRecord *h) {
- _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+ _cleanup_(password_cache_free) PasswordCache cache = {};
int r;
assert(h);
/* Note that we don't check if $HOME is actually mounted, since we want to avoid disk accesses on
* that mount until we have resumed the device. */
- r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+ r = user_record_authenticate(h, h, &cache, /* strict_verify= */ false);
if (r < 0)
return r;
- r = home_unlock_luks(h, &pkcs11_decrypted_passwords);
+ r = home_unlock_luks(h, &cache);
if (r < 0)
return r;
/* Well known return values of these operations, that systemd-homed knows and converts to proper D-Bus errors:
*
- * EMSGSIZE → file systems of this type cannnot be shrinked
- * ETXTBSY → file systems of this type can only be shrinked offline
+ * EMSGSIZE → file systems of this type cannot be shrunk
+ * ETXTBSY → file systems of this type can only be shrunk offline
* ERANGE → file system size too small
* ENOLINK → system does not support selected storage backend
* EPROTONOSUPPORT → system does not support selected file system
* ESOCKTNOSUPPORT → operation not support on this file system
* ENOKEY → password incorrect (or not sufficient, or not supplied)
* EBADSLT → similar, but PKCS#11 device is defined and might be able to provide password, if it was plugged in which it is not
- * ENOANO → suitable PKCS#11 device found, but PIN is missing to unlock it
+ * ENOANO → suitable PKCS#11/FIDO2 device found, but PIN is missing to unlock it
* ERFKILL → suitable PKCS#11 device found, but OK to ask for on-device interactive authentication not given
- * EOWNERDEAD → suitable PKCS#11 device found, but its PIN is locked
- * ENOLCK → suitable PKCS#11 device found, but PIN incorrect
+ * EMEDIUMTYPE → suitable FIDO2 device found, but OK to ask for user presence not given
+ * ENOSTR → suitable FIDO2 device found, but user didn't react to action request on token quickly enough
+ * EOWNERDEAD → suitable PKCS#11/FIDO2 device found, but its PIN is locked
+ * ENOLCK → suitable PKCS#11/FIDO2 device found, but PIN incorrect
* ETOOMANYREFS → suitable PKCS#11 device found, but PIN incorrect, and only few tries left
* EUCLEAN → suitable PKCS#11 device found, but PIN incorrect, and only one try left
* EBUSY → file system is currently active
* ENOEXEC → file system is currently not active
* ENOSPC → not enough disk space for operation
+ * EKEYREVOKED → user record has not suitable hashed password or pkcs#11 entry, we cannot authenticate
*/
if (streq(argv[1], "activate"))