Skip to content

Commit

Permalink
journalctl: verify that old entries are not sealed with too recent key (
Browse files Browse the repository at this point in the history
systemd#28885)

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>
  • Loading branch information
felixdoerre and Felix Dörre committed Oct 6, 2023
1 parent 09ff0aa commit 3846d3a
Showing 1 changed file with 24 additions and 2 deletions.
26 changes: 24 additions & 2 deletions src/libsystemd/sd-journal/journal-verify.c
Expand Up @@ -819,6 +819,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;
Expand Down Expand Up @@ -1070,6 +1071,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;

Expand Down Expand Up @@ -1135,19 +1139,37 @@ 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,
rt + f->fss_interval_usec);
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
Expand Down

0 comments on commit 3846d3a

Please sign in to comment.