Compare commits

..

11 Commits

Author SHA1 Message Date
Zbigniew Jędrzejewski-Szmek fbc6d1716f
Merge pull request #15869 from poettering/cant-auth
homed: fix logging into unfixated home directories
2020-05-22 08:20:54 +02:00
Lennart Poettering c8f145adbb homed: don't insist on authentication against host-copy user record
homed maintains two or three copies of the user's identity record per
home directory: one on the host, one inside the LUKS header, and one
embedded in the home directory.

Previously we'd insist that if a user logs in they have to authenticate
against all three, as a safety feature. This broke logging into
unfixated records however, since in that case the host version is
synthetic and thus does not carry any authentication data.

Let's hence losen the strictness here: accept authentication against
host records that carry no auth data. This should be safe as we know
after all that the second/third record will catch invalid accesses.

Fixes: #15178
2020-05-21 23:39:30 +02:00
Lennart Poettering 1a53adb3ab homed: include error string when in log message if quota doesn't work 2020-05-21 23:39:30 +02:00
Lennart Poettering 2fcbf417b6 bus-util: actually register the object manager 2020-05-21 23:39:30 +02:00
Lennart Poettering fa3709c5fb homed: also fsync private/public key pair when storing it 2020-05-21 23:39:30 +02:00
Lennart Poettering e8dd54ab3c homed: fix typo 2020-05-21 23:39:30 +02:00
Lennart Poettering b847192699 homed: make sure we log about invalid user records we load 2020-05-21 23:39:30 +02:00
Lennart Poettering 20f4a308bf homed: automatically clean up empty user record files
See: #15178
2020-05-21 23:39:30 +02:00
Lennart Poettering e4005ffe00 homed: when updating local copy of user record, sync to disk
Apparently xfs needs us to sync explicitly, see #15178.
2020-05-21 23:28:49 +02:00
Lennart Poettering 5b3f4a20ea fileio: sync directory after rename, too 2020-05-21 23:28:48 +02:00
Lennart Poettering cbffdcecae homed: return a better error when a home has no authentication information defined
We can't log into home entries that have no password or PKCS#11 token.
Return a proper, useful error in that case.

See: #15178
2020-05-21 23:28:48 +02:00
9 changed files with 85 additions and 19 deletions

View File

@ -202,6 +202,13 @@ static int write_string_file_atomic(
goto fail; goto fail;
} }
if (FLAGS_SET(flags, WRITE_STRING_FILE_SYNC)) {
/* Sync the rename, too */
r = fsync_directory_of_file(fileno(f));
if (r < 0)
return r;
}
return 0; return 0;
fail: fail:

View File

@ -292,7 +292,7 @@ int home_save_record(Home *h) {
fn = strjoina("/var/lib/systemd/home/", h->user_name, ".identity"); fn = strjoina("/var/lib/systemd/home/", h->user_name, ".identity");
r = write_string_file(fn, text, WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MODE_0600); r = write_string_file(fn, text, WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MODE_0600|WRITE_STRING_FILE_SYNC);
if (r < 0) if (r < 0)
return r; return r;
@ -471,6 +471,8 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s is currently not active", h->user_name); return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s is currently not active", h->user_name);
case -ENOSPC: case -ENOSPC:
return sd_bus_error_setf(error, BUS_ERROR_NO_DISK_SPACE, "Not enough disk space for home %s", h->user_name); return sd_bus_error_setf(error, BUS_ERROR_NO_DISK_SPACE, "Not enough disk space for home %s", h->user_name);
case -EKEYREVOKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_CANT_AUTHENTICATE, "Home %s has no password or other authentication mechanism defined.", h->user_name);
} }
return 0; return 0;

View File

@ -329,21 +329,36 @@ static int manager_add_home_by_record(
_cleanup_(user_record_unrefp) UserRecord *hr = NULL; _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
unsigned line, column; unsigned line, column;
int r, is_signed; int r, is_signed;
struct stat st;
Home *h; Home *h;
assert(m); assert(m);
assert(name); assert(name);
assert(fname); assert(fname);
if (fstatat(dir_fd, fname, &st, 0) < 0)
return log_error_errno(errno, "Failed to stat identity record %s: %m", fname);
if (!S_ISREG(st.st_mode)) {
log_debug("Identity record file %s is not a regular file, ignoring.", fname);
return 0;
}
if (st.st_size == 0)
goto unlink_this_file;
r = json_parse_file_at(NULL, dir_fd, fname, JSON_PARSE_SENSITIVE, &v, &line, &column); r = json_parse_file_at(NULL, dir_fd, fname, JSON_PARSE_SENSITIVE, &v, &line, &column);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to parse identity record at %s:%u%u: %m", fname, line, column); return log_error_errno(r, "Failed to parse identity record at %s:%u%u: %m", fname, line, column);
if (json_variant_is_blank_object(v))
goto unlink_this_file;
hr = user_record_new(); hr = user_record_new();
if (!hr) if (!hr)
return log_oom(); return log_oom();
r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET); r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_LOG);
if (r < 0) if (r < 0)
return r; return r;
@ -394,6 +409,19 @@ static int manager_add_home_by_record(
h->signed_locally = is_signed == USER_RECORD_SIGNED_EXCLUSIVE; h->signed_locally = is_signed == USER_RECORD_SIGNED_EXCLUSIVE;
return 1; return 1;
unlink_this_file:
/* If this is an empty file, then let's just remove it. An empty file is not useful in any case, and
* apparently xfs likes to leave empty files around when not unmounted cleanly (see
* https://github.com/systemd/systemd/issues/15178 for example). Note that we don't delete non-empty
* files even if they are invalid, because that's just too risky, we might delete data the user still
* needs. But empty files are never useful, hence let's just remove them. */
if (unlinkat(dir_fd, fname, 0) < 0)
return log_error_errno(errno, "Failed to remove empty user record file %s: %m", fname);
log_notice("Discovered empty user record file /var/lib/systemd/home/%s, removed automatically.", fname);
return 0;
} }
static int manager_enumerate_records(Manager *m) { static int manager_enumerate_records(Manager *m) {
@ -485,7 +513,7 @@ static int search_quota(uid_t uid, const char *exclude_quota_path) {
if (ERRNO_IS_NOT_SUPPORTED(r)) if (ERRNO_IS_NOT_SUPPORTED(r))
log_debug_errno(r, "No UID quota support on %s, ignoring.", where); log_debug_errno(r, "No UID quota support on %s, ignoring.", where);
else else
log_warning_errno(r, "Failed to query quota on %s, ignoring.", where); log_warning_errno(r, "Failed to query quota on %s, ignoring: %m", where);
continue; continue;
} }
@ -1309,7 +1337,7 @@ static int manager_generate_key_pair(Manager *m) {
if (PEM_write_PUBKEY(fpublic, m->private_key) <= 0) if (PEM_write_PUBKEY(fpublic, m->private_key) <= 0)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write public key."); return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write public key.");
r = fflush_and_check(fpublic); r = fflush_sync_and_check(fpublic);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to write private key: %m"); return log_error_errno(r, "Failed to write private key: %m");
@ -1323,7 +1351,7 @@ static int manager_generate_key_pair(Manager *m) {
if (PEM_write_PrivateKey(fprivate, m->private_key, NULL, NULL, 0, NULL, 0) <= 0) if (PEM_write_PrivateKey(fprivate, m->private_key, NULL, NULL, 0, NULL, 0) <= 0)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write private key pair."); return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write private key pair.");
r = fflush_and_check(fprivate); r = fflush_sync_and_check(fprivate);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to write private key: %m"); return log_error_errno(r, "Failed to write private key: %m");
@ -1337,10 +1365,14 @@ static int manager_generate_key_pair(Manager *m) {
if (rename(temp_private, "/var/lib/systemd/home/local.private") < 0) { if (rename(temp_private, "/var/lib/systemd/home/local.private") < 0) {
(void) unlink_noerrno("/var/lib/systemd/home/local.public"); /* try to remove the file we already created */ (void) unlink_noerrno("/var/lib/systemd/home/local.public"); /* try to remove the file we already created */
return log_error_errno(errno, "Failed to move privtate key file into place: %m"); return log_error_errno(errno, "Failed to move private key file into place: %m");
} }
temp_private = mfree(temp_private); temp_private = mfree(temp_private);
r = fsync_path_at(AT_FDCWD, "/var/lib/systemd/home/");
if (r < 0)
log_warning_errno(r, "Failed to sync /var/lib/systemd/home/, ignoring: %m");
return 1; return 1;
} }

View File

@ -727,9 +727,10 @@ static int luks_validate_home_record(
if (!user_record_compatible(h, lhr)) if (!user_record_compatible(h, lhr))
return log_error_errno(SYNTHETIC_ERRNO(EREMCHG), "LUKS home record not compatible with host record, refusing."); return log_error_errno(SYNTHETIC_ERRNO(EREMCHG), "LUKS home record not compatible with host record, refusing.");
r = user_record_authenticate(lhr, h, pkcs11_decrypted_passwords); r = user_record_authenticate(lhr, h, pkcs11_decrypted_passwords, /* strict_verify= */ true);
if (r < 0) if (r < 0)
return r; return r;
assert(r > 0); /* Insist that a password was verified */
*ret_luks_home_record = TAKE_PTR(lhr); *ret_luks_home_record = TAKE_PTR(lhr);
return 0; return 0;

View File

@ -35,7 +35,8 @@
int user_record_authenticate( int user_record_authenticate(
UserRecord *h, UserRecord *h,
UserRecord *secret, UserRecord *secret,
char ***pkcs11_decrypted_passwords) { char ***pkcs11_decrypted_passwords,
bool strict_verify) {
bool need_password = false, need_token = false, need_pin = false, need_protected_authentication_path_permitted = false, 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; pin_locked = false, pin_incorrect = false, pin_incorrect_few_tries_left = false, pin_incorrect_one_try_left = false;
@ -66,7 +67,7 @@ int user_record_authenticate(
return log_error_errno(r, "Failed to validate password of record: %m"); return log_error_errno(r, "Failed to validate password of record: %m");
else { else {
log_info("Provided password unlocks user record."); 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 */ /* Second, let's see if any of the PKCS#11 security tokens are plugged in and help us */
@ -86,7 +87,7 @@ int user_record_authenticate(
return log_error_errno(r, "Failed to check supplied PKCS#11 password: %m"); return log_error_errno(r, "Failed to check supplied PKCS#11 password: %m");
if (r > 0) { if (r > 0) {
log_info("Previously acquired PKCS#11 password unlocks user record."); log_info("Previously acquired PKCS#11 password unlocks user record.");
return 0; return 1;
} }
} }
@ -129,7 +130,7 @@ int user_record_authenticate(
if (r < 0) if (r < 0)
return log_oom(); return log_oom();
return 0; return 1;
} }
#else #else
need_token = true; need_token = true;
@ -156,7 +157,18 @@ int user_record_authenticate(
return -ENOKEY; return -ENOKEY;
/* Hmm, this means neither PCKS#11 nor classic hashed passwords were supplied, we cannot authenticate this reasonably */ /* 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."); if (strict_verify)
return log_debug_errno(SYNTHETIC_ERRNO(EKEYREVOKED),
"No hashed passwords and no PKCS#11 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) { int home_setup_undo(HomeSetup *setup) {
@ -402,9 +414,10 @@ int home_load_embedded_identity(
return log_error_errno(SYNTHETIC_ERRNO(EREMCHG), "Embedded home record not compatible with host record, refusing."); 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. */ /* 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, pkcs11_decrypted_passwords, /* strict_verify= */ true);
if (r < 0) if (r < 0)
return r; return r;
assert(r > 0); /* Insist that a password was verified */
/* At this point we have three records to deal with: /* At this point we have three records to deal with:
* *
@ -615,7 +628,7 @@ static int home_activate(UserRecord *h, UserRecord **ret_home) {
if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS)) 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))); 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, &pkcs11_decrypted_passwords, /* strict_verify= */ false);
if (r < 0) if (r < 0)
return r; return r;
@ -1177,9 +1190,10 @@ static int home_update(UserRecord *h, UserRecord **ret) {
assert(h); assert(h);
assert(ret); assert(ret);
r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords); r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords, /* strict_verify= */ true);
if (r < 0) if (r < 0)
return r; return r;
assert(r > 0); /* Insist that a password was verified */
r = home_validate_update(h, &setup); r = home_validate_update(h, &setup);
if (r < 0) if (r < 0)
@ -1233,9 +1247,10 @@ static int home_resize(UserRecord *h, UserRecord **ret) {
if (h->disk_size == UINT64_MAX) if (h->disk_size == UINT64_MAX)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No target size specified, refusing."); 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, &pkcs11_decrypted_passwords, /* strict_verify= */ true);
if (r < 0) if (r < 0)
return r; return r;
assert(r > 0); /* Insist that a password was verified */
r = home_validate_update(h, &setup); r = home_validate_update(h, &setup);
if (r < 0) if (r < 0)
@ -1343,7 +1358,7 @@ static int home_inspect(UserRecord *h, UserRecord **ret_home) {
assert(h); assert(h);
assert(ret_home); assert(ret_home);
r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords); r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords, /* strict_verify= */ false);
if (r < 0) if (r < 0)
return r; return r;
@ -1413,7 +1428,7 @@ static int home_unlock(UserRecord *h) {
/* Note that we don't check if $HOME is actually mounted, since we want to avoid disk accesses on /* 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. */ * that mount until we have resumed the device. */
r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords); r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords, /* strict_verify= */ false);
if (r < 0) if (r < 0)
return r; return r;
@ -1489,6 +1504,7 @@ static int run(int argc, char *argv[]) {
* EBUSY file system is currently active * EBUSY file system is currently active
* ENOEXEC file system is currently not active * ENOEXEC file system is currently not active
* ENOSPC not enough disk space for operation * 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")) if (streq(argv[1], "activate"))

View File

@ -56,6 +56,6 @@ int home_load_embedded_identity(UserRecord *h, int root_fd, UserRecord *header_h
int home_store_embedded_identity(UserRecord *h, int root_fd, uid_t uid, UserRecord *old_home); int home_store_embedded_identity(UserRecord *h, int root_fd, uid_t uid, UserRecord *old_home);
int home_extend_embedded_identity(UserRecord *h, UserRecord *used, HomeSetup *setup); int home_extend_embedded_identity(UserRecord *h, UserRecord *used, HomeSetup *setup);
int user_record_authenticate(UserRecord *h, UserRecord *secret, char ***pkcs11_decrypted_passwords); int user_record_authenticate(UserRecord *h, UserRecord *secret, char ***pkcs11_decrypted_passwords, bool strict_verify);
int home_sync_and_statfs(int root_fd, struct statfs *ret); int home_sync_and_statfs(int root_fd, struct statfs *ret);

View File

@ -134,6 +134,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_HOME_NOT_LOCKED, ENOEXEC), SD_BUS_ERROR_MAP(BUS_ERROR_HOME_NOT_LOCKED, ENOEXEC),
SD_BUS_ERROR_MAP(BUS_ERROR_TOO_MANY_OPERATIONS, ENOBUFS), SD_BUS_ERROR_MAP(BUS_ERROR_TOO_MANY_OPERATIONS, ENOBUFS),
SD_BUS_ERROR_MAP(BUS_ERROR_AUTHENTICATION_LIMIT_HIT, ETOOMANYREFS), SD_BUS_ERROR_MAP(BUS_ERROR_AUTHENTICATION_LIMIT_HIT, ETOOMANYREFS),
SD_BUS_ERROR_MAP(BUS_ERROR_HOME_CANT_AUTHENTICATE, EKEYREVOKED),
SD_BUS_ERROR_MAP_END SD_BUS_ERROR_MAP_END
}; };

View File

@ -115,5 +115,6 @@
#define BUS_ERROR_NO_DISK_SPACE "org.freedesktop.home1.NoDiskSpace" #define BUS_ERROR_NO_DISK_SPACE "org.freedesktop.home1.NoDiskSpace"
#define BUS_ERROR_TOO_MANY_OPERATIONS "org.freedesktop.home1.TooManyOperations" #define BUS_ERROR_TOO_MANY_OPERATIONS "org.freedesktop.home1.TooManyOperations"
#define BUS_ERROR_AUTHENTICATION_LIMIT_HIT "org.freedesktop.home1.AuthenticationLimitHit" #define BUS_ERROR_AUTHENTICATION_LIMIT_HIT "org.freedesktop.home1.AuthenticationLimitHit"
#define BUS_ERROR_HOME_CANT_AUTHENTICATE "org.freedesktop.home1.HomeCantAuthenticate"
BUS_ERROR_MAP_ELF_USE(bus_common_errors); BUS_ERROR_MAP_ELF_USE(bus_common_errors);

View File

@ -1591,6 +1591,12 @@ int bus_add_implementation(sd_bus *bus, const BusObjectImplementation *impl, voi
impl->path); impl->path);
} }
if (impl->manager) {
r = sd_bus_add_object_manager(bus, NULL, impl->path);
if (r < 0)
return log_error_errno(r, "Failed to add object manager for %s: %m", impl->path);
}
for (size_t i = 0; impl->children && impl->children[i]; i++) { for (size_t i = 0; impl->children && impl->children[i]; i++) {
r = bus_add_implementation(bus, impl->children[i], userdata); r = bus_add_implementation(bus, impl->children[i], userdata);
if (r < 0) if (r < 0)