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 | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/src/libsystemd/sd-journal/journal-verify.c b/src/libsystemd/sd-journal/journal-verify.c
index d5b4919..7a9df10 100644
--- a/src/libsystemd/sd-journal/journal-verify.c
+++ b/src/libsystemd/sd-journal/journal-verify.c
@@ -811,6 +811,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;
         bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
         uint64_t n_weird = 0, 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;
@@ -1023,6 +1024,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;
 
@@ -1099,16 +1103,28 @@ int journal_file_verify(
 
 #if HAVE_GCRYPT
                         if (f->seal) {
-                                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");
                                         r = -EBADMSG;
                                         goto fail;
                                 }
+                                if (max_entry_realtime >= rt_end) {
+                                        error(p, "Entry realtime is too late with respect to tag");
+                                        r = -EBADMSG;
+                                        goto fail;
+                                }
+                                if (min_entry_realtime < rt) {
+                                        error(p, "Entry realtime is too early with respect to tag");
+                                        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
-- 
2.33.0

