diff --git a/src/pcrlock/pcrlock.c b/src/pcrlock/pcrlock.c index 2d084d8eb1..cb39737b1e 100644 --- a/src/pcrlock/pcrlock.c +++ b/src/pcrlock/pcrlock.c @@ -1179,6 +1179,7 @@ static int event_log_load_userspace(EventLog *el) { _cleanup_free_ char *b = NULL; bool beginning = true; const char *path; + struct stat st; size_t bn = 0; int r; @@ -1197,6 +1198,13 @@ static int event_log_load_userspace(EventLog *el) { if (flock(fileno(f), LOCK_SH) < 0) return log_error_errno(errno, "Failed to lock userspace TPM measurement log file: %m"); + /* The sticky bit marks the log as incomplete: a writer died between updating a PCR and appending + * the matching record. Load the log anyway, the affected PCRs will simply fail validation. */ + if (fstat(fileno(f), &st) < 0) + log_warning_errno(errno, "Failed to stat userspace TPM measurement log file, cannot determine whether it is complete, ignoring: %m"); + else if (FLAGS_SET(st.st_mode, S_ISVTX)) + log_warning("Userspace TPM measurement log file is marked as incomplete, PCR validation may fail."); + for (;;) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL; EventLogRecord *record; diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 3e393f053a..c43616df51 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -7778,8 +7778,9 @@ static int tpm2_userspace_log_dirty(int fd) { /* We set the sticky bit when we are about to append to the log file. We'll unset it afterwards * again. If we manage to take a lock on a file that has it set we know we didn't write it fully and - * it is corrupted. Ideally we'd like to use user xattrs for this, but unfortunately tmpfs (which is - * our assumed backend fs) doesn't know user xattrs. */ + * it is corrupted. We return -ESTALE then; callers shall not reset the marker when they are done, + * so that the incompleteness remains detectable. Ideally we'd like to use user xattrs for this, but + * unfortunately tmpfs (which is our assumed backend fs) doesn't know user xattrs. */ if (fstat(fd, &st) < 0) return log_debug_errno(errno, "Failed to fstat TPM log file, ignoring: %m"); @@ -7793,7 +7794,7 @@ static int tpm2_userspace_log_dirty(int fd) { return 0; } -static int tpm2_userspace_log_clean(int fd) { +static int tpm2_userspace_log_clean(int fd, bool reset_marker) { int r; if (fd < 0) /* Apparently tpm2_local_log_open() failed earlier, let's not complain again */ @@ -7802,6 +7803,12 @@ static int tpm2_userspace_log_clean(int fd) { if (fsync(fd) < 0) return log_debug_errno(errno, "Failed to sync JSON data: %m"); + /* If the dirty marker was already set when we acquired the log, an earlier writer died before + * writing its record, i.e. the log is missing a record. Keep the marker then, so that the + * incompleteness remains detectable. */ + if (!reset_marker) + return 0; + /* Unset S_ISVTX again */ if (fchmod(fd, 0600) < 0) return log_debug_errno(errno, "Failed to chmod() TPM log file, ignoring: %m"); @@ -7820,7 +7827,8 @@ static int tpm2_userspace_log( const char *nv_index_name, const TPML_DIGEST_VALUES *values, Tpm2UserspaceEventType event_type, - const char *description) { + const char *description, + bool reset_marker) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *array = NULL; _cleanup_free_ char *f = NULL; @@ -7909,7 +7917,7 @@ static int tpm2_userspace_log( if (r < 0) return log_debug_errno(r, "Failed to write JSON data to log: %m"); - r = tpm2_userspace_log_clean(fd); + r = tpm2_userspace_log_clean(fd, reset_marker); if (r < 0) return r; @@ -7986,7 +7994,7 @@ int tpm2_pcr_extend_bytes( * and our measurement and change either */ log_fd = tpm2_userspace_log_open(); - (void) tpm2_userspace_log_dirty(log_fd); + bool reset_marker = tpm2_userspace_log_dirty(log_fd) >= 0; rc = sym_Esys_PCR_Extend( c->esys_context, ESYS_TR_PCR0 + pcr_index, @@ -8009,7 +8017,8 @@ int tpm2_pcr_extend_bytes( /* nv_index_name= */ NULL, &values, event_type, - description); + description, + reset_marker); return 0; #else /* HAVE_OPENSSL */ @@ -8179,7 +8188,7 @@ static int nvpcr_extend_bytes( log_debug("Successfully acquired handle to existing NV index 0x%" PRIx32 ".", p.nv_index); - (void) tpm2_userspace_log_dirty(log_fd); + bool reset_marker = tpm2_userspace_log_dirty(log_fd) >= 0; r = tpm2_extend_nvpcr_nv_index( c, @@ -8203,7 +8212,8 @@ static int nvpcr_extend_bytes( name, &digest_values, event_type, - description); + description, + reset_marker); return 0; #else /* HAVE_OPENSSL */ @@ -8739,7 +8749,7 @@ int tpm2_nvpcr_initialize( log_debug("Successfully acquired handle to NV index 0x%" PRIx32 ".", p.nv_index); - tpm2_userspace_log_dirty(log_fd); + bool reset_marker = tpm2_userspace_log_dirty(log_fd) >= 0; rc = sym_Esys_NV_Extend( c->esys_context, /* authHandle= */ nv_handle->esys_handle, @@ -8777,7 +8787,7 @@ int tpm2_nvpcr_initialize( if (r < 0) return log_debug_errno(r, "Failed to write anchor file: %m"); - tpm2_userspace_log_clean(log_fd); + (void) tpm2_userspace_log_clean(log_fd, reset_marker); log_fd = safe_close(log_fd); /* Now also measure the initialization into PCR 9, so that there's a trace of it in regular PCRs. You