89 lines
4.9 KiB
Diff
89 lines
4.9 KiB
Diff
From 9627e6a72f9c5c336a285b11515bda49345e7bfe Mon Sep 17 00:00:00 2001
|
|
From: felixdoerre <felixdoerre@users.noreply.github.com>
|
|
Date: Fri, 6 Oct 2023 05:18:21 +0200
|
|
Subject: [PATCH] journalctl: verify that old entries are not sealed with too
|
|
recent key (#28885)
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
When verifying seals produced with forward secure sealing, the verification
|
|
currently does not check that old entries are only sealed with the key for
|
|
their epoch and not a more recent one. This missing check allows an attacker
|
|
to remove seals, and create new ones with the currently available key, and
|
|
verify will claim everything is in order, although all entries could have
|
|
been modified.
|
|
|
|
This resolves CVE-2023-31439.
|
|
|
|
Co-authored-by: Felix Dörre <felix.doerre@kit.edu>
|
|
(cherry picked from commit 3846d3aa292a6daa1916f667bdd79ebee9cb4ac4)
|
|
(cherry picked from commit ea67d4755b5d81a42a9013d6ce72c9cf7adb56b9)
|
|
(cherry picked from commit e140c1d10b04c757832adf2366ed6fbdfb2e92c9)
|
|
---
|
|
src/libsystemd/sd-journal/journal-verify.c | 26 ++++++++++++++++++++--
|
|
1 file changed, 24 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/src/libsystemd/sd-journal/journal-verify.c b/src/libsystemd/sd-journal/journal-verify.c
|
|
index ad4039dee0f..fe4465c5e65 100644
|
|
--- a/src/libsystemd/sd-journal/journal-verify.c
|
|
+++ b/src/libsystemd/sd-journal/journal-verify.c
|
|
@@ -820,6 +820,7 @@ int journal_file_verify(
|
|
uint64_t p = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0;
|
|
|
|
uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
|
|
+ usec_t min_entry_realtime = USEC_INFINITY, max_entry_realtime = 0;
|
|
sd_id128_t entry_boot_id = {}; /* Unnecessary initialization to appease gcc */
|
|
bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
|
|
uint64_t n_objects = 0, n_entries = 0, n_data = 0, n_fields = 0, n_data_hash_tables = 0, n_field_hash_tables = 0, n_entry_arrays = 0, n_tags = 0;
|
|
@@ -1071,6 +1072,9 @@ int journal_file_verify(
|
|
entry_realtime = le64toh(o->entry.realtime);
|
|
entry_realtime_set = true;
|
|
|
|
+ max_entry_realtime = MAX(max_entry_realtime, le64toh(o->entry.realtime));
|
|
+ min_entry_realtime = MIN(min_entry_realtime, le64toh(o->entry.realtime));
|
|
+
|
|
n_entries++;
|
|
break;
|
|
|
|
@@ -1136,12 +1140,13 @@ int journal_file_verify(
|
|
|
|
#if HAVE_GCRYPT
|
|
if (JOURNAL_HEADER_SEALED(f->header)) {
|
|
- uint64_t q, rt;
|
|
+ uint64_t q, rt, rt_end;
|
|
|
|
debug(p, "Checking tag %"PRIu64"...", le64toh(o->tag.seqnum));
|
|
|
|
rt = f->fss_start_usec + le64toh(o->tag.epoch) * f->fss_interval_usec;
|
|
- if (entry_realtime_set && entry_realtime >= rt + f->fss_interval_usec) {
|
|
+ rt_end = usec_add(rt, f->fss_interval_usec);
|
|
+ if (entry_realtime_set && entry_realtime >= rt_end) {
|
|
error(p,
|
|
"tag/entry realtime timestamp out of synchronization (%"PRIu64" >= %"PRIu64")",
|
|
entry_realtime,
|
|
@@ -1149,6 +1154,23 @@ int journal_file_verify(
|
|
r = -EBADMSG;
|
|
goto fail;
|
|
}
|
|
+ if (max_entry_realtime >= rt_end) {
|
|
+ error(p,
|
|
+ "Entry realtime (%"PRIu64", %s) is too late with respect to tag (%"PRIu64", %s)",
|
|
+ max_entry_realtime, FORMAT_TIMESTAMP(max_entry_realtime),
|
|
+ rt_end, FORMAT_TIMESTAMP(rt_end));
|
|
+ r = -EBADMSG;
|
|
+ goto fail;
|
|
+ }
|
|
+ if (min_entry_realtime < rt) {
|
|
+ error(p,
|
|
+ "Entry realtime (%"PRIu64", %s) is too early with respect to tag (%"PRIu64", %s)",
|
|
+ min_entry_realtime, FORMAT_TIMESTAMP(min_entry_realtime),
|
|
+ rt, FORMAT_TIMESTAMP(rt));
|
|
+ r = -EBADMSG;
|
|
+ goto fail;
|
|
+ }
|
|
+ min_entry_realtime = USEC_INFINITY;
|
|
|
|
/* OK, now we know the epoch. So let's now set
|
|
* it, and calculate the HMAC for everything
|