diff --git a/README b/README index a198637d13..9a551f76fa 100644 --- a/README +++ b/README @@ -230,7 +230,6 @@ REQUIREMENTS: libarchive >= 3.0 (optional) libxkbcommon >= 0.3.0 (optional) libpcre2 (optional) - libgcrypt (optional) libqrencode >= 3 (optional) libmicrohttpd >= 0.9.33 (optional) libcurl >= 7.32.0 (optional) diff --git a/man/sd-journal.xml b/man/sd-journal.xml index da4e4e1f82..bcde6779b5 100644 --- a/man/sd-journal.xml +++ b/man/sd-journal.xml @@ -94,7 +94,7 @@ sd_journal objects might cause optional shared libraries to be dynamically loaded via dlopen3, - such as decompression libraries (xz, lz4, zstd) or cryptographic libraries (gcrypt). + such as decompression libraries (xz, lz4, zstd). diff --git a/meson.build b/meson.build index f953eeab13..93df01181b 100644 --- a/meson.build +++ b/meson.build @@ -1206,8 +1206,9 @@ libqrencode = dependency('libqrencode', libqrencode_cflags = libqrencode.partial_dependency(includes: true, compile_args: true) conf.set10('HAVE_QRENCODE', libqrencode.found()) +feature = get_option('gcrypt') libgcrypt = dependency('libgcrypt', - required : get_option('gcrypt')) + required : feature.disabled() ? feature : false) conf.set10('HAVE_GCRYPT', libgcrypt.found()) if libgcrypt.found() libgcrypt_cflags = libgcrypt.partial_dependency(includes: true, compile_args: true) @@ -1813,8 +1814,7 @@ if static_libsystemd != 'false' install_tag: 'libsystemd', install_dir : libdir, pic : static_libsystemd_pic, - dependencies : [libgcrypt_cflags, - liblz4_cflags, + dependencies : [liblz4_cflags, libm, libucontext, libxz_cflags, diff --git a/src/basic/build.c b/src/basic/build.c index 213ccca5f4..965d4f5a36 100644 --- a/src/basic/build.c +++ b/src/basic/build.c @@ -64,12 +64,6 @@ const char* const systemd_features = /* cryptographic libraries */ -#if HAVE_GCRYPT - " +GCRYPT" -#else - " -GCRYPT" -#endif - #if HAVE_GNUTLS " +GNUTLS" #else diff --git a/src/basic/meson.build b/src/basic/meson.build index 90a464f2c4..31fd129871 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -42,7 +42,6 @@ basic_sources = files( 'format-ifname.c', 'format-util.c', 'fs-util.c', - 'gcrypt-util.c', 'glob-util.c', 'glyph-util.c', 'gunicode.c', @@ -214,7 +213,6 @@ libbasic_static = static_library( include_directories : basic_includes, implicit_include_directories : false, dependencies : [libbzip2_cflags, - libgcrypt_cflags, liblz4_cflags, libxz_cflags, libz_cflags, diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c index 95d28ba1a5..44b169c591 100644 --- a/src/journal-remote/journal-remote-main.c +++ b/src/journal-remote/journal-remote-main.c @@ -15,6 +15,7 @@ #include "format-util.h" #include "fileio.h" #include "hashmap.h" +#include "journal-authenticate.h" #include "journal-compression-util.h" #include "journal-remote.h" #include "journal-remote-write.h" @@ -1166,6 +1167,8 @@ static int run(int argc, char **argv) { log_setup(); + journal_auth_init(); + r = parse_config(); if (r < 0) return r; diff --git a/src/journal/journalctl-authenticate.c b/src/journal/journalctl-authenticate.c index 721404a1f5..73c1579b88 100644 --- a/src/journal/journalctl-authenticate.c +++ b/src/journal/journalctl-authenticate.c @@ -7,11 +7,11 @@ #include "alloc-util.h" #include "ansi-color.h" #include "chattr-util.h" +#include "crypto-util.h" #include "errno-util.h" #include "fd-util.h" #include "fs-util.h" -#include "fsprg.h" -#include "gcrypt-util.h" +#include "fsprg-openssl.h" #include "hostname-setup.h" #include "hostname-util.h" #include "io-util.h" @@ -30,7 +30,7 @@ #include "time-util.h" #include "tmpfile-util.h" -#if HAVE_GCRYPT +#if HAVE_OPENSSL static int format_key( const struct iovec *seed, uint64_t start, @@ -60,7 +60,7 @@ static int format_key( #endif int action_setup_keys(void) { -#if HAVE_GCRYPT +#if HAVE_OPENSSL _cleanup_(unlink_and_freep) char *tmpfile = NULL; _cleanup_close_ int fd = -EBADF; _cleanup_free_ char *path = NULL; @@ -70,7 +70,7 @@ int action_setup_keys(void) { assert(arg_action == ACTION_SETUP_KEYS); - r = DLOPEN_GCRYPT(LOG_ERR, SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED); + r = DLOPEN_LIBCRYPTO(LOG_ERR, SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED); if (r < 0) return r; @@ -103,9 +103,8 @@ int action_setup_keys(void) { "Sealing key file %s exists already. Use --force to recreate.", path); _cleanup_(iovec_erase) struct iovec - mpk = IOVEC_ALLOCA(FSPRG_mpkinbytes(FSPRG_RECOMMENDED_SECPAR)), seed = IOVEC_ALLOCA(FSPRG_RECOMMENDED_SEEDLEN), - state = IOVEC_ALLOCA(FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR)); + state = IOVEC_ALLOCA(fsprg_state_size(FSPRG_RECOMMENDED_SECPAR)); if (!arg_quiet) log_info("Generating seed..."); @@ -113,15 +112,9 @@ int action_setup_keys(void) { if (r < 0) return log_error_errno(r, "Failed to acquire random seed: %m"); - if (!arg_quiet) - log_info("Generating key pair..."); - r = FSPRG_GenMK(NULL, mpk.iov_base, seed.iov_base, seed.iov_len, FSPRG_RECOMMENDED_SECPAR); - if (r < 0) - return log_error_errno(r, "Failed to generate key pair: %m"); - if (!arg_quiet) log_info("Generating sealing key..."); - r = FSPRG_GenState0(state.iov_base, mpk.iov_base, seed.iov_base, seed.iov_len); + r = fsprg_generate_state(FSPRG_RECOMMENDED_SECPAR, /* epoch= */ 0, &seed, &state); if (r < 0) return log_error_errno(r, "Failed to generate sealing key: %m"); diff --git a/src/journal/journalctl-misc.c b/src/journal/journalctl-misc.c index be0b09d8fe..000c1c6a2b 100644 --- a/src/journal/journalctl-misc.c +++ b/src/journal/journalctl-misc.c @@ -9,6 +9,7 @@ #include "format-table.h" #include "format-util.h" #include "hashmap.h" +#include "journal-authenticate.h" #include "journal-internal.h" #include "journal-verify.h" #include "journalctl.h" @@ -40,6 +41,8 @@ int action_verify(void) { assert(arg_action == ACTION_VERIFY); + journal_auth_init(); + r = acquire_journal(&j); if (r < 0) return r; diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index b5c4ec7aee..3ee4da9013 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -81,7 +81,7 @@ bool arg_file_stdin = false; int arg_priorities = 0; Set *arg_facilities = NULL; char *arg_verify_key = NULL; -#if HAVE_GCRYPT +#if HAVE_OPENSSL usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC; bool arg_force = false; #endif @@ -769,7 +769,7 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) { OPTION_GROUP("Forward Secure Sealing (FSS) Options"): {} OPTION_LONG("interval", "TIME", "Time interval for changing the FSS sealing key"): -#if HAVE_GCRYPT +#if HAVE_OPENSSL r = parse_sec(opts.arg, &arg_interval); if (r < 0 || arg_interval <= 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), @@ -781,7 +781,7 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) { #endif OPTION_LONG("verify-key", "KEY", "Specify FSS verification key"): -#if HAVE_GCRYPT +#if HAVE_OPENSSL erase_and_free(arg_verify_key); arg_verify_key = strdup(opts.arg); if (!arg_verify_key) @@ -800,7 +800,7 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) { #endif OPTION_LONG("force", NULL, "Override of the FSS key pair with --setup-keys"): -#if HAVE_GCRYPT +#if HAVE_OPENSSL arg_force = true; break; #else @@ -909,7 +909,7 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) { break; OPTION_LONG("setup-keys", NULL, "Generate a new FSS key pair"): -#if HAVE_GCRYPT +#if HAVE_OPENSSL arg_action = ACTION_SETUP_KEYS; break; #else diff --git a/src/journal/journalctl.h b/src/journal/journalctl.h index 406a16077b..504e5e6dd1 100644 --- a/src/journal/journalctl.h +++ b/src/journal/journalctl.h @@ -57,7 +57,7 @@ extern bool arg_file_stdin; extern int arg_priorities; extern Set *arg_facilities; extern char *arg_verify_key; -#if HAVE_GCRYPT +#if HAVE_OPENSSL extern usec_t arg_interval; extern bool arg_force; #endif diff --git a/src/journal/journald-manager.c b/src/journal/journald-manager.c index 764e5091ab..68f95ea967 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 a06752d32e..91f02178f5 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -52,6 +52,8 @@ static int run(int argc, char *argv[]) { sigbus_install(); + journal_auth_init(); + r = manager_new(&m); if (r < 0) return log_oom(); @@ -110,14 +112,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)) + 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/journal/meson.build b/src/journal/meson.build index 38f2e7bb7e..e39381f03f 100644 --- a/src/journal/meson.build +++ b/src/journal/meson.build @@ -94,6 +94,7 @@ executables += [ 'link_with' : journalctl_link_with, 'dependencies' : [ liblz4_cflags, + libopenssl_cflags, libxz_cflags, libzstd_cflags, ], diff --git a/src/libsystemd/meson.build b/src/libsystemd/meson.build index 24979e8882..d1797722a4 100644 --- a/src/libsystemd/meson.build +++ b/src/libsystemd/meson.build @@ -3,8 +3,7 @@ sd_journal_sources = files( 'sd-journal/audit-type.c', '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 0000000000..acb4c221bc --- /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-internal.h" +#include "journal-file.h" + +static const JournalAuthOps *auth_ops = NULL; + +void journal_auth_set_ops(const JournalAuthOps *ops) { + assert(ops); + assert(!auth_ops || auth_ops == ops); + + auth_ops = ops; +} + +bool journal_auth_supported(void) { + return !!auth_ops; +} + +void journal_file_auth_done(JournalFile *f) { + assert(f); + + if (!auth_ops) + return; + + assert(auth_ops->free); + f->auth_context = auth_ops->free(f->auth_context); +} + +int journal_file_auth_load(JournalFile *f) { + assert(f); + + if (!auth_ops) + return -EOPNOTSUPP; + + if (f->auth_context) + return -EBUSY; + + assert(auth_ops->load); + return auth_ops->load(&f->auth_context); +} + +int journal_file_auth_load_key(JournalFile *f, const char *key) { + assert(f); + + if (!auth_ops) + return -EOPNOTSUPP; + + if (f->auth_context) + return -EBUSY; + + assert(auth_ops->load_key); + return auth_ops->load_key(&f->auth_context, key); +} + +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; + + assert(auth_ops); + assert(auth_ops->epoch_to_realtime_usec); + return auth_ops->epoch_to_realtime_usec(f->auth_context, epoch, ret_start, ret_end); +} + +int journal_file_auth_next_evolve_usec(JournalFile *f, usec_t *ret) { + assert(f); + + if (!JOURNAL_HEADER_SEALED(f->header)) + return -EOPNOTSUPP; + + assert(auth_ops); + assert(auth_ops->next_evolve_usec); + return auth_ops->next_evolve_usec(f->auth_context, ret); +} + +int journal_file_auth_seek(JournalFile *f, uint64_t goal) { + assert(f); + + if (!JOURNAL_HEADER_SEALED(f->header)) + return 0; + + assert(auth_ops); + assert(auth_ops->seek); + return auth_ops->seek(f->auth_context, goal); +} + +int journal_file_auth_start(JournalFile *f) { + assert(f); + + if (!JOURNAL_HEADER_SEALED(f->header)) + return 0; + + assert(auth_ops); + assert(auth_ops->start); + return auth_ops->start(f->auth_context); +} + +int journal_file_auth_end(JournalFile *f, uint8_t ret[static TAG_LENGTH]) { + assert(f); + + if (!JOURNAL_HEADER_SEALED(f->header)) + return -EOPNOTSUPP; + + assert(auth_ops); + assert(auth_ops->end); + return auth_ops->end(f->auth_context, ret); +} + +int journal_file_auth_put_header(JournalFile *f) { + assert(f); + + if (!JOURNAL_HEADER_SEALED(f->header)) + return 0; + + assert(auth_ops); + assert(auth_ops->put_header); + return auth_ops->put_header(f->auth_context, f); +} + +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; + + assert(auth_ops); + assert(auth_ops->put_object); + return auth_ops->put_object(f->auth_context, f, type, o, p); +} + +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; + + assert(auth_ops); + assert(auth_ops->append_tag); + return auth_ops->append_tag(f->auth_context, f); +} + +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; + + assert(auth_ops); + assert(auth_ops->append_tag_first); + return auth_ops->append_tag_first(f->auth_context, f); +} + +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; + + assert(auth_ops); + assert(auth_ops->append_tag_maybe); + return auth_ops->append_tag_maybe(f->auth_context, f, realtime); +} 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 0000000000..5d5d94be20 --- /dev/null +++ b/src/libsystemd/sd-journal/journal-authenticate-internal.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "journal-def.h" +#include "sd-forward.h" + +typedef struct JournalAuthOps { + JournalAuthContext* (*free)(JournalAuthContext *c); + int (*load)(JournalAuthContext **ret); + int (*load_key)(JournalAuthContext **ret, const char *key); + int (*epoch_to_realtime_usec)(const JournalAuthContext *c, uint64_t epoch, usec_t *ret_start, usec_t *ret_end); + int (*next_evolve_usec)(const JournalAuthContext *c, usec_t *ret); + int (*seek)(JournalAuthContext *c, uint64_t goal); + int (*start)(JournalAuthContext *c); + int (*end)(JournalAuthContext *c, uint8_t ret[static TAG_LENGTH]); + int (*put_header)(JournalAuthContext *c, JournalFile *f); + int (*put_object)(JournalAuthContext *c, JournalFile *f, ObjectType type, Object *o, uint64_t p); + int (*append_tag)(JournalAuthContext *c, JournalFile *f); + int (*append_tag_first)(JournalAuthContext *c, JournalFile *f); + int (*append_tag_maybe)(JournalAuthContext *c, JournalFile *f, usec_t realtime); +} JournalAuthOps; + +void journal_auth_set_ops(const JournalAuthOps *ops); +bool journal_auth_supported(void); + +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 deleted file mode 100644 index 86be008aa1..0000000000 --- a/src/libsystemd/sd-journal/journal-authenticate.c +++ /dev/null @@ -1,555 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include -#include -#include - -#include "alloc-util.h" -#include "fd-util.h" -#include "fsprg.h" -#include "gcrypt-util.h" -#include "hexdecoct.h" -#include "iovec-util.h" -#include "journal-authenticate.h" -#include "journal-def.h" -#include "journal-file.h" -#include "log.h" -#include "memory-util.h" -#include "string-util.h" -#include "time-util.h" - -static void* fssheader_free(FSSHeader *p) { - /* mmap() returns MAP_FAILED on error and sets the errno */ - if (!p || p == MAP_FAILED) - return NULL; - - assert_se(munmap(p, PAGE_ALIGN(sizeof(FSSHeader))) >= 0); - return NULL; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(FSSHeader*, fssheader_free); - -int journal_file_append_tag(JournalFile *f) { -#if HAVE_GCRYPT - int r; - - assert(f); - - if (!JOURNAL_HEADER_SEALED(f->header)) - return 0; - - if (!f->hmac_running) { - r = journal_file_hmac_start(f); - if (r < 0) - return r; - } - - assert(f->hmac); - - Object *o; - uint64_t p; - r = journal_file_append_object(f, OBJECT_TAG, sizeof(struct TagObject), &o, &p); - if (r < 0) - return r; - - uint64_t seqnum = le64toh(f->header->n_tags) + 1; - f->header->n_tags = htole64(seqnum); - - o->tag.seqnum = htole64(seqnum); - o->tag.epoch = htole64(FSPRG_GetEpoch(f->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); - 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 -} - -int journal_file_hmac_start(JournalFile *f) { -#if HAVE_GCRYPT - int r; - - assert(f); - - if (!JOURNAL_HEADER_SEALED(f->header)) - return 0; - - if (f->hmac_running) - return 0; - - /* Prepare HMAC for next cycle */ - sym_gcry_md_reset(f->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); - if (r < 0) - return r; - - gcry_error_t err = sym_gcry_md_setkey(f->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; - - return 0; -#else - return -EOPNOTSUPP; -#endif -} - -static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) { - uint64_t t; - - assert(f); - assert(epoch); - assert(JOURNAL_HEADER_SEALED(f->header)); - - if (f->fss_start_usec == 0 || f->fss_interval_usec == 0) - return -EOPNOTSUPP; - - if (realtime < f->fss_start_usec) - return -ESTALE; - - t = realtime - f->fss_start_usec; - t = t / f->fss_interval_usec; - - *epoch = t; - - return 0; -} - -static int journal_file_fsprg_need_evolve(JournalFile *f, uint64_t realtime) { - uint64_t goal, epoch; - int r; - - assert(f); - - if (!JOURNAL_HEADER_SEALED(f->header)) - return 0; - - r = journal_file_get_epoch(f, realtime, &goal); - if (r < 0) - return r; - - epoch = FSPRG_GetEpoch(f->fsprg_state.iov_base); - if (epoch > goal) - return -ESTALE; - - return epoch != goal; -} - -int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) { - uint64_t goal, epoch; - int r; - - assert(f); - - if (!JOURNAL_HEADER_SEALED(f->header)) - return 0; - - r = journal_file_get_epoch(f, realtime, &goal); - if (r < 0) - return r; - - epoch = FSPRG_GetEpoch(f->fsprg_state.iov_base); - if (epoch < goal) - log_debug("Evolving FSPRG key from epoch %"PRIu64" to %"PRIu64".", epoch, goal); - - for (;;) { - if (epoch > goal) - return -ESTALE; - if (epoch == goal) - return 0; - - r = FSPRG_Evolve(f->fsprg_state.iov_base); - if (r < 0) - return r; - - epoch = FSPRG_GetEpoch(f->fsprg_state.iov_base); - if (epoch < goal) { - r = journal_file_append_tag(f); - if (r < 0) - return r; - } - } -} - -int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) { - int r; - - assert(f); - - if (!JOURNAL_HEADER_SEALED(f->header)) - return 0; - - assert(iovec_is_set(&f->fsprg_seed)); - - if (iovec_is_set(&f->fsprg_state)) { - /* Cheaper... */ - - uint64_t epoch = FSPRG_GetEpoch(f->fsprg_state.iov_base); - if (goal == epoch) - return 0; - - if (goal == epoch + 1) - return FSPRG_Evolve(f->fsprg_state.iov_base); - } else { - r = iovec_alloc(FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR), &f->fsprg_state); - if (r < 0) - return r; - } - - 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); - 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); -} - -int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) { - int r; - - assert(f); - - if (!JOURNAL_HEADER_SEALED(f->header)) - return 0; - - if (realtime <= 0) - realtime = now(CLOCK_REALTIME); - - r = journal_file_fsprg_need_evolve(f, realtime); - if (r <= 0) - return 0; - - r = journal_file_append_tag(f); - if (r < 0) - return r; - - r = journal_file_fsprg_evolve(f, realtime); - if (r < 0) - return r; - - return 0; -} - -int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) { -#if HAVE_GCRYPT - int r; - - assert(f); - - if (!JOURNAL_HEADER_SEALED(f->header)) - return 0; - - r = journal_file_hmac_start(f); - if (r < 0) - return r; - - if (!o) { - r = journal_file_move_to_object(f, type, p, &o); - if (r < 0) - return r; - } else if (type > OBJECT_UNUSED && o->object.type != type) - return -EBADMSG; - - sym_gcry_md_write(f->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)); - 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)); - break; - - case OBJECT_ENTRY: - /* All */ - sym_gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(Object, entry.seqnum)); - break; - - case OBJECT_FIELD_HASH_TABLE: - case OBJECT_DATA_HASH_TABLE: - case OBJECT_ENTRY_ARRAY: - /* Nothing: everything is mutable */ - break; - - 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)); - break; - default: - return -EINVAL; - } - - return 0; -#else - return -EOPNOTSUPP; -#endif -} - -int journal_file_hmac_put_header(JournalFile *f) { -#if HAVE_GCRYPT - int r; - - assert(f); - - if (!JOURNAL_HEADER_SEALED(f->header)) - return 0; - - r = journal_file_hmac_start(f); - if (r < 0) - return r; - - /* All but state+reserved, boot_id, arena_size, - * tail_object_offset, n_objects, n_entries, - * tail_entry_seqnum, head_entry_seqnum, entry_array_offset, - * head_entry_realtime, tail_entry_realtime, - * 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)); - - return 0; -#else - return -EOPNOTSUPP; -#endif -} - -int journal_file_fss_load(JournalFile *f) { - _cleanup_close_ int fd = -EBADF; - _cleanup_free_ char *path = NULL; - _cleanup_(fssheader_freep) FSSHeader *header = NULL; - struct stat st; - sd_id128_t machine; - int r; - - assert(f); - - /* 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. */ - - r = sd_id128_get_machine(&machine); - if (r < 0) - return r; - - if (asprintf(&path, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss", - SD_ID128_FORMAT_VAL(machine)) < 0) - return -ENOMEM; - - fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600); - if (fd < 0) { - if (errno != ENOENT) - log_error_errno(errno, "Failed to open %s: %m", path); - - return -errno; - } - - if (fstat(fd, &st) < 0) - return -errno; - - if (st.st_size < (off_t) sizeof(FSSHeader)) - return -ENODATA; - - header = mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0); - if (header == MAP_FAILED) - return -errno; - - if (memcmp(header->signature, (const uint8_t[]) FSS_HEADER_SIGNATURE, 8) != 0) - return -EBADMSG; - - if (header->incompatible_flags != 0) - return -EPROTONOSUPPORT; - - if (le64toh(header->header_size) < sizeof(FSSHeader)) - return -EBADMSG; - - if (le64toh(header->fsprg_state_size) != FSPRG_stateinbytes(le16toh(header->fsprg_secpar))) - return -EBADMSG; - - f->fss_file_size = le64toh(header->header_size) + le64toh(header->fsprg_state_size); - if ((uint64_t) st.st_size < f->fss_file_size) - return -ENODATA; - - if (!sd_id128_equal(machine, header->machine_id)) - return -EHOSTDOWN; - - if (le64toh(header->start_usec) <= 0 || le64toh(header->interval_usec) <= 0) - return -EBADMSG; - - size_t sz = PAGE_ALIGN(f->fss_file_size); - assert(sz < SIZE_MAX); - f->fss_file = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - if (f->fss_file == MAP_FAILED) { - f->fss_file = NULL; - return -errno; - } - - f->fss_start_usec = le64toh(f->fss_file->start_usec); - f->fss_interval_usec = le64toh(f->fss_file->interval_usec); - - f->fsprg_state = IOVEC_MAKE( - (uint8_t*) f->fss_file + le64toh(f->fss_file->header_size), - le64toh(f->fss_file->fsprg_state_size)); - - return 0; -} - -int journal_file_hmac_setup(JournalFile *f) { -#if HAVE_GCRYPT - gcry_error_t e; - int r; - - if (!JOURNAL_HEADER_SEALED(f->header)) - return 0; - - r = initialize_libgcrypt(true); - if (r < 0) - return r; - - e = sym_gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); - if (e != 0) - return -EOPNOTSUPP; - - return 0; -#else - return -EOPNOTSUPP; -#endif -} - -int journal_file_append_first_tag(JournalFile *f) { - uint64_t p; - int r; - - if (!JOURNAL_HEADER_SEALED(f->header)) - return 0; - - log_debug("Calculating first tag..."); - - r = journal_file_hmac_put_header(f); - if (r < 0) - return r; - - p = le64toh(f->header->field_hash_table_offset); - if (p < offsetof(Object, hash_table.items)) - return -EINVAL; - p -= offsetof(Object, hash_table.items); - - r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, NULL, p); - if (r < 0) - return r; - - p = le64toh(f->header->data_hash_table_offset); - if (p < offsetof(Object, hash_table.items)) - return -EINVAL; - p -= offsetof(Object, hash_table.items); - - r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, NULL, p); - if (r < 0) - return r; - - r = journal_file_append_tag(f); - if (r < 0) - return r; - - return 0; -} - -int journal_file_parse_verification_key(JournalFile *f, const char *key) { - _cleanup_(erase_and_freep) uint8_t *seed = NULL; - size_t seed_size; - const char *k; - int r; - - assert(f); - assert(key); - - seed_size = FSPRG_RECOMMENDED_SEEDLEN; - seed = malloc(seed_size); - if (!seed) - return -ENOMEM; - - k = key; - for (size_t c = 0; c < seed_size; c++) { - int x, y; - - k = skip_leading_chars(k, "-"); - - x = unhexchar(*k); - if (x < 0) - return -EKEYREJECTED; - k++; - - y = unhexchar(*k); - if (y < 0) - return -EKEYREJECTED; - k++; - - seed[c] = (uint8_t) (x * 16 + y); - } - - if (*k != '/') - return -EKEYREJECTED; - k++; - - uint64_t start, interval; - r = sscanf(k, "%"PRIx64"-%"PRIx64, &start, &interval); - if (r != 2) - return -EKEYREJECTED; - - if (start == 0 || interval == 0) - return -EKEYREJECTED; - - uint64_t start_usec; - 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; - - return 0; -} - -bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u) { - uint64_t epoch; - - assert(f); - assert(u); - - if (!JOURNAL_HEADER_SEALED(f->header)) - return false; - - epoch = FSPRG_GetEpoch(f->fsprg_state.iov_base); - - *u = (usec_t) (f->fss_start_usec + f->fss_interval_usec * epoch + f->fss_interval_usec); - - return true; -} diff --git a/src/libsystemd/sd-journal/journal-authenticate.h b/src/libsystemd/sd-journal/journal-authenticate.h deleted file mode 100644 index 89897fc0a7..0000000000 --- a/src/libsystemd/sd-journal/journal-authenticate.h +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#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); - -int journal_file_hmac_setup(JournalFile *f); -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); - -int journal_file_fss_load(JournalFile *f); -int journal_file_parse_verification_key(JournalFile *f, const char *key); - -int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime); -int journal_file_fsprg_seek(JournalFile *f, uint64_t goal); - -bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u); diff --git a/src/libsystemd/sd-journal/journal-def.h b/src/libsystemd/sd-journal/journal-def.h index 026aa4c941..a48554cee1 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" */ @@ -207,7 +210,8 @@ enum { HEADER_COMPATIBLE_TAIL_ENTRY_BOOT_ID | HEADER_COMPATIBLE_SEALED_CONTINUOUS, - HEADER_COMPATIBLE_SUPPORTED = (HAVE_GCRYPT ? HEADER_COMPATIBLE_SEALED | HEADER_COMPATIBLE_SEALED_CONTINUOUS : 0) | + HEADER_COMPATIBLE_SUPPORTED = HEADER_COMPATIBLE_SEALED | + HEADER_COMPATIBLE_SEALED_CONTINUOUS | HEADER_COMPATIBLE_TAIL_ENTRY_BOOT_ID, }; diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c index 37392ea9bb..41e06086d2 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))), @@ -472,14 +457,19 @@ static int journal_file_refresh_header(JournalFile *f) { } static bool warn_wrong_flags(const JournalFile *f, bool compatible) { - const uint32_t any = compatible ? HEADER_COMPATIBLE_ANY : HEADER_INCOMPATIBLE_ANY, - supported = compatible ? HEADER_COMPATIBLE_SUPPORTED : HEADER_INCOMPATIBLE_SUPPORTED; + const uint32_t any = compatible ? HEADER_COMPATIBLE_ANY : HEADER_INCOMPATIBLE_ANY; + uint32_t supported = compatible ? HEADER_COMPATIBLE_SUPPORTED : HEADER_INCOMPATIBLE_SUPPORTED; const char *type = compatible ? "compatible" : "incompatible"; uint32_t flags; assert(f); assert(f->header); + /* When sealing is not supported, refuse to write to an already sealed journal file, but still allow + * reading sealed journal files. */ + if (compatible && journal_file_writable(f) && !journal_auth_supported()) + supported &= ~(HEADER_COMPATIBLE_SEALED | HEADER_COMPATIBLE_SEALED_CONTINUOUS); + flags = le32toh(compatible ? f->header->compatible_flags : f->header->incompatible_flags); if (flags & ~supported) { @@ -1833,11 +1823,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 +1936,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 +2184,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 +2428,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 +2615,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 +4264,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) { @@ -4304,12 +4282,6 @@ int journal_file_open( goto fail; } -#if HAVE_GCRYPT - r = journal_file_hmac_setup(f); - if (r < 0) - goto fail; -#endif - if (newly_created) { r = journal_file_setup_field_hash_table(f); if (r < 0) @@ -4319,11 +4291,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 fea181cafd..8499fa2b66 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 02420840df..dd3b6f32d4 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,21 +835,20 @@ 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; + } else if (JOURNAL_HEADER_SEALED(f->header)) { + /* For a sealed journal file, request the verification key when journal sealing is supported. + * Otherwise, log that seal verification is skipped. */ + if (journal_auth_supported()) + return -ENOKEY; + else + log_notice("Journal file is sealed, but journal sealing support is disabled. Skipping seal verification."); + } r = var_tmp_dir(&tmp_dir); if (r < 0) { @@ -1155,14 +1153,15 @@ int journal_file_verify( } } -#if HAVE_GCRYPT - if (JOURNAL_HEADER_SEALED(f->header)) { + if (JOURNAL_HEADER_SEALED(f->header) && journal_auth_supported()) { 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 +1191,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 +1213,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 +1225,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-verify.c b/src/libsystemd/sd-journal/test-journal-verify.c index 2d797a18f3..6a1401764b 100644 --- a/src/libsystemd/sd-journal/test-journal-verify.c +++ b/src/libsystemd/sd-journal/test-journal-verify.c @@ -9,6 +9,7 @@ #include "chattr-util.h" #include "fd-util.h" #include "iovec-util.h" +#include "journal-authenticate.h" #include "journal-file-util.h" #include "journal-verify.h" #include "log.h" @@ -174,6 +175,8 @@ int main(int argc, char *argv[]) { const char *verification_key = NULL; int max_iterations = 512; + journal_auth_init(); + if (argc > 1) { /* Don't limit the number of iterations when the verification key * is provided on the command line, we want to do that only in CIs */ @@ -187,10 +190,9 @@ int main(int argc, char *argv[]) { ASSERT_OK_ERRNO(setenv("SYSTEMD_JOURNAL_COMPACT", "1", 1)); run_test(verification_key, max_iterations); -#if HAVE_GCRYPT - /* If we're running without any arguments and we're compiled with gcrypt + /* If we're running without any arguments and journal sealing support is enabled, * check the journal verification stuff with a valid key as well */ - if (argc <= 1) { + if (argc <= 1 && journal_auth_supported()) { verification_key = "c262bd-85187f-0b1b04-877cc5/1c7af8-35a4e900"; ASSERT_OK_ERRNO(setenv("SYSTEMD_JOURNAL_COMPACT", "0", 1)); @@ -199,7 +201,6 @@ int main(int argc, char *argv[]) { ASSERT_OK_ERRNO(setenv("SYSTEMD_JOURNAL_COMPACT", "1", 1)); run_test(verification_key, max_iterations); } -#endif return 0; } diff --git a/src/libsystemd/sd-journal/test-journal.c b/src/libsystemd/sd-journal/test-journal.c index bcd7293dba..645636d0d0 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 @@ -590,6 +588,8 @@ static int intro(void) { if (access("/etc/machine-id", F_OK) != 0) return log_tests_skipped("/etc/machine-id not found"); + journal_auth_init(); + return EXIT_SUCCESS; } diff --git a/src/shared/crypto-util.c b/src/shared/crypto-util.c index 4277657195..5b1d6d8136 100644 --- a/src/shared/crypto-util.c +++ b/src/shared/crypto-util.c @@ -17,7 +17,6 @@ #include "strv.h" #if HAVE_OPENSSL -# include # include # include # include @@ -37,14 +36,14 @@ struct OpenSSLAskPasswordUI { #endif }; -static DLSYM_PROTOTYPE(ASN1_INTEGER_dup) = NULL; -static DLSYM_PROTOTYPE(ASN1_INTEGER_free) = NULL; -static DLSYM_PROTOTYPE(ASN1_INTEGER_set) = NULL; DLSYM_PROTOTYPE(ASN1_ANY_it) = NULL; DLSYM_PROTOTYPE(ASN1_BIT_STRING_it) = NULL; DLSYM_PROTOTYPE(ASN1_BMPSTRING_it) = NULL; DLSYM_PROTOTYPE(ASN1_BMPSTRING_new) = NULL; DLSYM_PROTOTYPE(ASN1_IA5STRING_it) = NULL; +static DLSYM_PROTOTYPE(ASN1_INTEGER_dup) = NULL; +static DLSYM_PROTOTYPE(ASN1_INTEGER_free) = NULL; +static DLSYM_PROTOTYPE(ASN1_INTEGER_set) = NULL; DLSYM_PROTOTYPE(ASN1_OBJECT_it) = NULL; DLSYM_PROTOTYPE(ASN1_OCTET_STRING_free) = NULL; DLSYM_PROTOTYPE(ASN1_OCTET_STRING_it) = NULL; @@ -73,12 +72,32 @@ DLSYM_PROTOTYPE(BIO_s_mem) = NULL; DLSYM_PROTOTYPE(BIO_write) = NULL; DLSYM_PROTOTYPE(BN_CTX_free) = NULL; DLSYM_PROTOTYPE(BN_CTX_new) = NULL; +DLSYM_PROTOTYPE(BN_CTX_secure_new) = NULL; +DLSYM_PROTOTYPE(BN_add) = NULL; +DLSYM_PROTOTYPE(BN_add_word) = NULL; DLSYM_PROTOTYPE(BN_bin2bn) = NULL; -static DLSYM_PROTOTYPE(BN_bn2bin) = NULL; +DLSYM_PROTOTYPE(BN_bn2bin) = NULL; +DLSYM_PROTOTYPE(BN_bn2binpad) = NULL; DLSYM_PROTOTYPE(BN_bn2nativepad) = NULL; +DLSYM_PROTOTYPE(BN_check_prime) = NULL; +DLSYM_PROTOTYPE(BN_clear_free) = NULL; +DLSYM_PROTOTYPE(BN_cmp) = NULL; +DLSYM_PROTOTYPE(BN_copy) = NULL; DLSYM_PROTOTYPE(BN_free) = NULL; +DLSYM_PROTOTYPE(BN_is_negative) = NULL; +DLSYM_PROTOTYPE(BN_mod_exp) = NULL; +DLSYM_PROTOTYPE(BN_mod_inverse) = NULL; +DLSYM_PROTOTYPE(BN_mod_lshift1_quick) = NULL; +DLSYM_PROTOTYPE(BN_mod_mul) = NULL; +DLSYM_PROTOTYPE(BN_mod_sqr) = NULL; +DLSYM_PROTOTYPE(BN_mod_sub) = NULL; +DLSYM_PROTOTYPE(BN_mul) = NULL; DLSYM_PROTOTYPE(BN_new) = NULL; +DLSYM_PROTOTYPE(BN_nnmod) = NULL; DLSYM_PROTOTYPE(BN_num_bits) = NULL; +DLSYM_PROTOTYPE(BN_secure_new) = NULL; +DLSYM_PROTOTYPE(BN_set_word) = NULL; +DLSYM_PROTOTYPE(BN_sub_word) = NULL; DLSYM_PROTOTYPE(CRYPTO_free) = NULL; DLSYM_PROTOTYPE(ECDSA_SIG_free) = NULL; DLSYM_PROTOTYPE(EC_GROUP_free) = NULL; @@ -130,12 +149,13 @@ static DLSYM_PROTOTYPE(EVP_KDF_fetch) = NULL; static DLSYM_PROTOTYPE(EVP_KDF_free) = NULL; DLSYM_PROTOTYPE(EVP_MAC_CTX_free) = NULL; static DLSYM_PROTOTYPE(EVP_MAC_CTX_get_mac_size) = NULL; -static DLSYM_PROTOTYPE(EVP_MAC_CTX_new) = NULL; -static DLSYM_PROTOTYPE(EVP_MAC_fetch) = NULL; -static DLSYM_PROTOTYPE(EVP_MAC_final) = NULL; +DLSYM_PROTOTYPE(EVP_MAC_CTX_new) = NULL; +DLSYM_PROTOTYPE(EVP_MAC_fetch) = NULL; +DLSYM_PROTOTYPE(EVP_MAC_final) = NULL; DLSYM_PROTOTYPE(EVP_MAC_free) = NULL; -static DLSYM_PROTOTYPE(EVP_MAC_init) = NULL; -static DLSYM_PROTOTYPE(EVP_MAC_update) = NULL; +DLSYM_PROTOTYPE(EVP_MAC_init) = NULL; +DLSYM_PROTOTYPE(EVP_MAC_update) = NULL; +DLSYM_PROTOTYPE(EVP_MD_CTX_copy_ex) = NULL; DLSYM_PROTOTYPE(EVP_MD_CTX_free) = NULL; DLSYM_PROTOTYPE(EVP_MD_CTX_get0_md) = NULL; DLSYM_PROTOTYPE(EVP_MD_CTX_new) = NULL; @@ -185,8 +205,6 @@ DLSYM_PROTOTYPE(EVP_sha256) = NULL; DLSYM_PROTOTYPE(EVP_sha384) = NULL; DLSYM_PROTOTYPE(EVP_sha512) = NULL; DLSYM_PROTOTYPE(HMAC) = NULL; -DLSYM_PROTOTYPE(SHA1) = NULL; -DLSYM_PROTOTYPE(SHA512) = NULL; DLSYM_PROTOTYPE(OBJ_nid2obj) = NULL; DLSYM_PROTOTYPE(OBJ_nid2sn) = NULL; DLSYM_PROTOTYPE(OBJ_sn2nid) = NULL; @@ -197,11 +215,11 @@ DLSYM_PROTOTYPE(OPENSSL_sk_pop_free) = NULL; DLSYM_PROTOTYPE(OPENSSL_sk_push) = NULL; DLSYM_PROTOTYPE(OPENSSL_sk_value) = NULL; DLSYM_PROTOTYPE(OSSL_EC_curve_nid2name) = NULL; -static DLSYM_PROTOTYPE(OSSL_PARAM_BLD_new) = NULL; -static DLSYM_PROTOTYPE(OSSL_PARAM_BLD_free) = NULL; +DLSYM_PROTOTYPE(OSSL_PARAM_BLD_free) = NULL; +DLSYM_PROTOTYPE(OSSL_PARAM_BLD_new) = NULL; static DLSYM_PROTOTYPE(OSSL_PARAM_BLD_push_octet_string) = NULL; -static DLSYM_PROTOTYPE(OSSL_PARAM_BLD_push_utf8_string) = NULL; -static DLSYM_PROTOTYPE(OSSL_PARAM_BLD_to_param) = NULL; +DLSYM_PROTOTYPE(OSSL_PARAM_BLD_push_utf8_string) = NULL; +DLSYM_PROTOTYPE(OSSL_PARAM_BLD_to_param) = NULL; DLSYM_PROTOTYPE(OSSL_PARAM_construct_BN) = NULL; DLSYM_PROTOTYPE(OSSL_PARAM_construct_end) = NULL; DLSYM_PROTOTYPE(OSSL_PARAM_construct_octet_string) = NULL; @@ -220,12 +238,12 @@ DLSYM_PROTOTYPE(PEM_read_PrivateKey) = NULL; DLSYM_PROTOTYPE(PEM_read_X509) = NULL; static DLSYM_PROTOTYPE(PEM_read_bio_PrivateKey) = NULL; static DLSYM_PROTOTYPE(PEM_read_bio_X509) = NULL; -DLSYM_PROTOTYPE(PKCS5_PBKDF2_HMAC) = NULL; DLSYM_PROTOTYPE(PEM_write_PUBKEY) = NULL; DLSYM_PROTOTYPE(PEM_write_PrivateKey) = NULL; DLSYM_PROTOTYPE(PEM_write_X509) = NULL; -DLSYM_PROTOTYPE(PKCS7_SIGNER_INFO_free) = NULL; +DLSYM_PROTOTYPE(PKCS5_PBKDF2_HMAC) = NULL; DLSYM_PROTOTYPE(PKCS7_ATTR_SIGN_it) = NULL; +DLSYM_PROTOTYPE(PKCS7_SIGNER_INFO_free) = NULL; static DLSYM_PROTOTYPE(PKCS7_SIGNER_INFO_new) = NULL; static DLSYM_PROTOTYPE(PKCS7_SIGNER_INFO_set) = NULL; DLSYM_PROTOTYPE(PKCS7_add0_attrib_signing_time) = NULL; @@ -246,22 +264,24 @@ DLSYM_PROTOTYPE(PKCS7_set_content) = NULL; static DLSYM_PROTOTYPE(PKCS7_set_type) = NULL; DLSYM_PROTOTYPE(PKCS7_sign) = NULL; DLSYM_PROTOTYPE(PKCS7_verify) = NULL; -static DLSYM_PROTOTYPE(X509_ALGOR_set0) = NULL; -DLSYM_PROTOTYPE(X509_NAME_free) = NULL; +DLSYM_PROTOTYPE(SHA1) = NULL; +DLSYM_PROTOTYPE(SHA512) = NULL; DLSYM_PROTOTYPE(X509_ALGOR_free) = NULL; +static DLSYM_PROTOTYPE(X509_ALGOR_set0) = NULL; DLSYM_PROTOTYPE(X509_ATTRIBUTE_free) = NULL; +DLSYM_PROTOTYPE(X509_NAME_free) = NULL; DLSYM_PROTOTYPE(X509_NAME_oneline) = NULL; static DLSYM_PROTOTYPE(X509_NAME_set) = NULL; DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set1_host) = NULL; DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set1_ip) = NULL; DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set_hostflags) = NULL; DLSYM_PROTOTYPE(X509_free) = NULL; -DLSYM_PROTOTYPE(X509_gmtime_adj) = NULL; static DLSYM_PROTOTYPE(X509_get0_serialNumber) = NULL; static DLSYM_PROTOTYPE(X509_get_issuer_name) = NULL; DLSYM_PROTOTYPE(X509_get_pubkey) = NULL; static DLSYM_PROTOTYPE(X509_get_signature_info) = NULL; DLSYM_PROTOTYPE(X509_get_subject_name) = NULL; +DLSYM_PROTOTYPE(X509_gmtime_adj) = NULL; DLSYM_PROTOTYPE(d2i_ASN1_OCTET_STRING) = NULL; DLSYM_PROTOTYPE(d2i_ECPKParameters) = NULL; DLSYM_PROTOTYPE(d2i_PKCS7) = NULL; @@ -276,7 +296,6 @@ static DLSYM_PROTOTYPE(i2d_PublicKey) = NULL; DLSYM_PROTOTYPE(i2d_X509) = NULL; DLSYM_PROTOTYPE(i2d_X509_NAME) = NULL; -DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(OSSL_PARAM_BLD*, sym_OSSL_PARAM_BLD_free, OSSL_PARAM_BLD_freep, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(OSSL_STORE_CTX*, sym_OSSL_STORE_close, OSSL_STORE_closep, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(OSSL_STORE_INFO*, sym_OSSL_STORE_INFO_free, OSSL_STORE_INFO_freep, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(EVP_KDF*, sym_EVP_KDF_free, EVP_KDF_freep, NULL); @@ -304,11 +323,11 @@ DLSYM_PROTOTYPE(EC_KEY_new) = NULL; DLSYM_PROTOTYPE(EC_KEY_set_group) = NULL; DLSYM_PROTOTYPE(EC_KEY_set_public_key) = NULL; DLSYM_PROTOTYPE(EVP_PKEY_assign) = NULL; +DLSYM_PROTOTYPE(RSAPublicKey_dup) = NULL; DLSYM_PROTOTYPE(RSA_free) = NULL; DLSYM_PROTOTYPE(RSA_new) = NULL; DLSYM_PROTOTYPE(RSA_set0_key) = NULL; DLSYM_PROTOTYPE(RSA_size) = NULL; -DLSYM_PROTOTYPE(RSAPublicKey_dup) = NULL; REENABLE_WARNING; #endif @@ -348,15 +367,10 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG(ASN1_BIT_STRING_it), DLSYM_ARG(ASN1_BMPSTRING_it), DLSYM_ARG(ASN1_BMPSTRING_new), - DLSYM_ARG(ASN1_get_object), DLSYM_ARG(ASN1_IA5STRING_it), DLSYM_ARG(ASN1_INTEGER_dup), DLSYM_ARG(ASN1_INTEGER_free), DLSYM_ARG(ASN1_INTEGER_set), - DLSYM_ARG(ASN1_item_d2i), - DLSYM_ARG(ASN1_item_free), - DLSYM_ARG(ASN1_item_i2d), - DLSYM_ARG(ASN1_item_new), DLSYM_ARG(ASN1_OBJECT_it), DLSYM_ARG(ASN1_OCTET_STRING_free), DLSYM_ARG(ASN1_OCTET_STRING_it), @@ -369,35 +383,56 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG(ASN1_TIME_free), DLSYM_ARG(ASN1_TIME_set), DLSYM_ARG(ASN1_TYPE_new), + DLSYM_ARG(ASN1_get_object), + DLSYM_ARG(ASN1_item_d2i), + DLSYM_ARG(ASN1_item_free), + DLSYM_ARG(ASN1_item_i2d), + DLSYM_ARG(ASN1_item_new), DLSYM_ARG(BIO_ctrl), DLSYM_ARG(BIO_find_type), - DLSYM_ARG(BIO_free_all), DLSYM_ARG(BIO_free), + DLSYM_ARG(BIO_free_all), + DLSYM_ARG(BIO_new), DLSYM_ARG(BIO_new_mem_buf), DLSYM_ARG(BIO_new_socket), - DLSYM_ARG(BIO_new), DLSYM_ARG(BIO_s_mem), DLSYM_ARG(BIO_write), - DLSYM_ARG(BN_bin2bn), - DLSYM_ARG(BN_bn2bin), - DLSYM_ARG(BN_bn2nativepad), DLSYM_ARG(BN_CTX_free), DLSYM_ARG(BN_CTX_new), + DLSYM_ARG(BN_CTX_secure_new), + DLSYM_ARG(BN_add), + DLSYM_ARG(BN_add_word), + DLSYM_ARG(BN_bin2bn), + DLSYM_ARG(BN_bn2bin), + DLSYM_ARG(BN_bn2binpad), + DLSYM_ARG(BN_bn2nativepad), + DLSYM_ARG(BN_check_prime), + DLSYM_ARG(BN_clear_free), + DLSYM_ARG(BN_cmp), + DLSYM_ARG(BN_copy), DLSYM_ARG(BN_free), + DLSYM_ARG(BN_is_negative), + DLSYM_ARG(BN_mod_exp), + DLSYM_ARG(BN_mod_inverse), + DLSYM_ARG(BN_mod_lshift1_quick), + DLSYM_ARG(BN_mod_mul), + DLSYM_ARG(BN_mod_sqr), + DLSYM_ARG(BN_mod_sub), + DLSYM_ARG(BN_mul), DLSYM_ARG(BN_new), + DLSYM_ARG(BN_nnmod), DLSYM_ARG(BN_num_bits), + DLSYM_ARG(BN_secure_new), + DLSYM_ARG(BN_set_word), + DLSYM_ARG(BN_sub_word), DLSYM_ARG(CRYPTO_free), - DLSYM_ARG(d2i_ASN1_OCTET_STRING), - DLSYM_ARG(d2i_ECPKParameters), - DLSYM_ARG(d2i_PKCS7), - DLSYM_ARG(d2i_PUBKEY), - DLSYM_ARG(d2i_X509), + DLSYM_ARG(ECDSA_SIG_free), DLSYM_ARG(EC_GROUP_free), - DLSYM_ARG(EC_GROUP_get_curve_name), - DLSYM_ARG(EC_GROUP_get_curve), - DLSYM_ARG(EC_GROUP_get_field_type), DLSYM_ARG(EC_GROUP_get0_generator), DLSYM_ARG(EC_GROUP_get0_order), + DLSYM_ARG(EC_GROUP_get_curve), + DLSYM_ARG(EC_GROUP_get_curve_name), + DLSYM_ARG(EC_GROUP_get_field_type), DLSYM_ARG(EC_GROUP_new_by_curve_name), DLSYM_ARG(EC_POINT_free), DLSYM_ARG(EC_POINT_new), @@ -405,36 +440,11 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG(EC_POINT_point2buf), DLSYM_ARG(EC_POINT_point2oct), DLSYM_ARG(EC_POINT_set_affine_coordinates), - DLSYM_ARG(ECDSA_SIG_free), -#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0) - DLSYM_ARG_FORCE(ENGINE_by_id), - DLSYM_ARG_FORCE(ENGINE_free), - DLSYM_ARG_FORCE(ENGINE_init), - DLSYM_ARG_FORCE(ENGINE_load_private_key), -#endif -#if !defined(OPENSSL_NO_DEPRECATED_3_0) - DLSYM_ARG_FORCE(EC_KEY_check_key), - DLSYM_ARG_FORCE(EC_KEY_free), - DLSYM_ARG_FORCE(EC_KEY_new), - DLSYM_ARG_FORCE(EC_KEY_set_group), - DLSYM_ARG_FORCE(EC_KEY_set_public_key), - DLSYM_ARG_FORCE(ECDSA_do_verify), - DLSYM_ARG_FORCE(ECDSA_SIG_new), - DLSYM_ARG_FORCE(ECDSA_SIG_set0), - DLSYM_ARG_FORCE(EVP_PKEY_assign), - DLSYM_ARG_FORCE(RSA_free), - DLSYM_ARG_FORCE(RSA_new), - DLSYM_ARG_FORCE(RSA_set0_key), - DLSYM_ARG_FORCE(RSA_size), - DLSYM_ARG_FORCE(RSAPublicKey_dup), -#endif DLSYM_ARG(ERR_clear_error), - DLSYM_ARG(ERR_error_string_n), DLSYM_ARG(ERR_error_string), + DLSYM_ARG(ERR_error_string_n), DLSYM_ARG(ERR_get_error), DLSYM_ARG(ERR_peek_last_error), - DLSYM_ARG(EVP_aes_256_ctr), - DLSYM_ARG(EVP_aes_256_gcm), DLSYM_ARG(EVP_CIPHER_CTX_ctrl), DLSYM_ARG(EVP_CIPHER_CTX_free), DLSYM_ARG(EVP_CIPHER_CTX_get_block_size), @@ -456,11 +466,9 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG(EVP_DigestVerify), DLSYM_ARG(EVP_DigestVerifyInit), DLSYM_ARG(EVP_EncryptFinal_ex), - DLSYM_ARG(EVP_EncryptInit_ex), DLSYM_ARG(EVP_EncryptInit), + DLSYM_ARG(EVP_EncryptInit_ex), DLSYM_ARG(EVP_EncryptUpdate), - DLSYM_ARG(EVP_get_cipherbyname), - DLSYM_ARG(EVP_get_digestbyname), DLSYM_ARG(EVP_KDF_CTX_free), DLSYM_ARG(EVP_KDF_CTX_new), DLSYM_ARG(EVP_KDF_derive), @@ -474,59 +482,56 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG(EVP_MAC_free), DLSYM_ARG(EVP_MAC_init), DLSYM_ARG(EVP_MAC_update), + DLSYM_ARG(EVP_MD_CTX_copy_ex), DLSYM_ARG(EVP_MD_CTX_free), DLSYM_ARG(EVP_MD_CTX_get0_md), DLSYM_ARG(EVP_MD_CTX_new), DLSYM_ARG(EVP_MD_CTX_set_pkey_ctx), DLSYM_ARG(EVP_MD_fetch), DLSYM_ARG(EVP_MD_free), + DLSYM_ARG(EVP_MD_get0_name), DLSYM_ARG(EVP_MD_get_size), DLSYM_ARG(EVP_MD_get_type), - DLSYM_ARG(EVP_MD_get0_name), DLSYM_ARG(EVP_PKEY_CTX_free), + DLSYM_ARG(EVP_PKEY_CTX_new), DLSYM_ARG(EVP_PKEY_CTX_new_from_name), DLSYM_ARG(EVP_PKEY_CTX_new_id), - DLSYM_ARG(EVP_PKEY_CTX_new), + DLSYM_ARG(EVP_PKEY_CTX_set0_rsa_oaep_label), DLSYM_ARG(EVP_PKEY_CTX_set_ec_paramgen_curve_nid), DLSYM_ARG(EVP_PKEY_CTX_set_rsa_oaep_md), DLSYM_ARG(EVP_PKEY_CTX_set_rsa_padding), DLSYM_ARG(EVP_PKEY_CTX_set_signature_md), - DLSYM_ARG(EVP_PKEY_CTX_set0_rsa_oaep_label), + DLSYM_ARG(EVP_PKEY_derive), DLSYM_ARG(EVP_PKEY_derive_init), DLSYM_ARG(EVP_PKEY_derive_set_peer), - DLSYM_ARG(EVP_PKEY_derive), - DLSYM_ARG(EVP_PKEY_encrypt_init), DLSYM_ARG(EVP_PKEY_encrypt), + DLSYM_ARG(EVP_PKEY_encrypt_init), DLSYM_ARG(EVP_PKEY_eq), DLSYM_ARG(EVP_PKEY_free), - DLSYM_ARG(EVP_PKEY_fromdata_init), DLSYM_ARG(EVP_PKEY_fromdata), + DLSYM_ARG(EVP_PKEY_fromdata_init), + DLSYM_ARG(EVP_PKEY_get1_encoded_public_key), DLSYM_ARG(EVP_PKEY_get_base_id), DLSYM_ARG(EVP_PKEY_get_bits), DLSYM_ARG(EVP_PKEY_get_bn_param), DLSYM_ARG(EVP_PKEY_get_group_name), DLSYM_ARG(EVP_PKEY_get_id), DLSYM_ARG(EVP_PKEY_get_utf8_string_param), - DLSYM_ARG(EVP_PKEY_get1_encoded_public_key), - DLSYM_ARG(EVP_PKEY_keygen_init), DLSYM_ARG(EVP_PKEY_keygen), - DLSYM_ARG(EVP_PKEY_new_raw_public_key), + DLSYM_ARG(EVP_PKEY_keygen_init), DLSYM_ARG(EVP_PKEY_new), - DLSYM_ARG(EVP_PKEY_verify_init), + DLSYM_ARG(EVP_PKEY_new_raw_public_key), DLSYM_ARG(EVP_PKEY_verify), + DLSYM_ARG(EVP_PKEY_verify_init), + DLSYM_ARG(EVP_aes_256_ctr), + DLSYM_ARG(EVP_aes_256_gcm), + DLSYM_ARG(EVP_get_cipherbyname), + DLSYM_ARG(EVP_get_digestbyname), DLSYM_ARG(EVP_sha1), DLSYM_ARG(EVP_sha256), DLSYM_ARG(EVP_sha384), DLSYM_ARG(EVP_sha512), DLSYM_ARG(HMAC), - DLSYM_ARG(i2d_ASN1_INTEGER), - DLSYM_ARG(i2d_PKCS7_fp), - DLSYM_ARG(i2d_PKCS7), - DLSYM_ARG(i2d_PUBKEY_fp), - DLSYM_ARG(i2d_PUBKEY), - DLSYM_ARG(i2d_PublicKey), - DLSYM_ARG(i2d_X509_NAME), - DLSYM_ARG(i2d_X509), DLSYM_ARG(OBJ_nid2obj), DLSYM_ARG(OBJ_nid2sn), DLSYM_ARG(OBJ_sn2nid), @@ -548,30 +553,33 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG(OSSL_PARAM_construct_utf8_string), DLSYM_ARG(OSSL_PARAM_free), DLSYM_ARG(OSSL_PROVIDER_try_load), - DLSYM_ARG(OSSL_STORE_close), - DLSYM_ARG(OSSL_STORE_expect), DLSYM_ARG(OSSL_STORE_INFO_free), DLSYM_ARG(OSSL_STORE_INFO_get1_CERT), DLSYM_ARG(OSSL_STORE_INFO_get1_PKEY), + DLSYM_ARG(OSSL_STORE_close), + DLSYM_ARG(OSSL_STORE_expect), DLSYM_ARG(OSSL_STORE_load), DLSYM_ARG(OSSL_STORE_open), + DLSYM_ARG(PEM_read_PUBKEY), + DLSYM_ARG(PEM_read_PrivateKey), + DLSYM_ARG(PEM_read_X509), DLSYM_ARG(PEM_read_bio_PrivateKey), DLSYM_ARG(PEM_read_bio_X509), - DLSYM_ARG(PEM_read_PrivateKey), - DLSYM_ARG(PEM_read_PUBKEY), - DLSYM_ARG(PEM_read_X509), - DLSYM_ARG(PEM_write_PrivateKey), DLSYM_ARG(PEM_write_PUBKEY), + DLSYM_ARG(PEM_write_PrivateKey), DLSYM_ARG(PEM_write_X509), DLSYM_ARG(PKCS5_PBKDF2_HMAC), + DLSYM_ARG(PKCS7_ATTR_SIGN_it), + DLSYM_ARG(PKCS7_SIGNER_INFO_free), + DLSYM_ARG(PKCS7_SIGNER_INFO_new), + DLSYM_ARG(PKCS7_SIGNER_INFO_set), + DLSYM_ARG(PKCS7_add0_attrib_signing_time), + DLSYM_ARG(PKCS7_add1_attrib_digest), DLSYM_ARG(PKCS7_add_attrib_content_type), DLSYM_ARG(PKCS7_add_attrib_smimecap), DLSYM_ARG(PKCS7_add_certificate), DLSYM_ARG(PKCS7_add_signed_attribute), DLSYM_ARG(PKCS7_add_signer), - DLSYM_ARG(PKCS7_add0_attrib_signing_time), - DLSYM_ARG(PKCS7_add1_attrib_digest), - DLSYM_ARG(PKCS7_ATTR_SIGN_it), DLSYM_ARG(PKCS7_content_new), DLSYM_ARG(PKCS7_ctrl), DLSYM_ARG(PKCS7_dataFinal), @@ -582,43 +590,76 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG(PKCS7_set_content), DLSYM_ARG(PKCS7_set_type), DLSYM_ARG(PKCS7_sign), - DLSYM_ARG(PKCS7_SIGNER_INFO_free), - DLSYM_ARG(PKCS7_SIGNER_INFO_new), - DLSYM_ARG(PKCS7_SIGNER_INFO_set), DLSYM_ARG(PKCS7_verify), DLSYM_ARG(SHA1), DLSYM_ARG(SHA512), -#ifndef OPENSSL_NO_UI_CONSOLE - DLSYM_ARG(UI_create_method), - DLSYM_ARG(UI_destroy_method), - DLSYM_ARG(UI_get_default_method), - DLSYM_ARG(UI_get_method), - DLSYM_ARG(UI_get_string_type), - DLSYM_ARG(UI_get0_output_string), - DLSYM_ARG(UI_method_get_ex_data), - DLSYM_ARG(UI_method_get_reader), - DLSYM_ARG(UI_method_set_ex_data), - DLSYM_ARG(UI_method_set_reader), - DLSYM_ARG(UI_OpenSSL), - DLSYM_ARG(UI_set_default_method), - DLSYM_ARG(UI_set_result), -#endif DLSYM_ARG(X509_ALGOR_free), DLSYM_ARG(X509_ALGOR_set0), DLSYM_ARG(X509_ATTRIBUTE_free), + DLSYM_ARG(X509_NAME_free), + DLSYM_ARG(X509_NAME_oneline), + DLSYM_ARG(X509_NAME_set), + DLSYM_ARG(X509_VERIFY_PARAM_set1_host), + DLSYM_ARG(X509_VERIFY_PARAM_set1_ip), + DLSYM_ARG(X509_VERIFY_PARAM_set_hostflags), DLSYM_ARG(X509_free), + DLSYM_ARG(X509_get0_serialNumber), DLSYM_ARG(X509_get_issuer_name), DLSYM_ARG(X509_get_pubkey), DLSYM_ARG(X509_get_signature_info), DLSYM_ARG(X509_get_subject_name), - DLSYM_ARG(X509_get0_serialNumber), DLSYM_ARG(X509_gmtime_adj), - DLSYM_ARG(X509_NAME_free), - DLSYM_ARG(X509_NAME_oneline), - DLSYM_ARG(X509_NAME_set), - DLSYM_ARG(X509_VERIFY_PARAM_set_hostflags), - DLSYM_ARG(X509_VERIFY_PARAM_set1_host), - DLSYM_ARG(X509_VERIFY_PARAM_set1_ip)); + DLSYM_ARG(d2i_ASN1_OCTET_STRING), + DLSYM_ARG(d2i_ECPKParameters), + DLSYM_ARG(d2i_PKCS7), + DLSYM_ARG(d2i_PUBKEY), + DLSYM_ARG(d2i_X509), + DLSYM_ARG(i2d_ASN1_INTEGER), + DLSYM_ARG(i2d_PKCS7), + DLSYM_ARG(i2d_PKCS7_fp), + DLSYM_ARG(i2d_PUBKEY), + DLSYM_ARG(i2d_PUBKEY_fp), + DLSYM_ARG(i2d_PublicKey), + DLSYM_ARG(i2d_X509), + DLSYM_ARG(i2d_X509_NAME), +#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0) + DLSYM_ARG_FORCE(ENGINE_by_id), + DLSYM_ARG_FORCE(ENGINE_free), + DLSYM_ARG_FORCE(ENGINE_init), + DLSYM_ARG_FORCE(ENGINE_load_private_key), +#endif +#if !defined(OPENSSL_NO_DEPRECATED_3_0) + DLSYM_ARG_FORCE(ECDSA_SIG_new), + DLSYM_ARG_FORCE(ECDSA_SIG_set0), + DLSYM_ARG_FORCE(ECDSA_do_verify), + DLSYM_ARG_FORCE(EC_KEY_check_key), + DLSYM_ARG_FORCE(EC_KEY_free), + DLSYM_ARG_FORCE(EC_KEY_new), + DLSYM_ARG_FORCE(EC_KEY_set_group), + DLSYM_ARG_FORCE(EC_KEY_set_public_key), + DLSYM_ARG_FORCE(EVP_PKEY_assign), + DLSYM_ARG_FORCE(RSAPublicKey_dup), + DLSYM_ARG_FORCE(RSA_free), + DLSYM_ARG_FORCE(RSA_new), + DLSYM_ARG_FORCE(RSA_set0_key), + DLSYM_ARG_FORCE(RSA_size), +#endif +#ifndef OPENSSL_NO_UI_CONSOLE + DLSYM_ARG(UI_OpenSSL), + DLSYM_ARG(UI_create_method), + DLSYM_ARG(UI_destroy_method), + DLSYM_ARG(UI_get0_output_string), + DLSYM_ARG(UI_get_default_method), + DLSYM_ARG(UI_get_method), + DLSYM_ARG(UI_get_string_type), + DLSYM_ARG(UI_method_get_ex_data), + DLSYM_ARG(UI_method_get_reader), + DLSYM_ARG(UI_method_set_ex_data), + DLSYM_ARG(UI_method_set_reader), + DLSYM_ARG(UI_set_default_method), + DLSYM_ARG(UI_set_result), +#endif + NULL); if (r >= 0) break; } diff --git a/src/shared/crypto-util.h b/src/shared/crypto-util.h index 00e0b4f452..bd7dda0931 100644 --- a/src/shared/crypto-util.h +++ b/src/shared/crypto-util.h @@ -47,6 +47,7 @@ int dlopen_libcrypto(int log_level); # include /* IWYU pragma: export */ # include /* IWYU pragma: export */ +# include /* IWYU pragma: export */ # include /* IWYU pragma: export */ # include /* IWYU pragma: export */ # include /* IWYU pragma: export */ @@ -64,12 +65,7 @@ extern DLSYM_PROTOTYPE(ASN1_ANY_it); extern DLSYM_PROTOTYPE(ASN1_BIT_STRING_it); extern DLSYM_PROTOTYPE(ASN1_BMPSTRING_it); extern DLSYM_PROTOTYPE(ASN1_BMPSTRING_new); -extern DLSYM_PROTOTYPE(ASN1_get_object); extern DLSYM_PROTOTYPE(ASN1_IA5STRING_it); -extern DLSYM_PROTOTYPE(ASN1_item_d2i); -extern DLSYM_PROTOTYPE(ASN1_item_free); -extern DLSYM_PROTOTYPE(ASN1_item_i2d); -extern DLSYM_PROTOTYPE(ASN1_item_new); extern DLSYM_PROTOTYPE(ASN1_OBJECT_it); extern DLSYM_PROTOTYPE(ASN1_OCTET_STRING_free); extern DLSYM_PROTOTYPE(ASN1_OCTET_STRING_it); @@ -82,46 +78,65 @@ extern DLSYM_PROTOTYPE(ASN1_STRING_set0); extern DLSYM_PROTOTYPE(ASN1_TIME_free); extern DLSYM_PROTOTYPE(ASN1_TIME_set); extern DLSYM_PROTOTYPE(ASN1_TYPE_new); +extern DLSYM_PROTOTYPE(ASN1_get_object); +extern DLSYM_PROTOTYPE(ASN1_item_d2i); +extern DLSYM_PROTOTYPE(ASN1_item_free); +extern DLSYM_PROTOTYPE(ASN1_item_i2d); +extern DLSYM_PROTOTYPE(ASN1_item_new); extern DLSYM_PROTOTYPE(BIO_ctrl); extern DLSYM_PROTOTYPE(BIO_find_type); -extern DLSYM_PROTOTYPE(BIO_free_all); extern DLSYM_PROTOTYPE(BIO_free); +extern DLSYM_PROTOTYPE(BIO_free_all); +extern DLSYM_PROTOTYPE(BIO_new); extern DLSYM_PROTOTYPE(BIO_new_mem_buf); extern DLSYM_PROTOTYPE(BIO_new_socket); -extern DLSYM_PROTOTYPE(BIO_new); extern DLSYM_PROTOTYPE(BIO_s_mem); extern DLSYM_PROTOTYPE(BIO_write); -extern DLSYM_PROTOTYPE(BN_bin2bn); -extern DLSYM_PROTOTYPE(BN_bn2nativepad); extern DLSYM_PROTOTYPE(BN_CTX_free); extern DLSYM_PROTOTYPE(BN_CTX_new); +extern DLSYM_PROTOTYPE(BN_CTX_secure_new); +extern DLSYM_PROTOTYPE(BN_add); +extern DLSYM_PROTOTYPE(BN_add_word); +extern DLSYM_PROTOTYPE(BN_bin2bn); +extern DLSYM_PROTOTYPE(BN_bn2bin); +extern DLSYM_PROTOTYPE(BN_bn2binpad); +extern DLSYM_PROTOTYPE(BN_bn2nativepad); +extern DLSYM_PROTOTYPE(BN_check_prime); +extern DLSYM_PROTOTYPE(BN_clear_free); +extern DLSYM_PROTOTYPE(BN_cmp); +extern DLSYM_PROTOTYPE(BN_copy); extern DLSYM_PROTOTYPE(BN_free); +extern DLSYM_PROTOTYPE(BN_is_negative); +extern DLSYM_PROTOTYPE(BN_mod_exp); +extern DLSYM_PROTOTYPE(BN_mod_inverse); +extern DLSYM_PROTOTYPE(BN_mod_lshift1_quick); +extern DLSYM_PROTOTYPE(BN_mod_mul); +extern DLSYM_PROTOTYPE(BN_mod_sqr); +extern DLSYM_PROTOTYPE(BN_mod_sub); +extern DLSYM_PROTOTYPE(BN_mul); extern DLSYM_PROTOTYPE(BN_new); +extern DLSYM_PROTOTYPE(BN_nnmod); extern DLSYM_PROTOTYPE(BN_num_bits); +extern DLSYM_PROTOTYPE(BN_secure_new); +extern DLSYM_PROTOTYPE(BN_set_word); +extern DLSYM_PROTOTYPE(BN_sub_word); extern DLSYM_PROTOTYPE(CRYPTO_free); -extern DLSYM_PROTOTYPE(d2i_ASN1_OCTET_STRING); -extern DLSYM_PROTOTYPE(d2i_ECPKParameters); -extern DLSYM_PROTOTYPE(d2i_PKCS7); -extern DLSYM_PROTOTYPE(d2i_PUBKEY); -extern DLSYM_PROTOTYPE(d2i_X509); +extern DLSYM_PROTOTYPE(ECDSA_SIG_free); extern DLSYM_PROTOTYPE(EC_GROUP_free); -extern DLSYM_PROTOTYPE(EC_GROUP_get_curve_name); -extern DLSYM_PROTOTYPE(EC_GROUP_get_curve); -extern DLSYM_PROTOTYPE(EC_GROUP_get_field_type); extern DLSYM_PROTOTYPE(EC_GROUP_get0_generator); extern DLSYM_PROTOTYPE(EC_GROUP_get0_order); +extern DLSYM_PROTOTYPE(EC_GROUP_get_curve); +extern DLSYM_PROTOTYPE(EC_GROUP_get_curve_name); +extern DLSYM_PROTOTYPE(EC_GROUP_get_field_type); extern DLSYM_PROTOTYPE(EC_GROUP_new_by_curve_name); extern DLSYM_PROTOTYPE(EC_POINT_free); extern DLSYM_PROTOTYPE(EC_POINT_new); extern DLSYM_PROTOTYPE(EC_POINT_oct2point); extern DLSYM_PROTOTYPE(EC_POINT_point2oct); -extern DLSYM_PROTOTYPE(ECDSA_SIG_free); extern DLSYM_PROTOTYPE(ERR_clear_error); -extern DLSYM_PROTOTYPE(ERR_error_string_n); extern DLSYM_PROTOTYPE(ERR_error_string); +extern DLSYM_PROTOTYPE(ERR_error_string_n); extern DLSYM_PROTOTYPE(ERR_get_error); -extern DLSYM_PROTOTYPE(EVP_aes_256_ctr); -extern DLSYM_PROTOTYPE(EVP_aes_256_gcm); extern DLSYM_PROTOTYPE(EVP_CIPHER_CTX_ctrl); extern DLSYM_PROTOTYPE(EVP_CIPHER_CTX_free); extern DLSYM_PROTOTYPE(EVP_CIPHER_CTX_new); @@ -141,46 +156,48 @@ extern DLSYM_PROTOTYPE(EVP_DigestVerifyInit); extern DLSYM_PROTOTYPE(EVP_EncryptFinal_ex); extern DLSYM_PROTOTYPE(EVP_EncryptInit_ex); extern DLSYM_PROTOTYPE(EVP_EncryptUpdate); -extern DLSYM_PROTOTYPE(EVP_get_cipherbyname); -extern DLSYM_PROTOTYPE(EVP_get_digestbyname); extern DLSYM_PROTOTYPE(EVP_MAC_CTX_free); +extern DLSYM_PROTOTYPE(EVP_MAC_CTX_new); +extern DLSYM_PROTOTYPE(EVP_MAC_fetch); +extern DLSYM_PROTOTYPE(EVP_MAC_final); extern DLSYM_PROTOTYPE(EVP_MAC_free); +extern DLSYM_PROTOTYPE(EVP_MAC_init); +extern DLSYM_PROTOTYPE(EVP_MAC_update); +extern DLSYM_PROTOTYPE(EVP_MD_CTX_copy_ex); extern DLSYM_PROTOTYPE(EVP_MD_CTX_free); extern DLSYM_PROTOTYPE(EVP_MD_CTX_get0_md); extern DLSYM_PROTOTYPE(EVP_MD_CTX_new); extern DLSYM_PROTOTYPE(EVP_MD_CTX_set_pkey_ctx); extern DLSYM_PROTOTYPE(EVP_MD_free); -extern DLSYM_PROTOTYPE(EVP_MD_get_size); extern DLSYM_PROTOTYPE(EVP_MD_get0_name); +extern DLSYM_PROTOTYPE(EVP_MD_get_size); extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_free); +extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_new); extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_new_from_name); extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_new_id); -extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_new); extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_set_rsa_padding); extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_set_signature_md); extern DLSYM_PROTOTYPE(EVP_PKEY_eq); extern DLSYM_PROTOTYPE(EVP_PKEY_free); -extern DLSYM_PROTOTYPE(EVP_PKEY_fromdata_init); extern DLSYM_PROTOTYPE(EVP_PKEY_fromdata); +extern DLSYM_PROTOTYPE(EVP_PKEY_fromdata_init); extern DLSYM_PROTOTYPE(EVP_PKEY_get_base_id); extern DLSYM_PROTOTYPE(EVP_PKEY_get_id); -extern DLSYM_PROTOTYPE(EVP_PKEY_keygen_init); extern DLSYM_PROTOTYPE(EVP_PKEY_keygen); -extern DLSYM_PROTOTYPE(EVP_PKEY_new_raw_public_key); +extern DLSYM_PROTOTYPE(EVP_PKEY_keygen_init); extern DLSYM_PROTOTYPE(EVP_PKEY_new); -extern DLSYM_PROTOTYPE(EVP_PKEY_verify_init); +extern DLSYM_PROTOTYPE(EVP_PKEY_new_raw_public_key); extern DLSYM_PROTOTYPE(EVP_PKEY_verify); +extern DLSYM_PROTOTYPE(EVP_PKEY_verify_init); +extern DLSYM_PROTOTYPE(EVP_aes_256_ctr); +extern DLSYM_PROTOTYPE(EVP_aes_256_gcm); +extern DLSYM_PROTOTYPE(EVP_get_cipherbyname); +extern DLSYM_PROTOTYPE(EVP_get_digestbyname); extern DLSYM_PROTOTYPE(EVP_sha1); extern DLSYM_PROTOTYPE(EVP_sha256); extern DLSYM_PROTOTYPE(EVP_sha384); extern DLSYM_PROTOTYPE(EVP_sha512); extern DLSYM_PROTOTYPE(HMAC); -extern DLSYM_PROTOTYPE(i2d_ASN1_INTEGER); -extern DLSYM_PROTOTYPE(i2d_PKCS7_fp); -extern DLSYM_PROTOTYPE(i2d_PKCS7); -extern DLSYM_PROTOTYPE(i2d_PUBKEY); -extern DLSYM_PROTOTYPE(i2d_X509_NAME); -extern DLSYM_PROTOTYPE(i2d_X509); extern DLSYM_PROTOTYPE(OBJ_nid2obj); extern DLSYM_PROTOTYPE(OBJ_nid2sn); extern DLSYM_PROTOTYPE(OBJ_sn2nid); @@ -191,24 +208,29 @@ extern DLSYM_PROTOTYPE(OPENSSL_sk_pop_free); extern DLSYM_PROTOTYPE(OPENSSL_sk_push); extern DLSYM_PROTOTYPE(OPENSSL_sk_value); extern DLSYM_PROTOTYPE(OSSL_EC_curve_nid2name); +extern DLSYM_PROTOTYPE(OSSL_PARAM_BLD_free); +extern DLSYM_PROTOTYPE(OSSL_PARAM_BLD_new); +extern DLSYM_PROTOTYPE(OSSL_PARAM_BLD_push_utf8_string); +extern DLSYM_PROTOTYPE(OSSL_PARAM_BLD_to_param); extern DLSYM_PROTOTYPE(OSSL_PARAM_construct_BN); extern DLSYM_PROTOTYPE(OSSL_PARAM_construct_end); extern DLSYM_PROTOTYPE(OSSL_PARAM_construct_octet_string); extern DLSYM_PROTOTYPE(OSSL_PARAM_construct_utf8_string); extern DLSYM_PROTOTYPE(OSSL_PARAM_free); -extern DLSYM_PROTOTYPE(PEM_read_PrivateKey); extern DLSYM_PROTOTYPE(PEM_read_PUBKEY); +extern DLSYM_PROTOTYPE(PEM_read_PrivateKey); extern DLSYM_PROTOTYPE(PEM_read_X509); -extern DLSYM_PROTOTYPE(PEM_write_PrivateKey); extern DLSYM_PROTOTYPE(PEM_write_PUBKEY); +extern DLSYM_PROTOTYPE(PEM_write_PrivateKey); extern DLSYM_PROTOTYPE(PEM_write_X509); extern DLSYM_PROTOTYPE(PKCS5_PBKDF2_HMAC); +extern DLSYM_PROTOTYPE(PKCS7_ATTR_SIGN_it); +extern DLSYM_PROTOTYPE(PKCS7_SIGNER_INFO_free); +extern DLSYM_PROTOTYPE(PKCS7_add0_attrib_signing_time); +extern DLSYM_PROTOTYPE(PKCS7_add1_attrib_digest); extern DLSYM_PROTOTYPE(PKCS7_add_attrib_content_type); extern DLSYM_PROTOTYPE(PKCS7_add_attrib_smimecap); extern DLSYM_PROTOTYPE(PKCS7_add_signed_attribute); -extern DLSYM_PROTOTYPE(PKCS7_add0_attrib_signing_time); -extern DLSYM_PROTOTYPE(PKCS7_add1_attrib_digest); -extern DLSYM_PROTOTYPE(PKCS7_ATTR_SIGN_it); extern DLSYM_PROTOTYPE(PKCS7_content_new); extern DLSYM_PROTOTYPE(PKCS7_ctrl); extern DLSYM_PROTOTYPE(PKCS7_dataFinal); @@ -218,21 +240,31 @@ extern DLSYM_PROTOTYPE(PKCS7_get_signer_info); extern DLSYM_PROTOTYPE(PKCS7_new); extern DLSYM_PROTOTYPE(PKCS7_set_content); extern DLSYM_PROTOTYPE(PKCS7_sign); -extern DLSYM_PROTOTYPE(PKCS7_SIGNER_INFO_free); extern DLSYM_PROTOTYPE(PKCS7_verify); extern DLSYM_PROTOTYPE(SHA1); extern DLSYM_PROTOTYPE(SHA512); extern DLSYM_PROTOTYPE(X509_ALGOR_free); extern DLSYM_PROTOTYPE(X509_ATTRIBUTE_free); +extern DLSYM_PROTOTYPE(X509_NAME_free); +extern DLSYM_PROTOTYPE(X509_NAME_oneline); +extern DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set1_host); +extern DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set1_ip); +extern DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set_hostflags); extern DLSYM_PROTOTYPE(X509_free); extern DLSYM_PROTOTYPE(X509_get_pubkey); extern DLSYM_PROTOTYPE(X509_get_subject_name); extern DLSYM_PROTOTYPE(X509_gmtime_adj); -extern DLSYM_PROTOTYPE(X509_NAME_free); -extern DLSYM_PROTOTYPE(X509_NAME_oneline); -extern DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set_hostflags); -extern DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set1_host); -extern DLSYM_PROTOTYPE(X509_VERIFY_PARAM_set1_ip); +extern DLSYM_PROTOTYPE(d2i_ASN1_OCTET_STRING); +extern DLSYM_PROTOTYPE(d2i_ECPKParameters); +extern DLSYM_PROTOTYPE(d2i_PKCS7); +extern DLSYM_PROTOTYPE(d2i_PUBKEY); +extern DLSYM_PROTOTYPE(d2i_X509); +extern DLSYM_PROTOTYPE(i2d_ASN1_INTEGER); +extern DLSYM_PROTOTYPE(i2d_PKCS7); +extern DLSYM_PROTOTYPE(i2d_PKCS7_fp); +extern DLSYM_PROTOTYPE(i2d_PUBKEY); +extern DLSYM_PROTOTYPE(i2d_X509); +extern DLSYM_PROTOTYPE(i2d_X509_NAME); #if !defined(OPENSSL_NO_DEPRECATED_3_0) DISABLE_WARNING_DEPRECATED_DECLARATIONS; @@ -245,11 +277,11 @@ extern DLSYM_PROTOTYPE(EC_KEY_new); extern DLSYM_PROTOTYPE(EC_KEY_set_group); extern DLSYM_PROTOTYPE(EC_KEY_set_public_key); extern DLSYM_PROTOTYPE(EVP_PKEY_assign); +extern DLSYM_PROTOTYPE(RSAPublicKey_dup); extern DLSYM_PROTOTYPE(RSA_free); extern DLSYM_PROTOTYPE(RSA_new); extern DLSYM_PROTOTYPE(RSA_set0_key); extern DLSYM_PROTOTYPE(RSA_size); -extern DLSYM_PROTOTYPE(RSAPublicKey_dup); REENABLE_WARNING; DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(EC_KEY*, sym_EC_KEY_free, EC_KEY_freep, NULL); @@ -262,6 +294,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(RSA*, sym_RSA_free, RSA_freep, NULL); #define sym_BIO_get_mem_ptr(b, pp) sym_BIO_ctrl((b), BIO_C_GET_BUF_MEM_PTR, 0, (char *) (pp)) #define sym_BIO_reset(b) sym_BIO_ctrl((b), BIO_CTRL_RESET, 0, NULL) #define sym_BN_num_bytes(a) ((sym_BN_num_bits(a) + 7) / 8) +#define sym_BN_one(a) sym_BN_set_word(a, 1) #define sym_EVP_MD_CTX_get_size(ctx) sym_EVP_MD_get_size(sym_EVP_MD_CTX_get0_md(ctx)) #define sym_EVP_MD_CTX_get0_name(ctx) sym_EVP_MD_get0_name(sym_EVP_MD_CTX_get0_md(ctx)) #define sym_EVP_PKEY_assign_RSA(pkey, rsa) sym_EVP_PKEY_assign((pkey), EVP_PKEY_RSA, (rsa)) @@ -272,6 +305,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO_RENAME(void*, sym_OPENSSL_free, OPENSSL_f DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(ASN1_OCTET_STRING*, sym_ASN1_OCTET_STRING_free, ASN1_OCTET_STRING_freep, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(ASN1_TIME*, sym_ASN1_TIME_free, ASN1_TIME_freep, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(BIGNUM*, sym_BN_free, BN_freep, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(BIGNUM*, sym_BN_clear_free, BN_clear_freep, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(BIO*, sym_BIO_free_all, BIO_free_allp, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(BIO*, sym_BIO_free, BIO_freep, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(BN_CTX*, sym_BN_CTX_free, BN_CTX_freep, NULL); @@ -286,6 +320,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(EVP_MD_CTX*, sym_EVP_MD_CTX_free, EVP_MD DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(EVP_MD*, sym_EVP_MD_free, EVP_MD_freep, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(EVP_PKEY_CTX*, sym_EVP_PKEY_CTX_free, EVP_PKEY_CTX_freep, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(EVP_PKEY*, sym_EVP_PKEY_free, EVP_PKEY_freep, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(OSSL_PARAM_BLD*, sym_OSSL_PARAM_BLD_free, OSSL_PARAM_BLD_freep, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(OSSL_PARAM*, sym_OSSL_PARAM_free, OSSL_PARAM_freep, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(PKCS7_SIGNER_INFO*, sym_PKCS7_SIGNER_INFO_free, PKCS7_SIGNER_INFO_freep, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(PKCS7*, sym_PKCS7_free, PKCS7_freep, NULL); diff --git a/src/shared/fsprg-openssl.c b/src/shared/fsprg-openssl.c new file mode 100644 index 0000000000..167f0607f4 --- /dev/null +++ b/src/shared/fsprg-openssl.c @@ -0,0 +1,668 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +/* + * This is based on + * fsprg v0.1 - (seekable) forward-secure pseudorandom generator + * Copyright © 2012 B. Poettering + * Contact: fsprg@point-at-infinity.org + * + * OpenSSL port of the original libgcrypt-based implementation. + * + * See "Practical Secure Logging: Seekable Sequential Key Generators" + * by G. A. Marson, B. Poettering for details: + * + * http://eprint.iacr.org/2013/397 + */ + +#include + +#include "alloc-util.h" /* IWYU pragma: keep */ +#include "crypto-util.h" +#include "fsprg-openssl.h" +#include "iovec-util.h" +#include "logarithm.h" +#include "sparse-endian.h" +#include "unaligned.h" + +#define RND_GEN_P 0x01 +#define RND_GEN_Q 0x02 +#define RND_GEN_X 0x03 + +/* Suppress a false positive from GCC's -Wstringop-overflow warning. Keep a local helper so the compiler can + * propagate the range check within this translation unit. */ +static bool secpar_is_valid(uint16_t secpar) { + return + secpar % 16 == 0 && + secpar >= 16 && + secpar <= 16384; +} + +bool fsprg_secpar_is_valid(uint16_t secpar) { + return secpar_is_valid(secpar); +} + +size_t fsprg_state_size(uint16_t secpar) { + assert(secpar_is_valid(secpar)); + + /* See comment in parse_state(). */ + return sizeof(uint16_t) + 2 * secpar / 8 + sizeof(uint64_t); +} + +#if HAVE_OPENSSL +static int mpi_export(struct iovec *iov, const BIGNUM *x) { + assert(iovec_is_set(iov)); + assert(x); + + if (sym_BN_is_negative(x)) + return -EINVAL; + + if (sym_BN_num_bytes(x) > (int) iov->iov_len) + return -ENOSPC; + + if (sym_BN_bn2binpad(x, iov->iov_base, iov->iov_len) != (int) iov->iov_len) + return -EIO; + + return 0; +} + +static int mpi_import(const struct iovec *iov, BIGNUM **ret) { + assert(iovec_is_set(iov)); + assert(ret); + + /* Allocate a new BIGNUM. */ + _cleanup_(BN_clear_freep) BIGNUM *x = sym_BN_secure_new(); + if (!x) + return -ENOMEM; + + if (!sym_BN_bin2bn(iov->iov_base, iov->iov_len, x)) + return -EIO; + + *ret = TAKE_PTR(x); + return 0; +} + +static int det_randomize(const struct iovec *seed, uint32_t idx, struct iovec *iov) { + assert(iovec_is_set(seed)); + assert(iovec_is_valid(iov)); + + /* Expand (seed, idx) into a deterministic pseudorandom byte stream. */ + + if (!iovec_is_set(iov)) + return 0; + + _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *ctx = sym_EVP_MD_CTX_new(); + if (!ctx) + return -ENOMEM; + + if (sym_EVP_DigestInit_ex(ctx, sym_EVP_sha256(), NULL) <= 0) + return -EIO; + + if (sym_EVP_DigestUpdate(ctx, seed->iov_base, seed->iov_len) <= 0) + return -EIO; + + if (sym_EVP_DigestUpdate(ctx, (be32_t[]) { htobe32(idx) }, sizeof(be32_t)) <= 0) + return -EIO; + + struct iovec v = *iov; + for (uint32_t ctr = 0; iovec_is_set(&v); ctr++) { + _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *tmp = sym_EVP_MD_CTX_new(); + if (!tmp) + return -ENOMEM; + + if (sym_EVP_MD_CTX_copy_ex(tmp, ctx) <= 0) + return -EIO; + + if (sym_EVP_DigestUpdate(tmp, (be32_t[]) { htobe32(ctr) }, sizeof(be32_t)) <= 0) + return -EIO; + + unsigned dlen; + uint8_t digest[EVP_MAX_MD_SIZE]; + CLEANUP_ERASE(digest); + + if (sym_EVP_DigestFinal_ex(tmp, digest, &dlen) <= 0) + return -EIO; + + size_t n = MIN(v.iov_len, dlen); + memcpy(v.iov_base, digest, n); + iovec_inc(&v, n); + } + + return 0; +} + +/* deterministically generate from seed/idx a prime of length (secpar / 2) bits that is 3 mod 4 */ +static int generate_prime3mod4( + uint16_t secpar, + const struct iovec *seed, + uint32_t idx, + BN_CTX *bn_ctx, + BIGNUM **ret) { + + int r; + + assert(iovec_is_set(seed)); + assert(bn_ctx); + assert(ret); + + if (!secpar_is_valid(secpar)) + return -EINVAL; + + _cleanup_(iovec_erase) struct iovec iov = IOVEC_ALLOCA(secpar / 2 / 8); + r = det_randomize(seed, idx, &iov); + if (r < 0) + return r; + + uint8_t *buf = iov.iov_base; + + /* Set the upper two bits so that n = pq has the maximum size. */ + buf[0] |= 0xc0; + + /* Make the candidate congruent to 3 (mod 4). */ + buf[iov.iov_len - 1] |= 0x03; + + _cleanup_(BN_clear_freep) BIGNUM *p = NULL; + r = mpi_import(&iov, &p); + if (r < 0) + return r; + + for (;;) { + /* negative: error, 0: composite, 1: prime */ + r = sym_BN_check_prime(p, bn_ctx, /* cb= */ NULL); + if (r < 0) + return -EIO; + if (r > 0) + break; + + /* Increment p by 4. */ + if (sym_BN_add_word(p, 4) <= 0) + return -EIO; + + /* Check if p is still (secpar / 2) bits. */ + if (sym_BN_num_bits(p) != secpar / 2) + return -EOVERFLOW; + } + + *ret = TAKE_PTR(p); + return 0; +} + +static int generate_keys( + uint16_t secpar, + const struct iovec *seed, + BN_CTX *bn_ctx, + BIGNUM **ret_p, /* prime */ + BIGNUM **ret_q, /* prime */ + BIGNUM **ret_n) { /* n = p * q */ + + int r; + + assert(iovec_is_set(seed)); + assert(bn_ctx); + assert(ret_p); + assert(ret_q); + assert(ret_n); + + if (!secpar_is_valid(secpar)) + return -EINVAL; + + _cleanup_(BN_clear_freep) BIGNUM *p = NULL; + r = generate_prime3mod4(secpar, seed, RND_GEN_P, bn_ctx, &p); + if (r < 0) + return r; + + _cleanup_(BN_clear_freep) BIGNUM *q = NULL; + r = generate_prime3mod4(secpar, seed, RND_GEN_Q, bn_ctx, &q); + if (r < 0) + return r; + + _cleanup_(BN_clear_freep) BIGNUM *n = sym_BN_secure_new(); + if (!n) + return -ENOMEM; + + if (sym_BN_mul(n, p, q, bn_ctx) <= 0) + return -EIO; + + if (sym_BN_num_bits(n) != secpar) + return -EIO; + + *ret_p = TAKE_PTR(p); + *ret_q = TAKE_PTR(q); + *ret_n = TAKE_PTR(n); + return 0; +} + +/* deterministically generate from seed/idx a quadratic residue (mod n) */ +static int generate_square( + uint16_t secpar, + const struct iovec *seed, + uint32_t idx, + const BIGNUM *n, + BN_CTX *bn_ctx, + BIGNUM **ret) { + + int r; + + assert(iovec_is_set(seed)); + assert(n); + assert(bn_ctx); + assert(ret); + + if (!secpar_is_valid(secpar)) + return -EINVAL; + + _cleanup_(iovec_erase) struct iovec iov = IOVEC_ALLOCA(secpar / 8); + r = det_randomize(seed, idx, &iov); + if (r < 0) + return r; + + *(uint8_t*) iov.iov_base &= 0x7f; /* Clear the upper bit so that we are likely to have x < n. */ + + _cleanup_(BN_clear_freep) BIGNUM *x = NULL; + r = mpi_import(&iov, &x); + if (r < 0) + return r; + + /* x < n should always hold for a valid modulus generated by generate_keys(). */ + if (sym_BN_cmp(x, n) >= 0) + return -EINVAL; + + /* x := x^2 mod n */ + if (sym_BN_mod_sqr(x, x, n, bn_ctx) <= 0) + return -EIO; + + *ret = TAKE_PTR(x); + return 0; +} + +/* Compute 2^m mod phi(p), where p is prime and phi(p) = p - 1. */ +static int compute_two_pow_mod_phi( + uint64_t m, + const BIGNUM *p, + BN_CTX *bn_ctx, + BIGNUM **ret) { + + assert(p); + assert(bn_ctx); + assert(ret); + + _cleanup_(BN_clear_freep) BIGNUM *phi = sym_BN_secure_new(); + if (!phi) + return -ENOMEM; + + /* phi := p */ + if (!sym_BN_copy(phi, p)) + return -EIO; + + /* phi := p - 1 */ + if (sym_BN_sub_word(phi, 1) <= 0) + return -EIO; + + _cleanup_(BN_clear_freep) BIGNUM *x = sym_BN_secure_new(); + if (!x) + return -ENOMEM; + + /* x := 1 */ + if (!sym_BN_one(x)) + return -EIO; + + if (m == 0) { + /* 2^0 mod phi = 1. */ + *ret = TAKE_PTR(x); + return 0; + } + + /* Square-and-multiply. Iterate over the bits of m from MSB to LSB. */ + for (int n = LOG2ULL(m); n >= 0; n--) { + if (sym_BN_mod_sqr(x, x, phi, bn_ctx) <= 0) /* x := x^2 mod phi */ + return -EIO; + + if (m & (UINT64_C(1) << n) && + sym_BN_mod_lshift1_quick(x, x, phi) <= 0) /* x := 2x mod phi */ + return -EIO; + } + + *ret = TAKE_PTR(x); + return 0; +} + +/* Decompose x ∈ Z_n into (xp, xq) ∈ Z_p × Z_q using the Chinese Remainder Theorem. */ +static int crt_decompose( + const BIGNUM *x, + const BIGNUM *p, + const BIGNUM *q, + BN_CTX *bn_ctx, + BIGNUM **ret_xp, + BIGNUM **ret_xq) { + + assert(x); + assert(p); + assert(q); + assert(bn_ctx); + assert(ret_xp); + assert(ret_xq); + + _cleanup_(BN_clear_freep) BIGNUM *xp = sym_BN_secure_new(); + if (!xp) + return -ENOMEM; + + _cleanup_(BN_clear_freep) BIGNUM *xq = sym_BN_secure_new(); + if (!xq) + return -ENOMEM; + + if (sym_BN_nnmod(xp, x, p, bn_ctx) <= 0) + return -EIO; + + if (sym_BN_nnmod(xq, x, q, bn_ctx) <= 0) + return -EIO; + + *ret_xp = TAKE_PTR(xp); + *ret_xq = TAKE_PTR(xq); + return 0; +} + +/* Compose (xp, xq) ∈ Z_p × Z_q into x ∈ Z_n using the Chinese Remainder Theorem. */ +static int crt_compose( + const BIGNUM *xp, + const BIGNUM *xq, + const BIGNUM *p, + const BIGNUM *q, + BN_CTX *bn_ctx, + BIGNUM **ret) { + + assert(xp); + assert(xq); + assert(p); + assert(q); + assert(bn_ctx); + assert(ret); + + _cleanup_(BN_clear_freep) BIGNUM *x = sym_BN_secure_new(); + if (!x) + return -ENOMEM; + + /* x := xq - xp mod q */ + if (sym_BN_mod_sub(x, xq, xp, q, bn_ctx) <= 0) + return -EIO; + + /* Compute p^-1 mod q. */ + _cleanup_(BN_clear_freep) BIGNUM *p_inv = sym_BN_secure_new(); + if (!p_inv) + return -ENOMEM; + + if (!sym_BN_mod_inverse(p_inv, p, q, bn_ctx)) + return -EIO; + + /* x := (xq - xp) * p^-1 mod q */ + if (sym_BN_mod_mul(x, x, p_inv, q, bn_ctx) <= 0) + return -EIO; + + /* x := p * ((xq - xp) * p^-1 mod q) */ + if (sym_BN_mul(x, x, p, bn_ctx) <= 0) + return -EIO; + + /* x := p * ((xq - xp) * p^-1 mod q) + xp */ + if (sym_BN_add(x, x, xp) <= 0) + return -EIO; + + *ret = TAKE_PTR(x); + return 0; +} + +static int save_state( + uint16_t secpar, + const BIGNUM *modulus, + const BIGNUM *current, + uint64_t epoch, + struct iovec *state) { + + int r; + + assert(modulus); + assert(current); + assert(iovec_is_set(state)); + + if (!secpar_is_valid(secpar)) + return -EINVAL; + + if (state->iov_len != fsprg_state_size(secpar)) + return -EINVAL; + + uint8_t *s = state->iov_base; + + /* header */ + unaligned_write_be16(s, secpar / 16 - 1); + s += sizeof(uint16_t); + + /* modulus */ + r = mpi_export(&IOVEC_MAKE(s, secpar / 8), modulus); + if (r < 0) + return r; + s += secpar / 8; + + /* current */ + r = mpi_export(&IOVEC_MAKE(s, secpar / 8), current); + if (r < 0) + return r; + s += secpar / 8; + + /* epoch */ + unaligned_write_be64(s, epoch); + return 0; +} + +static int parse_state( + const struct iovec *state, + uint16_t *ret_secpar, + BIGNUM **ret_modulus, + BIGNUM **ret_current, + uint64_t *ret_epoch) { + + int r; + + assert(iovec_is_set(state)); + + /* The serialized state consists of: + * - header: 2 bytes. Encodes secpar / 16 - 1, where 16 <= secpar <= 16384. + * - modulus (a.k.a. n): secpar / 8 bytes. + * - current (a.k.a. x): secpar / 8 bytes. + * - epoch: 8 bytes. */ + + if (state->iov_len < sizeof(uint16_t)) + return -EBADMSG; + + uint16_t header = unaligned_read_be16(state->iov_base); + if (header >= 1024) + return -EBADMSG; + + uint16_t secpar = 16 * (header + 1); + if (state->iov_len != fsprg_state_size(secpar)) + return -EBADMSG; + + _cleanup_(BN_clear_freep) BIGNUM *modulus = NULL; + if (ret_modulus) { + r = mpi_import(&IOVEC_MAKE((uint8_t*) state->iov_base + sizeof(uint16_t), secpar / 8), &modulus); + if (r < 0) + return r; + } + + _cleanup_(BN_clear_freep) BIGNUM *current = NULL; + if (ret_current) { + r = mpi_import(&IOVEC_MAKE((uint8_t*) state->iov_base + sizeof(uint16_t) + secpar / 8, secpar / 8), ¤t); + if (r < 0) + return r; + } + + if (ret_secpar) + *ret_secpar = secpar; + if (ret_modulus) + *ret_modulus = TAKE_PTR(modulus); + if (ret_current) + *ret_current = TAKE_PTR(current); + if (ret_epoch) + *ret_epoch = unaligned_read_be64((uint8_t*) state->iov_base + sizeof(uint16_t) + 2 * secpar / 8); + return 0; +} +#endif + +int fsprg_generate_state( + uint16_t secpar, + uint64_t epoch, + const struct iovec *seed, + struct iovec *state) { + +#if HAVE_OPENSSL + int r; + + assert(iovec_is_set(seed)); + assert(iovec_is_set(state)); + + r = dlopen_libcrypto(LOG_DEBUG); + if (r < 0) + return r; + + if (!secpar_is_valid(secpar)) + return -EINVAL; + + _cleanup_(BN_CTX_freep) BN_CTX *bn_ctx = sym_BN_CTX_secure_new(); + if (!bn_ctx) + return -ENOMEM; + + _cleanup_(BN_clear_freep) BIGNUM *p = NULL, *q = NULL, *n = NULL; + r = generate_keys(secpar, seed, bn_ctx, &p, &q, &n); + if (r < 0) + return r; + + _cleanup_(BN_clear_freep) BIGNUM *x = NULL; + r = generate_square(secpar, seed, RND_GEN_X, n, bn_ctx, &x); + if (r < 0) + return r; + + if (epoch != 0) { + /* Decompose x from Z_n into Z_p × Z_q using CRT. */ + _cleanup_(BN_clear_freep) BIGNUM *xp = NULL, *xq = NULL; + r = crt_decompose(x, p, q, bn_ctx, &xp, &xq); + if (r < 0) + return r; + + /* Compute 2^epoch (mod phi(p)). */ + _cleanup_(BN_clear_freep) BIGNUM *kp = NULL; + r = compute_two_pow_mod_phi(epoch, p, bn_ctx, &kp); + if (r < 0) + return r; + + /* Compute 2^epoch (mod phi(q)). */ + _cleanup_(BN_clear_freep) BIGNUM *kq = NULL; + r = compute_two_pow_mod_phi(epoch, q, bn_ctx, &kq); + if (r < 0) + return r; + + /* Compute x^(2^epoch) (mod p). */ + if (sym_BN_mod_exp(xp, xp, kp, p, bn_ctx) <= 0) + return -EIO; + + /* Compute x^(2^epoch) (mod q). */ + if (sym_BN_mod_exp(xq, xq, kq, q, bn_ctx) <= 0) + return -EIO; + + /* Reconstruct x modulo n from its residues modulo p and q. */ + BN_clear_freep(&x); + r = crt_compose(xp, xq, p, q, bn_ctx, &x); + if (r < 0) + return r; + } + + return save_state(secpar, n, x, epoch, state); +#else + return -EOPNOTSUPP; +#endif +} + +int fsprg_evolve(struct iovec *state) { +#if HAVE_OPENSSL + int r; + + assert(iovec_is_set(state)); + + r = dlopen_libcrypto(LOG_DEBUG); + if (r < 0) + return r; + + uint16_t secpar; + uint64_t epoch; + _cleanup_(BN_clear_freep) BIGNUM *n = NULL, *x = NULL; + r = parse_state(state, &secpar, &n, &x, &epoch); + if (r < 0) + return r; + + _cleanup_(BN_CTX_freep) BN_CTX *bn_ctx = sym_BN_CTX_secure_new(); + if (!bn_ctx) + return -ENOMEM; + + /* Update the current state x := x^2 mod n */ + if (sym_BN_mod_sqr(x, x, n, bn_ctx) <= 0) + return -EIO; + + /* Store the updated current state. */ + uint8_t *s = (uint8_t*) state->iov_base + sizeof(uint16_t) + secpar / 8; + r = mpi_export(&IOVEC_MAKE(s, secpar / 8), x); + if (r < 0) + return r; + + /* Increment epoch */ + unaligned_write_be64(s + secpar / 8, epoch + 1); + return 0; +#else + return -EOPNOTSUPP; +#endif +} + +int fsprg_get_epoch(const struct iovec *state, uint64_t *ret) { +#if HAVE_OPENSSL + int r; + + assert(iovec_is_set(state)); + + r = dlopen_libcrypto(LOG_DEBUG); + if (r < 0) + return r; + + return parse_state( + state, + /* ret_secpar= */ NULL, + /* ret_modulus= */ NULL, + /* ret_current= */ NULL, + ret); +#else + return -EOPNOTSUPP; +#endif +} + +int fsprg_get_key(const struct iovec *state, struct iovec *key) { +#if HAVE_OPENSSL + int r; + + assert(iovec_is_set(state)); + assert(iovec_is_valid(key)); + + r = dlopen_libcrypto(LOG_DEBUG); + if (r < 0) + return r; + + uint16_t secpar; + r = parse_state(state, + /* ret_secpar= */ &secpar, + /* ret_modulus= */ NULL, + /* ret_current= */ NULL, + /* ret_epoch= */ NULL); + if (r < 0) + return r; + + struct iovec seed = IOVEC_MAKE( + (uint8_t*) state->iov_base + sizeof(uint16_t), + 2 * secpar / 8 + sizeof(uint64_t)); + + return det_randomize(&seed, /* idx= */ 0, key); +#else + return -EOPNOTSUPP; +#endif +} diff --git a/src/shared/fsprg-openssl.h b/src/shared/fsprg-openssl.h new file mode 100644 index 0000000000..e9e5649617 --- /dev/null +++ b/src/shared/fsprg-openssl.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "sd-forward.h" + +#define FSPRG_RECOMMENDED_SECPAR 1536 +#define FSPRG_RECOMMENDED_SEEDLEN (96/8) + +bool fsprg_secpar_is_valid(uint16_t secpar); +size_t fsprg_state_size(uint16_t secpar); +int fsprg_generate_state( + uint16_t secpar, + uint64_t epoch, + const struct iovec *seed, + struct iovec *state); +int fsprg_evolve(struct iovec *state); +int fsprg_get_epoch(const struct iovec *state, uint64_t *ret); +int fsprg_get_key(const struct iovec *state, struct iovec *key); diff --git a/src/shared/journal-authenticate.c b/src/shared/journal-authenticate.c new file mode 100644 index 0000000000..20434c9b0b --- /dev/null +++ b/src/shared/journal-authenticate.c @@ -0,0 +1,596 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include +#include + +#include "alloc-util.h" +#include "crypto-util.h" +#include "fd-util.h" +#include "fsprg-openssl.h" +#include "hexdecoct.h" +#include "iovec-util.h" +#include "journal-authenticate.h" +#include "journal-def.h" +#include "journal-file.h" +#include "log.h" +#include "memory-util.h" +#include "string-util.h" +#include "time-util.h" + +#if HAVE_OPENSSL + +struct JournalAuthContext { + EVP_MAC *hmac; + EVP_MAC_CTX *hmac_ctx; + OSSL_PARAM *ossl_params; + bool hmac_running; + + FSSHeader *fss_file; + size_t fss_mmap_size; + + uint64_t fss_start_usec; + uint64_t fss_interval_usec; + + struct iovec fsprg_state; + struct iovec fsprg_seed; +}; + +static JournalAuthContext* journal_auth_free(JournalAuthContext *c) { + if (!c) + return NULL; + + if (c->fss_file) + munmap(c->fss_file, c->fss_mmap_size); + else + iovec_done_erase(&c->fsprg_state); + + iovec_done_erase(&c->fsprg_seed); + + if (c->ossl_params) + sym_OSSL_PARAM_free(c->ossl_params); + if (c->hmac_ctx) + sym_EVP_MAC_CTX_free(c->hmac_ctx); + if (c->hmac) + sym_EVP_MAC_free(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) + return NULL; + + assert_se(munmap(p, PAGE_ALIGN(sizeof(FSSHeader))) >= 0); + return NULL; +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(FSSHeader*, fssheader_free); + +static int journal_auth_load(JournalAuthContext **ret) { + int r; + + 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. */ + + sd_id128_t machine; + r = sd_id128_get_machine(&machine); + if (r < 0) + return r; + + _cleanup_free_ char *path = NULL; + if (asprintf(&path, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss", + SD_ID128_FORMAT_VAL(machine)) < 0) + return -ENOMEM; + + _cleanup_close_ int fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600); + if (fd < 0) { + if (errno != ENOENT) + log_error_errno(errno, "Failed to open %s: %m", path); + + return -errno; + } + + struct stat st; + if (fstat(fd, &st) < 0) + return -errno; + + if (st.st_size < (off_t) sizeof(FSSHeader)) + return -ENODATA; + + _cleanup_(fssheader_freep) FSSHeader *header = + mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0); + if (header == MAP_FAILED) + return -errno; + + if (memcmp(header->signature, (const uint8_t[]) FSS_HEADER_SIGNATURE, 8) != 0) + return -EBADMSG; + + if (header->incompatible_flags != 0) + return -EPROTONOSUPPORT; + + if (le64toh(header->header_size) < sizeof(FSSHeader)) + return -EBADMSG; + + uint16_t secpar = le16toh(header->fsprg_secpar); + if (!fsprg_secpar_is_valid(secpar)) + return -EBADMSG; + + if (le64toh(header->fsprg_state_size) != fsprg_state_size(secpar)) + return -EBADMSG; + + uint64_t fss_file_size; + if (!ADD_SAFE(&fss_file_size, le64toh(header->header_size), le64toh(header->fsprg_state_size))) + return -EBADMSG; + + if (fss_file_size >= SIZE_MAX) + return -EBADMSG; + + if ((uint64_t) st.st_size < fss_file_size) + return -ENODATA; + + if (!sd_id128_equal(machine, header->machine_id)) + return -EHOSTDOWN; + + 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; + + FSSHeader *p = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (p == MAP_FAILED) + return -errno; + + *c = (JournalAuthContext) { + .fss_mmap_size = sz, + .fss_file = p, + + .fss_start_usec = le64toh(p->start_usec), + .fss_interval_usec = le64toh(p->interval_usec), + + .fsprg_state = IOVEC_MAKE( + (uint8_t*) p + le64toh(p->header_size), + le64toh(p->fsprg_state_size)), + }; + + *ret = TAKE_PTR(c); + return 0; +} + +static int journal_auth_load_key(JournalAuthContext **ret, const char *key) { + int r; + + assert(ret); + assert(key); + + size_t seed_size = FSPRG_RECOMMENDED_SEEDLEN; + _cleanup_(erase_and_freep) uint8_t *seed = malloc(seed_size); + if (!seed) + return -ENOMEM; + + const char *k = key; + for (size_t c = 0; c < seed_size; c++) { + int x, y; + + k = skip_leading_chars(k, "-"); + + x = unhexchar(*k); + if (x < 0) + return -EKEYREJECTED; + k++; + + y = unhexchar(*k); + if (y < 0) + return -EKEYREJECTED; + k++; + + seed[c] = (uint8_t) (x * 16 + y); + } + + if (*k != '/') + return -EKEYREJECTED; + k++; + + uint64_t start, interval; + r = sscanf(k, "%"PRIx64"-%"PRIx64, &start, &interval); + if (r != 2) + return -EKEYREJECTED; + + if (start == 0 || interval == 0) + return -EKEYREJECTED; + + uint64_t start_usec; + if (!MUL_SAFE(&start_usec, start, interval)) + return -EKEYREJECTED; + + _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(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, c->fss_interval_usec) || + !INC_SAFE(&start, c->fss_start_usec) || + !ADD_SAFE(&end, start, c->fss_interval_usec)) + return -ERANGE; + + if (ret_start) + *ret_start = start; + if (ret_end) + *ret_end = end; + + return 0; +} + +static int journal_auth_next_evolve_usec(const JournalAuthContext *c, usec_t *ret) { + int r; + + assert(c); + + uint64_t epoch; + r = fsprg_get_epoch(&c->fsprg_state, &epoch); + if (r < 0) + return r; + + return journal_auth_epoch_to_realtime_usec(c, epoch, /* ret_start= */ NULL, ret); +} + +static int journal_auth_seek(JournalAuthContext *c, uint64_t goal) { + int r; + + assert(c); + assert(iovec_is_set(&c->fsprg_seed)); + + if (iovec_is_set(&c->fsprg_state)) { + uint64_t epoch; + r = fsprg_get_epoch(&c->fsprg_state, &epoch); + if (r < 0) + return r; + if (goal == epoch) + return 0; + + if (goal == epoch + 1) + return fsprg_evolve(&c->fsprg_state); + } else { + r = iovec_alloc(fsprg_state_size(FSPRG_RECOMMENDED_SECPAR), &c->fsprg_state); + if (r < 0) + return r; + } + + log_debug("Seeking FSPRG key to %"PRIu64".", goal); + return fsprg_generate_state(FSPRG_RECOMMENDED_SECPAR, goal, &c->fsprg_seed, &c->fsprg_state); +} + +static int journal_auth_setup(JournalAuthContext *c) { + int r; + + assert(c); + + if (c->hmac) + return 0; + + r = dlopen_libcrypto(LOG_DEBUG); + if (r < 0) + return r; + + _cleanup_(EVP_MAC_freep) EVP_MAC *hmac = sym_EVP_MAC_fetch(NULL, "HMAC", NULL); + if (!hmac) + return log_openssl_errors(LOG_DEBUG, "EVP_MAC_fetch() failed"); + + _cleanup_(EVP_MAC_CTX_freep) EVP_MAC_CTX *ctx = sym_EVP_MAC_CTX_new(hmac); + if (!ctx) + return log_openssl_errors(LOG_DEBUG, "EVP_MAC_CTX_new() failed"); + + _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = sym_OSSL_PARAM_BLD_new(); + if (!bld) + return log_openssl_errors(LOG_DEBUG, "OSSL_PARAM_BLD_new() failed"); + + if (sym_OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_MAC_PARAM_DIGEST, "SHA256", 0) <= 0) + return log_openssl_errors(LOG_DEBUG, "OSSL_PARAM_BLD_push_utf8_string() failed"); + + _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = sym_OSSL_PARAM_BLD_to_param(bld); + if (!params) + return log_openssl_errors(LOG_DEBUG, "OSSL_PARAM_BLD_to_param() failed"); + + c->hmac = TAKE_PTR(hmac); + c->hmac_ctx = TAKE_PTR(ctx); + c->ossl_params = TAKE_PTR(params); + return 0; +} + +static int journal_auth_start(JournalAuthContext *c) { + int r; + + assert(c); + + if (c->hmac_running) + return 0; + + r = journal_auth_setup(c); + if (r < 0) + return r; + + uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */ + CLEANUP_ERASE(key); + r = fsprg_get_key(&c->fsprg_state, &IOVEC_MAKE(key, sizeof(key))); + if (r < 0) + return r; + + /* Prepare HMAC for next cycle */ + if (sym_EVP_MAC_init(c->hmac_ctx, key, sizeof(key), c->ossl_params) <= 0) + return log_openssl_errors(LOG_DEBUG, "sym_EVP_MAC_init() failed"); + + c->hmac_running = true; + return 0; +} + +static int journal_auth_end(JournalAuthContext *c, uint8_t ret[static TAG_LENGTH]) { + assert(c); + assert(ret); + + if (!c->hmac_running) + return -EINVAL; + + c->hmac_running = false; + + uint8_t tag[TAG_LENGTH]; + CLEANUP_ERASE(tag); + + size_t len; + if (sym_EVP_MAC_final(c->hmac_ctx, tag, &len, TAG_LENGTH) <= 0 || len != TAG_LENGTH) + return -EIO; + + memcpy(ret, tag, TAG_LENGTH); + return 0; +} + +static int journal_auth_put_header(JournalAuthContext *c, JournalFile *f) { + int r; + + assert(c); + assert(f); + + r = journal_auth_start(c); + if (r < 0) + return r; + + /* All but state+reserved, boot_id, arena_size, + * tail_object_offset, n_objects, n_entries, + * tail_entry_seqnum, head_entry_seqnum, entry_array_offset, + * head_entry_realtime, tail_entry_realtime, + * tail_entry_monotonic, n_data, n_fields, n_tags, + * n_entry_arrays. */ + + if (sym_EVP_MAC_update(c->hmac_ctx, f->header->signature, offsetof(Header, state) - offsetof(Header, signature)) <= 0) + return -EIO; + if (sym_EVP_MAC_update(c->hmac_ctx, (void*) &f->header->file_id, offsetof(Header, tail_entry_boot_id) - offsetof(Header, file_id)) <= 0) + return -EIO; + if (sym_EVP_MAC_update(c->hmac_ctx, (void*) &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id)) <= 0) + return -EIO; + if (sym_EVP_MAC_update(c->hmac_ctx, (void*) &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset)) <= 0) + return -EIO; + + return 0; +} + +static int journal_auth_put_object(JournalAuthContext *c, JournalFile *f, ObjectType type, Object *o, uint64_t p) { + int r; + + assert(c); + assert(f); + + r = journal_auth_start(c); + if (r < 0) + return r; + + if (!o) { + r = journal_file_move_to_object(f, type, p, &o); + if (r < 0) + return r; + } else if (type > OBJECT_UNUSED && o->object.type != type) + return -EBADMSG; + + if (sym_EVP_MAC_update(c->hmac_ctx, (void*) o, offsetof(ObjectHeader, payload)) <= 0) + return -EIO; + + switch (o->object.type) { + + case OBJECT_DATA: + /* All but hash and payload are mutable */ + if (sym_EVP_MAC_update(c->hmac_ctx, (void*) &o->data.hash, sizeof(o->data.hash)) <= 0) + return -EIO; + if (sym_EVP_MAC_update(c->hmac_ctx, journal_file_data_payload_field(f, o), le64toh(o->object.size) - journal_file_data_payload_offset(f)) <= 0) + return -EIO; + break; + + case OBJECT_FIELD: + /* Same here */ + if (sym_EVP_MAC_update(c->hmac_ctx, (void*) &o->field.hash, sizeof(o->field.hash)) <= 0) + return -EIO; + if (sym_EVP_MAC_update(c->hmac_ctx, o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload)) <= 0) + return -EIO; + break; + + case OBJECT_ENTRY: + /* All */ + if (sym_EVP_MAC_update(c->hmac_ctx, (void*) &o->entry.seqnum, le64toh(o->object.size) - offsetof(Object, entry.seqnum)) <= 0) + return -EIO; + break; + + case OBJECT_FIELD_HASH_TABLE: + case OBJECT_DATA_HASH_TABLE: + case OBJECT_ENTRY_ARRAY: + /* Nothing: everything is mutable */ + break; + + case OBJECT_TAG: + /* All but the tag itself */ + if (sym_EVP_MAC_update(c->hmac_ctx, (void*) &o->tag.seqnum, sizeof(o->tag.seqnum)) <= 0) + return -EIO; + if (sym_EVP_MAC_update(c->hmac_ctx, (void*) &o->tag.epoch, sizeof(o->tag.epoch)) <= 0) + return -EIO; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int journal_auth_append_tag(JournalAuthContext *c, JournalFile *f) { + int r; + + assert(c); + assert(f); + + r = journal_auth_start(c); + if (r < 0) + return r; + + Object *o; + uint64_t p; + r = journal_file_append_object(f, OBJECT_TAG, sizeof(struct TagObject), &o, &p); + if (r < 0) + return r; + + uint64_t seqnum = le64toh(f->header->n_tags) + 1; + f->header->n_tags = htole64(seqnum); + + o->tag.seqnum = htole64(seqnum); + + uint64_t epoch; + r = fsprg_get_epoch(&c->fsprg_state, &epoch); + if (r < 0) + return r; + o->tag.epoch = htole64(epoch); + + log_debug("Writing tag %"PRIu64" for epoch %"PRIu64"", + le64toh(o->tag.seqnum), epoch); + + /* Add the tag object itself, so that we can protect its + * header. This will exclude the actual hash value in it */ + 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 */ + return journal_auth_end(c, o->tag.tag); +} + +static int journal_auth_append_tag_first(JournalAuthContext *c, JournalFile *f) { + uint64_t p; + int r; + + assert(c); + assert(f); + + log_debug("Calculating first tag..."); + + r = journal_auth_put_header(c, f); + if (r < 0) + return r; + + p = le64toh(f->header->field_hash_table_offset); + if (p < offsetof(Object, hash_table.items)) + return -EINVAL; + p -= offsetof(Object, hash_table.items); + + r = journal_auth_put_object(c, f, OBJECT_FIELD_HASH_TABLE, NULL, p); + if (r < 0) + return r; + + p = le64toh(f->header->data_hash_table_offset); + if (p < offsetof(Object, hash_table.items)) + return -EINVAL; + p -= offsetof(Object, hash_table.items); + + r = journal_auth_put_object(c, f, OBJECT_DATA_HASH_TABLE, NULL, p); + if (r < 0) + return r; + + return journal_auth_append_tag(c, f); +} + +static 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 (realtime <= 0) + realtime = now(CLOCK_REALTIME); + + uint64_t goal = usec_sub_unsigned(realtime, c->fss_start_usec) / c->fss_interval_usec; + + for (;;) { + uint64_t epoch; + r = fsprg_get_epoch(&c->fsprg_state, &epoch); + if (r < 0) + return r; + if (epoch >= goal) + return 0; + + r = journal_auth_append_tag(c, f); + if (r < 0) + return r; + + r = fsprg_evolve(&c->fsprg_state); + if (r < 0) + return r; + } +} + +static const JournalAuthOps journal_auth_ops = { + .free = journal_auth_free, + .load = journal_auth_load, + .load_key = journal_auth_load_key, + .epoch_to_realtime_usec = journal_auth_epoch_to_realtime_usec, + .next_evolve_usec = journal_auth_next_evolve_usec, + .seek = journal_auth_seek, + .start = journal_auth_start, + .end = journal_auth_end, + .put_header = journal_auth_put_header, + .put_object = journal_auth_put_object, + .append_tag = journal_auth_append_tag, + .append_tag_first = journal_auth_append_tag_first, + .append_tag_maybe = journal_auth_append_tag_maybe, +}; + +void journal_auth_init(void) { + journal_auth_set_ops(&journal_auth_ops); +} + +#else + +void journal_auth_init(void) { +} + +#endif /* HAVE_OPENSSL */ diff --git a/src/shared/journal-authenticate.h b/src/shared/journal-authenticate.h new file mode 100644 index 0000000000..a12afb9ac7 --- /dev/null +++ b/src/shared/journal-authenticate.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "journal-authenticate-internal.h" /* IWYU pragma: export */ +#include "shared-forward.h" + +void journal_auth_init(void); diff --git a/src/shared/journal-file-util.c b/src/shared/journal-file-util.c index 8aecda9610..1567defac5 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 d3ffe4e7a6..a3a8781218 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); diff --git a/src/shared/meson.build b/src/shared/meson.build index def42815c7..1b1ab668b2 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -87,6 +87,7 @@ shared_sources = files( 'firewall-util.c', 'fork-notify.c', 'format-table.c', + 'fsprg-openssl.c', 'fstab-util.c', 'generator.c', 'geneve-util.c', @@ -110,6 +111,7 @@ shared_sources = files( 'ioprio-util.c', 'ip-protocol-list.c', 'ipvlan-util.c', + 'journal-authenticate.c', 'journal-file-util.c', 'journal-importer.c', 'journal-util.c', @@ -405,7 +407,6 @@ libshared_deps = [libacl_cflags, libelf_cflags, libfdisk_cflags, libfido2_cflags, - libgcrypt_cflags, libgnutls_cflags, libidn2_cflags, libkmod_cflags, diff --git a/src/libsystemd/sd-journal/fsprg.c b/src/test/fsprg.c similarity index 100% rename from src/libsystemd/sd-journal/fsprg.c rename to src/test/fsprg.c diff --git a/src/libsystemd/sd-journal/fsprg.h b/src/test/fsprg.h similarity index 100% rename from src/libsystemd/sd-journal/fsprg.h rename to src/test/fsprg.h diff --git a/src/basic/gcrypt-util.c b/src/test/gcrypt-util.c similarity index 96% rename from src/basic/gcrypt-util.c rename to src/test/gcrypt-util.c index 824fc6c415..6197ddb9c2 100644 --- a/src/basic/gcrypt-util.c +++ b/src/test/gcrypt-util.c @@ -40,7 +40,6 @@ DLSYM_PROTOTYPE(gcry_mpi_subm) = NULL; DLSYM_PROTOTYPE(gcry_mpi_sub_ui) = NULL; DLSYM_PROTOTYPE(gcry_prime_check) = NULL; DLSYM_PROTOTYPE(gcry_randomize) = NULL; -DLSYM_PROTOTYPE(gcry_strerror) = NULL; #endif int dlopen_gcrypt(int log_level) { @@ -82,8 +81,7 @@ int dlopen_gcrypt(int log_level) { DLSYM_ARG(gcry_mpi_subm), DLSYM_ARG(gcry_mpi_sub_ui), DLSYM_ARG(gcry_prime_check), - DLSYM_ARG(gcry_randomize), - DLSYM_ARG(gcry_strerror)); + DLSYM_ARG(gcry_randomize)); #else return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP), "gcrypt support is not compiled in."); diff --git a/src/basic/gcrypt-util.h b/src/test/gcrypt-util.h similarity index 94% rename from src/basic/gcrypt-util.h rename to src/test/gcrypt-util.h index 2a85fa7e1d..0340488697 100644 --- a/src/basic/gcrypt-util.h +++ b/src/test/gcrypt-util.h @@ -56,7 +56,6 @@ extern DLSYM_PROTOTYPE(gcry_mpi_subm); extern DLSYM_PROTOTYPE(gcry_mpi_sub_ui); extern DLSYM_PROTOTYPE(gcry_prime_check); extern DLSYM_PROTOTYPE(gcry_randomize); -extern DLSYM_PROTOTYPE(gcry_strerror); /* Copied from gcry_md_putc from gcrypt.h due to the need to call the sym_ variant */ #define sym_gcry_md_putc(h,c) \ @@ -66,14 +65,8 @@ extern DLSYM_PROTOTYPE(gcry_strerror); sym_gcry_md_write((h__), NULL, 0); \ (h__)->buf[(h__)->bufpos++] = (c) & 0xff; \ } while(false) -#else -#define DLOPEN_GCRYPT(log_level, priority) dlopen_gcrypt(log_level) - -typedef struct gcry_md_handle *gcry_md_hd_t; - -static inline void sym_gcry_md_close(gcry_md_hd_t h) { - assert(h == NULL); -} -#endif DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(gcry_md_hd_t, sym_gcry_md_close, NULL); +#else +#define DLOPEN_GCRYPT(log_level, priority) dlopen_gcrypt(log_level) +#endif diff --git a/src/test/meson.build b/src/test/meson.build index cd54f4d8dc..9d5a79c78e 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -300,10 +300,14 @@ executables += [ 'type' : 'manual', }, test_template + { - 'sources' : files('test-dlopen-so.c'), + 'sources' : files( + 'test-dlopen-so.c', + 'gcrypt-util.c', + ), 'dependencies' : [ libblkid_cflags, libfdisk_cflags, + libgcrypt_cflags, libgnutls_cflags, libkmod_cflags, libmicrohttpd_cflags, @@ -336,6 +340,18 @@ executables += [ 'link_with' : libsystemd, 'type' : 'manual', }, + test_template + { + 'sources' : files( + 'test-fsprg.c', + 'fsprg.c', + 'gcrypt-util.c', + ), + 'dependencies' : [ + libgcrypt_cflags, + libopenssl_cflags, + ], + 'conditions' : ['HAVE_OPENSSL'], + }, test_template + { 'sources' : files( 'test-hashmap.c', diff --git a/src/test/test-fsprg.c b/src/test/test-fsprg.c new file mode 100644 index 0000000000..94183e2246 --- /dev/null +++ b/src/test/test-fsprg.c @@ -0,0 +1,511 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#if HAVE_VALGRIND_VALGRIND_H +# include +#endif + +#include "crypto-util.h" +#include "fsprg.h" +#include "fsprg-openssl.h" +#include "gcrypt-util.h" +#include "iovec-util.h" +#include "journal-def.h" +#include "stdio-util.h" +#include "tests.h" + +#define STATE_LENGTH (2 + 2 * FSPRG_RECOMMENDED_SECPAR / 8 + 8) +#define KEY_LENGTH 32 + +static const uint8_t expected_state[4][STATE_LENGTH] = { + { + 0x00, 0x5f, 0xcd, 0x65, 0x12, 0x14, 0xa6, 0x15, + 0xe4, 0xd3, 0xc9, 0xe7, 0x5c, 0x70, 0x2c, 0x37, + 0xca, 0xa7, 0x21, 0xf4, 0x40, 0xf3, 0x6a, 0x88, + 0xe3, 0xce, 0x85, 0x9b, 0xe4, 0x8f, 0x90, 0x8c, + 0x4a, 0xad, 0xf4, 0xcf, 0x99, 0xcf, 0xdc, 0x3b, + 0x29, 0xad, 0x59, 0x72, 0x05, 0xa3, 0xca, 0x3e, + 0xf9, 0x72, 0x9f, 0x7c, 0xb6, 0xa4, 0xd6, 0x86, + 0xb4, 0x64, 0x5a, 0xc4, 0xe0, 0x95, 0x98, 0xb7, + 0xdf, 0xc5, 0x79, 0x8a, 0x48, 0xff, 0xd6, 0x71, + 0xab, 0x0f, 0x17, 0x54, 0xdd, 0x3d, 0xd0, 0x23, + 0x18, 0xc9, 0x4c, 0xf4, 0x40, 0xcf, 0x34, 0xc9, + 0x33, 0x04, 0x35, 0x4b, 0x2c, 0xd9, 0xbd, 0xcf, + 0x59, 0x62, 0x18, 0x2d, 0x9e, 0x5a, 0x11, 0x71, + 0x4c, 0x1f, 0x78, 0x0e, 0x65, 0x75, 0xe6, 0x15, + 0x2f, 0x61, 0x32, 0x3c, 0xac, 0xaa, 0xa8, 0x6a, + 0x1d, 0x11, 0xb4, 0x91, 0x38, 0xd3, 0x34, 0x31, + 0xd7, 0xfa, 0xff, 0xef, 0xbf, 0xb9, 0x90, 0x4f, + 0x0d, 0x29, 0x16, 0xb6, 0x64, 0x48, 0x5b, 0x7b, + 0x00, 0x42, 0x7a, 0xab, 0x2b, 0xe7, 0xee, 0x9b, + 0x5d, 0x0c, 0xe9, 0x14, 0x74, 0x72, 0x29, 0xd9, + 0x10, 0xa5, 0xd5, 0x95, 0xbb, 0x9f, 0x85, 0xc0, + 0xa2, 0xfa, 0xbb, 0x84, 0xf5, 0x59, 0x11, 0xdb, + 0xf4, 0xce, 0x08, 0xe4, 0x36, 0x1c, 0xeb, 0xc9, + 0xa6, 0xcd, 0xdb, 0xf2, 0x10, 0xab, 0x51, 0xb2, + 0x8c, 0x9d, 0xc9, 0xf8, 0x35, 0xe1, 0xec, 0x25, + 0xd5, 0xca, 0x98, 0x7a, 0x4f, 0xbd, 0x45, 0x50, + 0xbf, 0xff, 0x1c, 0xd8, 0x42, 0x95, 0xcb, 0xc5, + 0xa7, 0x0f, 0x75, 0xda, 0x6d, 0x5b, 0xa2, 0xeb, + 0x5b, 0x26, 0x9e, 0xe8, 0xa3, 0x23, 0x04, 0x4b, + 0x5b, 0x26, 0xb7, 0xe4, 0x7f, 0xcd, 0x82, 0x66, + 0x23, 0x7d, 0xf8, 0x57, 0x2c, 0x22, 0x0b, 0x7e, + 0x87, 0x3f, 0xe1, 0xc1, 0x1a, 0xea, 0xc3, 0x42, + 0x40, 0x03, 0xaf, 0x11, 0xd5, 0x07, 0xa1, 0x79, + 0x55, 0x34, 0x4a, 0x41, 0x6a, 0x75, 0x68, 0x13, + 0x37, 0xfd, 0x38, 0x97, 0x52, 0x4f, 0xbb, 0x68, + 0xb4, 0x9f, 0xa8, 0xda, 0x9c, 0x4c, 0x8e, 0x65, + 0x3e, 0x42, 0x19, 0x40, 0x5f, 0x47, 0xe8, 0x50, + 0x0c, 0xb3, 0xc5, 0x6e, 0x0e, 0xe6, 0x6b, 0xe7, + 0xb9, 0x1f, 0xe0, 0x66, 0x2a, 0x38, 0x8e, 0x38, + 0xa8, 0x7f, 0xfd, 0x2c, 0x92, 0xf8, 0xf2, 0x91, + 0x36, 0x90, 0xfd, 0x14, 0x1d, 0x07, 0xae, 0x71, + 0x7f, 0x3c, 0xaa, 0xd6, 0xc4, 0x10, 0x20, 0xe5, + 0xd0, 0xc2, 0xa2, 0xe7, 0x5e, 0x36, 0x91, 0xf7, + 0xff, 0x27, 0x4e, 0x81, 0xc0, 0x85, 0xab, 0x0a, + 0x46, 0x88, 0xe7, 0x32, 0xce, 0x83, 0x4a, 0x09, + 0x72, 0xb3, 0xae, 0xdd, 0x65, 0xf8, 0x13, 0xd0, + 0x9f, 0x4f, 0x45, 0x83, 0xfa, 0x41, 0x1f, 0x8d, + 0xfe, 0x96, 0xbd, 0xe0, 0x32, 0x5c, 0xb9, 0x87, + 0xd8, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + }, + { + 0x00, 0x5f, 0xcd, 0x65, 0x12, 0x14, 0xa6, 0x15, + 0xe4, 0xd3, 0xc9, 0xe7, 0x5c, 0x70, 0x2c, 0x37, + 0xca, 0xa7, 0x21, 0xf4, 0x40, 0xf3, 0x6a, 0x88, + 0xe3, 0xce, 0x85, 0x9b, 0xe4, 0x8f, 0x90, 0x8c, + 0x4a, 0xad, 0xf4, 0xcf, 0x99, 0xcf, 0xdc, 0x3b, + 0x29, 0xad, 0x59, 0x72, 0x05, 0xa3, 0xca, 0x3e, + 0xf9, 0x72, 0x9f, 0x7c, 0xb6, 0xa4, 0xd6, 0x86, + 0xb4, 0x64, 0x5a, 0xc4, 0xe0, 0x95, 0x98, 0xb7, + 0xdf, 0xc5, 0x79, 0x8a, 0x48, 0xff, 0xd6, 0x71, + 0xab, 0x0f, 0x17, 0x54, 0xdd, 0x3d, 0xd0, 0x23, + 0x18, 0xc9, 0x4c, 0xf4, 0x40, 0xcf, 0x34, 0xc9, + 0x33, 0x04, 0x35, 0x4b, 0x2c, 0xd9, 0xbd, 0xcf, + 0x59, 0x62, 0x18, 0x2d, 0x9e, 0x5a, 0x11, 0x71, + 0x4c, 0x1f, 0x78, 0x0e, 0x65, 0x75, 0xe6, 0x15, + 0x2f, 0x61, 0x32, 0x3c, 0xac, 0xaa, 0xa8, 0x6a, + 0x1d, 0x11, 0xb4, 0x91, 0x38, 0xd3, 0x34, 0x31, + 0xd7, 0xfa, 0xff, 0xef, 0xbf, 0xb9, 0x90, 0x4f, + 0x0d, 0x29, 0x16, 0xb6, 0x64, 0x48, 0x5b, 0x7b, + 0x00, 0x42, 0x7a, 0xab, 0x2b, 0xe7, 0xee, 0x9b, + 0x5d, 0x0c, 0xe9, 0x14, 0x74, 0x72, 0x29, 0xd9, + 0x10, 0xa5, 0xd5, 0x95, 0xbb, 0x9f, 0x85, 0xc0, + 0xa2, 0xfa, 0xbb, 0x84, 0xf5, 0x59, 0x11, 0xdb, + 0xf4, 0xce, 0x08, 0xe4, 0x36, 0x1c, 0xeb, 0xc9, + 0xa6, 0xcd, 0xdb, 0xf2, 0x10, 0xab, 0x51, 0xb2, + 0x8c, 0x9d, 0x4a, 0x62, 0x5d, 0xd1, 0x34, 0x5b, + 0x56, 0x44, 0x04, 0x10, 0x9e, 0x43, 0x7f, 0x62, + 0x18, 0x1f, 0x5d, 0x68, 0x8f, 0xa8, 0x26, 0xab, + 0xd4, 0xf9, 0xa6, 0xef, 0xfa, 0x3f, 0x41, 0x09, + 0xae, 0x44, 0x8f, 0x9e, 0xd3, 0x4c, 0xc5, 0x7b, + 0x35, 0x50, 0x7f, 0xc0, 0xba, 0xd9, 0x42, 0xe9, + 0x89, 0x9b, 0x4f, 0x30, 0x40, 0x02, 0x96, 0xed, + 0x7c, 0x67, 0x7e, 0xab, 0x26, 0x3b, 0xc9, 0x65, + 0xf6, 0x16, 0x9b, 0x7e, 0x28, 0xf2, 0xed, 0xb9, + 0xfd, 0x9d, 0xe4, 0x68, 0x67, 0x65, 0xf2, 0x5d, + 0x54, 0x43, 0xd5, 0x2d, 0xfd, 0xf4, 0x2d, 0xfd, + 0x49, 0x00, 0xca, 0x35, 0xc9, 0x87, 0x7c, 0xd4, + 0xfe, 0xfb, 0x9d, 0x8d, 0xda, 0x1c, 0x54, 0xc2, + 0x26, 0xed, 0xe5, 0xaa, 0xde, 0xa9, 0x41, 0x88, + 0x88, 0xec, 0x17, 0xab, 0x12, 0x00, 0xc4, 0x71, + 0x8f, 0xe4, 0x5a, 0xf9, 0x55, 0xe6, 0xd9, 0x56, + 0x45, 0x86, 0x6a, 0x1c, 0xd1, 0x09, 0xb1, 0xfc, + 0x0d, 0xd0, 0x15, 0x2c, 0xa1, 0xb6, 0xe9, 0x95, + 0x01, 0xe1, 0x71, 0xb3, 0xb7, 0xbf, 0x57, 0xd5, + 0x15, 0xec, 0xe7, 0xde, 0x85, 0x90, 0xb2, 0x47, + 0x51, 0x0f, 0x0f, 0x9b, 0xcf, 0x21, 0x0c, 0x77, + 0x21, 0xe7, 0xbb, 0x44, 0xbb, 0x70, 0xbf, 0xfb, + 0x29, 0x58, 0x50, 0xdc, 0x01, 0xfb, 0x2b, 0xdf, + 0x8d, 0x74, 0xc5, 0x89, 0xe1, 0x29, 0xcd, 0x97, + 0xe5, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, + }, + { + 0x00, 0x5f, 0xcd, 0x65, 0x12, 0x14, 0xa6, 0x15, + 0xe4, 0xd3, 0xc9, 0xe7, 0x5c, 0x70, 0x2c, 0x37, + 0xca, 0xa7, 0x21, 0xf4, 0x40, 0xf3, 0x6a, 0x88, + 0xe3, 0xce, 0x85, 0x9b, 0xe4, 0x8f, 0x90, 0x8c, + 0x4a, 0xad, 0xf4, 0xcf, 0x99, 0xcf, 0xdc, 0x3b, + 0x29, 0xad, 0x59, 0x72, 0x05, 0xa3, 0xca, 0x3e, + 0xf9, 0x72, 0x9f, 0x7c, 0xb6, 0xa4, 0xd6, 0x86, + 0xb4, 0x64, 0x5a, 0xc4, 0xe0, 0x95, 0x98, 0xb7, + 0xdf, 0xc5, 0x79, 0x8a, 0x48, 0xff, 0xd6, 0x71, + 0xab, 0x0f, 0x17, 0x54, 0xdd, 0x3d, 0xd0, 0x23, + 0x18, 0xc9, 0x4c, 0xf4, 0x40, 0xcf, 0x34, 0xc9, + 0x33, 0x04, 0x35, 0x4b, 0x2c, 0xd9, 0xbd, 0xcf, + 0x59, 0x62, 0x18, 0x2d, 0x9e, 0x5a, 0x11, 0x71, + 0x4c, 0x1f, 0x78, 0x0e, 0x65, 0x75, 0xe6, 0x15, + 0x2f, 0x61, 0x32, 0x3c, 0xac, 0xaa, 0xa8, 0x6a, + 0x1d, 0x11, 0xb4, 0x91, 0x38, 0xd3, 0x34, 0x31, + 0xd7, 0xfa, 0xff, 0xef, 0xbf, 0xb9, 0x90, 0x4f, + 0x0d, 0x29, 0x16, 0xb6, 0x64, 0x48, 0x5b, 0x7b, + 0x00, 0x42, 0x7a, 0xab, 0x2b, 0xe7, 0xee, 0x9b, + 0x5d, 0x0c, 0xe9, 0x14, 0x74, 0x72, 0x29, 0xd9, + 0x10, 0xa5, 0xd5, 0x95, 0xbb, 0x9f, 0x85, 0xc0, + 0xa2, 0xfa, 0xbb, 0x84, 0xf5, 0x59, 0x11, 0xdb, + 0xf4, 0xce, 0x08, 0xe4, 0x36, 0x1c, 0xeb, 0xc9, + 0xa6, 0xcd, 0xdb, 0xf2, 0x10, 0xab, 0x51, 0xb2, + 0x8c, 0x9d, 0x57, 0xb9, 0x1b, 0x5d, 0x82, 0x02, + 0x77, 0x8c, 0x96, 0x3e, 0x36, 0x0c, 0x17, 0x8f, + 0x61, 0x3b, 0x3f, 0x17, 0x7d, 0x1d, 0xa9, 0x03, + 0xf8, 0x57, 0x4b, 0xf7, 0x48, 0xb8, 0x65, 0xfc, + 0xbb, 0xd4, 0x2b, 0x19, 0x06, 0xa3, 0x82, 0x61, + 0xe0, 0x21, 0x62, 0x31, 0xb4, 0xd2, 0x5b, 0xf0, + 0x4b, 0x21, 0xcd, 0xca, 0x84, 0x84, 0xc0, 0x1b, + 0x55, 0x4d, 0xf0, 0x32, 0xc9, 0x37, 0x57, 0xd9, + 0x63, 0x1b, 0x00, 0x65, 0x61, 0x93, 0x1d, 0xa4, + 0xb0, 0x86, 0xef, 0x4f, 0x15, 0x88, 0xf8, 0x58, + 0xcd, 0xfb, 0xc6, 0x2b, 0x09, 0xcd, 0x9a, 0x32, + 0x44, 0x97, 0x8e, 0xea, 0xfa, 0x3c, 0x5a, 0x7d, + 0x94, 0xda, 0x09, 0x94, 0x27, 0xa0, 0xc5, 0x57, + 0xa8, 0x2d, 0xc5, 0xc4, 0xdf, 0x7c, 0x5f, 0xb4, + 0x9b, 0x27, 0x0b, 0x62, 0xc1, 0x55, 0xcc, 0x55, + 0xb6, 0xfa, 0xf8, 0x70, 0x66, 0x48, 0xba, 0x21, + 0xe5, 0x9c, 0x27, 0x3f, 0x6b, 0xc5, 0xf3, 0x00, + 0x03, 0x52, 0x90, 0x06, 0xa3, 0xb9, 0xda, 0xee, + 0x87, 0xab, 0x78, 0x96, 0x93, 0x69, 0x34, 0x5a, + 0x04, 0x77, 0xcf, 0x0d, 0x20, 0xa3, 0xd7, 0x42, + 0x27, 0x8d, 0x59, 0xfe, 0x89, 0x67, 0x3e, 0x5d, + 0xe0, 0x0c, 0xb6, 0xad, 0xf0, 0x36, 0x09, 0x60, + 0xc1, 0x7c, 0xce, 0x2d, 0xe4, 0x75, 0xaa, 0xf5, + 0xa3, 0x5f, 0x72, 0x3b, 0xf2, 0x73, 0xb4, 0x9b, + 0xb5, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, + }, + { + 0x00, 0x5f, 0xcd, 0x65, 0x12, 0x14, 0xa6, 0x15, + 0xe4, 0xd3, 0xc9, 0xe7, 0x5c, 0x70, 0x2c, 0x37, + 0xca, 0xa7, 0x21, 0xf4, 0x40, 0xf3, 0x6a, 0x88, + 0xe3, 0xce, 0x85, 0x9b, 0xe4, 0x8f, 0x90, 0x8c, + 0x4a, 0xad, 0xf4, 0xcf, 0x99, 0xcf, 0xdc, 0x3b, + 0x29, 0xad, 0x59, 0x72, 0x05, 0xa3, 0xca, 0x3e, + 0xf9, 0x72, 0x9f, 0x7c, 0xb6, 0xa4, 0xd6, 0x86, + 0xb4, 0x64, 0x5a, 0xc4, 0xe0, 0x95, 0x98, 0xb7, + 0xdf, 0xc5, 0x79, 0x8a, 0x48, 0xff, 0xd6, 0x71, + 0xab, 0x0f, 0x17, 0x54, 0xdd, 0x3d, 0xd0, 0x23, + 0x18, 0xc9, 0x4c, 0xf4, 0x40, 0xcf, 0x34, 0xc9, + 0x33, 0x04, 0x35, 0x4b, 0x2c, 0xd9, 0xbd, 0xcf, + 0x59, 0x62, 0x18, 0x2d, 0x9e, 0x5a, 0x11, 0x71, + 0x4c, 0x1f, 0x78, 0x0e, 0x65, 0x75, 0xe6, 0x15, + 0x2f, 0x61, 0x32, 0x3c, 0xac, 0xaa, 0xa8, 0x6a, + 0x1d, 0x11, 0xb4, 0x91, 0x38, 0xd3, 0x34, 0x31, + 0xd7, 0xfa, 0xff, 0xef, 0xbf, 0xb9, 0x90, 0x4f, + 0x0d, 0x29, 0x16, 0xb6, 0x64, 0x48, 0x5b, 0x7b, + 0x00, 0x42, 0x7a, 0xab, 0x2b, 0xe7, 0xee, 0x9b, + 0x5d, 0x0c, 0xe9, 0x14, 0x74, 0x72, 0x29, 0xd9, + 0x10, 0xa5, 0xd5, 0x95, 0xbb, 0x9f, 0x85, 0xc0, + 0xa2, 0xfa, 0xbb, 0x84, 0xf5, 0x59, 0x11, 0xdb, + 0xf4, 0xce, 0x08, 0xe4, 0x36, 0x1c, 0xeb, 0xc9, + 0xa6, 0xcd, 0xdb, 0xf2, 0x10, 0xab, 0x51, 0xb2, + 0x8c, 0x9d, 0x24, 0x8b, 0xa0, 0xb7, 0xf0, 0xd8, + 0xea, 0x6e, 0x24, 0x61, 0x57, 0xfa, 0xd8, 0xd3, + 0xf8, 0x10, 0xaf, 0x7e, 0x45, 0xc5, 0xea, 0x7d, + 0xa4, 0x0d, 0x14, 0x2b, 0x4c, 0xc9, 0x64, 0x48, + 0x12, 0xf1, 0x36, 0x2d, 0xef, 0x33, 0xb6, 0x74, + 0x4c, 0xdf, 0xd5, 0x59, 0x80, 0xa4, 0xfc, 0xa5, + 0x7c, 0x09, 0x24, 0xd7, 0xd5, 0xdf, 0xd6, 0xa5, + 0x2f, 0xa3, 0x49, 0x8a, 0xa3, 0x23, 0xa6, 0x11, + 0x76, 0x0e, 0xe3, 0xcd, 0xd7, 0x64, 0x0f, 0xbe, + 0x3f, 0x99, 0x0d, 0x05, 0x14, 0xd7, 0xa8, 0x2b, + 0x79, 0x6d, 0xb3, 0x12, 0x76, 0x93, 0xae, 0x35, + 0xbe, 0x81, 0xb7, 0xd2, 0xcf, 0x39, 0xd2, 0xad, + 0xb9, 0xc4, 0x3f, 0xfb, 0xe2, 0xc3, 0x32, 0x46, + 0x4d, 0x0b, 0x84, 0x8a, 0x2c, 0x77, 0x2f, 0x49, + 0xac, 0x96, 0xe0, 0x30, 0xeb, 0x38, 0x8f, 0x2f, + 0x01, 0x1b, 0xf3, 0x3c, 0xb5, 0xee, 0x8c, 0x62, + 0x9c, 0x9d, 0x0d, 0xb4, 0x57, 0x6c, 0x8d, 0xf5, + 0xe2, 0x16, 0xc0, 0x62, 0x9c, 0x52, 0x49, 0xf8, + 0xc7, 0xac, 0xb0, 0xa0, 0xe8, 0x81, 0xd9, 0x05, + 0x87, 0xda, 0x09, 0x6f, 0xf7, 0x95, 0xc7, 0x68, + 0x69, 0xbb, 0x68, 0xf4, 0x76, 0x3e, 0x2f, 0xd1, + 0x9d, 0x18, 0xc1, 0x47, 0x59, 0x3d, 0x56, 0x57, + 0x26, 0x10, 0x64, 0xc5, 0x9c, 0x35, 0xe8, 0x58, + 0x1b, 0x0e, 0x20, 0x97, 0xdf, 0xfa, 0xcc, 0x88, + 0xa0, 0xeb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, + }, +}, expected_key[4][KEY_LENGTH] = { + { + 0xca, 0xe4, 0x2e, 0xba, 0x74, 0x55, 0x4f, 0x10, + 0x4f, 0x72, 0xa4, 0x3a, 0x0b, 0x00, 0xa0, 0xc8, + 0xd8, 0x8f, 0xa7, 0x11, 0x8d, 0x56, 0x2a, 0x72, + 0xe4, 0xa8, 0xd2, 0x69, 0xf9, 0xd8, 0xa0, 0xf1, + }, + { + 0x63, 0x1d, 0x52, 0x11, 0xed, 0x13, 0xa8, 0x8e, + 0x4c, 0xe0, 0x06, 0x2e, 0xe6, 0xa3, 0xa1, 0xf6, + 0x7a, 0xf1, 0xc4, 0x1e, 0x18, 0xbf, 0x74, 0xe2, + 0x52, 0x23, 0x86, 0x56, 0x1b, 0x0e, 0x78, 0x7d, + }, + { + 0x3f, 0xb2, 0xb6, 0x39, 0xe3, 0x54, 0x41, 0x04, + 0xf3, 0x48, 0x5f, 0x50, 0xf7, 0x8b, 0xd6, 0xe7, + 0x8e, 0x45, 0xc2, 0xee, 0xbf, 0xdd, 0xfd, 0x83, + 0x34, 0x7a, 0x9a, 0x8a, 0xf3, 0x4d, 0xd4, 0xc6, + }, + { + 0x98, 0x76, 0x29, 0xe2, 0x27, 0xf0, 0x0e, 0x2a, + 0xff, 0xc8, 0xfd, 0x3b, 0xcf, 0xcb, 0x9f, 0x6d, + 0xe9, 0x80, 0xfe, 0x6e, 0xd9, 0xc0, 0x43, 0x29, + 0xc5, 0x53, 0x5f, 0xf7, 0x86, 0x33, 0x3b, 0x70, + }, +}, expected_tag[4][TAG_LENGTH] = { + { + 0x10, 0xa4, 0x14, 0x19, 0xec, 0xf1, 0x9b, 0x6d, + 0xe5, 0xe3, 0x12, 0x2b, 0x06, 0xee, 0x5b, 0xac, + 0xa7, 0x11, 0x0b, 0x70, 0x15, 0xfd, 0x47, 0xa4, + 0xb9, 0xd7, 0x45, 0x19, 0x4b, 0xde, 0x92, 0x5d, + }, + { + 0x75, 0x54, 0x5b, 0x6a, 0xfb, 0x30, 0x43, 0x3b, + 0x29, 0x94, 0x58, 0xee, 0x62, 0xd9, 0x62, 0x2b, + 0x61, 0x88, 0xed, 0xce, 0xc9, 0x95, 0x1e, 0x82, + 0x9e, 0xd6, 0xd1, 0x90, 0x23, 0xc6, 0x90, 0x65, + }, + { + 0x2f, 0x8b, 0x64, 0xa4, 0x21, 0xe8, 0x74, 0x93, + 0xa2, 0x3a, 0x6a, 0xa9, 0x0d, 0x7e, 0xb0, 0x80, + 0xce, 0x74, 0x80, 0xb9, 0x06, 0xae, 0xec, 0x86, + 0x4c, 0x32, 0x99, 0xf9, 0x48, 0x1f, 0x0c, 0xad, + }, + { + 0x39, 0x9e, 0xf6, 0xc3, 0x1e, 0xca, 0x02, 0xb2, + 0x6a, 0xe2, 0xa6, 0x47, 0x0b, 0xb5, 0xb6, 0xaf, + 0x46, 0xd7, 0x99, 0x13, 0xc6, 0x07, 0xa8, 0x80, + 0x73, 0x35, 0x75, 0x73, 0xfd, 0x03, 0x5b, 0x3e, + }, +}; + +static void generate_seed(struct iovec *seed) { + assert(iovec_is_set(seed)); + + uint8_t *p = seed->iov_base; + for (size_t i = 0; i < seed->iov_len; i++) + *p++ = (uint8_t) i; +} + +TEST(fsprg) { + size_t n_states = 20; + + /* Generating private keys is slow when running on valgrind or with sanitizers. */ +#if HAVE_VALGRIND_VALGRIND_H + if (RUNNING_ON_VALGRIND) + n_states = ELEMENTSOF(expected_state); +#endif +#if HAS_FEATURE_ADDRESS_SANITIZER + n_states = ELEMENTSOF(expected_state); +#endif + + struct iovec *states = new0(struct iovec, n_states); + CLEANUP_ARRAY(states, n_states, iovec_array_free); + + if (initialize_libgcrypt(false) >= 0) { + struct iovec seed = IOVEC_ALLOCA(FSPRG_RECOMMENDED_SEEDLEN), + msk = IOVEC_ALLOCA(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR)), + mpk = IOVEC_ALLOCA(FSPRG_mpkinbytes(FSPRG_RECOMMENDED_SECPAR)), + state = IOVEC_ALLOCA(FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR)), + key = IOVEC_ALLOCA(KEY_LENGTH); + + generate_seed(&seed); + + /* Generate secret keys */ + ASSERT_OK(FSPRG_GenMK(msk.iov_base, mpk.iov_base, seed.iov_base, seed.iov_len, FSPRG_RECOMMENDED_SECPAR)); + + /* Generate states by FSPRG_Seek() */ + for (size_t i = 0; i < n_states; i++) { + ASSERT_OK(FSPRG_Seek(state.iov_base, i, msk.iov_base, seed.iov_base, seed.iov_len)); + + /* Verify state */ + if (i < ELEMENTSOF(expected_state)) + ASSERT_TRUE(iovec_equal(&state, &IOVEC_MAKE(expected_state[i], sizeof(expected_state[i])))); + + /* Verify key */ + ASSERT_OK(FSPRG_GetKey(state.iov_base, key.iov_base, key.iov_len, 0)); + if (i < ELEMENTSOF(expected_key)) + ASSERT_TRUE(iovec_equal(&key, &IOVEC_MAKE(expected_key[i], sizeof(expected_key[i])))); + + /* Verify epoch */ + ASSERT_EQ(FSPRG_GetEpoch(state.iov_base), (uint64_t) i); + + ASSERT_NOT_NULL(iovec_memdup(&state, &states[i])); + } + + /* Generate state by FSPRG_GetState0() */ + ASSERT_OK(FSPRG_GenState0(state.iov_base, mpk.iov_base, seed.iov_base, seed.iov_len)); + ASSERT_TRUE(iovec_equal(&state, &states[0])); + + /* Generate states by FSPRG_Evolve() */ + for (size_t i = 1; i < n_states; i++) { + ASSERT_OK(FSPRG_Evolve(state.iov_base)); + ASSERT_TRUE(iovec_equal(&state, &states[i])); + } + } + + if (dlopen_libcrypto(LOG_DEBUG) >= 0) { + struct iovec seed = IOVEC_ALLOCA(FSPRG_RECOMMENDED_SEEDLEN), + state = IOVEC_ALLOCA(fsprg_state_size(FSPRG_RECOMMENDED_SECPAR)), + key = IOVEC_ALLOCA(32); + + generate_seed(&seed); + + /* Generate states by fsprg_generate_state() */ + for (size_t i = 0; i < n_states; i++) { + ASSERT_OK(fsprg_generate_state(FSPRG_RECOMMENDED_SECPAR, i, &seed, &state)); + + /* Verify state */ + if (i < ELEMENTSOF(expected_state)) + ASSERT_EQ(iovec_memcmp(&state, &IOVEC_MAKE(expected_state[i], sizeof(expected_state[i]))), 0); + + /* Verify key */ + ASSERT_OK(fsprg_get_key(&state, &key)); + if (i < ELEMENTSOF(expected_key)) + ASSERT_EQ(iovec_memcmp(&key, &IOVEC_MAKE(expected_key[i], sizeof(expected_key[i]))), 0); + + /* Verify epoch */ + uint64_t epoch; + ASSERT_OK(fsprg_get_epoch(&state, &epoch)); + ASSERT_EQ(epoch, (uint64_t) i); + + if (iovec_is_set(&states[i])) + ASSERT_EQ(iovec_memcmp(&state, &states[i]), 0); + else + ASSERT_NOT_NULL(iovec_memdup(&state, &states[i])); + } + + /* Generate state by fsprg_generate_state(0) */ + ASSERT_OK(fsprg_generate_state(FSPRG_RECOMMENDED_SECPAR, 0, &seed, &state)); + ASSERT_EQ(iovec_memcmp(&state, &states[0]), 0); + + /* Generate states by fsprg_evolve() */ + for (unsigned i = 1; i < n_states; i++) { + ASSERT_OK(fsprg_evolve(&state)); + ASSERT_EQ(iovec_memcmp(&state, &states[i]), 0); + } + } +} + +TEST(hmac) { + /* This emulates what journald and journalctl does. + * 1. journalctl generates random seed, calculate initial state, and write the state in + * /var/log/journal/$MACHINE_ID/fss. + * 2. journald loads the fss file when a new journal file is created by journal_file_fss_load(). + * 3. Allocate HMAC with SHA256 by journal_file_hmac_setup(). + * 4. Write inital tag in journal file by journal_file_append_first_tag(). + * 5. For each journal entry, + * - initialize HMAC with fsprg key by journal_file_hmac_start(), + * - put each object data to the HMAC by journal_file_hmac_put_object(), + * - finalize HMAC and write the value with the fsprg epoch to a tag object by + * journal_file_maybe_append_tag(). */ + + size_t n_tags = 20; + struct iovec *tags = new0(struct iovec, n_tags); + CLEANUP_ARRAY(tags, n_tags, iovec_array_free); + +#if HAVE_GCRYPT + if (initialize_libgcrypt(false) >= 0) { + struct iovec seed = IOVEC_ALLOCA(FSPRG_RECOMMENDED_SEEDLEN), + msk = IOVEC_ALLOCA(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR)), + mpk = IOVEC_ALLOCA(FSPRG_mpkinbytes(FSPRG_RECOMMENDED_SECPAR)), + state = IOVEC_ALLOCA(FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR)), + key = IOVEC_ALLOCA(KEY_LENGTH), + tag = IOVEC_ALLOCA(TAG_LENGTH); + + generate_seed(&seed); + + /* Generate secret keys */ + ASSERT_OK(FSPRG_GenMK(msk.iov_base, mpk.iov_base, seed.iov_base, seed.iov_len, FSPRG_RECOMMENDED_SECPAR)); + + /* Calculate initial state */ + ASSERT_OK(FSPRG_GenState0(state.iov_base, mpk.iov_base, seed.iov_base, seed.iov_len)); + + /* Allocate HMAC */ + _cleanup_(sym_gcry_md_closep) gcry_md_hd_t hmac = NULL; + ASSERT_EQ(gcry_err_code(sym_gcry_md_open(&hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC)), (gcry_err_code_t) GPG_ERR_NO_ERROR); + + for (size_t i = 0; i < n_tags; i++) { + /* Reset the previous state */ + sym_gcry_md_reset(hmac); + + /* Initialize HMAC with fsprg key */ + ASSERT_OK(FSPRG_GetKey(state.iov_base, key.iov_base, key.iov_len, 0)); + ASSERT_EQ(gcry_err_code(sym_gcry_md_setkey(hmac, key.iov_base, key.iov_len)), (gcry_err_code_t) GPG_ERR_NO_ERROR); + + /* Put dummy data */ + _cleanup_free_ char *str = ASSERT_NOT_NULL(asprintf_safe("dummy data: %zu, hoge hoge hoge hoge hoge", i)); + sym_gcry_md_write(hmac, str, strlen(str)); + + /* Finalize HMAC and generate tag. */ + memcpy(tag.iov_base, sym_gcry_md_read(hmac, 0), tag.iov_len); + + /* Verify tag */ + if (i < ELEMENTSOF(expected_tag)) + ASSERT_TRUE(iovec_equal(&tag, &IOVEC_MAKE(expected_tag[i], sizeof(expected_tag[i])))); + + /* Save tag */ + ASSERT_NOT_NULL(iovec_memdup(&tag, &tags[i])); + + /* Increment epoch */ + ASSERT_OK(FSPRG_Evolve(state.iov_base)); + } + } +#endif + +#if HAVE_OPENSSL + if (dlopen_libcrypto(LOG_DEBUG) >= 0) { + struct iovec seed = IOVEC_ALLOCA(FSPRG_RECOMMENDED_SEEDLEN), + state = IOVEC_ALLOCA(fsprg_state_size(FSPRG_RECOMMENDED_SECPAR)), + key = IOVEC_ALLOCA(KEY_LENGTH), + tag = IOVEC_ALLOCA(TAG_LENGTH); + + generate_seed(&seed); + + /* Calculate initial state */ + ASSERT_OK(fsprg_generate_state(FSPRG_RECOMMENDED_SECPAR, 0, &seed, &state)); + + /* Allocate HMAC */ + _cleanup_(EVP_MAC_freep) EVP_MAC *hmac = ASSERT_NOT_NULL(sym_EVP_MAC_fetch(NULL, "HMAC", NULL)); + _cleanup_(EVP_MAC_CTX_freep) EVP_MAC_CTX *hmac_ctx = ASSERT_NOT_NULL(sym_EVP_MAC_CTX_new(hmac)); + _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = ASSERT_NOT_NULL(sym_OSSL_PARAM_BLD_new()); + ASSERT_OK_POSITIVE(sym_OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_MAC_PARAM_DIGEST, "SHA256", 0)); + _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = ASSERT_NOT_NULL(sym_OSSL_PARAM_BLD_to_param(bld)); + + for (size_t i = 0; i < n_tags; i++) { + /* Initialize HMAC with fsprg key */ + ASSERT_OK(fsprg_get_key(&state, &key)); + ASSERT_OK_POSITIVE(sym_EVP_MAC_init(hmac_ctx, key.iov_base, key.iov_len, params)); + + /* Put dummy data */ + _cleanup_(iovec_done) struct iovec data = + IOVEC_MAKE_STRING(ASSERT_NOT_NULL(asprintf_safe("dummy data: %zu, hoge hoge hoge hoge hoge", i))); + ASSERT_OK_POSITIVE(sym_EVP_MAC_update(hmac_ctx, data.iov_base, data.iov_len)); + + /* Finalize HMAC and generate tag. */ + size_t len; + ASSERT_OK_POSITIVE(sym_EVP_MAC_final(hmac_ctx, tag.iov_base, &len, tag.iov_len)); + ASSERT_EQ(len, tag.iov_len); + + /* Verify tag */ + if (i < ELEMENTSOF(expected_tag)) + ASSERT_TRUE(iovec_equal(&tag, &IOVEC_MAKE(expected_tag[i], sizeof(expected_tag[i])))); + + /* Compare with the tag generated by gcrypt */ + if (iovec_is_set(&tags[i])) + ASSERT_TRUE(iovec_equal(&tag, &tags[i])); + + /* Increment epoch */ + ASSERT_OK(fsprg_evolve(&state)); + } + } +#endif +} + +static int intro(void) { + if (DLOPEN_GCRYPT(LOG_DEBUG, SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED) < 0 && + DLOPEN_LIBCRYPTO(LOG_DEBUG, SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED) < 0) + return EXIT_TEST_SKIP; + + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro); diff --git a/test/units/TEST-04-JOURNAL.fss.sh b/test/units/TEST-04-JOURNAL.fss.sh index 50f0608280..b3f27195a7 100755 --- a/test/units/TEST-04-JOURNAL.fss.sh +++ b/test/units/TEST-04-JOURNAL.fss.sh @@ -5,8 +5,8 @@ set -o pipefail # Forward Secure Sealing -if ! journalctl --version | grep -F +GCRYPT >/dev/null; then - echo "Built without gcrypt, skipping the FSS tests" +if ! journalctl --version | grep -F +OPENSSL >/dev/null; then + echo "Built without openssl, skipping the FSS tests" exit 0 fi