journal: several cleanups for journal sealing feature (#42770)
This commit is contained in:
@@ -16,6 +16,17 @@ const struct iovec iovec_empty = {
|
||||
.iov_len = 0,
|
||||
};
|
||||
|
||||
int iovec_alloc(size_t n, struct iovec *ret) {
|
||||
assert(ret);
|
||||
|
||||
void *buf = malloc(n ?: 1);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = IOVEC_MAKE(buf, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t iovec_total_size(const struct iovec *iovec, size_t n) {
|
||||
size_t sum = 0;
|
||||
|
||||
@@ -63,6 +74,15 @@ struct iovec* iovec_make_string(struct iovec *iovec, const char *s) {
|
||||
return iovec;
|
||||
}
|
||||
|
||||
void iovec_erase(struct iovec *iovec) {
|
||||
assert(iovec);
|
||||
|
||||
/* Unlike iovec_done_erase(), which derives the buffer size with MALLOC_SIZEOF_SAFE(), this uses
|
||||
* iov_len as the buffer size. Hence, it can be used with iovec referring to a static array or a
|
||||
* buffer allocated on the stack. */
|
||||
explicit_bzero_safe(iovec->iov_base, iovec->iov_len);
|
||||
}
|
||||
|
||||
void iovec_done_erase(struct iovec *iovec) {
|
||||
assert(iovec);
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
extern const struct iovec iovec_nul_byte; /* Points to a single NUL byte */
|
||||
extern const struct iovec iovec_empty; /* Points to an empty, but valid (i.e. non-NULL) pointer */
|
||||
|
||||
int iovec_alloc(size_t n, struct iovec *ret);
|
||||
|
||||
size_t iovec_total_size(const struct iovec *iovec, size_t n) _nonnull_if_nonzero_(1, 2);
|
||||
|
||||
bool iovec_inc_many(struct iovec *iovec, size_t n, size_t k) _nonnull_if_nonzero_(1, 2);
|
||||
@@ -31,6 +33,13 @@ struct iovec* iovec_make_string(struct iovec *iovec, const char *s);
|
||||
.iov_len = 1, \
|
||||
}
|
||||
|
||||
#define IOVEC_ALLOCA(n) \
|
||||
({ \
|
||||
size_t _n_ = (n); \
|
||||
IOVEC_MAKE(alloca_safe(_n_), _n_); \
|
||||
})
|
||||
|
||||
void iovec_erase(struct iovec *iovec);
|
||||
void iovec_done_erase(struct iovec *iovec);
|
||||
|
||||
char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "hostname-setup.h"
|
||||
#include "hostname-util.h"
|
||||
#include "io-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "journal-def.h"
|
||||
#include "journalctl.h"
|
||||
#include "journalctl-authenticate.h"
|
||||
@@ -31,8 +32,7 @@
|
||||
|
||||
#if HAVE_GCRYPT
|
||||
static int format_key(
|
||||
const void *seed,
|
||||
size_t seed_size,
|
||||
const struct iovec *seed,
|
||||
uint64_t start,
|
||||
uint64_t interval,
|
||||
char **ret) {
|
||||
@@ -40,18 +40,17 @@ static int format_key(
|
||||
_cleanup_(memstream_done) MemStream m = {};
|
||||
FILE *f;
|
||||
|
||||
assert(seed);
|
||||
assert(seed_size > 0);
|
||||
assert(iovec_is_set(seed));
|
||||
assert(ret);
|
||||
|
||||
f = memstream_init(&m);
|
||||
if (!f)
|
||||
return -ENOMEM;
|
||||
|
||||
for (size_t i = 0; i < seed_size; i++) {
|
||||
for (size_t i = 0; i < seed->iov_len; i++) {
|
||||
if (i > 0 && i % 3 == 0)
|
||||
fputc('-', f);
|
||||
fprintf(f, "%02x", ((uint8_t*) seed)[i]);
|
||||
fprintf(f, "%02x", ((uint8_t*) seed->iov_base)[i]);
|
||||
}
|
||||
|
||||
fprintf(f, "/%"PRIx64"-%"PRIx64, start, interval);
|
||||
@@ -65,8 +64,6 @@ int action_setup_keys(void) {
|
||||
_cleanup_(unlink_and_freep) char *tmpfile = NULL;
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
_cleanup_free_ char *path = NULL;
|
||||
size_t mpk_size, seed_size, state_size;
|
||||
uint8_t *mpk, *seed, *state;
|
||||
sd_id128_t machine, boot;
|
||||
uint64_t n;
|
||||
int r;
|
||||
@@ -105,30 +102,26 @@ int action_setup_keys(void) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
|
||||
"Sealing key file %s exists already. Use --force to recreate.", path);
|
||||
|
||||
mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
|
||||
mpk = alloca_safe(mpk_size);
|
||||
|
||||
seed_size = FSPRG_RECOMMENDED_SEEDLEN;
|
||||
seed = alloca_safe(seed_size);
|
||||
|
||||
state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
|
||||
state = alloca_safe(state_size);
|
||||
_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));
|
||||
|
||||
if (!arg_quiet)
|
||||
log_info("Generating seed...");
|
||||
r = crypto_random_bytes(seed, seed_size);
|
||||
r = crypto_random_bytes(seed.iov_base, seed.iov_len);
|
||||
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, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
|
||||
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, mpk, seed, seed_size);
|
||||
r = FSPRG_GenState0(state.iov_base, mpk.iov_base, seed.iov_base, seed.iov_len);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to generate sealing key: %m");
|
||||
|
||||
@@ -146,21 +139,21 @@ int action_setup_keys(void) {
|
||||
r, "Failed to set file attributes on a temporary file for '%s', ignoring: %m", path);
|
||||
|
||||
struct FSSHeader h = {
|
||||
.signature = { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' },
|
||||
.signature = FSS_HEADER_SIGNATURE,
|
||||
.machine_id = machine,
|
||||
.boot_id = boot,
|
||||
.header_size = htole64(sizeof(h)),
|
||||
.start_usec = htole64(n * arg_interval),
|
||||
.interval_usec = htole64(arg_interval),
|
||||
.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR),
|
||||
.fsprg_state_size = htole64(state_size),
|
||||
.fsprg_state_size = htole64(state.iov_len),
|
||||
};
|
||||
|
||||
r = loop_write(fd, &h, sizeof(h));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write header: %m");
|
||||
|
||||
r = loop_write(fd, state, state_size);
|
||||
r = loop_write(fd, state.iov_base, state.iov_len);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write state: %m");
|
||||
|
||||
@@ -170,8 +163,8 @@ int action_setup_keys(void) {
|
||||
|
||||
tmpfile = mfree(tmpfile);
|
||||
|
||||
_cleanup_free_ char *key = NULL;
|
||||
r = format_key(seed, seed_size, n, arg_interval, &key);
|
||||
_cleanup_(erase_and_freep) char *key = NULL;
|
||||
r = format_key(&seed, n, arg_interval, &key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -240,7 +233,7 @@ int action_setup_keys(void) {
|
||||
fputs(ansi_normal(), stderr);
|
||||
|
||||
#if HAVE_QRENCODE
|
||||
_cleanup_free_ char *url = NULL;
|
||||
_cleanup_(erase_and_freep) char *url = NULL;
|
||||
url = strjoin("fss://", key, "?machine=", SD_ID128_TO_STRING(machine), hn ? ";hostname=" : "", hn);
|
||||
if (!url)
|
||||
return log_oom();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-table.h"
|
||||
#include "format-util.h"
|
||||
@@ -35,7 +36,7 @@ int action_print_header(void) {
|
||||
|
||||
int action_verify(void) {
|
||||
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
|
||||
int r;
|
||||
int r, ret = 0;
|
||||
|
||||
assert(arg_action == ACTION_VERIFY);
|
||||
|
||||
@@ -47,43 +48,40 @@ int action_verify(void) {
|
||||
|
||||
JournalFile *f;
|
||||
ORDERED_HASHMAP_FOREACH(f, j->files) {
|
||||
int k;
|
||||
usec_t first = 0, validated = 0, last = 0;
|
||||
|
||||
#if HAVE_GCRYPT
|
||||
if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
|
||||
log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
|
||||
#endif
|
||||
|
||||
k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, /* show_progress= */ !arg_quiet);
|
||||
if (k == -EINVAL)
|
||||
r = journal_file_verify(f, arg_verify_key, &first, &validated, &last, /* show_progress= */ !arg_quiet);
|
||||
if (r == -EKEYREJECTED)
|
||||
/* If the key was invalid give up right-away. */
|
||||
return k;
|
||||
if (k < 0)
|
||||
r = log_warning_errno(k, "FAIL: %s (%m)", f->path);
|
||||
else {
|
||||
char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX];
|
||||
log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "PASS: %s", f->path);
|
||||
return r;
|
||||
if (r == -ENOKEY)
|
||||
log_notice_errno(r, "Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
|
||||
if (r < 0) {
|
||||
RET_GATHER(ret, log_warning_errno(r, "FAIL: %s: %m", f->path));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
|
||||
if (validated > 0) {
|
||||
log_full(arg_quiet ? LOG_DEBUG : LOG_INFO,
|
||||
"=> Validated from %s to %s, final %s entries not sealed.",
|
||||
format_timestamp_maybe_utc(a, sizeof(a), first),
|
||||
format_timestamp_maybe_utc(b, sizeof(b), validated),
|
||||
FORMAT_TIMESPAN(last > validated ? last - validated : 0, 0));
|
||||
} else if (last > 0)
|
||||
log_full(arg_quiet ? LOG_DEBUG : LOG_INFO,
|
||||
"=> No sealing yet, %s of entries not sealed.",
|
||||
FORMAT_TIMESPAN(last - first, 0));
|
||||
else
|
||||
log_full(arg_quiet ? LOG_DEBUG : LOG_INFO,
|
||||
"=> No sealing yet, no entries in file.");
|
||||
}
|
||||
log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "PASS: %s", f->path);
|
||||
|
||||
if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
|
||||
if (validated > 0) {
|
||||
char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX];
|
||||
log_full(arg_quiet ? LOG_DEBUG : LOG_INFO,
|
||||
"=> Validated from %s to %s, final %s entries not sealed.",
|
||||
format_timestamp_maybe_utc(a, sizeof(a), first),
|
||||
format_timestamp_maybe_utc(b, sizeof(b), validated),
|
||||
FORMAT_TIMESPAN(usec_sub_unsigned(last, validated), 0));
|
||||
} else if (last > 0)
|
||||
log_full(arg_quiet ? LOG_DEBUG : LOG_INFO,
|
||||
"=> No sealing yet, %s of entries not sealed.",
|
||||
FORMAT_TIMESPAN(usec_sub_unsigned(last, first), 0));
|
||||
else
|
||||
log_full(arg_quiet ? LOG_DEBUG : LOG_INFO,
|
||||
"=> No sealing yet, no entries in file.");
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int action_disk_usage(void) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#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"
|
||||
@@ -28,23 +29,8 @@ static void* fssheader_free(FSSHeader *p) {
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(FSSHeader*, fssheader_free);
|
||||
|
||||
#if HAVE_GCRYPT
|
||||
static uint64_t journal_file_tag_seqnum(JournalFile *f) {
|
||||
uint64_t r;
|
||||
|
||||
assert(f);
|
||||
|
||||
r = le64toh(f->header->n_tags) + 1;
|
||||
f->header->n_tags = htole64(r);
|
||||
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
int journal_file_append_tag(JournalFile *f) {
|
||||
#if HAVE_GCRYPT
|
||||
Object *o;
|
||||
uint64_t p;
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
@@ -60,16 +46,20 @@ int journal_file_append_tag(JournalFile *f) {
|
||||
|
||||
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;
|
||||
|
||||
o->tag.seqnum = htole64(journal_file_tag_seqnum(f));
|
||||
o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state));
|
||||
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),
|
||||
FSPRG_GetEpoch(f->fsprg_state));
|
||||
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 */
|
||||
@@ -89,8 +79,6 @@ int journal_file_append_tag(JournalFile *f) {
|
||||
|
||||
int journal_file_hmac_start(JournalFile *f) {
|
||||
#if HAVE_GCRYPT
|
||||
uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
|
||||
gcry_error_t err;
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
@@ -104,11 +92,13 @@ int journal_file_hmac_start(JournalFile *f) {
|
||||
/* Prepare HMAC for next cycle */
|
||||
sym_gcry_md_reset(f->hmac);
|
||||
|
||||
r = FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0);
|
||||
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;
|
||||
|
||||
err = sym_gcry_md_setkey(f->hmac, key, sizeof(key));
|
||||
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",
|
||||
@@ -156,7 +146,7 @@ static int journal_file_fsprg_need_evolve(JournalFile *f, uint64_t realtime) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
epoch = FSPRG_GetEpoch(f->fsprg_state);
|
||||
epoch = FSPRG_GetEpoch(f->fsprg_state.iov_base);
|
||||
if (epoch > goal)
|
||||
return -ESTALE;
|
||||
|
||||
@@ -176,7 +166,7 @@ int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
epoch = FSPRG_GetEpoch(f->fsprg_state);
|
||||
epoch = FSPRG_GetEpoch(f->fsprg_state.iov_base);
|
||||
if (epoch < goal)
|
||||
log_debug("Evolving FSPRG key from epoch %"PRIu64" to %"PRIu64".", epoch, goal);
|
||||
|
||||
@@ -186,11 +176,11 @@ int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) {
|
||||
if (epoch == goal)
|
||||
return 0;
|
||||
|
||||
r = FSPRG_Evolve(f->fsprg_state);
|
||||
r = FSPRG_Evolve(f->fsprg_state.iov_base);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
epoch = FSPRG_GetEpoch(f->fsprg_state);
|
||||
epoch = FSPRG_GetEpoch(f->fsprg_state.iov_base);
|
||||
if (epoch < goal) {
|
||||
r = journal_file_append_tag(f);
|
||||
if (r < 0)
|
||||
@@ -200,8 +190,6 @@ int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) {
|
||||
}
|
||||
|
||||
int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
|
||||
void *msk;
|
||||
uint64_t epoch;
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
@@ -209,33 +197,31 @@ int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
|
||||
if (!JOURNAL_HEADER_SEALED(f->header))
|
||||
return 0;
|
||||
|
||||
assert(f->fsprg_seed);
|
||||
assert(iovec_is_set(&f->fsprg_seed));
|
||||
|
||||
if (f->fsprg_state) {
|
||||
if (iovec_is_set(&f->fsprg_state)) {
|
||||
/* Cheaper... */
|
||||
|
||||
epoch = FSPRG_GetEpoch(f->fsprg_state);
|
||||
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);
|
||||
return FSPRG_Evolve(f->fsprg_state.iov_base);
|
||||
} else {
|
||||
f->fsprg_state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
|
||||
f->fsprg_state = malloc(f->fsprg_state_size);
|
||||
if (!f->fsprg_state)
|
||||
return -ENOMEM;
|
||||
r = iovec_alloc(FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR), &f->fsprg_state);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
log_debug("Seeking FSPRG key to %"PRIu64".", goal);
|
||||
|
||||
msk = alloca_safe(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR));
|
||||
|
||||
r = FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR);
|
||||
_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, goal, msk, f->fsprg_seed, f->fsprg_seed_size);
|
||||
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) {
|
||||
@@ -396,7 +382,7 @@ int journal_file_fss_load(JournalFile *f) {
|
||||
if (header == MAP_FAILED)
|
||||
return -errno;
|
||||
|
||||
if (memcmp(header->signature, FSS_HEADER_SIGNATURE, 8) != 0)
|
||||
if (memcmp(header->signature, (const uint8_t[]) FSS_HEADER_SIGNATURE, 8) != 0)
|
||||
return -EBADMSG;
|
||||
|
||||
if (header->incompatible_flags != 0)
|
||||
@@ -429,8 +415,9 @@ int journal_file_fss_load(JournalFile *f) {
|
||||
f->fss_start_usec = le64toh(f->fss_file->start_usec);
|
||||
f->fss_interval_usec = le64toh(f->fss_file->interval_usec);
|
||||
|
||||
f->fsprg_state = (uint8_t*) f->fss_file + le64toh(f->fss_file->header_size);
|
||||
f->fsprg_state_size = le64toh(f->fss_file->fsprg_state_size);
|
||||
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;
|
||||
}
|
||||
@@ -496,10 +483,9 @@ int journal_file_append_first_tag(JournalFile *f) {
|
||||
}
|
||||
|
||||
int journal_file_parse_verification_key(JournalFile *f, const char *key) {
|
||||
_cleanup_free_ uint8_t *seed = NULL;
|
||||
_cleanup_(erase_and_freep) uint8_t *seed = NULL;
|
||||
size_t seed_size;
|
||||
const char *k;
|
||||
unsigned long long start, interval;
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
@@ -518,29 +504,35 @@ int journal_file_parse_verification_key(JournalFile *f, const char *key) {
|
||||
|
||||
x = unhexchar(*k);
|
||||
if (x < 0)
|
||||
return -EINVAL;
|
||||
return -EKEYREJECTED;
|
||||
k++;
|
||||
|
||||
y = unhexchar(*k);
|
||||
if (y < 0)
|
||||
return -EINVAL;
|
||||
return -EKEYREJECTED;
|
||||
k++;
|
||||
|
||||
seed[c] = (uint8_t) (x * 16 + y);
|
||||
}
|
||||
|
||||
if (*k != '/')
|
||||
return -EINVAL;
|
||||
return -EKEYREJECTED;
|
||||
k++;
|
||||
|
||||
r = sscanf(k, "%llx-%llx", &start, &interval);
|
||||
uint64_t start, interval;
|
||||
r = sscanf(k, "%"PRIx64"-%"PRIx64, &start, &interval);
|
||||
if (r != 2)
|
||||
return -EINVAL;
|
||||
return -EKEYREJECTED;
|
||||
|
||||
f->fsprg_seed = TAKE_PTR(seed);
|
||||
f->fsprg_seed_size = seed_size;
|
||||
if (start == 0 || interval == 0)
|
||||
return -EKEYREJECTED;
|
||||
|
||||
f->fss_start_usec = start * interval;
|
||||
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;
|
||||
@@ -555,7 +547,7 @@ bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u) {
|
||||
if (!JOURNAL_HEADER_SEALED(f->header))
|
||||
return false;
|
||||
|
||||
epoch = FSPRG_GetEpoch(f->fsprg_state);
|
||||
epoch = FSPRG_GetEpoch(f->fsprg_state.iov_base);
|
||||
|
||||
*u = (usec_t) (f->fss_start_usec + f->fss_interval_usec * epoch + f->fss_interval_usec);
|
||||
|
||||
|
||||
@@ -261,7 +261,7 @@ assert_cc(sizeof(struct Header) == sizeof(struct Header__packed));
|
||||
assert_cc(sizeof(struct Header) == 272);
|
||||
|
||||
#define FSS_HEADER_SIGNATURE \
|
||||
((const char[]) { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' })
|
||||
{ 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' }
|
||||
|
||||
struct FSSHeader {
|
||||
uint8_t signature[8]; /* "KSHHRHLP" */
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "gcrypt-util.h"
|
||||
#include "hashmap.h"
|
||||
#include "id128-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "journal-authenticate.h"
|
||||
#include "journal-def.h"
|
||||
#include "journal-file.h"
|
||||
@@ -312,9 +313,9 @@ JournalFile* journal_file_close(JournalFile *f) {
|
||||
assert(sz < SIZE_MAX);
|
||||
munmap(f->fss_file, sz);
|
||||
} else
|
||||
free(f->fsprg_state);
|
||||
iovec_done_erase(&f->fsprg_state);
|
||||
|
||||
free(f->fsprg_seed);
|
||||
iovec_done_erase(&f->fsprg_seed);
|
||||
|
||||
if (f->hmac)
|
||||
sym_gcry_md_close(f->hmac);
|
||||
|
||||
@@ -107,11 +107,8 @@ typedef struct JournalFile {
|
||||
uint64_t fss_start_usec;
|
||||
uint64_t fss_interval_usec;
|
||||
|
||||
void *fsprg_state;
|
||||
size_t fsprg_state_size;
|
||||
|
||||
void *fsprg_seed;
|
||||
size_t fsprg_seed_size;
|
||||
struct iovec fsprg_state;
|
||||
struct iovec fsprg_seed;
|
||||
|
||||
/* 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;
|
||||
|
||||
@@ -814,12 +814,14 @@ static int verify_hash_table(
|
||||
int journal_file_verify(
|
||||
JournalFile *f,
|
||||
const char *key,
|
||||
usec_t *first_contained, usec_t *last_validated, usec_t *last_contained,
|
||||
usec_t *ret_first_contained,
|
||||
usec_t *ret_last_validated,
|
||||
usec_t *ret_last_contained,
|
||||
bool show_progress) {
|
||||
|
||||
int r;
|
||||
Object *o;
|
||||
uint64_t p = 0, last_epoch = 0, last_tag_realtime = 0;
|
||||
|
||||
uint64_t p = 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 */
|
||||
@@ -842,10 +844,8 @@ int journal_file_verify(
|
||||
if (key) {
|
||||
#if HAVE_GCRYPT
|
||||
r = journal_file_parse_verification_key(f, key);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse seed.");
|
||||
return r;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to load verification key: %m");
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
@@ -1167,7 +1167,7 @@ int journal_file_verify(
|
||||
error(p,
|
||||
"tag/entry realtime timestamp out of synchronization (%"PRIu64" >= %"PRIu64")",
|
||||
entry_realtime,
|
||||
rt + f->fss_interval_usec);
|
||||
rt_end);
|
||||
r = -EBADMSG;
|
||||
goto fail;
|
||||
}
|
||||
@@ -1234,6 +1234,7 @@ int journal_file_verify(
|
||||
|
||||
f->hmac_running = false;
|
||||
last_tag_realtime = rt;
|
||||
last_tag_realtime_end = rt_end;
|
||||
}
|
||||
|
||||
last_tag = p + ALIGN64(le64toh(o->object.size));
|
||||
@@ -1404,14 +1405,12 @@ int journal_file_verify(
|
||||
mmap_cache_fd_free(cache_entry_fd);
|
||||
mmap_cache_fd_free(cache_entry_array_fd);
|
||||
|
||||
if (first_contained)
|
||||
*first_contained = le64toh(f->header->head_entry_realtime);
|
||||
#if HAVE_GCRYPT
|
||||
if (last_validated)
|
||||
*last_validated = last_tag_realtime + f->fss_interval_usec;
|
||||
#endif
|
||||
if (last_contained)
|
||||
*last_contained = le64toh(f->header->tail_entry_realtime);
|
||||
if (ret_first_contained)
|
||||
*ret_first_contained = le64toh(f->header->head_entry_realtime);
|
||||
if (ret_last_validated)
|
||||
*ret_last_validated = last_tag_realtime_end;
|
||||
if (ret_last_contained)
|
||||
*ret_last_contained = le64toh(f->header->tail_entry_realtime);
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -3,4 +3,10 @@
|
||||
|
||||
#include "journal-file.h"
|
||||
|
||||
int journal_file_verify(JournalFile *f, const char *key, usec_t *first_contained, usec_t *last_validated, usec_t *last_contained, bool show_progress);
|
||||
int journal_file_verify(
|
||||
JournalFile *f,
|
||||
const char *key,
|
||||
usec_t *ret_first_contained,
|
||||
usec_t *ret_last_validated,
|
||||
usec_t *ret_last_contained,
|
||||
bool show_progress);
|
||||
|
||||
@@ -292,7 +292,7 @@ int journal_importer_process_data(JournalImporter *imp) {
|
||||
|
||||
switch (imp->state) {
|
||||
case IMPORTER_STATE_LINE: {
|
||||
char *line, *sep;
|
||||
char *line = NULL; /* avoid false maybe-uninitialized warning */
|
||||
size_t n = 0;
|
||||
|
||||
assert(imp->data_size == 0);
|
||||
@@ -317,7 +317,7 @@ int journal_importer_process_data(JournalImporter *imp) {
|
||||
COREDUMP\n
|
||||
LLLLLLLL0011223344...\n
|
||||
*/
|
||||
sep = memchr(line, '=', n);
|
||||
char *sep = memchr(line, '=', n);
|
||||
if (sep) {
|
||||
/* chomp newline */
|
||||
n--;
|
||||
|
||||
Reference in New Issue
Block a user