Merge pull request #16938 from poettering/homed-rtc-wrong

homed: don't refuse logins when RTC is wrong
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2020-09-22 20:51:39 +02:00 committed by GitHub
commit a5d815bb7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 65 additions and 20 deletions

View File

@ -1574,7 +1574,7 @@ static int home_may_change_password(
assert(h); assert(h);
r = user_record_test_password_change_required(h->record); r = user_record_test_password_change_required(h->record);
if (IN_SET(r, -EKEYREVOKED, -EOWNERDEAD, -EKEYEXPIRED)) if (IN_SET(r, -EKEYREVOKED, -EOWNERDEAD, -EKEYEXPIRED, -ESTALE))
return 0; /* expired in some form, but changing is allowed */ return 0; /* expired in some form, but changing is allowed */
if (IN_SET(r, -EKEYREJECTED, -EROFS)) if (IN_SET(r, -EKEYREJECTED, -EROFS))
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Expiration settings of account %s do not allow changing of password.", h->user_name); return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Expiration settings of account %s do not allow changing of password.", h->user_name);

View File

@ -1885,7 +1885,24 @@ int home_create_luks(
fstype = user_record_file_system_type(h); fstype = user_record_file_system_type(h);
if (!supported_fstype(fstype)) if (!supported_fstype(fstype))
return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "Unsupported file system type: %s", h->file_system_type); return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "Unsupported file system type: %s", fstype);
r = mkfs_exists(fstype);
if (r < 0)
return log_error_errno(r, "Failed to check if mkfs binary for %s exists: %m", fstype);
if (r == 0) {
if (h->file_system_type || streq(fstype, "ext4") || !supported_fstype("ext4"))
return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mkfs binary for file system type %s does not exist.", fstype);
/* If the record does not explicitly declare a file system to use, and the compiled-in
* default does not actually exist, than do an automatic fallback onto ext4, as the baseline
* fs of Linux. We won't search for a working fs type here beyond ext4, i.e. nothing fancier
* than a single, conservative fallback to baseline. This should be useful in minimal
* environments where mkfs.btrfs or so are not made available, but mkfs.ext4 as Linux' most
* boring, most basic fs is. */
log_info("Formatting tool for compiled-in default file system %s not available, falling back to ext4 instead.", fstype);
fstype = "ext4";
}
if (sd_id128_is_null(h->partition_uuid)) { if (sd_id128_is_null(h->partition_uuid)) {
r = sd_id128_randomize(&partition_uuid); r = sd_id128_randomize(&partition_uuid);
@ -1964,7 +1981,8 @@ int home_create_luks(
host_size = DISK_SIZE_ROUND_DOWN(h->disk_size); host_size = DISK_SIZE_ROUND_DOWN(h->disk_size);
if (!supported_fs_size(fstype, host_size)) if (!supported_fs_size(fstype, host_size))
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Selected file system size too small for %s.", h->file_system_type); return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
"Selected file system size too small for %s.", fstype);
/* After creation we should reference this partition by its UUID instead of the block /* After creation we should reference this partition by its UUID instead of the block
* device. That's preferable since the user might have specified a device node such as * device. That's preferable since the user might have specified a device node such as
@ -1997,7 +2015,7 @@ int home_create_luks(
return r; return r;
if (!supported_fs_size(fstype, host_size)) if (!supported_fs_size(fstype, host_size))
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Selected file system size too small for %s.", h->file_system_type); return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Selected file system size too small for %s.", fstype);
r = tempfn_random(ip, "homework", &temporary_image_path); r = tempfn_random(ip, "homework", &temporary_image_path);
if (r < 0) if (r < 0)

View File

@ -846,8 +846,8 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
switch (r) { switch (r) {
case -ESTALE: case -ESTALE:
(void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is newer than current system time, prohibiting access."); pam_syslog(handle, LOG_WARNING, "User record for '%s' is newer than current system time, assuming incorrect system clock, allowing access.", ur->user_name);
return PAM_ACCT_EXPIRED; break;
case -ENOLCK: case -ENOLCK:
(void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is blocked, prohibiting access."); (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is blocked, prohibiting access.");
@ -904,6 +904,11 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
(void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password will expire soon, please change."); (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password will expire soon, please change.");
break; break;
case -ESTALE:
/* If the system clock is wrong, let's log but continue */
pam_syslog(handle, LOG_WARNING, "Couldn't check if password change is required, last change is in the future, system clock likely wrong.");
break;
case -EROFS: case -EROFS:
/* All good, just means the password if we wanted to change we couldn't, but we don't need to */ /* All good, just means the password if we wanted to change we couldn't, but we don't need to */
break; break;

View File

@ -1295,10 +1295,12 @@ int user_record_ratelimit(UserRecord *h) {
usec = now(CLOCK_REALTIME); usec = now(CLOCK_REALTIME);
if (h->ratelimit_begin_usec != UINT64_MAX && h->ratelimit_begin_usec > usec) if (h->ratelimit_begin_usec != UINT64_MAX && h->ratelimit_begin_usec > usec) {
/* Hmm, time is running backwards? Say no! */ /* Hmm, start-time is after the current time? If so, the RTC most likely doesn't work. */
return 0; new_ratelimit_begin_usec = usec;
else if (h->ratelimit_begin_usec == UINT64_MAX || new_ratelimit_count = 1;
log_debug("Rate limit timestamp is in the future, assuming incorrect system clock, resetting limit.");
} else if (h->ratelimit_begin_usec == UINT64_MAX ||
usec_add(h->ratelimit_begin_usec, user_record_ratelimit_interval_usec(h)) <= usec) { usec_add(h->ratelimit_begin_usec, user_record_ratelimit_interval_usec(h)) <= usec) {
/* Fresh start */ /* Fresh start */
new_ratelimit_begin_usec = usec; new_ratelimit_begin_usec = usec;

View File

@ -45,6 +45,10 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
if (hr->last_change_usec != USEC_INFINITY) { if (hr->last_change_usec != USEC_INFINITY) {
char buf[FORMAT_TIMESTAMP_MAX]; char buf[FORMAT_TIMESTAMP_MAX];
printf(" Last Change: %s\n", format_timestamp(buf, sizeof(buf), hr->last_change_usec)); printf(" Last Change: %s\n", format_timestamp(buf, sizeof(buf), hr->last_change_usec));
if (hr->last_change_usec > now(CLOCK_REALTIME))
printf(" %sModification time lies in the future, system clock wrong?%s\n",
ansi_highlight_yellow(), ansi_normal());
} }
if (hr->last_password_change_usec != USEC_INFINITY && if (hr->last_password_change_usec != USEC_INFINITY &&
@ -56,10 +60,6 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
r = user_record_test_blocked(hr); r = user_record_test_blocked(hr);
switch (r) { switch (r) {
case -ESTALE:
printf(" Login OK: %sno%s (last change time is in the future)\n", ansi_highlight_red(), ansi_normal());
break;
case -ENOLCK: case -ENOLCK:
printf(" Login OK: %sno%s (record is locked)\n", ansi_highlight_red(), ansi_normal()); printf(" Login OK: %sno%s (record is locked)\n", ansi_highlight_red(), ansi_normal());
break; break;
@ -72,10 +72,11 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
printf(" Login OK: %sno%s (record not valid anymore))\n", ansi_highlight_red(), ansi_normal()); printf(" Login OK: %sno%s (record not valid anymore))\n", ansi_highlight_red(), ansi_normal());
break; break;
case -ESTALE:
default: { default: {
usec_t y; usec_t y;
if (r < 0) { if (r < 0 && r != -ESTALE) {
errno = -r; errno = -r;
printf(" Login OK: %sno%s (%m)\n", ansi_highlight_red(), ansi_normal()); printf(" Login OK: %sno%s (%m)\n", ansi_highlight_red(), ansi_normal());
break; break;
@ -123,6 +124,10 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
printf(" Password OK: %schange not permitted%s\n", ansi_highlight_yellow(), ansi_normal()); printf(" Password OK: %schange not permitted%s\n", ansi_highlight_yellow(), ansi_normal());
break; break;
case -ESTALE:
printf(" Password OK: %slast password change in future%s\n", ansi_highlight_yellow(), ansi_normal());
break;
default: default:
if (r < 0) { if (r < 0) {
errno = -r; errno = -r;

View File

@ -1919,6 +1919,11 @@ uint64_t user_record_ratelimit_next_try(UserRecord *h) {
h->ratelimit_count == UINT64_MAX) h->ratelimit_count == UINT64_MAX)
return UINT64_MAX; return UINT64_MAX;
if (h->ratelimit_begin_usec > now(CLOCK_REALTIME)) /* If the ratelimit time is in the future, then
* the local clock is probably incorrect. Let's
* not refuse login then. */
return UINT64_MAX;
if (h->ratelimit_count < user_record_ratelimit_burst(h)) if (h->ratelimit_count < user_record_ratelimit_burst(h))
return 0; return 0;
@ -2025,19 +2030,20 @@ int user_record_test_blocked(UserRecord *h) {
assert(h); assert(h);
n = now(CLOCK_REALTIME);
if (h->last_change_usec != UINT64_MAX &&
h->last_change_usec > n) /* Don't allow log ins when the record is from the future */
return -ESTALE;
if (h->locked > 0) if (h->locked > 0)
return -ENOLCK; return -ENOLCK;
n = now(CLOCK_REALTIME);
if (h->not_before_usec != UINT64_MAX && n < h->not_before_usec) if (h->not_before_usec != UINT64_MAX && n < h->not_before_usec)
return -EL2HLT; return -EL2HLT;
if (h->not_after_usec != UINT64_MAX && n > h->not_after_usec) if (h->not_after_usec != UINT64_MAX && n > h->not_after_usec)
return -EL3HLT; return -EL3HLT;
if (h->last_change_usec != UINT64_MAX &&
h->last_change_usec > n) /* Complain during log-ins when the record is from the future */
return -ESTALE;
return 0; return 0;
} }
@ -2055,6 +2061,7 @@ int user_record_test_password_change_required(UserRecord *h) {
-EKEYEXPIRED: Password is about to expire, warn user -EKEYEXPIRED: Password is about to expire, warn user
-ENETDOWN: Record has expiration info but no password change timestamp -ENETDOWN: Record has expiration info but no password change timestamp
-EROFS: No password change required nor permitted -EROFS: No password change required nor permitted
-ESTALE: RTC likely incorrect, last password change is in the future
0: No password change required, but permitted 0: No password change required, but permitted
*/ */
@ -2064,6 +2071,14 @@ int user_record_test_password_change_required(UserRecord *h) {
n = now(CLOCK_REALTIME); n = now(CLOCK_REALTIME);
/* Password change in the future? Then our RTC is likely incorrect */
if (h->last_password_change_usec != UINT64_MAX &&
h->last_password_change_usec > n &&
(h->password_change_min_usec != UINT64_MAX ||
h->password_change_max_usec != UINT64_MAX ||
h->password_change_inactive_usec != UINT64_MAX))
return -ESTALE;
/* Then, let's check if password changing is currently allowed at all */ /* Then, let's check if password changing is currently allowed at all */
if (h->password_change_min_usec != UINT64_MAX) { if (h->password_change_min_usec != UINT64_MAX) {