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);
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 */
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);

View File

@ -1885,7 +1885,24 @@ int home_create_luks(
fstype = user_record_file_system_type(h);
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)) {
r = sd_id128_randomize(&partition_uuid);
@ -1964,7 +1981,8 @@ int home_create_luks(
host_size = DISK_SIZE_ROUND_DOWN(h->disk_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
* 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;
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);
if (r < 0)

View File

@ -846,8 +846,8 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
switch (r) {
case -ESTALE:
(void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is newer than current system time, prohibiting access.");
return PAM_ACCT_EXPIRED;
pam_syslog(handle, LOG_WARNING, "User record for '%s' is newer than current system time, assuming incorrect system clock, allowing access.", ur->user_name);
break;
case -ENOLCK:
(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.");
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:
/* All good, just means the password if we wanted to change we couldn't, but we don't need to */
break;

View File

@ -1295,10 +1295,12 @@ int user_record_ratelimit(UserRecord *h) {
usec = now(CLOCK_REALTIME);
if (h->ratelimit_begin_usec != UINT64_MAX && h->ratelimit_begin_usec > usec)
/* Hmm, time is running backwards? Say no! */
return 0;
else if (h->ratelimit_begin_usec == UINT64_MAX ||
if (h->ratelimit_begin_usec != UINT64_MAX && h->ratelimit_begin_usec > usec) {
/* Hmm, start-time is after the current time? If so, the RTC most likely doesn't work. */
new_ratelimit_begin_usec = usec;
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) {
/* Fresh start */
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) {
char buf[FORMAT_TIMESTAMP_MAX];
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 &&
@ -56,10 +60,6 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
r = user_record_test_blocked(hr);
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:
printf(" Login OK: %sno%s (record is locked)\n", ansi_highlight_red(), ansi_normal());
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());
break;
case -ESTALE:
default: {
usec_t y;
if (r < 0) {
if (r < 0 && r != -ESTALE) {
errno = -r;
printf(" Login OK: %sno%s (%m)\n", ansi_highlight_red(), ansi_normal());
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());
break;
case -ESTALE:
printf(" Password OK: %slast password change in future%s\n", ansi_highlight_yellow(), ansi_normal());
break;
default:
if (r < 0) {
errno = -r;

View File

@ -1919,6 +1919,11 @@ uint64_t user_record_ratelimit_next_try(UserRecord *h) {
h->ratelimit_count == 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))
return 0;
@ -2025,19 +2030,20 @@ int user_record_test_blocked(UserRecord *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)
return -ENOLCK;
n = now(CLOCK_REALTIME);
if (h->not_before_usec != UINT64_MAX && n < h->not_before_usec)
return -EL2HLT;
if (h->not_after_usec != UINT64_MAX && n > h->not_after_usec)
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;
}
@ -2055,6 +2061,7 @@ int user_record_test_password_change_required(UserRecord *h) {
-EKEYEXPIRED: Password is about to expire, warn user
-ENETDOWN: Record has expiration info but no password change timestamp
-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
*/
@ -2064,6 +2071,14 @@ int user_record_test_password_change_required(UserRecord *h) {
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 */
if (h->password_change_min_usec != UINT64_MAX) {