mirror of
https://github.com/systemd/systemd.git
synced 2025-01-23 02:04:32 +03:00
homework: Accept volume key from keyring
This bypasses authentication (i.e. user_record_authenticate) if the volume key was loaded from the keyring and no secret section is provided. This also changes Update() and Resize() to always try and load the volume key from the keyring. This makes the secret section optional for these methods while still letting them function (as long as the home area is active)
This commit is contained in:
parent
d0eff7a12d
commit
5ec87d577f
@ -320,10 +320,10 @@ node /org/freedesktop/home1 {
|
||||
interface.</para>
|
||||
|
||||
<para><function>UpdateHome()</function> updates a locally registered user record. Takes a fully
|
||||
specified JSON user record as argument (including the <literal>secret</literal> section). A user with a
|
||||
matching name and realm must be registered locally already, and the last change timestamp of the newly
|
||||
supplied record must be newer than the previously existing user record. Note this operation updates the
|
||||
user record only, it does not propagate passwords/authentication tokens from the user record to the
|
||||
specified JSON user record as argument (possibly including the <literal>secret</literal> section). A user
|
||||
with a matching name and realm must be registered locally already, and the last change timestamp of the
|
||||
newly supplied record must be newer than the previously existing user record. Note this operation updates
|
||||
the user record only, it does not propagate passwords/authentication tokens from the user record to the
|
||||
storage back-end, or resizes the storage back-end. Typically a home directory is first updated, and then
|
||||
the password of the underlying storage updated using <function>ChangePasswordHome()</function> as well
|
||||
as the storage resized using <function>ResizeHome()</function>. This method is equivalent to
|
||||
@ -338,13 +338,12 @@ node /org/freedesktop/home1 {
|
||||
on the <classname>org.freedesktop.home1.Home</classname> interface.</para>
|
||||
|
||||
<para><function>ResizeHome()</function> resizes the storage associated with a user record. Takes a user
|
||||
name, a disk size in bytes and a user record consisting only of the <literal>secret</literal> section
|
||||
as argument. If the size is specified as <constant>UINT64_MAX</constant> the storage is resized to the
|
||||
size already specified in the user record. Typically, if the user record is updated using
|
||||
name, a disk size in bytes, and optionally a user record consisting only of the <literal>secret</literal>
|
||||
section as arguments. If the size is specified as <constant>UINT64_MAX</constant> the storage is resized to
|
||||
the size already specified in the user record. Typically, if the user record is updated using
|
||||
<function>UpdateHome()</function> above this is used to propagate the size configured there-in down to
|
||||
the underlying storage back-end. This method is equivalent to
|
||||
<function>Resize()</function> on the <classname>org.freedesktop.home1.Home</classname>
|
||||
interface.</para>
|
||||
the underlying storage back-end. This method is equivalent to <function>Resize()</function> on the
|
||||
<classname>org.freedesktop.home1.Home</classname> interface.</para>
|
||||
|
||||
<para><function>ChangePasswordHome()</function> changes the passwords/authentication tokens of a home
|
||||
directory. Takes a user name, and two JSON user record objects, each consisting only of the
|
||||
|
@ -473,7 +473,7 @@ int bus_home_method_update(
|
||||
|
||||
assert(message);
|
||||
|
||||
r = bus_message_read_home_record(message, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_REQUIRE_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_PERMISSIVE, &hr, error);
|
||||
r = bus_message_read_home_record(message, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_PERMISSIVE, &hr, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -521,7 +521,7 @@ int bus_home_method_resize(
|
||||
if (r == 0)
|
||||
return 1; /* Will call us back */
|
||||
|
||||
r = home_resize(h, sz, secret, /* automatic= */ false, error);
|
||||
r = home_resize(h, sz, secret, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -1810,7 +1810,6 @@ int home_update(Home *h, UserRecord *hr, Hashmap *blobs, uint64_t flags, sd_bus_
|
||||
int home_resize(Home *h,
|
||||
uint64_t disk_size,
|
||||
UserRecord *secret,
|
||||
bool automatic,
|
||||
sd_bus_error *error) {
|
||||
|
||||
_cleanup_(user_record_unrefp) UserRecord *c = NULL;
|
||||
@ -1886,7 +1885,7 @@ int home_resize(Home *h,
|
||||
c = TAKE_PTR(signed_c);
|
||||
}
|
||||
|
||||
r = home_update_internal(h, automatic ? "resize-auto" : "resize", c, secret, NULL, 0, error);
|
||||
r = home_update_internal(h, "resize", c, secret, NULL, 0, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -192,7 +192,7 @@ int home_deactivate(Home *h, bool force, sd_bus_error *error);
|
||||
int home_create(Home *h, UserRecord *secret, Hashmap *blobs, uint64_t flags, sd_bus_error *error);
|
||||
int home_remove(Home *h, sd_bus_error *error);
|
||||
int home_update(Home *h, UserRecord *new_record, Hashmap *blobs, uint64_t flags, sd_bus_error *error);
|
||||
int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, bool automatic, sd_bus_error *error);
|
||||
int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *error);
|
||||
int home_passwd(Home *h, UserRecord *new_secret, UserRecord *old_secret, sd_bus_error *error);
|
||||
int home_unregister(Home *h, sd_bus_error *error);
|
||||
int home_lock(Home *h, sd_bus_error *error);
|
||||
|
@ -2025,7 +2025,7 @@ static int manager_rebalance_apply(Manager *m) {
|
||||
|
||||
h->rebalance_pending = false;
|
||||
|
||||
r = home_resize(h, h->rebalance_goal, /* secret= */ NULL, /* automatic= */ true, &error);
|
||||
r = home_resize(h, h->rebalance_goal, /* secret= */ NULL, &error);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to resize home '%s' for rebalancing, ignoring: %s",
|
||||
h->user_name, bus_error_message(&error, r));
|
||||
|
@ -66,9 +66,25 @@ int user_record_authenticate(
|
||||
* 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. */
|
||||
* PKCS#11/FIDO2 dance for the relevant token again and again.
|
||||
*
|
||||
* The 'cache' parameter might also contain the LUKS volume key, loaded from the kernel keyring.
|
||||
* In this case, authentication becomes optional - if a secret section is provided it will be
|
||||
* verified, but if missing then authentication is skipped entirely. Thus, callers should
|
||||
* consider carefuly whether it is safe to load the volume key into 'cache' before doing so.
|
||||
* Note that most of the time this is safe, because the home area must be active for the key
|
||||
* to exist in the keyring, and the user would have had to authenticate when activating their
|
||||
* home area; however, for some methods (i.e. ChangePassword, Authenticate) it makes more sense
|
||||
* to force re-authentication. */
|
||||
|
||||
/* First, let's see if the supplied plain-text passwords work? */
|
||||
/* First, let's see if we already have a volume key from the keyring */
|
||||
if (cache && cache->volume_key &&
|
||||
json_variant_is_blank_object(json_variant_by_key(secret->json, "secret"))) {
|
||||
log_info("LUKS volume key from keyring unlocks user record.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Next, let's see if the supplied plain-text passwords work? */
|
||||
r = user_record_test_password(h, secret);
|
||||
if (r == -ENOKEY)
|
||||
need_password = true;
|
||||
@ -101,7 +117,7 @@ int user_record_authenticate(
|
||||
else
|
||||
log_info("None of the supplied plaintext passwords unlock the user record's hashed recovery keys.");
|
||||
|
||||
/* Second, test cached PKCS#11 passwords */
|
||||
/* Next, test cached PKCS#11 passwords */
|
||||
for (size_t n = 0; n < h->n_pkcs11_encrypted_key; n++)
|
||||
STRV_FOREACH(pp, cache->pkcs11_passwords) {
|
||||
r = test_password_one(h->pkcs11_encrypted_key[n].hashed_password, *pp);
|
||||
@ -113,7 +129,7 @@ int user_record_authenticate(
|
||||
}
|
||||
}
|
||||
|
||||
/* Third, test cached FIDO2 passwords */
|
||||
/* Next, test cached FIDO2 passwords */
|
||||
for (size_t n = 0; n < h->n_fido2_hmac_salt; n++)
|
||||
/* See if any of the previously calculated passwords work */
|
||||
STRV_FOREACH(pp, cache->fido2_passwords) {
|
||||
@ -126,7 +142,7 @@ int user_record_authenticate(
|
||||
}
|
||||
}
|
||||
|
||||
/* Fourth, let's see if any of the PKCS#11 security tokens are plugged in and help us */
|
||||
/* Next, 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 = {
|
||||
@ -182,7 +198,7 @@ int user_record_authenticate(
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Fifth, let's see if any of the FIDO2 security tokens are plugged in and help us */
|
||||
/* Next, 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;
|
||||
@ -1599,6 +1615,8 @@ static int home_update(UserRecord *h, Hashmap *blobs, UserRecord **ret) {
|
||||
assert(h);
|
||||
assert(ret);
|
||||
|
||||
password_cache_load_keyring(h, &cache);
|
||||
|
||||
r = user_record_authenticate(h, h, &cache, /* strict_verify= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -1654,7 +1672,7 @@ static int home_update(UserRecord *h, Hashmap *blobs, UserRecord **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int home_resize(UserRecord *h, bool automatic, UserRecord **ret) {
|
||||
static int home_resize(UserRecord *h, UserRecord **ret) {
|
||||
_cleanup_(home_setup_done) HomeSetup setup = HOME_SETUP_INIT;
|
||||
_cleanup_(password_cache_free) PasswordCache cache = {};
|
||||
HomeSetupFlags flags = 0;
|
||||
@ -1666,26 +1684,17 @@ static int home_resize(UserRecord *h, bool automatic, UserRecord **ret) {
|
||||
if (h->disk_size == UINT64_MAX)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No target size specified, refusing.");
|
||||
|
||||
if (automatic)
|
||||
/* In automatic mode don't want to ask the user for the password, hence load it from the kernel keyring */
|
||||
password_cache_load_keyring(h, &cache);
|
||||
else {
|
||||
/* In manual mode let's ensure the user is fully authenticated */
|
||||
r = user_record_authenticate(h, h, &cache, /* strict_verify= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
assert(r > 0); /* Insist that a password was verified */
|
||||
}
|
||||
password_cache_load_keyring(h, &cache);
|
||||
|
||||
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, &flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* In automatic mode let's skip syncing identities, because we can't validate them, since we can't
|
||||
* ask the user for reauthentication */
|
||||
if (automatic)
|
||||
flags |= HOME_SETUP_RESIZE_DONT_SYNC_IDENTITIES;
|
||||
|
||||
switch (user_record_storage(h)) {
|
||||
|
||||
case USER_LUKS:
|
||||
@ -2053,10 +2062,8 @@ static int run(int argc, char *argv[]) {
|
||||
r = home_remove(home);
|
||||
else if (streq(argv[1], "update"))
|
||||
r = home_update(home, blobs, &new_home);
|
||||
else if (streq(argv[1], "resize")) /* Resize on user request */
|
||||
r = home_resize(home, false, &new_home);
|
||||
else if (streq(argv[1], "resize-auto")) /* Automatic resize */
|
||||
r = home_resize(home, true, &new_home);
|
||||
else if (streq(argv[1], "resize"))
|
||||
r = home_resize(home, &new_home);
|
||||
else if (streq(argv[1], "passwd"))
|
||||
r = home_passwd(home, &new_home);
|
||||
else if (streq(argv[1], "inspect"))
|
||||
|
@ -89,6 +89,37 @@ inspect test-user
|
||||
homectl deactivate test-user
|
||||
inspect test-user
|
||||
|
||||
# Do some keyring tests, but only on real kernels, since keyring access inside of containers will fail
|
||||
# (See: https://github.com/systemd/systemd/issues/17606)
|
||||
if ! systemd-detect-virt -cq ; then
|
||||
PASSWORD=xEhErW0ndafV4s homectl activate test-user
|
||||
inspect test-user
|
||||
|
||||
# Key should now be in the keyring
|
||||
homectl update test-user --real-name "Keyring Test"
|
||||
inspect test-user
|
||||
|
||||
# These commands shouldn't use the keyring
|
||||
(! timeout 5s homectl authenticate test-user )
|
||||
(! NEWPASSWORD="foobar" timeout 5s homectl passwd test-user )
|
||||
|
||||
homectl lock test-user
|
||||
inspect test-user
|
||||
|
||||
# Key should be gone from keyring
|
||||
(! timeout 5s homectl update test-user --real-name "Keyring Test 2" )
|
||||
|
||||
PASSWORD=xEhErW0ndafV4s homectl unlock test-user
|
||||
inspect test-user
|
||||
|
||||
# Key should have been re-instantiated into the keyring
|
||||
homectl update test-user --real-name "Keyring Test 3"
|
||||
inspect test-user
|
||||
|
||||
homectl deactivate test-user
|
||||
inspect test-user
|
||||
fi
|
||||
|
||||
# Do some resize tests, but only if we run on real kernels, as quota inside of containers will fail
|
||||
if ! systemd-detect-virt -cq ; then
|
||||
# grow while inactive
|
||||
@ -150,6 +181,11 @@ if ! systemd-detect-virt -cq ; then
|
||||
homectl rebalance
|
||||
inspect test-user
|
||||
inspect test-user2
|
||||
|
||||
wait_for_state test-user2 active
|
||||
homectl deactivate test-user2
|
||||
wait_for_state test-user2 inactive
|
||||
homectl remove test-user2
|
||||
fi
|
||||
|
||||
PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
|
||||
@ -161,13 +197,6 @@ PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
|
||||
wait_for_state test-user inactive
|
||||
homectl remove test-user
|
||||
|
||||
if ! systemd-detect-virt -cq ; then
|
||||
wait_for_state test-user2 active
|
||||
homectl deactivate test-user2
|
||||
wait_for_state test-user2 inactive
|
||||
homectl remove test-user2
|
||||
fi
|
||||
|
||||
# blob directory tests
|
||||
# See docs/USER_RECORD_BLOB_DIRS.md
|
||||
checkblob() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user