From: Yu Watanabe Date: Thu, 25 Jun 2026 16:26:21 +0000 (+0900) Subject: sd-journal: introduce JournalAuthContext X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=917763586fa62a676f95c64cab1a7ac7487bebd9;p=thirdparty%2Fsystemd.git sd-journal: introduce JournalAuthContext Then, move several components for journal tagging in JournalFile to JournalAuthContext. This also introduces wrapper functions that checks gcrypt support. --- diff --git a/src/journal/journald-manager.c b/src/journal/journald-manager.c index 764e5091ab3..68f95ea9678 100644 --- a/src/journal/journald-manager.c +++ b/src/journal/journald-manager.c @@ -697,7 +697,10 @@ static int manager_archive_offline_user_journals(Manager *m) { TAKE_FD(fd); /* Donated to journal_file_open() */ - journal_file_write_final_tag(f); + r = journal_file_auth_append_tag(f); + if (r < 0) + log_debug_errno(r, "Failed to append tag when closing journal, ignoring: %m"); + r = journal_file_archive(f, NULL); if (r < 0) log_debug_errno(r, "Failed to archive journal file '%s', ignoring: %m", full); @@ -2539,18 +2542,16 @@ int manager_init(Manager *m) { } void manager_maybe_append_tags(Manager *m) { -#if HAVE_GCRYPT - JournalFile *f; - usec_t n; + assert(m); - n = now(CLOCK_REALTIME); + usec_t n = now(CLOCK_REALTIME); if (m->system_journal) - journal_file_maybe_append_tag(m->system_journal, n); + journal_file_auth_append_tag_maybe(m->system_journal, n); + JournalFile *f; ORDERED_HASHMAP_FOREACH(f, m->user_journals) - journal_file_maybe_append_tag(f, n); -#endif + journal_file_auth_append_tag_maybe(f, n); } Manager* manager_free(Manager *m) { diff --git a/src/journal/journald.c b/src/journal/journald.c index 9ab5fdfdedb..f72ba2cc907 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -110,14 +110,12 @@ static int run(int argc, char *argv[]) { } else t = USEC_INFINITY; -#if HAVE_GCRYPT if (m->system_journal) { usec_t u; - if (journal_file_next_evolve_usec(m->system_journal, &u) >= 0) + if (journal_file_auth_next_evolve_usec(m->system_journal, &u) >= 0) t = MIN(t, usec_sub_unsigned(u, n)); } -#endif r = sd_event_run(m->event, t); if (r < 0) diff --git a/src/libsystemd/meson.build b/src/libsystemd/meson.build index 24979e8882e..e27f3ce3764 100644 --- a/src/libsystemd/meson.build +++ b/src/libsystemd/meson.build @@ -5,6 +5,7 @@ sd_journal_sources = files( 'sd-journal/catalog.c', 'sd-journal/fsprg.c', 'sd-journal/journal-authenticate.c', + 'sd-journal/journal-authenticate-internal.c', 'sd-journal/journal-file.c', 'sd-journal/journal-send.c', 'sd-journal/journal-vacuum.c', diff --git a/src/libsystemd/sd-journal/journal-authenticate-internal.c b/src/libsystemd/sd-journal/journal-authenticate-internal.c new file mode 100644 index 00000000000..2210bd1b3f3 --- /dev/null +++ b/src/libsystemd/sd-journal/journal-authenticate-internal.c @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "journal-authenticate.h" +#include "journal-authenticate-internal.h" +#include "journal-file.h" + +void journal_file_auth_done(JournalFile *f) { + assert(f); + +#if HAVE_GCRYPT + f->auth_context = journal_auth_free(f->auth_context); +#endif +} + +int journal_file_auth_load(JournalFile *f) { + assert(f); + +#if HAVE_GCRYPT + return journal_auth_load(&f->auth_context); +#else + return -EOPNOTSUPP; +#endif +} + +int journal_file_auth_load_key(JournalFile *f, const char *key) { + assert(f); + +#if HAVE_GCRYPT + return journal_auth_load_key(&f->auth_context, key); +#else + return -EOPNOTSUPP; +#endif +} + +int journal_file_auth_epoch_to_realtime_usec(JournalFile *f, uint64_t epoch, usec_t *ret_start, usec_t *ret_end) { + assert(f); + + if (!JOURNAL_HEADER_SEALED(f->header)) + return -EOPNOTSUPP; + +#if HAVE_GCRYPT + return journal_auth_epoch_to_realtime_usec(f->auth_context, epoch, ret_start, ret_end); +#else + assert_not_reached(); +#endif +} + +int journal_file_auth_next_evolve_usec(JournalFile *f, usec_t *ret) { + assert(f); + + if (!JOURNAL_HEADER_SEALED(f->header)) + return -EOPNOTSUPP; + +#if HAVE_GCRYPT + return journal_auth_next_evolve_usec(f->auth_context, ret); +#else + assert_not_reached(); +#endif +} + +int journal_file_auth_seek(JournalFile *f, uint64_t goal) { + assert(f); + + if (!JOURNAL_HEADER_SEALED(f->header)) + return 0; + +#if HAVE_GCRYPT + return journal_auth_seek(f->auth_context, goal); +#else + assert_not_reached(); +#endif +} + +int journal_file_auth_start(JournalFile *f) { + assert(f); + + if (!JOURNAL_HEADER_SEALED(f->header)) + return 0; + +#if HAVE_GCRYPT + return journal_auth_start(f->auth_context); +#else + assert_not_reached(); +#endif +} + +int journal_file_auth_end(JournalFile *f, uint8_t ret[static TAG_LENGTH]) { + assert(f); + + if (!JOURNAL_HEADER_SEALED(f->header)) + return -EOPNOTSUPP; + +#if HAVE_GCRYPT + return journal_auth_end(f->auth_context, ret); +#else + assert_not_reached(); +#endif +} + +int journal_file_auth_put_header(JournalFile *f) { + assert(f); + + if (!JOURNAL_HEADER_SEALED(f->header)) + return 0; + +#if HAVE_GCRYPT + return journal_auth_put_header(f->auth_context, f); +#else + assert_not_reached(); +#endif +} + +int journal_file_auth_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) { + assert(f); + + if (!JOURNAL_HEADER_SEALED(f->header)) + return 0; + +#if HAVE_GCRYPT + return journal_auth_put_object(f->auth_context, f, type, o, p); +#else + assert_not_reached(); +#endif +} + +int journal_file_auth_append_tag(JournalFile *f) { + assert(f); + + if (!JOURNAL_HEADER_SEALED(f->header)) + return 0; + + if (!journal_file_writable(f)) + return 0; + +#if HAVE_GCRYPT + return journal_auth_append_tag(f->auth_context, f); +#else + assert_not_reached(); +#endif +} + +int journal_file_auth_append_tag_first(JournalFile *f) { + assert(f); + + if (!JOURNAL_HEADER_SEALED(f->header)) + return 0; + + if (!journal_file_writable(f)) + return 0; + +#if HAVE_GCRYPT + return journal_auth_append_tag_first(f->auth_context, f); +#else + assert_not_reached(); +#endif +} + +int journal_file_auth_append_tag_maybe(JournalFile *f, usec_t realtime) { + assert(f); + + if (!JOURNAL_HEADER_SEALED(f->header)) + return 0; + + if (!journal_file_writable(f)) + return 0; + +#if HAVE_GCRYPT + return journal_auth_append_tag_maybe(f->auth_context, f, realtime); +#else + assert_not_reached(); +#endif +} diff --git a/src/libsystemd/sd-journal/journal-authenticate-internal.h b/src/libsystemd/sd-journal/journal-authenticate-internal.h new file mode 100644 index 00000000000..ee7ea6779fc --- /dev/null +++ b/src/libsystemd/sd-journal/journal-authenticate-internal.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "journal-def.h" +#include "sd-forward.h" + +void journal_file_auth_done(JournalFile *f); +int journal_file_auth_load(JournalFile *f); +int journal_file_auth_load_key(JournalFile *f, const char *key); +int journal_file_auth_epoch_to_realtime_usec(JournalFile *f, uint64_t epoch, usec_t *ret_start, usec_t *ret_end); +int journal_file_auth_next_evolve_usec(JournalFile *f, usec_t *ret); +int journal_file_auth_seek(JournalFile *f, uint64_t goal); +int journal_file_auth_start(JournalFile *f); +int journal_file_auth_end(JournalFile *f, uint8_t ret[static TAG_LENGTH]); +int journal_file_auth_put_header(JournalFile *f); +int journal_file_auth_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p); +int journal_file_auth_append_tag(JournalFile *f); +int journal_file_auth_append_tag_first(JournalFile *f); +int journal_file_auth_append_tag_maybe(JournalFile *f, usec_t realtime); diff --git a/src/libsystemd/sd-journal/journal-authenticate.c b/src/libsystemd/sd-journal/journal-authenticate.c index f2c42ce0ffb..fff7f221ab9 100644 --- a/src/libsystemd/sd-journal/journal-authenticate.c +++ b/src/libsystemd/sd-journal/journal-authenticate.c @@ -18,6 +18,43 @@ #include "string-util.h" #include "time-util.h" +#if HAVE_GCRYPT + +struct JournalAuthContext { + gcry_md_hd_t hmac; + bool hmac_running; + + FSSHeader *fss_file; + size_t fss_file_size; + + uint64_t fss_start_usec; + uint64_t fss_interval_usec; + + struct iovec fsprg_state; + struct iovec fsprg_seed; +}; + +JournalAuthContext* journal_auth_free(JournalAuthContext *c) { + if (!c) + return NULL; + + if (c->fss_file) { + size_t sz = PAGE_ALIGN(c->fss_file_size); + assert(sz < SIZE_MAX); + munmap(c->fss_file, sz); + } else + iovec_done_erase(&c->fsprg_state); + + iovec_done_erase(&c->fsprg_seed); + + if (c->hmac) + sym_gcry_md_close(c->hmac); + + return mfree(c); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(JournalAuthContext*, journal_auth_free); + static void* fssheader_free(FSSHeader *p) { /* mmap() returns MAP_FAILED on error and sets the errno */ if (!p || p == MAP_FAILED) @@ -29,10 +66,10 @@ static void* fssheader_free(FSSHeader *p) { DEFINE_TRIVIAL_CLEANUP_FUNC(FSSHeader*, fssheader_free); -int journal_file_fss_load(JournalFile *f) { +int journal_auth_load(JournalAuthContext **ret) { int r; - assert(f); + assert(ret); /* This function is used to determine whether sealing should be enabled in the journal header so we * can't check the header to check if sealing is enabled here. */ @@ -95,6 +132,10 @@ int journal_file_fss_load(JournalFile *f) { if (le64toh(header->start_usec) <= 0 || le64toh(header->interval_usec) <= 0) return -EBADMSG; + _cleanup_(journal_auth_freep) JournalAuthContext *c = new0(JournalAuthContext, 1); + if (!c) + return -ENOMEM; + size_t sz = PAGE_ALIGN(fss_file_size); if (sz >= SIZE_MAX) return -EBADMSG; @@ -103,23 +144,26 @@ int journal_file_fss_load(JournalFile *f) { if (p == MAP_FAILED) return -errno; - f->fss_file_size = fss_file_size; - f->fss_file = p; + *c = (JournalAuthContext) { + .fss_file_size = fss_file_size, + .fss_file = p, - f->fss_start_usec = le64toh(p->start_usec); - f->fss_interval_usec = le64toh(p->interval_usec); + .fss_start_usec = le64toh(p->start_usec), + .fss_interval_usec = le64toh(p->interval_usec), - f->fsprg_state = IOVEC_MAKE( - (uint8_t*) p + le64toh(p->header_size), - le64toh(p->fsprg_state_size)); + .fsprg_state = IOVEC_MAKE( + (uint8_t*) p + le64toh(p->header_size), + le64toh(p->fsprg_state_size)), + }; + *ret = TAKE_PTR(c); return 0; } -int journal_file_parse_verification_key(JournalFile *f, const char *key) { +int journal_auth_load_key(JournalAuthContext **ret, const char *key) { int r; - assert(f); + assert(ret); assert(key); size_t seed_size = FSPRG_RECOMMENDED_SEEDLEN; @@ -162,22 +206,30 @@ int journal_file_parse_verification_key(JournalFile *f, const char *key) { if (!MUL_SAFE(&start_usec, start, interval)) return -EKEYREJECTED; - f->fsprg_seed = IOVEC_MAKE(TAKE_PTR(seed), seed_size); - f->fss_start_usec = start_usec; - f->fss_interval_usec = interval; + _cleanup_(journal_auth_freep) JournalAuthContext *c = new(JournalAuthContext, 1); + if (!c) + return -ENOMEM; + *c = (JournalAuthContext) { + .fss_start_usec = start_usec, + .fss_interval_usec = interval, + + .fsprg_seed = IOVEC_MAKE(TAKE_PTR(seed), seed_size), + }; + + *ret = TAKE_PTR(c); return 0; } -static int journal_auth_epoch_to_realtime_usec(JournalFile *f, uint64_t epoch, usec_t *ret_start, usec_t *ret_end) { - assert(f); - assert(f->fss_start_usec > 0); - assert(f->fss_interval_usec > 0); +int journal_auth_epoch_to_realtime_usec(const JournalAuthContext *c, uint64_t epoch, usec_t *ret_start, usec_t *ret_end) { + assert(c); + assert(c->fss_start_usec > 0); + assert(c->fss_interval_usec > 0); uint64_t start, end; - if (!MUL_SAFE(&start, epoch, f->fss_interval_usec) || - !INC_SAFE(&start, f->fss_start_usec) || - !ADD_SAFE(&end, start, f->fss_interval_usec)) + if (!MUL_SAFE(&start, epoch, c->fss_interval_usec) || + !INC_SAFE(&start, c->fss_start_usec) || + !ADD_SAFE(&end, start, c->fss_interval_usec)) return -ERANGE; if (ret_start) @@ -188,38 +240,29 @@ static int journal_auth_epoch_to_realtime_usec(JournalFile *f, uint64_t epoch, u return 0; } -int journal_file_next_evolve_usec(JournalFile *f, usec_t *ret) { - assert(f); - - if (!JOURNAL_HEADER_SEALED(f->header)) - return -EOPNOTSUPP; +int journal_auth_next_evolve_usec(const JournalAuthContext *c, usec_t *ret) { + assert(c); - uint64_t epoch = FSPRG_GetEpoch(f->fsprg_state.iov_base); + uint64_t epoch = FSPRG_GetEpoch(c->fsprg_state.iov_base); - return journal_auth_epoch_to_realtime_usec(f, epoch, /* ret_start= */ NULL, ret); + return journal_auth_epoch_to_realtime_usec(c, epoch, /* ret_start= */ NULL, ret); } -int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) { +int journal_auth_seek(JournalAuthContext *c, uint64_t goal) { int r; - assert(f); - - if (!JOURNAL_HEADER_SEALED(f->header)) - return 0; - - assert(iovec_is_set(&f->fsprg_seed)); + assert(c); + assert(iovec_is_set(&c->fsprg_seed)); - if (iovec_is_set(&f->fsprg_state)) { - /* Cheaper... */ - - uint64_t epoch = FSPRG_GetEpoch(f->fsprg_state.iov_base); + if (iovec_is_set(&c->fsprg_state)) { + uint64_t epoch = FSPRG_GetEpoch(c->fsprg_state.iov_base); if (goal == epoch) return 0; if (goal == epoch + 1) - return FSPRG_Evolve(f->fsprg_state.iov_base); + return FSPRG_Evolve(c->fsprg_state.iov_base); } else { - r = iovec_alloc(FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR), &f->fsprg_state); + r = iovec_alloc(FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR), &c->fsprg_state); if (r < 0) return r; } @@ -227,82 +270,81 @@ int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) { log_debug("Seeking FSPRG key to %"PRIu64".", goal); _cleanup_(iovec_erase) struct iovec msk = IOVEC_ALLOCA(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR)); - r = FSPRG_GenMK(msk.iov_base, NULL, f->fsprg_seed.iov_base, f->fsprg_seed.iov_len, FSPRG_RECOMMENDED_SECPAR); + r = FSPRG_GenMK(msk.iov_base, NULL, c->fsprg_seed.iov_base, c->fsprg_seed.iov_len, FSPRG_RECOMMENDED_SECPAR); if (r < 0) return r; - return FSPRG_Seek(f->fsprg_state.iov_base, goal, msk.iov_base, f->fsprg_seed.iov_base, f->fsprg_seed.iov_len); + return FSPRG_Seek(c->fsprg_state.iov_base, goal, msk.iov_base, c->fsprg_seed.iov_base, c->fsprg_seed.iov_len); } -#if HAVE_GCRYPT -static int journal_file_hmac_setup(JournalFile *f) { +static int journal_auth_setup(JournalAuthContext *c) { int r; - assert(f); + assert(c); - if (f->hmac) + if (c->hmac) return 0; r = initialize_libgcrypt(true); if (r < 0) return r; - if (sym_gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC) != 0) + if (sym_gcry_md_open(&c->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC) != 0) return -EOPNOTSUPP; return 0; } -#endif -int journal_file_hmac_start(JournalFile *f) { -#if HAVE_GCRYPT +int journal_auth_start(JournalAuthContext *c) { int r; - assert(f); - - if (!JOURNAL_HEADER_SEALED(f->header)) - return 0; + assert(c); - if (f->hmac_running) + if (c->hmac_running) return 0; - r = journal_file_hmac_setup(f); + r = journal_auth_setup(c); if (r < 0) return r; /* Prepare HMAC for next cycle */ - sym_gcry_md_reset(f->hmac); + sym_gcry_md_reset(c->hmac); uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */ CLEANUP_ERASE(key); - r = FSPRG_GetKey(f->fsprg_state.iov_base, key, sizeof(key), 0); + r = FSPRG_GetKey(c->fsprg_state.iov_base, key, sizeof(key), 0); if (r < 0) return r; - gcry_error_t err = sym_gcry_md_setkey(f->hmac, key, sizeof(key)); + gcry_error_t err = sym_gcry_md_setkey(c->hmac, key, sizeof(key)); if (gcry_err_code(err) != GPG_ERR_NO_ERROR) return log_debug_errno(SYNTHETIC_ERRNO(EIO), "sym_gcry_md_setkey() failed with error code: %s", sym_gcry_strerror(err)); - f->hmac_running = true; + c->hmac_running = true; + return 0; +} + +int journal_auth_end(JournalAuthContext *c, uint8_t ret[static TAG_LENGTH]) { + assert(c); + assert(ret); + + if (!c->hmac_running) + return -EINVAL; + memcpy(ret, sym_gcry_md_read(c->hmac, 0), TAG_LENGTH); + c->hmac_running = false; return 0; -#else - return -EOPNOTSUPP; -#endif } -int journal_file_hmac_put_header(JournalFile *f) { -#if HAVE_GCRYPT +int journal_auth_put_header(JournalAuthContext *c, JournalFile *f) { int r; + assert(c); assert(f); - if (!JOURNAL_HEADER_SEALED(f->header)) - return 0; - - r = journal_file_hmac_start(f); + r = journal_auth_start(c); if (r < 0) return r; @@ -313,27 +355,21 @@ int journal_file_hmac_put_header(JournalFile *f) { * tail_entry_monotonic, n_data, n_fields, n_tags, * n_entry_arrays. */ - sym_gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature)); - sym_gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, tail_entry_boot_id) - offsetof(Header, file_id)); - sym_gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id)); - sym_gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset)); + sym_gcry_md_write(c->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature)); + sym_gcry_md_write(c->hmac, &f->header->file_id, offsetof(Header, tail_entry_boot_id) - offsetof(Header, file_id)); + sym_gcry_md_write(c->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id)); + sym_gcry_md_write(c->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset)); return 0; -#else - return -EOPNOTSUPP; -#endif } -int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) { -#if HAVE_GCRYPT +int journal_auth_put_object(JournalAuthContext *c, JournalFile *f, ObjectType type, Object *o, uint64_t p) { int r; + assert(c); assert(f); - if (!JOURNAL_HEADER_SEALED(f->header)) - return 0; - - r = journal_file_hmac_start(f); + r = journal_auth_start(c); if (r < 0) return r; @@ -344,25 +380,25 @@ int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uin } else if (type > OBJECT_UNUSED && o->object.type != type) return -EBADMSG; - sym_gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload)); + sym_gcry_md_write(c->hmac, o, offsetof(ObjectHeader, payload)); switch (o->object.type) { case OBJECT_DATA: /* All but hash and payload are mutable */ - sym_gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash)); - sym_gcry_md_write(f->hmac, journal_file_data_payload_field(f, o), le64toh(o->object.size) - journal_file_data_payload_offset(f)); + sym_gcry_md_write(c->hmac, &o->data.hash, sizeof(o->data.hash)); + sym_gcry_md_write(c->hmac, journal_file_data_payload_field(f, o), le64toh(o->object.size) - journal_file_data_payload_offset(f)); break; case OBJECT_FIELD: /* Same here */ - sym_gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash)); - sym_gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload)); + sym_gcry_md_write(c->hmac, &o->field.hash, sizeof(o->field.hash)); + sym_gcry_md_write(c->hmac, o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload)); break; case OBJECT_ENTRY: /* All */ - sym_gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(Object, entry.seqnum)); + sym_gcry_md_write(c->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(Object, entry.seqnum)); break; case OBJECT_FIELD_HASH_TABLE: @@ -373,29 +409,23 @@ int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uin case OBJECT_TAG: /* All but the tag itself */ - sym_gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum)); - sym_gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch)); + sym_gcry_md_write(c->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum)); + sym_gcry_md_write(c->hmac, &o->tag.epoch, sizeof(o->tag.epoch)); break; default: return -EINVAL; } return 0; -#else - return -EOPNOTSUPP; -#endif } -int journal_file_append_tag(JournalFile *f) { -#if HAVE_GCRYPT +int journal_auth_append_tag(JournalAuthContext *c, JournalFile *f) { int r; + assert(c); assert(f); - if (!JOURNAL_HEADER_SEALED(f->header)) - return 0; - - r = journal_file_hmac_start(f); + r = journal_auth_start(c); if (r < 0) return r; @@ -409,39 +439,31 @@ int journal_file_append_tag(JournalFile *f) { f->header->n_tags = htole64(seqnum); o->tag.seqnum = htole64(seqnum); - o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state.iov_base)); + o->tag.epoch = htole64(FSPRG_GetEpoch(c->fsprg_state.iov_base)); log_debug("Writing tag %"PRIu64" for epoch %"PRIu64"", le64toh(o->tag.seqnum), le64toh(o->tag.epoch)); /* Add the tag object itself, so that we can protect its * header. This will exclude the actual hash value in it */ - r = journal_file_hmac_put_object(f, OBJECT_TAG, o, p); + r = journal_auth_put_object(c, f, OBJECT_TAG, o, p); if (r < 0) return r; /* Get the HMAC tag and store it in the object */ - memcpy(o->tag.tag, sym_gcry_md_read(f->hmac, 0), TAG_LENGTH); - f->hmac_running = false; - - return 0; -#else - return -EOPNOTSUPP; -#endif + return journal_auth_end(c, o->tag.tag); } -int journal_file_append_first_tag(JournalFile *f) { +int journal_auth_append_tag_first(JournalAuthContext *c, JournalFile *f) { uint64_t p; int r; + assert(c); assert(f); - if (!JOURNAL_HEADER_SEALED(f->header)) - return 0; - log_debug("Calculating first tag..."); - r = journal_file_hmac_put_header(f); + r = journal_auth_put_header(c, f); if (r < 0) return r; @@ -450,7 +472,7 @@ int journal_file_append_first_tag(JournalFile *f) { return -EINVAL; p -= offsetof(Object, hash_table.items); - r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, NULL, p); + r = journal_auth_put_object(c, f, OBJECT_FIELD_HASH_TABLE, NULL, p); if (r < 0) return r; @@ -459,40 +481,39 @@ int journal_file_append_first_tag(JournalFile *f) { return -EINVAL; p -= offsetof(Object, hash_table.items); - r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, NULL, p); + r = journal_auth_put_object(c, f, OBJECT_DATA_HASH_TABLE, NULL, p); if (r < 0) return r; - return journal_file_append_tag(f); + return journal_auth_append_tag(c, f); } -int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) { +int journal_auth_append_tag_maybe(JournalAuthContext *c, JournalFile *f, usec_t realtime) { int r; + assert(c); + assert(c->fss_start_usec > 0); + assert(c->fss_interval_usec > 0); assert(f); - if (!JOURNAL_HEADER_SEALED(f->header)) - return 0; - - assert(f->fss_start_usec > 0); - assert(f->fss_interval_usec > 0); - if (realtime <= 0) realtime = now(CLOCK_REALTIME); - uint64_t goal = usec_sub_unsigned(realtime, f->fss_start_usec) / f->fss_interval_usec; + uint64_t goal = usec_sub_unsigned(realtime, c->fss_start_usec) / c->fss_interval_usec; for (;;) { - uint64_t epoch = FSPRG_GetEpoch(f->fsprg_state.iov_base); + uint64_t epoch = FSPRG_GetEpoch(c->fsprg_state.iov_base); if (epoch >= goal) return 0; - r = journal_file_append_tag(f); + r = journal_auth_append_tag(c, f); if (r < 0) return r; - r = FSPRG_Evolve(f->fsprg_state.iov_base); + r = FSPRG_Evolve(c->fsprg_state.iov_base); if (r < 0) return r; } } + +#endif /* HAVE_GCRYPT */ diff --git a/src/libsystemd/sd-journal/journal-authenticate.h b/src/libsystemd/sd-journal/journal-authenticate.h index f4164b32ad6..c50c83a7a2d 100644 --- a/src/libsystemd/sd-journal/journal-authenticate.h +++ b/src/libsystemd/sd-journal/journal-authenticate.h @@ -1,20 +1,24 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include "journal-authenticate-internal.h" /* IWYU pragma: export */ +#include "journal-def.h" #include "sd-forward.h" -#include "journal-file.h" -int journal_file_append_tag(JournalFile *f); -int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime); -int journal_file_append_first_tag(JournalFile *f); +#if HAVE_GCRYPT -int journal_file_hmac_start(JournalFile *f); -int journal_file_hmac_put_header(JournalFile *f); -int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p); +JournalAuthContext* journal_auth_free(JournalAuthContext *c); +int journal_auth_load(JournalAuthContext **ret); +int journal_auth_load_key(JournalAuthContext **ret, const char *key); +int journal_auth_epoch_to_realtime_usec(const JournalAuthContext *c, uint64_t epoch, usec_t *ret_start, usec_t *ret_end); +int journal_auth_next_evolve_usec(const JournalAuthContext *c, usec_t *ret); +int journal_auth_seek(JournalAuthContext *c, uint64_t goal); +int journal_auth_start(JournalAuthContext *c); +int journal_auth_end(JournalAuthContext *c, uint8_t ret[static TAG_LENGTH]); +int journal_auth_put_header(JournalAuthContext *c, JournalFile *f); +int journal_auth_put_object(JournalAuthContext *c, JournalFile *f, ObjectType type, Object *o, uint64_t p); +int journal_auth_append_tag(JournalAuthContext *c, JournalFile *f); +int journal_auth_append_tag_first(JournalAuthContext *c, JournalFile *f); +int journal_auth_append_tag_maybe(JournalAuthContext *c, JournalFile *f, usec_t realtime); -int journal_file_fss_load(JournalFile *f); -int journal_file_parse_verification_key(JournalFile *f, const char *key); - -int journal_file_fsprg_seek(JournalFile *f, uint64_t goal); - -int journal_file_next_evolve_usec(JournalFile *f, usec_t *ret); +#endif diff --git a/src/libsystemd/sd-journal/journal-def.h b/src/libsystemd/sd-journal/journal-def.h index 026aa4c9414..9dd3d25dc45 100644 --- a/src/libsystemd/sd-journal/journal-def.h +++ b/src/libsystemd/sd-journal/journal-def.h @@ -43,6 +43,9 @@ typedef struct HashItem HashItem; typedef struct FSSHeader FSSHeader; +typedef struct JournalFile JournalFile; +typedef struct JournalAuthContext JournalAuthContext; + /* Object types */ typedef enum ObjectType { OBJECT_UNUSED, /* also serves as "any type" or "additional category" */ diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c index 4dfcb9343f2..2a4e71b7764 100644 --- a/src/libsystemd/sd-journal/journal-file.c +++ b/src/libsystemd/sd-journal/journal-file.c @@ -19,11 +19,9 @@ #include "fd-util.h" #include "format-util.h" #include "fs-util.h" -#include "gcrypt-util.h" #include "hashmap.h" #include "id128-util.h" -#include "iovec-util.h" -#include "journal-authenticate.h" +#include "journal-authenticate-internal.h" #include "journal-def.h" #include "journal-file.h" #include "journal-internal.h" @@ -308,17 +306,7 @@ JournalFile* journal_file_close(JournalFile *f) { free(f->compress_buffer); #endif - if (f->fss_file) { - size_t sz = PAGE_ALIGN(f->fss_file_size); - assert(sz < SIZE_MAX); - munmap(f->fss_file, sz); - } else - iovec_done_erase(&f->fsprg_state); - - iovec_done_erase(&f->fsprg_seed); - - if (f->hmac) - sym_gcry_md_close(f->hmac); + journal_file_auth_done(f); return mfree(f); } @@ -404,16 +392,13 @@ static int journal_file_init_header( JournalFileFlags file_flags, JournalFile *template) { - bool seal = false; ssize_t k; int r; assert(f); -#if HAVE_GCRYPT /* Try to load the FSPRG state, and if we can't, then just don't do sealing */ - seal = FLAGS_SET(file_flags, JOURNAL_SEAL) && journal_file_fss_load(f) >= 0; -#endif + bool seal = FLAGS_SET(file_flags, JOURNAL_SEAL) && journal_file_auth_load(f) >= 0; Header h = { .header_size = htole64(ALIGN64(sizeof(h))), @@ -1833,11 +1818,9 @@ static int journal_file_append_field( /* The linking might have altered the window, so let's only pass the offset to hmac which will * move to the object again if needed. */ -#if HAVE_GCRYPT - r = journal_file_hmac_put_object(f, OBJECT_FIELD, NULL, p); + r = journal_file_auth_put_object(f, OBJECT_FIELD, NULL, p); if (r < 0) return r; -#endif if (ret_object) { r = journal_file_move_to_object(f, OBJECT_FIELD, p, ret_object); @@ -1948,11 +1931,9 @@ static int journal_file_append_data( if (r < 0) return r; -#if HAVE_GCRYPT - r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p); + r = journal_file_auth_put_object(f, OBJECT_DATA, o, p); if (r < 0) return r; -#endif /* Create field object ... */ r = journal_file_append_field(f, data, (uint8_t*) eq - (uint8_t*) data, &fo, NULL); @@ -2198,11 +2179,9 @@ static int link_entry_into_array( if (r < 0) return r; -#if HAVE_GCRYPT - r = journal_file_hmac_put_object(f, OBJECT_ENTRY_ARRAY, o, q); + r = journal_file_auth_put_object(f, OBJECT_ENTRY_ARRAY, o, q); if (r < 0) return r; -#endif write_entry_array_item(f, o, i, p); @@ -2444,11 +2423,9 @@ static int journal_file_append_entry_internal( for (size_t i = 0; i < n_items; i++) write_entry_item(f, o, i, &items[i]); -#if HAVE_GCRYPT - r = journal_file_hmac_put_object(f, OBJECT_ENTRY, o, np); + r = journal_file_auth_put_object(f, OBJECT_ENTRY, o, np); if (r < 0) return r; -#endif r = journal_file_link_entry(f, o, np, items, n_items); if (r < 0) @@ -2633,11 +2610,9 @@ int journal_file_append_entry( else machine_id = &_machine_id; -#if HAVE_GCRYPT - r = journal_file_maybe_append_tag(f, ts->realtime); + r = journal_file_auth_append_tag_maybe(f, ts->realtime); if (r < 0) return r; -#endif if (n_iovec < ALLOCA_MAX / sizeof(EntryItem) / 2) items = newa(EntryItem, n_iovec); @@ -4284,13 +4259,11 @@ int journal_file_open( goto fail; } -#if HAVE_GCRYPT if (!newly_created && journal_file_writable(f) && JOURNAL_HEADER_SEALED(f->header)) { - r = journal_file_fss_load(f); + r = journal_file_auth_load(f); if (r < 0) goto fail; } -#endif if (journal_file_writable(f)) { if (metrics) { @@ -4313,11 +4286,9 @@ int journal_file_open( if (r < 0) goto fail; -#if HAVE_GCRYPT - r = journal_file_append_first_tag(f); + r = journal_file_auth_append_tag_first(f); if (r < 0) goto fail; -#endif } if (mmap_cache_fd_got_sigbus(f->cache_fd)) { diff --git a/src/libsystemd/sd-journal/journal-file.h b/src/libsystemd/sd-journal/journal-file.h index fea181cafdc..8499fa2b662 100644 --- a/src/libsystemd/sd-journal/journal-file.h +++ b/src/libsystemd/sd-journal/journal-file.h @@ -6,7 +6,6 @@ #include "compress.h" #include "sd-forward.h" -#include "gcrypt-util.h" #include "journal-def.h" #include "mmap-cache.h" #include "sparse-endian.h" @@ -98,17 +97,7 @@ typedef struct JournalFile { void *compress_buffer; #endif - gcry_md_hd_t hmac; - bool hmac_running; - - FSSHeader *fss_file; - size_t fss_file_size; - - uint64_t fss_start_usec; - uint64_t fss_interval_usec; - - struct iovec fsprg_state; - struct iovec fsprg_seed; + JournalAuthContext *auth_context; /* When we insert this file into the per-boot priority queue 'newest_by_boot_id' in sd_journal, then by these keys */ sd_id128_t newest_boot_id; diff --git a/src/libsystemd/sd-journal/journal-verify.c b/src/libsystemd/sd-journal/journal-verify.c index 02420840dfc..b8cc3555f6b 100644 --- a/src/libsystemd/sd-journal/journal-verify.c +++ b/src/libsystemd/sd-journal/journal-verify.c @@ -9,8 +9,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" -#include "gcrypt-util.h" -#include "journal-authenticate.h" +#include "journal-authenticate-internal.h" #include "journal-def.h" #include "journal-file.h" #include "journal-verify.h" @@ -821,7 +820,7 @@ int journal_file_verify( int r; Object *o; - uint64_t p = 0, last_epoch = 0, last_tag_realtime = 0, last_tag_realtime_end = 0; + uint64_t p = 0, last_tag = 0, last_epoch = 0, last_tag_realtime = 0, last_tag_realtime_end = 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 */ @@ -836,19 +835,12 @@ int journal_file_verify( const char *tmp_dir = NULL; MMapCache *m; -#if HAVE_GCRYPT - uint64_t last_tag = 0; -#endif assert(f); if (key) { -#if HAVE_GCRYPT - r = journal_file_parse_verification_key(f, key); + r = journal_file_auth_load_key(f, key); if (r < 0) return log_error_errno(r, "Failed to load verification key: %m"); -#else - return -EOPNOTSUPP; -#endif } else if (JOURNAL_HEADER_SEALED(f->header)) return -ENOKEY; @@ -1155,14 +1147,15 @@ int journal_file_verify( } } -#if HAVE_GCRYPT if (JOURNAL_HEADER_SEALED(f->header)) { 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; - rt_end = usec_add(rt, f->fss_interval_usec); + r = journal_file_auth_epoch_to_realtime_usec(f, le64toh(o->tag.epoch), &rt, &rt_end); + if (r < 0) + goto fail; + if (entry_realtime_set && entry_realtime >= rt_end) { error(p, "tag/entry realtime timestamp out of synchronization (%"PRIu64" >= %"PRIu64")", @@ -1192,16 +1185,16 @@ int journal_file_verify( /* OK, now we know the epoch. So let's now set * it, and calculate the HMAC for everything * since the last tag. */ - r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch)); + r = journal_file_auth_seek(f, le64toh(o->tag.epoch)); if (r < 0) goto fail; - r = journal_file_hmac_start(f); + r = journal_file_auth_start(f); if (r < 0) goto fail; if (last_tag == 0) { - r = journal_file_hmac_put_header(f); + r = journal_file_auth_put_header(f); if (r < 0) goto fail; @@ -1214,7 +1207,7 @@ int journal_file_verify( if (r < 0) goto fail; - r = journal_file_hmac_put_object(f, OBJECT_UNUSED, o, q); + r = journal_file_auth_put_object(f, OBJECT_UNUSED, o, q); if (r < 0) goto fail; @@ -1226,20 +1219,24 @@ int journal_file_verify( if (r < 0) goto fail; - if (memcmp(o->tag.tag, sym_gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) { + uint8_t tag[TAG_LENGTH]; + CLEANUP_ERASE(tag); + + r = journal_file_auth_end(f, tag); + if (r < 0) + goto fail; + + if (memcmp(o->tag.tag, tag, TAG_LENGTH) != 0) { error(p, "Tag failed verification"); r = -EBADMSG; goto fail; } - f->hmac_running = false; last_tag_realtime = rt; last_tag_realtime_end = rt_end; } last_tag = p + ALIGN64(le64toh(o->object.size)); -#endif - last_epoch = le64toh(o->tag.epoch); n_tags++; diff --git a/src/libsystemd/sd-journal/test-journal.c b/src/libsystemd/sd-journal/test-journal.c index bcd7293dba0..d638192dc39 100644 --- a/src/libsystemd/sd-journal/test-journal.c +++ b/src/libsystemd/sd-journal/test-journal.c @@ -55,9 +55,8 @@ static void test_non_empty_one(void) { iovec = IOVEC_MAKE_STRING(test); ASSERT_OK_ZERO(journal_file_append_entry(f, &ts, &fake_boot_id, &iovec, 1, NULL, NULL, NULL, NULL)); -#if HAVE_GCRYPT - journal_file_append_tag(f); -#endif + journal_file_auth_append_tag(f); + journal_file_dump(f); ASSERT_EQ(journal_file_next_entry(f, 0, DIRECTION_DOWN, &o, &p), 1); @@ -200,9 +199,8 @@ static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) { iovec = IOVEC_MAKE(data, data_size); ASSERT_OK_ZERO(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL, NULL)); -#if HAVE_GCRYPT - journal_file_append_tag(f); -#endif + journal_file_auth_append_tag(f); + journal_file_dump(f); /* We have to partially reimplement some of the dump logic, because the normal next_entry does the diff --git a/src/shared/journal-file-util.c b/src/shared/journal-file-util.c index 8aecda96105..1567defac5f 100644 --- a/src/shared/journal-file-util.c +++ b/src/shared/journal-file-util.c @@ -405,23 +405,15 @@ bool journal_file_is_offlining(JournalFile *f) { return true; } -void journal_file_write_final_tag(JournalFile *f) { - assert(f); -#if HAVE_GCRYPT - if (!JOURNAL_HEADER_SEALED(f->header) || !journal_file_writable(f)) - return; - - int r = journal_file_append_tag(f); - if (r < 0) - log_debug_errno(r, "Failed to append tag when closing journal: %m"); -#endif -} - JournalFile* journal_file_offline_close(JournalFile *f) { + int r; + if (!f) return NULL; - journal_file_write_final_tag(f); + r = journal_file_auth_append_tag(f); + if (r < 0) + log_debug_errno(r, "Failed to append tag when closing journal, ignoring: %m"); if (sd_event_source_get_enabled(f->post_change_timer, NULL) > 0) journal_file_post_change(f); @@ -464,7 +456,10 @@ int journal_file_rotate( assert(f); assert(*f); - journal_file_write_final_tag(*f); + r = journal_file_auth_append_tag(*f); + if (r < 0) + log_debug_errno(r, "Failed to append tag when closing journal, ignoring: %m"); + r = journal_file_archive(*f, &path); if (r < 0) return r; diff --git a/src/shared/journal-file-util.h b/src/shared/journal-file-util.h index d3ffe4e7a6d..a3a87812187 100644 --- a/src/shared/journal-file-util.h +++ b/src/shared/journal-file-util.h @@ -6,7 +6,6 @@ int journal_file_set_offline(JournalFile *f, bool wait); bool journal_file_is_offlining(JournalFile *f); -void journal_file_write_final_tag(JournalFile *f); JournalFile* journal_file_offline_close(JournalFile *f); DEFINE_TRIVIAL_CLEANUP_FUNC(JournalFile*, journal_file_offline_close);