Merge pull request #15869 from poettering/cant-auth

homed: fix logging into unfixated home directories
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2020-05-22 08:20:54 +02:00 committed by GitHub
commit fbc6d1716f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 85 additions and 19 deletions

View File

@ -202,6 +202,13 @@ static int write_string_file_atomic(
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;
fail:

View File

@ -292,7 +292,7 @@ int home_save_record(Home *h) {
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)
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);
case -ENOSPC:
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;

View File

@ -329,21 +329,36 @@ static int manager_add_home_by_record(
_cleanup_(user_record_unrefp) UserRecord *hr = NULL;
unsigned line, column;
int r, is_signed;
struct stat st;
Home *h;
assert(m);
assert(name);
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);
if (r < 0)
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();
if (!hr)
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)
return r;
@ -394,6 +409,19 @@ static int manager_add_home_by_record(
h->signed_locally = is_signed == USER_RECORD_SIGNED_EXCLUSIVE;
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) {
@ -485,7 +513,7 @@ static int search_quota(uid_t uid, const char *exclude_quota_path) {
if (ERRNO_IS_NOT_SUPPORTED(r))
log_debug_errno(r, "No UID quota support on %s, ignoring.", where);
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;
}
@ -1309,7 +1337,7 @@ static int manager_generate_key_pair(Manager *m) {
if (PEM_write_PUBKEY(fpublic, m->private_key) <= 0)
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)
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)
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)
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) {
(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);
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;
}

View File

@ -727,9 +727,10 @@ static int luks_validate_home_record(
if (!user_record_compatible(h, lhr))
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)
return r;
assert(r > 0); /* Insist that a password was verified */
*ret_luks_home_record = TAKE_PTR(lhr);
return 0;

View File

@ -35,7 +35,8 @@
int user_record_authenticate(
UserRecord *h,
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,
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");
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 */
@ -86,7 +87,7 @@ int user_record_authenticate(
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 0;
return 1;
}
}
@ -129,7 +130,7 @@ int user_record_authenticate(
if (r < 0)
return log_oom();
return 0;
return 1;
}
#else
need_token = true;
@ -156,7 +157,18 @@ int user_record_authenticate(
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.");
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) {
@ -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.");
/* 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)
return r;
assert(r > 0); /* Insist that a password was verified */
/* 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))
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)
return r;
@ -1177,9 +1190,10 @@ static int home_update(UserRecord *h, UserRecord **ret) {
assert(h);
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)
return r;
assert(r > 0); /* Insist that a password was verified */
r = home_validate_update(h, &setup);
if (r < 0)
@ -1233,9 +1247,10 @@ static int home_resize(UserRecord *h, UserRecord **ret) {
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, &pkcs11_decrypted_passwords, /* 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)
@ -1343,7 +1358,7 @@ static int home_inspect(UserRecord *h, UserRecord **ret_home) {
assert(h);
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)
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
* 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)
return r;
@ -1489,6 +1504,7 @@ static int run(int argc, char *argv[]) {
* 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"))

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_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);

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_TOO_MANY_OPERATIONS, ENOBUFS),
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
};

View File

@ -115,5 +115,6 @@
#define BUS_ERROR_NO_DISK_SPACE "org.freedesktop.home1.NoDiskSpace"
#define BUS_ERROR_TOO_MANY_OPERATIONS "org.freedesktop.home1.TooManyOperations"
#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);

View File

@ -1591,6 +1591,12 @@ int bus_add_implementation(sd_bus *bus, const BusObjectImplementation *impl, voi
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++) {
r = bus_add_implementation(bus, impl->children[i], userdata);
if (r < 0)