From 0284adc6a60ce0af1107cb0b50041a65d731f39e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Aug 2012 01:51:54 +0200 Subject: journal: split up journal-file.c --- src/journal/journal-verify.c | 558 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 558 insertions(+) create mode 100644 src/journal/journal-verify.c (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c new file mode 100644 index 0000000000..f3182e876e --- /dev/null +++ b/src/journal/journal-verify.c @@ -0,0 +1,558 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "util.h" +#include "macro.h" +#include "journal-def.h" +#include "journal-file.h" +#include "journal-authenticate.h" +#include "journal-verify.h" + +static int journal_file_object_verify(JournalFile *f, Object *o) { + assert(f); + assert(o); + + /* This does various superficial tests about the length an + * possible field values. It does not follow any references to + * other objects. */ + + switch (o->object.type) { + case OBJECT_DATA: + if (le64toh(o->data.entry_offset) <= 0 || + le64toh(o->data.n_entries) <= 0) + return -EBADMSG; + + if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) + return -EBADMSG; + break; + + case OBJECT_FIELD: + if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) + return -EBADMSG; + break; + + case OBJECT_ENTRY: + if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) + return -EBADMSG; + + if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) + return -EBADMSG; + + if (le64toh(o->entry.seqnum) <= 0 || + le64toh(o->entry.realtime) <= 0) + return -EBADMSG; + + break; + + case OBJECT_DATA_HASH_TABLE: + case OBJECT_FIELD_HASH_TABLE: + if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0) + return -EBADMSG; + + break; + + case OBJECT_ENTRY_ARRAY: + if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0) + return -EBADMSG; + + break; + + case OBJECT_TAG: + if (le64toh(o->object.size) != sizeof(TagObject)) + return -EBADMSG; + break; + } + + return 0; +} + +static void draw_progress(uint64_t p, usec_t *last_usec) { + unsigned n, i, j, k; + usec_t z, x; + + if (!isatty(STDOUT_FILENO)) + return; + + z = now(CLOCK_MONOTONIC); + x = *last_usec; + + if (x != 0 && x + 40 * USEC_PER_MSEC > z) + return; + + *last_usec = z; + + n = (3 * columns()) / 4; + j = (n * (unsigned) p) / 65535ULL; + k = n - j; + + fputs("\r\x1B[?25l", stdout); + + for (i = 0; i < j; i++) + fputs("\xe2\x96\x88", stdout); + + for (i = 0; i < k; i++) + fputs("\xe2\x96\x91", stdout); + + printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU); + + fputs("\r\x1B[?25h", stdout); + fflush(stdout); +} + +static void flush_progress(void) { + unsigned n, i; + + if (!isatty(STDOUT_FILENO)) + return; + + n = (3 * columns()) / 4; + + putchar('\r'); + + for (i = 0; i < n + 5; i++) + putchar(' '); + + putchar('\r'); + fflush(stdout); +} + +static int write_uint64(int fd, uint64_t p) { + ssize_t k; + + k = write(fd, &p, sizeof(p)); + if (k < 0) + return -errno; + if (k != sizeof(p)) + return -EIO; + + return 0; +} + +static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) { + uint64_t a, b; + int r; + + assert(m); + assert(fd >= 0); + + /* Bisection ... */ + + a = 0; b = n; + while (a < b) { + uint64_t c, *z; + + c = (a + b) / 2; + + r = mmap_cache_get(m, fd, PROT_READ, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z); + if (r < 0) + return r; + + if (*z == p) + return 1; + + if (p < *z) + b = c; + else + a = c; + } + + return 0; +} + +int journal_file_verify(JournalFile *f, const char *key) { + int r; + Object *o; + uint64_t p = 0; + uint64_t tag_seqnum = 0, entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0; + sd_id128_t entry_boot_id; + bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false; + uint64_t n_weird = 0, n_objects = 0, n_entries = 0, n_data = 0, n_fields = 0, n_data_hash_tables = 0, n_field_hash_tables = 0, n_entry_arrays = 0; + usec_t last_usec = 0; + int data_fd = -1, entry_fd = -1, entry_array_fd = -1; + char data_path[] = "/var/tmp/journal-data-XXXXXX", + entry_path[] = "/var/tmp/journal-entry-XXXXXX", + entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX"; + + assert(f); + + data_fd = mkostemp(data_path, O_CLOEXEC); + if (data_fd < 0) { + log_error("Failed to create data file: %m"); + goto fail; + } + unlink(data_path); + + entry_fd = mkostemp(entry_path, O_CLOEXEC); + if (entry_fd < 0) { + log_error("Failed to create entry file: %m"); + goto fail; + } + unlink(entry_path); + + entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC); + if (entry_array_fd < 0) { + log_error("Failed to create entry array file: %m"); + goto fail; + } + unlink(entry_array_path); + + /* First iteration: we go through all objects, verify the + * superficial structure, headers, hashes. */ + + r = journal_file_hmac_put_header(f); + if (r < 0) { + log_error("Failed to calculate HMAC of header."); + goto fail; + } + + p = le64toh(f->header->header_size); + while (p != 0) { + draw_progress((0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec); + + r = journal_file_move_to_object(f, -1, p, &o); + if (r < 0) { + log_error("Invalid object at %llu", (unsigned long long) p); + goto fail; + } + + if (le64toh(f->header->tail_object_offset) < p) { + log_error("Invalid tail object pointer."); + r = -EBADMSG; + goto fail; + } + + n_objects ++; + + r = journal_file_object_verify(f, o); + if (r < 0) { + log_error("Invalid object contents at %llu", (unsigned long long) p); + goto fail; + } + + r = journal_file_hmac_put_object(f, -1, p); + if (r < 0) { + log_error("Failed to calculate HMAC at %llu", (unsigned long long) p); + goto fail; + } + + if (o->object.flags & OBJECT_COMPRESSED && + !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) { + log_error("Compressed object without compression at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + if (o->object.flags & OBJECT_COMPRESSED && + o->object.type != OBJECT_DATA) { + log_error("Compressed non-data object at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + if (o->object.type == OBJECT_TAG) { + + if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) { + log_error("Tag object without authentication at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + if (le64toh(o->tag.seqnum) != tag_seqnum) { + log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + } else if (o->object.type == OBJECT_ENTRY) { + + r = write_uint64(entry_fd, p); + if (r < 0) + goto fail; + + if (!entry_seqnum_set && + le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) { + log_error("Head entry sequence number incorrect"); + r = -EBADMSG; + goto fail; + } + + if (entry_seqnum_set && + entry_seqnum >= le64toh(o->entry.seqnum)) { + log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + entry_seqnum = le64toh(o->entry.seqnum); + entry_seqnum_set = true; + + if (entry_monotonic_set && + sd_id128_equal(entry_boot_id, o->entry.boot_id) && + entry_monotonic > le64toh(o->entry.monotonic)) { + log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + entry_monotonic = le64toh(o->entry.monotonic); + entry_boot_id = o->entry.boot_id; + entry_monotonic_set = true; + + if (!entry_realtime_set && + le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) { + log_error("Head entry realtime timestamp incorrect"); + r = -EBADMSG; + goto fail; + } + + entry_realtime = le64toh(o->entry.realtime); + entry_realtime_set = true; + + n_entries ++; + } else if (o->object.type == OBJECT_ENTRY_ARRAY) { + + r = write_uint64(entry_array_fd, p); + if (r < 0) + goto fail; + + if (p == le64toh(f->header->entry_array_offset)) { + if (found_main_entry_array) { + log_error("More than one main entry array at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + found_main_entry_array = true; + } + + n_entry_arrays++; + + } else if (o->object.type == OBJECT_DATA) { + + r = write_uint64(data_fd, p); + if (r < 0) + goto fail; + + n_data++; + + } else if (o->object.type == OBJECT_FIELD) + n_fields++; + else if (o->object.type == OBJECT_DATA_HASH_TABLE) { + n_data_hash_tables++; + + if (n_data_hash_tables > 1) { + log_error("More than one data hash table at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) || + le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) { + log_error("Header fields for data hash table invalid."); + r = -EBADMSG; + goto fail; + } + } else if (o->object.type == OBJECT_FIELD_HASH_TABLE) { + n_field_hash_tables++; + + if (n_field_hash_tables > 1) { + log_error("More than one field hash table at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) || + le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) { + log_error("Header fields for field hash table invalid."); + r = -EBADMSG; + goto fail; + } + } else if (o->object.type >= _OBJECT_TYPE_MAX) + n_weird ++; + + if (p == le64toh(f->header->tail_object_offset)) + p = 0; + else + p = p + ALIGN64(le64toh(o->object.size)); + } + + if (n_objects != le64toh(f->header->n_objects)) { + log_error("Object number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (n_entries != le64toh(f->header->n_entries)) { + log_error("Entry number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_data) && + n_data != le64toh(f->header->n_data)) { + log_error("Data number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) && + n_fields != le64toh(f->header->n_fields)) { + log_error("Field number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) && + tag_seqnum != le64toh(f->header->n_tags)) { + log_error("Tag number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (n_data_hash_tables != 1) { + log_error("Missing data hash table"); + r = -EBADMSG; + goto fail; + } + + if (n_field_hash_tables != 1) { + log_error("Missing field hash table"); + r = -EBADMSG; + goto fail; + } + + if (!found_main_entry_array) { + log_error("Missing entry array"); + r = -EBADMSG; + goto fail; + } + + if (entry_seqnum_set && + entry_seqnum != le64toh(f->header->tail_entry_seqnum)) { + log_error("Invalid tail seqnum"); + r = -EBADMSG; + goto fail; + } + + if (entry_monotonic_set && + (!sd_id128_equal(entry_boot_id, f->header->boot_id) || + entry_monotonic != le64toh(f->header->tail_entry_monotonic))) { + log_error("Invalid tail monotonic timestamp"); + r = -EBADMSG; + goto fail; + } + + if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) { + log_error("Invalid tail realtime timestamp"); + r = -EBADMSG; + goto fail; + } + + /* Second iteration: we go through all objects again, this + * time verify all pointers. */ + + p = le64toh(f->header->header_size); + while (p != 0) { + draw_progress(0x8000 + (0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec); + + r = journal_file_move_to_object(f, -1, p, &o); + if (r < 0) { + log_error("Invalid object at %llu", (unsigned long long) p); + goto fail; + } + + if (o->object.type == OBJECT_ENTRY_ARRAY) { + uint64_t i = 0, n; + + if (le64toh(o->entry_array.next_entry_array_offset) != 0 && + !contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, le64toh(o->entry_array.next_entry_array_offset))) { + log_error("Entry array chains up to invalid next array at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + n = journal_file_entry_array_n_items(o); + for (i = 0; i < n; i++) { + if (le64toh(o->entry_array.items[i]) != 0 && + !contains_uint64(f->mmap, entry_fd, n_entries, le64toh(o->entry_array.items[i]))) { + + log_error("Entry array points to invalid next array at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + } + + } + + r = journal_file_move_to_object(f, -1, p, &o); + if (r < 0) { + log_error("Invalid object at %llu", (unsigned long long) p); + goto fail; + } + + if (p == le64toh(f->header->tail_object_offset)) + p = 0; + else + p = p + ALIGN64(le64toh(o->object.size)); + } + + flush_progress(); + + mmap_cache_close_fd(f->mmap, data_fd); + mmap_cache_close_fd(f->mmap, entry_fd); + mmap_cache_close_fd(f->mmap, entry_array_fd); + + close_nointr_nofail(data_fd); + close_nointr_nofail(entry_fd); + close_nointr_nofail(entry_array_fd); + + return 0; + +fail: + flush_progress(); + + log_error("File corruption detected at %s:%llu (of %llu, %llu%%).", + f->path, + (unsigned long long) p, + (unsigned long long) f->last_stat.st_size, + (unsigned long long) (100 * p / f->last_stat.st_size)); + + if (data_fd >= 0) { + mmap_cache_close_fd(f->mmap, data_fd); + close_nointr_nofail(data_fd); + } + + if (entry_fd >= 0) { + mmap_cache_close_fd(f->mmap, entry_fd); + close_nointr_nofail(entry_fd); + } + + if (entry_array_fd >= 0) { + mmap_cache_close_fd(f->mmap, entry_array_fd); + close_nointr_nofail(entry_array_fd); + } + + return r; +} -- cgit v1.2.3-54-g00ecf From f59a5f6b873d8bf994e2d85671f2554b9fdd62db Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Aug 2012 01:59:25 +0200 Subject: journal: verify hashes only during actual verification, not all the time --- src/journal/journal-file.c | 20 -------------------- src/journal/journal-verify.c | 39 +++++++++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 30 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index ff439f2474..efa0910780 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -319,23 +319,6 @@ static int journal_file_move_to(JournalFile *f, int context, uint64_t offset, ui return mmap_cache_get(f->mmap, f->fd, f->prot, context, offset, size, ret); } -static bool verify_hash(Object *o) { - uint64_t h1, h2; - - assert(o); - - if (o->object.type == OBJECT_DATA && !(o->object.flags & OBJECT_COMPRESSED)) { - h1 = le64toh(o->data.hash); - h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload)); - } else if (o->object.type == OBJECT_FIELD) { - h1 = le64toh(o->field.hash); - h2 = hash64(o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload)); - } else - return true; - - return h1 == h2; -} - static uint64_t minimum_header_size(Object *o) { static uint64_t table[] = { @@ -394,9 +377,6 @@ int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Objec o = (Object*) t; } - if (!verify_hash(o)) - return -EBADMSG; - *ret = o; return 0; } diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index f3182e876e..9318f3df8b 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -29,6 +29,16 @@ #include "journal-file.h" #include "journal-authenticate.h" #include "journal-verify.h" +#include "lookup3.h" + +/* FIXME: + * + * - verify hashes of compressed objects + * - follow all chains + * - check for unreferenced objects + * - verify FSPRG + * + * */ static int journal_file_object_verify(JournalFile *f, Object *o) { assert(f); @@ -38,7 +48,12 @@ static int journal_file_object_verify(JournalFile *f, Object *o) { * possible field values. It does not follow any references to * other objects. */ + if ((o->object.flags & OBJECT_COMPRESSED) && + o->object.type != OBJECT_DATA) + return -EBADMSG; + switch (o->object.type) { + case OBJECT_DATA: if (le64toh(o->data.entry_offset) <= 0 || le64toh(o->data.n_entries) <= 0) @@ -46,6 +61,17 @@ static int journal_file_object_verify(JournalFile *f, Object *o) { if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) return -EBADMSG; + + if (!(o->object.flags & OBJECT_COMPRESSED)) { + uint64_t h1, h2; + + h1 = le64toh(o->data.hash); + h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload)); + + if (h1 != h2) + return -EBADMSG; + } + break; case OBJECT_FIELD: @@ -251,12 +277,6 @@ int journal_file_verify(JournalFile *f, const char *key) { goto fail; } - r = journal_file_hmac_put_object(f, -1, p); - if (r < 0) { - log_error("Failed to calculate HMAC at %llu", (unsigned long long) p); - goto fail; - } - if (o->object.flags & OBJECT_COMPRESSED && !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) { log_error("Compressed object without compression at %llu", (unsigned long long) p); @@ -264,10 +284,9 @@ int journal_file_verify(JournalFile *f, const char *key) { goto fail; } - if (o->object.flags & OBJECT_COMPRESSED && - o->object.type != OBJECT_DATA) { - log_error("Compressed non-data object at %llu", (unsigned long long) p); - r = -EBADMSG; + r = journal_file_hmac_put_object(f, -1, p); + if (r < 0) { + log_error("Failed to calculate HMAC at %llu", (unsigned long long) p); goto fail; } -- cgit v1.2.3-54-g00ecf From fd5dc3204d350142a9105d3e9c83bf29d3a900ee Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Aug 2012 03:43:07 +0200 Subject: journal: verify compressed objects --- src/journal/journal-verify.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 9318f3df8b..8ef91ce485 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -30,13 +30,14 @@ #include "journal-authenticate.h" #include "journal-verify.h" #include "lookup3.h" +#include "compress.h" /* FIXME: * - * - verify hashes of compressed objects * - follow all chains * - check for unreferenced objects * - verify FSPRG + * - Allow building without libgcrypt * * */ @@ -54,7 +55,9 @@ static int journal_file_object_verify(JournalFile *f, Object *o) { switch (o->object.type) { - case OBJECT_DATA: + case OBJECT_DATA: { + uint64_t h1, h2; + if (le64toh(o->data.entry_offset) <= 0 || le64toh(o->data.n_entries) <= 0) return -EBADMSG; @@ -62,17 +65,27 @@ static int journal_file_object_verify(JournalFile *f, Object *o) { if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) return -EBADMSG; - if (!(o->object.flags & OBJECT_COMPRESSED)) { - uint64_t h1, h2; + h1 = le64toh(o->data.hash); - h1 = le64toh(o->data.hash); - h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload)); + if (o->object.flags & OBJECT_COMPRESSED) { + void *b = NULL; + uint64_t alloc = 0, b_size; - if (h1 != h2) + if (!uncompress_blob(o->data.payload, + le64toh(o->object.size) - offsetof(Object, data.payload), + &b, &alloc, &b_size)) return -EBADMSG; - } + + h2 = hash64(b, b_size); + free(b); + } else + h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload)); + + if (h1 != h2) + return -EBADMSG; break; + } case OBJECT_FIELD: if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) -- cgit v1.2.3-54-g00ecf From f9fffc31cdc4be7a0e4437837ae06a0c111fe020 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Aug 2012 03:45:10 +0200 Subject: journal: add color to verification progress bar --- src/journal/journal-verify.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 8ef91ce485..1a9a73092e 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -146,11 +146,13 @@ static void draw_progress(uint64_t p, usec_t *last_usec) { j = (n * (unsigned) p) / 65535ULL; k = n - j; - fputs("\r\x1B[?25l", stdout); + fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout); for (i = 0; i < j; i++) fputs("\xe2\x96\x88", stdout); + fputs(ANSI_HIGHLIGHT_OFF, stdout); + for (i = 0; i < k; i++) fputs("\xe2\x96\x91", stdout); -- cgit v1.2.3-54-g00ecf From 86adf873be22a38dbc9c6e86124c30b6caecd185 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Aug 2012 17:09:53 +0200 Subject: journal: verify structural consistency --- src/journal/journal-def.h | 4 +- src/journal/journal-verify.c | 445 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 397 insertions(+), 52 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h index a77f69edcb..e61e81ca84 100644 --- a/src/journal/journal-def.h +++ b/src/journal/journal-def.h @@ -47,12 +47,12 @@ typedef struct FSPRGHeader FSPRGHeader; /* Object types */ enum { OBJECT_UNUSED, - OBJECT_DATA, + OBJECT_DATA, /* !!! */ OBJECT_FIELD, OBJECT_ENTRY, OBJECT_DATA_HASH_TABLE, OBJECT_FIELD_HASH_TABLE, - OBJECT_ENTRY_ARRAY, + OBJECT_ENTRY_ARRAY, /* !!! */ OBJECT_TAG, _OBJECT_TYPE_MAX }; diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 1a9a73092e..7c99d44838 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -34,10 +34,11 @@ /* FIXME: * - * - follow all chains - * - check for unreferenced objects * - verify FSPRG * - Allow building without libgcrypt + * - check with sparse + * - 64bit conversions + * - verification should use MAP_PRIVATE * * */ @@ -110,12 +111,18 @@ static int journal_file_object_verify(JournalFile *f, Object *o) { if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0) return -EBADMSG; + if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) + return -EBADMSG; + break; case OBJECT_ENTRY_ARRAY: if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0) return -EBADMSG; + if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) + return -EBADMSG; + break; case OBJECT_TAG: @@ -206,7 +213,7 @@ static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) { c = (a + b) / 2; - r = mmap_cache_get(m, fd, PROT_READ, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z); + r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z); if (r < 0) return r; @@ -222,6 +229,368 @@ static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) { return 0; } +static int entry_points_to_data( + JournalFile *f, + int entry_fd, + uint64_t n_entries, + uint64_t entry_p, + uint64_t data_p) { + + int r; + uint64_t i, n, a; + Object *o; + bool found = false; + + assert(f); + assert(entry_fd >= 0); + + if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) { + log_error("Data object references invalid entry at %llu", (unsigned long long) data_p); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o); + if (r < 0) + return r; + + n = journal_file_entry_n_items(o); + for (i = 0; i < n; i++) + if (le64toh(o->entry.items[i].object_offset) == data_p) { + found = true; + break; + } + + if (!found) { + log_error("Data object not referenced by linked entry at %llu", (unsigned long long) data_p); + return -EBADMSG; + } + + /* Check if this entry is also in main entry array. Since the + * main entry array has already been verified we can rely on + * its consistency.*/ + + n = le64toh(f->header->n_entries); + a = le64toh(f->header->entry_array_offset); + i = 0; + + while (i < n) { + uint64_t m, j; + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + + m = journal_file_entry_array_n_items(o); + for (j = 0; i < n && j < m; i++, j++) + if (le64toh(o->entry_array.items[j]) == entry_p) + return 0; + + a = le64toh(o->entry_array.next_entry_array_offset);; + } + + return 0; +} + +static int verify_data( + JournalFile *f, + Object *o, uint64_t p, + int entry_fd, uint64_t n_entries, + int entry_array_fd, uint64_t n_entry_arrays) { + + uint64_t i, n, a, last, q; + int r; + + assert(f); + assert(o); + assert(entry_fd >= 0); + assert(entry_array_fd >= 0); + + n = le64toh(o->data.n_entries); + a = le64toh(o->data.entry_array_offset); + + /* We already checked this earlier */ + assert(n > 0); + + last = q = le64toh(o->data.entry_offset); + r = entry_points_to_data(f, entry_fd, n_entries, q, p); + if (r < 0) + return r; + + while (i < n) { + uint64_t next, m, j; + + if (a == 0) { + log_error("Array chain too short at %llu.", (unsigned long long) p); + return -EBADMSG; + } + + if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) { + log_error("Invalid array at %llu.", (unsigned long long) p); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + + next = le64toh(o->entry_array.next_entry_array_offset); + if (next != 0 && next <= a) { + log_error("Array chain has cycle at %llu.", (unsigned long long) p); + return -EBADMSG; + } + + m = journal_file_entry_array_n_items(o); + for (j = 0; i < n && j < m; i++, j++) { + + q = le64toh(o->entry_array.items[j]); + if (q <= last) { + log_error("Data object's entry array not sorted at %llu.", (unsigned long long) p); + return -EBADMSG; + } + last = q; + + r = entry_points_to_data(f, entry_fd, n_entries, q, p); + if (r < 0) + return r; + + /* Pointer might have moved, reposition */ + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + } + + a = next; + } + + return 0; +} + +static int verify_hash_table( + JournalFile *f, + int data_fd, uint64_t n_data, + int entry_fd, uint64_t n_entries, + int entry_array_fd, uint64_t n_entry_arrays, + usec_t *last_usec) { + + uint64_t i, n; + int r; + + assert(f); + assert(data_fd >= 0); + assert(entry_fd >= 0); + assert(entry_array_fd >= 0); + assert(last_usec); + + n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem); + for (i = 0; i < n; i++) { + uint64_t last = 0, p; + + draw_progress(0xC000 + (0x3FFF * i / n), last_usec); + + p = le64toh(f->data_hash_table[i].head_hash_offset); + while (p != 0) { + Object *o; + uint64_t next; + + if (!contains_uint64(f->mmap, data_fd, n_data, p)) { + log_error("Invalid data object at hash entry %llu of %llu.", + (unsigned long long) i, (unsigned long long) n); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + next = le64toh(o->data.next_hash_offset); + if (next != 0 && next <= p) { + log_error("Hash chain has a cycle in hash entry %llu of %llu.", + (unsigned long long) i, (unsigned long long) n); + return -EBADMSG; + } + + if (le64toh(o->data.hash) % n != i) { + log_error("Hash value mismatch in hash entry %llu of %llu.", + (unsigned long long) i, (unsigned long long) n); + return -EBADMSG; + } + + r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays); + if (r < 0) + return r; + + last = p; + p = next; + } + + if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) { + log_error("Tail hash pointer mismatch in hash table."); + return -EBADMSG; + } + } + + return 0; +} + +static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) { + uint64_t n, h, q; + int r; + assert(f); + + n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem); + h = hash % n; + + q = le64toh(f->data_hash_table[h].head_hash_offset); + while (q != 0) { + Object *o; + + if (p == q) + return 1; + + r = journal_file_move_to_object(f, OBJECT_DATA, q, &o); + if (r < 0) + return r; + + q = le64toh(o->data.next_hash_offset); + } + + return 0; +} + +static int verify_entry( + JournalFile *f, + Object *o, uint64_t p, + int data_fd, uint64_t n_data) { + + uint64_t i, n; + int r; + + assert(f); + assert(o); + assert(data_fd >= 0); + + n = journal_file_entry_n_items(o); + for (i = 0; i < n; i++) { + uint64_t q, h; + Object *u; + + q = le64toh(o->entry.items[i].object_offset); + h = le64toh(o->entry.items[i].hash); + + if (!contains_uint64(f->mmap, data_fd, n_data, q)) { + log_error("Invalid data object at entry %llu.", + (unsigned long long) o); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_DATA, q, &u); + if (r < 0) + return r; + + if (le64toh(u->data.hash) != h) { + log_error("Hash mismatch for data object at entry %llu.", + (unsigned long long) p); + return -EBADMSG; + } + + r = data_object_in_hash_table(f, h, q); + if (r < 0) + return r; + if (r == 0) { + log_error("Data object missing from hash at entry %llu.", + (unsigned long long) p); + return -EBADMSG; + } + } + + return 0; +} + +static int verify_entry_array( + JournalFile *f, + int data_fd, uint64_t n_data, + int entry_fd, uint64_t n_entries, + int entry_array_fd, uint64_t n_entry_arrays, + usec_t *last_usec) { + + uint64_t i = 0, a, n, last = 0; + int r; + + assert(f); + assert(data_fd >= 0); + assert(entry_fd >= 0); + assert(entry_array_fd >= 0); + assert(last_usec); + + n = le64toh(f->header->n_entries); + a = le64toh(f->header->entry_array_offset); + while (i < n) { + uint64_t next, m, j; + Object *o; + + draw_progress(0x8000 + (0x3FFF * i / n), last_usec); + + if (a == 0) { + log_error("Array chain too short at %llu of %llu.", + (unsigned long long) i, (unsigned long long) n); + return -EBADMSG; + } + + if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) { + log_error("Invalid array at %llu of %llu.", + (unsigned long long) i, (unsigned long long) n); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + + next = le64toh(o->entry_array.next_entry_array_offset); + if (next != 0 && next <= a) { + log_error("Array chain has cycle at %llu of %llu.", + (unsigned long long) i, (unsigned long long) n); + return -EBADMSG; + } + + m = journal_file_entry_array_n_items(o); + for (j = 0; i < n && j < m; i++, j++) { + uint64_t p; + + p = le64toh(o->entry_array.items[j]); + if (p <= last) { + log_error("Entry array not sorted at %llu of %llu.", + (unsigned long long) i, (unsigned long long) n); + return -EBADMSG; + } + last = p; + + if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) { + log_error("Invalid array entry at %llu of %llu.", + (unsigned long long) i, (unsigned long long) n); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + r = verify_entry(f, o, p, data_fd, n_data); + if (r < 0) + return r; + + /* Pointer might have moved, reposition */ + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + } + + a = next; + } + + return 0; +} + int journal_file_verify(JournalFile *f, const char *key) { int r; Object *o; @@ -270,7 +639,7 @@ int journal_file_verify(JournalFile *f, const char *key) { p = le64toh(f->header->header_size); while (p != 0) { - draw_progress((0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec); + draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec); r = journal_file_move_to_object(f, -1, p, &o); if (r < 0) { @@ -504,53 +873,29 @@ int journal_file_verify(JournalFile *f, const char *key) { goto fail; } - /* Second iteration: we go through all objects again, this - * time verify all pointers. */ - - p = le64toh(f->header->header_size); - while (p != 0) { - draw_progress(0x8000 + (0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec); - - r = journal_file_move_to_object(f, -1, p, &o); - if (r < 0) { - log_error("Invalid object at %llu", (unsigned long long) p); - goto fail; - } - - if (o->object.type == OBJECT_ENTRY_ARRAY) { - uint64_t i = 0, n; - - if (le64toh(o->entry_array.next_entry_array_offset) != 0 && - !contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, le64toh(o->entry_array.next_entry_array_offset))) { - log_error("Entry array chains up to invalid next array at %llu", (unsigned long long) p); - r = -EBADMSG; - goto fail; - } - - n = journal_file_entry_array_n_items(o); - for (i = 0; i < n; i++) { - if (le64toh(o->entry_array.items[i]) != 0 && - !contains_uint64(f->mmap, entry_fd, n_entries, le64toh(o->entry_array.items[i]))) { - - log_error("Entry array points to invalid next array at %llu", (unsigned long long) p); - r = -EBADMSG; - goto fail; - } - } - - } - - r = journal_file_move_to_object(f, -1, p, &o); - if (r < 0) { - log_error("Invalid object at %llu", (unsigned long long) p); - goto fail; - } + /* Second iteration: we follow all objects referenced from the + * two entry points: the object hash table and the entry + * array. We also check that everything referenced (directly + * or indirectly) in the data hash table also exists in the + * entry array, and vice versa. Note that we do not care for + * unreferenced objects. We only care that everything that is + * referenced is consistent. */ + + r = verify_entry_array(f, + data_fd, n_data, + entry_fd, n_entries, + entry_array_fd, n_entry_arrays, + &last_usec); + if (r < 0) + goto fail; - if (p == le64toh(f->header->tail_object_offset)) - p = 0; - else - p = p + ALIGN64(le64toh(o->object.size)); - } + r = verify_hash_table(f, + data_fd, n_data, + entry_fd, n_entries, + entry_array_fd, n_entry_arrays, + &last_usec); + if (r < 0) + goto fail; flush_progress(); -- cgit v1.2.3-54-g00ecf From f5028bfaf06b58a59d45fcd875dced1413703999 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Aug 2012 17:19:47 +0200 Subject: journal: journal-send.h doesn't actually exist --- Makefile.am | 1 - src/journal/journal-verify.c | 1 - 2 files changed, 2 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/Makefile.am b/Makefile.am index 895dcfa334..3e7ef27ebd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2406,7 +2406,6 @@ libsystemd_journal_la_SOURCES = \ src/journal/lookup3.c \ src/journal/lookup3.h \ src/journal/journal-send.c \ - src/journal/journal-send.h \ src/journal/sparse-endian.h \ src/journal/journal-def.h \ src/journal/compress.h \ diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 7c99d44838..c68a22e81a 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -38,7 +38,6 @@ * - Allow building without libgcrypt * - check with sparse * - 64bit conversions - * - verification should use MAP_PRIVATE * * */ -- cgit v1.2.3-54-g00ecf From 2a7273ef923e23e8444d556ec8de420f39b1311f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Aug 2012 17:22:58 +0200 Subject: journal: fix unitialized var --- src/journal/journal-verify.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index c68a22e81a..ad96cca477 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -315,6 +315,7 @@ static int verify_data( if (r < 0) return r; + i = 1; while (i < n) { uint64_t next, m, j; -- cgit v1.2.3-54-g00ecf From 1137e6c73ba4456f6bb054a2a45f80972f568586 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Aug 2012 17:39:00 +0200 Subject: journal: fix variable initialization --- src/journal/journal-verify.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index ad96cca477..55ca52a641 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -610,6 +610,7 @@ int journal_file_verify(JournalFile *f, const char *key) { data_fd = mkostemp(data_path, O_CLOEXEC); if (data_fd < 0) { log_error("Failed to create data file: %m"); + r = -errno; goto fail; } unlink(data_path); @@ -617,6 +618,7 @@ int journal_file_verify(JournalFile *f, const char *key) { entry_fd = mkostemp(entry_path, O_CLOEXEC); if (entry_fd < 0) { log_error("Failed to create entry file: %m"); + r = -errno; goto fail; } unlink(entry_path); @@ -624,6 +626,7 @@ int journal_file_verify(JournalFile *f, const char *key) { entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC); if (entry_array_fd < 0) { log_error("Failed to create entry array file: %m"); + r = -errno; goto fail; } unlink(entry_array_path); -- cgit v1.2.3-54-g00ecf From 2dee23ebe09965308c67a661ed0a8d2cc0ae1d5f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Aug 2012 20:51:24 +0200 Subject: journal: count number of entry arrays in header --- src/journal/journal-def.h | 1 + src/journal/journal-file.c | 3 +++ src/journal/journal-verify.c | 7 +++++++ 3 files changed, 11 insertions(+) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h index e61e81ca84..660a92c147 100644 --- a/src/journal/journal-def.h +++ b/src/journal/journal-def.h @@ -185,6 +185,7 @@ _packed_ struct Header { le64_t n_fields; /* Added in 189 */ le64_t n_tags; + le64_t n_entry_arrays; }; #define FSPRG_HEADER_SIGNATURE ((char[]) { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' }) diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index e0b20ccefd..81952a08ee 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -839,6 +839,9 @@ static int link_entry_into_array(JournalFile *f, o->entry_array.next_entry_array_offset = htole64(q); } + if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays)) + f->header->n_entry_arrays = htole64(le64toh(f->header->n_entry_arrays) + 1); + *idx = htole64(hidx + 1); return 0; diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 55ca52a641..b4e34d18a8 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -837,6 +837,13 @@ int journal_file_verify(JournalFile *f, const char *key) { goto fail; } + if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) && + n_entry_arrays != le64toh(f->header->n_entry_arrays)) { + log_error("Entry array number mismatch"); + r = -EBADMSG; + goto fail; + } + if (n_data_hash_tables != 1) { log_error("Missing data hash table"); r = -EBADMSG; -- cgit v1.2.3-54-g00ecf From b7c9ae91d111b3e89d1ffc00e08f9ed97a8ff5db Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Aug 2012 20:51:43 +0200 Subject: journal: parse fsprg seed --- src/journal/journal-authenticate.c | 54 +++++++++++++------------------ src/journal/journal-file.c | 8 +++-- src/journal/journal-file.h | 13 ++++++-- src/journal/journal-verify.c | 66 +++++++++++++++++++++++++++++++++++++- 4 files changed, 104 insertions(+), 37 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c index 5a0314b4f6..809655e1ac 100644 --- a/src/journal/journal-authenticate.c +++ b/src/journal/journal-authenticate.c @@ -27,22 +27,6 @@ #include "journal-authenticate.h" #include "fsprg.h" -static void *fsprg_state(JournalFile *f) { - uint64_t a, b; - assert(f); - - if (!f->authenticate) - return NULL; - - a = le64toh(f->fsprg_header->header_size); - b = le64toh(f->fsprg_header->state_size); - - if (a + b > f->fsprg_size) - return NULL; - - return (uint8_t*) f->fsprg_header + a; -} - static uint64_t journal_file_tag_seqnum(JournalFile *f) { uint64_t r; @@ -67,7 +51,7 @@ int journal_file_append_tag(JournalFile *f) { if (!f->hmac_running) return 0; - log_debug("Writing tag for epoch %llu\n", (unsigned long long) FSPRG_GetEpoch(fsprg_state(f))); + log_debug("Writing tag for epoch %llu\n", (unsigned long long) FSPRG_GetEpoch(f->fsprg_state)); assert(f->hmac); @@ -103,7 +87,7 @@ static int journal_file_hmac_start(JournalFile *f) { /* Prepare HMAC for next cycle */ gcry_md_reset(f->hmac); - FSPRG_GetKey(fsprg_state(f), key, sizeof(key), 0); + FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0); gcry_md_setkey(f->hmac, key, sizeof(key)); f->hmac_running = true; @@ -118,15 +102,15 @@ static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *e assert(epoch); assert(f->authenticate); - if (le64toh(f->fsprg_header->fsprg_start_usec) == 0 || - le64toh(f->fsprg_header->fsprg_interval_usec) == 0) + if (f->fsprg_start_usec == 0 || + f->fsprg_interval_usec == 0) return -ENOTSUP; - if (realtime < le64toh(f->fsprg_header->fsprg_start_usec)) + if (realtime < f->fsprg_start_usec) return -ESTALE; - t = realtime - le64toh(f->fsprg_header->fsprg_start_usec); - t = t / le64toh(f->fsprg_header->fsprg_interval_usec); + t = realtime - f->fsprg_start_usec; + t = t / f->fsprg_interval_usec; *epoch = t; return 0; @@ -144,7 +128,7 @@ static int journal_file_need_evolve(JournalFile *f, uint64_t realtime) { if (r < 0) return r; - epoch = FSPRG_GetEpoch(fsprg_state(f)); + epoch = FSPRG_GetEpoch(f->fsprg_state); if (epoch > goal) return -ESTALE; @@ -164,7 +148,7 @@ static int journal_file_evolve(JournalFile *f, uint64_t realtime) { if (r < 0) return r; - epoch = FSPRG_GetEpoch(fsprg_state(f)); + epoch = FSPRG_GetEpoch(f->fsprg_state); if (epoch < goal) log_debug("Evolving FSPRG key from epoch %llu to %llu.", (unsigned long long) epoch, (unsigned long long) goal); @@ -174,8 +158,8 @@ static int journal_file_evolve(JournalFile *f, uint64_t realtime) { if (epoch == goal) return 0; - FSPRG_Evolve(fsprg_state(f)); - epoch = FSPRG_GetEpoch(fsprg_state(f)); + FSPRG_Evolve(f->fsprg_state); + epoch = FSPRG_GetEpoch(f->fsprg_state); } } @@ -345,8 +329,8 @@ int journal_file_load_fsprg(JournalFile *f) { goto finish; } - f->fsprg_size = le64toh(m->header_size) + le64toh(m->state_size); - if ((uint64_t) st.st_size < f->fsprg_size) { + f->fsprg_file_size = le64toh(m->header_size) + le64toh(m->state_size); + if ((uint64_t) st.st_size < f->fsprg_file_size) { r = -ENODATA; goto finish; } @@ -362,13 +346,19 @@ int journal_file_load_fsprg(JournalFile *f) { goto finish; } - f->fsprg_header = mmap(NULL, PAGE_ALIGN(f->fsprg_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - if (f->fsprg_header == MAP_FAILED) { - f->fsprg_header = NULL; + f->fsprg_file = mmap(NULL, PAGE_ALIGN(f->fsprg_file_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (f->fsprg_file == MAP_FAILED) { + f->fsprg_file = NULL; r = -errno; goto finish; } + f->fsprg_start_usec = le64toh(f->fsprg_file->fsprg_start_usec); + f->fsprg_interval_usec = le64toh(f->fsprg_file->fsprg_interval_usec); + + f->fsprg_state = (uint8_t*) f->fsprg_file + le64toh(f->fsprg_file->header_size); + f->fsprg_state_size = le64toh(f->fsprg_file->state_size); + r = 0; finish: diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 81952a08ee..3bb1e90fb3 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -96,8 +96,12 @@ void journal_file_close(JournalFile *f) { #endif #ifdef HAVE_GCRYPT - if (f->fsprg_header) - munmap(f->fsprg_header, PAGE_ALIGN(f->fsprg_size)); + if (f->fsprg_file) + munmap(f->fsprg_file, PAGE_ALIGN(f->fsprg_file_size)); + else if (f->fsprg_state) + free(f->fsprg_state); + + free(f->fsprg_seed); if (f->hmac) gcry_md_close(f->hmac); diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index 1da1d3b834..9d437ae791 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -73,8 +73,17 @@ typedef struct JournalFile { gcry_md_hd_t hmac; bool hmac_running; - FSPRGHeader *fsprg_header; - size_t fsprg_size; + FSPRGHeader *fsprg_file; + size_t fsprg_file_size; + + void *fsprg_state; + size_t fsprg_state_size; + + void *fsprg_seed; + size_t fsprg_seed_size; + + uint64_t fsprg_start_usec; + uint64_t fsprg_interval_usec; #endif } JournalFile; diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index b4e34d18a8..f6cec1eb52 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -31,6 +31,7 @@ #include "journal-verify.h" #include "lookup3.h" #include "compress.h" +#include "fsprg.h" /* FIXME: * @@ -591,7 +592,62 @@ static int verify_entry_array( return 0; } -int journal_file_verify(JournalFile *f, const char *key) { +static int journal_file_parse_seed(JournalFile *f, const char *s) { + uint8_t *seed; + size_t seed_size, c; + const char *k; + int r; + unsigned long long start, interval; + + seed_size = FSPRG_RECOMMENDED_SEEDLEN; + seed = malloc(seed_size); + if (!seed) + return -ENOMEM; + + k = s; + for (c = 0; c < seed_size; c++) { + int x, y; + + while (*k == '-') + k++; + + x = unhexchar(*k); + if (x < 0) { + free(seed); + return -EINVAL; + } + k++; + y = unhexchar(*k); + if (y < 0) { + free(seed); + return -EINVAL; + } + k++; + + seed[c] = (uint8_t) (x * 16 + y); + } + + if (*k != '/') { + free(seed); + return -EINVAL; + } + k++; + + r = sscanf(k, "%llx-%llx", &start, &interval); + if (r != 2) { + free(seed); + return -EINVAL; + } + + f->fsprg_seed = seed; + f->fsprg_seed_size = seed_size; + f->fsprg_start_usec = start; + f->fsprg_interval_usec = interval; + + return 0; +} + +int journal_file_verify(JournalFile *f, const char *seed) { int r; Object *o; uint64_t p = 0; @@ -607,6 +663,14 @@ int journal_file_verify(JournalFile *f, const char *key) { assert(f); + if (seed) { + r = journal_file_parse_seed(f, seed); + if (r < 0) { + log_error("Failed to parse seed."); + goto fail; + } + } + data_fd = mkostemp(data_path, O_CLOEXEC); if (data_fd < 0) { log_error("Failed to create data file: %m"); -- cgit v1.2.3-54-g00ecf From 56e81f7ca8276e40f8c88c4c30713a5b54009613 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Aug 2012 21:00:34 +0200 Subject: journalctl: immeidately terminate on invalid seed --- src/journal/journal-verify.c | 2 +- src/journal/journalctl.c | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index f6cec1eb52..6871433402 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -667,7 +667,7 @@ int journal_file_verify(JournalFile *f, const char *seed) { r = journal_file_parse_seed(f, seed); if (r < 0) { log_error("Failed to parse seed."); - goto fail; + return r; } } diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 3d274c8eb5..25e441b022 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -601,13 +601,18 @@ static int verify(sd_journal *j) { HASHMAP_FOREACH(f, j->files, i) { int k; +#ifdef HAVE_GCRYPT if (!arg_verify_seed && journal_file_fsprg_enabled(f)) log_warning("Journal file %s has authentication enabled but verification seed has not been passed using --verify-seed=.", f->path); +#endif k = journal_file_verify(f, arg_verify_seed); - if (k < 0) { + if (k == -EINVAL) { + /* If the seed was invalid give up right-away. */ + return k; + } else if (k < 0) { log_warning("FAIL: %s (%s)", f->path, strerror(-k)); - r = -r; + r = k; } else log_info("PASS: %s", f->path); } -- cgit v1.2.3-54-g00ecf From a8e5f51484ba832e299a38f2a54e455e445d2896 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Aug 2012 21:22:11 +0200 Subject: journal: fix tag sequence number verification --- src/journal/journal-verify.c | 118 +++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 59 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 6871433402..94f90b670d 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -650,8 +650,8 @@ static int journal_file_parse_seed(JournalFile *f, const char *s) { int journal_file_verify(JournalFile *f, const char *seed) { int r; Object *o; - uint64_t p = 0; - uint64_t tag_seqnum = 0, entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0; + uint64_t p = 0, last_tag = 0; + uint64_t n_tags = 0, entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0; sd_id128_t entry_boot_id; bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false; uint64_t n_weird = 0, n_objects = 0, n_entries = 0, n_data = 0, n_fields = 0, n_data_hash_tables = 0, n_field_hash_tables = 0, n_entry_arrays = 0; @@ -698,12 +698,6 @@ int journal_file_verify(JournalFile *f, const char *seed) { /* First iteration: we go through all objects, verify the * superficial structure, headers, hashes. */ - r = journal_file_hmac_put_header(f); - if (r < 0) { - log_error("Failed to calculate HMAC of header."); - goto fail; - } - p = le64toh(f->header->header_size); while (p != 0) { draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec); @@ -735,28 +729,21 @@ int journal_file_verify(JournalFile *f, const char *seed) { goto fail; } - r = journal_file_hmac_put_object(f, -1, p); - if (r < 0) { - log_error("Failed to calculate HMAC at %llu", (unsigned long long) p); - goto fail; - } + switch (o->object.type) { - if (o->object.type == OBJECT_TAG) { - - if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) { - log_error("Tag object without authentication at %llu", (unsigned long long) p); - r = -EBADMSG; + case OBJECT_DATA: + r = write_uint64(data_fd, p); + if (r < 0) goto fail; - } - if (le64toh(o->tag.seqnum) != tag_seqnum) { - log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p); - r = -EBADMSG; - goto fail; - } + n_data++; + break; - } else if (o->object.type == OBJECT_ENTRY) { + case OBJECT_FIELD: + n_fields++; + break; + case OBJECT_ENTRY: r = write_uint64(entry_fd, p); if (r < 0) goto fail; @@ -801,37 +788,9 @@ int journal_file_verify(JournalFile *f, const char *seed) { entry_realtime_set = true; n_entries ++; - } else if (o->object.type == OBJECT_ENTRY_ARRAY) { - - r = write_uint64(entry_array_fd, p); - if (r < 0) - goto fail; - - if (p == le64toh(f->header->entry_array_offset)) { - if (found_main_entry_array) { - log_error("More than one main entry array at %llu", (unsigned long long) p); - r = -EBADMSG; - goto fail; - } - - found_main_entry_array = true; - } - - n_entry_arrays++; - - } else if (o->object.type == OBJECT_DATA) { - - r = write_uint64(data_fd, p); - if (r < 0) - goto fail; - - n_data++; - - } else if (o->object.type == OBJECT_FIELD) - n_fields++; - else if (o->object.type == OBJECT_DATA_HASH_TABLE) { - n_data_hash_tables++; + break; + case OBJECT_DATA_HASH_TABLE: if (n_data_hash_tables > 1) { log_error("More than one data hash table at %llu", (unsigned long long) p); r = -EBADMSG; @@ -844,9 +803,11 @@ int journal_file_verify(JournalFile *f, const char *seed) { r = -EBADMSG; goto fail; } - } else if (o->object.type == OBJECT_FIELD_HASH_TABLE) { - n_field_hash_tables++; + n_data_hash_tables++; + break; + + case OBJECT_FIELD_HASH_TABLE: if (n_field_hash_tables > 1) { log_error("More than one field hash table at %llu", (unsigned long long) p); r = -EBADMSG; @@ -859,8 +820,47 @@ int journal_file_verify(JournalFile *f, const char *seed) { r = -EBADMSG; goto fail; } - } else if (o->object.type >= _OBJECT_TYPE_MAX) + + n_field_hash_tables++; + break; + + case OBJECT_ENTRY_ARRAY: + r = write_uint64(entry_array_fd, p); + if (r < 0) + goto fail; + + if (p == le64toh(f->header->entry_array_offset)) { + if (found_main_entry_array) { + log_error("More than one main entry array at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + found_main_entry_array = true; + } + + n_entry_arrays++; + break; + + case OBJECT_TAG: + if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) { + log_error("Tag object without authentication at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + if (le64toh(o->tag.seqnum) != n_tags + 1) { + log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + n_tags ++; + break; + + default: n_weird ++; + } if (p == le64toh(f->header->tail_object_offset)) p = 0; @@ -895,7 +895,7 @@ int journal_file_verify(JournalFile *f, const char *seed) { } if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) && - tag_seqnum != le64toh(f->header->n_tags)) { + n_tags != le64toh(f->header->n_tags)) { log_error("Tag number mismatch"); r = -EBADMSG; goto fail; -- cgit v1.2.3-54-g00ecf From 14d10188de1fd58e663d73683a400d8d7dc67dba Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Aug 2012 23:58:14 +0200 Subject: journal: add FSPRG journal authentication --- src/journal/journal-authenticate.c | 52 +++++++++++++++++++++++++---- src/journal/journal-authenticate.h | 3 ++ src/journal/journal-def.h | 1 + src/journal/journal-file.c | 8 ++--- src/journal/journal-verify.c | 68 +++++++++++++++++++++++++++++++++++--- src/journal/journalctl.c | 63 ++++++++++++++++++++--------------- src/journal/test-journal-verify.c | 5 +-- 7 files changed, 157 insertions(+), 43 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c index 809655e1ac..c087ad4c8b 100644 --- a/src/journal/journal-authenticate.c +++ b/src/journal/journal-authenticate.c @@ -60,6 +60,7 @@ int journal_file_append_tag(JournalFile *f) { return r; o->tag.seqnum = htole64(journal_file_tag_seqnum(f)); + o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state)); /* Add the tag object itself, so that we can protect its * header. This will exclude the actual hash value in it */ @@ -74,9 +75,8 @@ int journal_file_append_tag(JournalFile *f) { return 0; } -static int journal_file_hmac_start(JournalFile *f) { +int journal_file_hmac_start(JournalFile *f) { uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */ - assert(f); if (!f->authenticate) @@ -163,6 +163,44 @@ static int journal_file_evolve(JournalFile *f, uint64_t realtime) { } } +int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) { + void *msk; + uint64_t epoch; + + assert(f); + + if (!f->authenticate) + return 0; + + assert(f->fsprg_seed); + + if (f->fsprg_state) { + /* Cheaper... */ + + epoch = FSPRG_GetEpoch(f->fsprg_state); + if (goal == epoch) + return 0; + + if (goal == epoch+1) { + FSPRG_Evolve(f->fsprg_state); + return 0; + } + } else { + f->fsprg_state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR); + f->fsprg_state = malloc(f->fsprg_state_size); + + if (!f->fsprg_state) + return -ENOMEM; + } + + log_debug("Seeking FSPRG key to %llu.", (unsigned long long) goal); + + msk = alloca(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR)); + FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR); + FSPRG_Seek(f->fsprg_state, goal, msk, f->fsprg_seed, f->fsprg_seed_size); + return 0; +} + int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) { int r; @@ -212,7 +250,7 @@ int journal_file_hmac_put_object(JournalFile *f, int type, uint64_t p) { switch (o->object.type) { case OBJECT_DATA: - /* All but: hash and payload are mutable */ + /* All but hash and payload are mutable */ gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash)); gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload)); break; @@ -231,6 +269,7 @@ int journal_file_hmac_put_object(JournalFile *f, int type, uint64_t p) { case OBJECT_TAG: /* All but the tag itself */ gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum)); + gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch)); break; default: return -EINVAL; @@ -252,15 +291,16 @@ int journal_file_hmac_put_header(JournalFile *f) { return r; /* All but state+reserved, boot_id, arena_size, - * tail_object_offset, n_objects, n_entries, tail_seqnum, + * 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, header_tag */ + * tail_entry_monotonic, n_data, n_fields, n_tags, + * n_entry_arrays. */ gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature)); gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, boot_id) - offsetof(Header, file_id)); gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id)); gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset)); - gcry_md_write(f->hmac, &f->header->head_entry_seqnum, offsetof(Header, head_entry_realtime) - offsetof(Header, head_entry_seqnum)); return 0; } diff --git a/src/journal/journal-authenticate.h b/src/journal/journal-authenticate.h index 566d7a81a9..282c73f68c 100644 --- a/src/journal/journal-authenticate.h +++ b/src/journal/journal-authenticate.h @@ -30,6 +30,7 @@ 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_start(JournalFile *f); int journal_file_hmac_put_header(JournalFile *f); int journal_file_hmac_put_object(JournalFile *f, int type, uint64_t p); @@ -38,3 +39,5 @@ int journal_file_load_fsprg(JournalFile *f); int journal_file_setup_hmac(JournalFile *f); bool journal_file_fsprg_enabled(JournalFile *f); + +int journal_file_fsprg_seek(JournalFile *f, uint64_t epoch); diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h index 660a92c147..ab4988037c 100644 --- a/src/journal/journal-def.h +++ b/src/journal/journal-def.h @@ -125,6 +125,7 @@ _packed_ struct EntryArrayObject { _packed_ struct TagObject { ObjectHeader object; uint64_t seqnum; + uint64_t epoch; uint8_t tag[TAG_LENGTH]; /* SHA-256 HMAC */ }; diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 3bb1e90fb3..274f22db17 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -2049,12 +2049,12 @@ int journal_file_open( r = journal_file_refresh_header(f); if (r < 0) goto fail; - - r = journal_file_setup_hmac(f); - if (r < 0) - goto fail; } + r = journal_file_setup_hmac(f); + if (r < 0) + goto fail; + if (newly_created) { r = journal_file_setup_field_hash_table(f); if (r < 0) diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 94f90b670d..e646e38ddc 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -35,7 +35,10 @@ /* FIXME: * - * - verify FSPRG + * - write tag only if non-tag objects have been written + * - change terms + * - write bit mucking test + * * - Allow building without libgcrypt * - check with sparse * - 64bit conversions @@ -650,11 +653,11 @@ static int journal_file_parse_seed(JournalFile *f, const char *s) { int journal_file_verify(JournalFile *f, const char *seed) { int r; Object *o; - uint64_t p = 0, last_tag = 0; - uint64_t n_tags = 0, entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0; + uint64_t p = 0, last_tag = 0, last_epoch = 0; + uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0; sd_id128_t entry_boot_id; bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false; - uint64_t n_weird = 0, n_objects = 0, n_entries = 0, n_data = 0, n_fields = 0, n_data_hash_tables = 0, n_field_hash_tables = 0, n_entry_arrays = 0; + uint64_t n_weird = 0, n_objects = 0, n_entries = 0, n_data = 0, n_fields = 0, n_data_hash_tables = 0, n_field_hash_tables = 0, n_entry_arrays = 0, n_tags = 0; usec_t last_usec = 0; int data_fd = -1, entry_fd = -1, entry_array_fd = -1; char data_path[] = "/var/tmp/journal-data-XXXXXX", @@ -842,7 +845,9 @@ int journal_file_verify(JournalFile *f, const char *seed) { n_entry_arrays++; break; - case OBJECT_TAG: + case OBJECT_TAG: { + uint64_t q; + if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) { log_error("Tag object without authentication at %llu", (unsigned long long) p); r = -EBADMSG; @@ -855,8 +860,61 @@ int journal_file_verify(JournalFile *f, const char *seed) { goto fail; } + if (le64toh(o->tag.epoch) < last_epoch) { + log_error("Epoch sequence out of synchronization at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + /* 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)); + if (r < 0) + goto fail; + + r = journal_file_hmac_start(f); + if (r < 0) + goto fail; + + if (last_tag == 0) { + r = journal_file_hmac_put_header(f); + if (r < 0) + goto fail; + + q = le64toh(f->header->header_size); + } else + q = last_tag; + + while (q <= p) { + r = journal_file_move_to_object(f, -1, q, &o); + if (r < 0) + goto fail; + + r = journal_file_hmac_put_object(f, -1, q); + if (r < 0) + goto fail; + + q = q + ALIGN64(le64toh(o->object.size)); + } + + /* Position might have changed, let's reposition things */ + r = journal_file_move_to_object(f, -1, p, &o); + if (r < 0) + goto fail; + + if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) { + log_error("Tag did not authenticate at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + f->hmac_running = false; + + last_tag = p + ALIGN64(le64toh(o->object.size)); n_tags ++; break; + } default: n_weird ++; diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 25e441b022..f0654fe4e8 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -60,6 +60,7 @@ static bool arg_this_boot = false; static const char *arg_directory = NULL; static int arg_priorities = 0xFF; static const char *arg_verify_seed = NULL; +static usec_t arg_evolve = DEFAULT_FSPRG_INTERVAL_USEC; static enum { ACTION_SHOW, @@ -73,26 +74,27 @@ static int help(void) { printf("%s [OPTIONS...] [MATCH]\n\n" "Send control commands to or query the journal.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --no-pager Do not pipe output into a pager\n" - " -a --all Show all fields, including long and unprintable\n" - " -f --follow Follow journal\n" - " -n --lines=INTEGER Journal entries to show\n" - " --no-tail Show all lines, even in follow mode\n" - " -o --output=STRING Change journal output mode (short, short-monotonic,\n" - " verbose, export, json, cat)\n" - " -q --quiet Don't show privilege warning\n" - " -l --local Only local entries\n" - " -b --this-boot Show data only from current boot\n" - " -D --directory=PATH Show journal files from directory\n" - " -p --priority=RANGE Show only messages within the specified priority range\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " -a --all Show all fields, including long and unprintable\n" + " -f --follow Follow journal\n" + " -n --lines=INTEGER Journal entries to show\n" + " --no-tail Show all lines, even in follow mode\n" + " -o --output=STRING Change journal output mode (short, short-monotonic,\n" + " verbose, export, json, cat)\n" + " -q --quiet Don't show privilege warning\n" + " -l --local Only local entries\n" + " -b --this-boot Show data only from current boot\n" + " -D --directory=PATH Show journal files from directory\n" + " -p --priority=RANGE Show only messages within the specified priority range\n\n" "Commands:\n" - " --new-id128 Generate a new 128 Bit ID\n" - " --header Show journal header information\n" - " --verify Verify journal file consistency\n" - " --verify-seed=SEED Specify FSPRG seed for verification\n" - " --setup-keys Generate new FSPRG key and seed\n", + " --new-id128 Generate a new 128 Bit ID\n" + " --header Show journal header information\n" + " --verify Verify journal file consistency\n" + " --verify-seed=SEED Specify FSPRG seed for verification\n" + " --setup-keys Generate new FSPRG key and seed\n" + " --evolve=TIME How of to evolve FSPRG keys\n", program_invocation_short_name); return 0; @@ -108,7 +110,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_HEADER, ARG_SETUP_KEYS, ARG_VERIFY, - ARG_VERIFY_SEED + ARG_VERIFY_SEED, + ARG_EVOLVE }; static const struct option options[] = { @@ -130,6 +133,7 @@ static int parse_argv(int argc, char *argv[]) { { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS }, { "verify", no_argument, NULL, ARG_VERIFY }, { "verify-seed", required_argument, NULL, ARG_VERIFY_SEED }, + { "evolve", required_argument, NULL, ARG_EVOLVE }, { NULL, 0, NULL, 0 } }; @@ -222,6 +226,14 @@ static int parse_argv(int argc, char *argv[]) { arg_verify_seed = optarg; break; + case ARG_EVOLVE: + r = parse_usec(optarg, &arg_evolve); + if (r < 0 || arg_evolve <= 0) { + log_error("Failed to parse evolve interval: %s", optarg); + return -EINVAL; + } + break; + case 'p': { const char *dots; @@ -445,7 +457,7 @@ static int setup_keys(void) { sd_id128_t machine, boot; char *p = NULL, *k = NULL; struct FSPRGHeader h; - uint64_t n, interval; + uint64_t n; r = sd_id128_get_machine(&machine); if (r < 0) { @@ -505,9 +517,8 @@ static int setup_keys(void) { log_info("Generating evolving key..."); FSPRG_GenState0(state, mpk, seed, seed_size); - interval = DEFAULT_FSPRG_INTERVAL_USEC; n = now(CLOCK_REALTIME); - n /= interval; + n /= arg_evolve; close_nointr_nofail(fd); fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY); @@ -522,8 +533,8 @@ static int setup_keys(void) { h.machine_id = machine; h.boot_id = boot; h.header_size = htole64(sizeof(h)); - h.fsprg_start_usec = htole64(n * interval); - h.fsprg_interval_usec = htole64(interval); + h.fsprg_start_usec = htole64(n * arg_evolve); + h.fsprg_interval_usec = htole64(arg_evolve); h.secpar = htole16(FSPRG_RECOMMENDED_SECPAR); h.state_size = htole64(state_size); @@ -567,7 +578,7 @@ static int setup_keys(void) { printf("%02x", ((uint8_t*) seed)[i]); } - printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) interval); + printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_evolve); if (isatty(STDOUT_FILENO)) fputs(ANSI_HIGHLIGHT_OFF "\n", stderr); diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c index bada498fab..8a7d998ff9 100644 --- a/src/journal/test-journal-verify.c +++ b/src/journal/test-journal-verify.c @@ -35,6 +35,7 @@ int main(int argc, char *argv[]) { char t[] = "/tmp/journal-XXXXXX"; unsigned n; JournalFile *f; + const char *verification_key = argv[1]; log_set_max_level(LOG_DEBUG); @@ -43,7 +44,7 @@ int main(int argc, char *argv[]) { log_info("Generating..."); - assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, &f) == 0); + assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, !!verification_key, NULL, NULL, NULL, &f) == 0); for (n = 0; n < N_ENTRIES; n++) { struct iovec iovec; @@ -67,7 +68,7 @@ int main(int argc, char *argv[]) { log_info("Verifying..."); assert_se(journal_file_open("test.journal", O_RDONLY, 0666, false, false, NULL, NULL, NULL, &f) == 0); - assert_se(journal_file_verify(f, NULL) >= 0); + assert_se(journal_file_verify(f, verification_key) >= 0); journal_file_close(f); log_info("Exiting..."); -- cgit v1.2.3-54-g00ecf From baed47c3c20512507e497058d388782400a072f6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 17 Aug 2012 00:45:18 +0200 Subject: journal: rework terminology Let's clean up our terminology a bit. New terminology: FSS = Forward Secure Sealing FSPRG = Forward Secure Pseudo-Random Generator FSS is the combination of FSPRG and a HMAC. Sealing = process of adding authentication tags to the journal. Verification = process of checking authentication tags to the journal. Sealing Key = The key used for adding authentication tags to the journal. Verification Key = The key used for checking authentication tags of the journal. Key pair = The pair of Sealing Key and Verification Key Internally, the Sealing Key is the combination of the FSPRG State plus change interval/start time. Internally, the Verification Key is the combination of the FSPRG Seed plus change interval/start time. --- src/journal/journal-authenticate.c | 88 ++++++++++----------- src/journal/journal-authenticate.h | 9 +-- src/journal/journal-def.h | 16 ++-- src/journal/journal-file.c | 44 +++++------ src/journal/journal-file.h | 18 ++--- src/journal/journal-verify.c | 22 +++--- src/journal/journalctl.c | 151 +++++++++++++++++++------------------ 7 files changed, 178 insertions(+), 170 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c index c087ad4c8b..ae64d3c8e0 100644 --- a/src/journal/journal-authenticate.c +++ b/src/journal/journal-authenticate.c @@ -45,7 +45,7 @@ int journal_file_append_tag(JournalFile *f) { assert(f); - if (!f->authenticate) + if (!f->seal) return 0; if (!f->hmac_running) @@ -79,7 +79,7 @@ int journal_file_hmac_start(JournalFile *f) { uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */ assert(f); - if (!f->authenticate) + if (!f->seal) return 0; if (f->hmac_running) @@ -100,28 +100,28 @@ static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *e assert(f); assert(epoch); - assert(f->authenticate); + assert(f->seal); - if (f->fsprg_start_usec == 0 || - f->fsprg_interval_usec == 0) + if (f->fss_start_usec == 0 || + f->fss_interval_usec == 0) return -ENOTSUP; - if (realtime < f->fsprg_start_usec) + if (realtime < f->fss_start_usec) return -ESTALE; - t = realtime - f->fsprg_start_usec; - t = t / f->fsprg_interval_usec; + t = realtime - f->fss_start_usec; + t = t / f->fss_interval_usec; *epoch = t; return 0; } -static int journal_file_need_evolve(JournalFile *f, uint64_t realtime) { +static int journal_file_fsprg_need_evolve(JournalFile *f, uint64_t realtime) { uint64_t goal, epoch; int r; assert(f); - if (!f->authenticate) + if (!f->seal) return 0; r = journal_file_get_epoch(f, realtime, &goal); @@ -135,13 +135,13 @@ static int journal_file_need_evolve(JournalFile *f, uint64_t realtime) { return epoch != goal; } -static int journal_file_evolve(JournalFile *f, uint64_t realtime) { +int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) { uint64_t goal, epoch; int r; assert(f); - if (!f->authenticate) + if (!f->seal) return 0; r = journal_file_get_epoch(f, realtime, &goal); @@ -169,7 +169,7 @@ int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) { assert(f); - if (!f->authenticate) + if (!f->seal) return 0; assert(f->fsprg_seed); @@ -206,10 +206,10 @@ int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) { assert(f); - if (!f->authenticate) + if (!f->seal) return 0; - r = journal_file_need_evolve(f, realtime); + r = journal_file_fsprg_need_evolve(f, realtime); if (r <= 0) return 0; @@ -217,7 +217,7 @@ int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) { if (r < 0) return r; - r = journal_file_evolve(f, realtime); + r = journal_file_fsprg_evolve(f, realtime); if (r < 0) return r; @@ -234,7 +234,7 @@ int journal_file_hmac_put_object(JournalFile *f, int type, uint64_t p) { assert(f); - if (!f->authenticate) + if (!f->seal) return 0; r = journal_file_hmac_start(f); @@ -283,7 +283,7 @@ int journal_file_hmac_put_header(JournalFile *f) { assert(f); - if (!f->authenticate) + if (!f->seal) return 0; r = journal_file_hmac_start(f); @@ -305,23 +305,23 @@ int journal_file_hmac_put_header(JournalFile *f) { return 0; } -int journal_file_load_fsprg(JournalFile *f) { +int journal_file_fss_load(JournalFile *f) { int r, fd = -1; char *p = NULL; struct stat st; - FSPRGHeader *m = NULL; + FSSHeader *m = NULL; sd_id128_t machine; assert(f); - if (!f->authenticate) + if (!f->seal) return 0; r = sd_id128_get_machine(&machine); if (r < 0) return r; - if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg", + if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss", SD_ID128_FORMAT_VAL(machine)) < 0) return -ENOMEM; @@ -337,19 +337,19 @@ int journal_file_load_fsprg(JournalFile *f) { goto finish; } - if (st.st_size < (off_t) sizeof(FSPRGHeader)) { + if (st.st_size < (off_t) sizeof(FSSHeader)) { r = -ENODATA; goto finish; } - m = mmap(NULL, PAGE_ALIGN(sizeof(FSPRGHeader)), PROT_READ, MAP_SHARED, fd, 0); + m = mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0); if (m == MAP_FAILED) { m = NULL; r = -errno; goto finish; } - if (memcmp(m->signature, FSPRG_HEADER_SIGNATURE, 8) != 0) { + if (memcmp(m->signature, FSS_HEADER_SIGNATURE, 8) != 0) { r = -EBADMSG; goto finish; } @@ -359,18 +359,18 @@ int journal_file_load_fsprg(JournalFile *f) { goto finish; } - if (le64toh(m->header_size) < sizeof(FSPRGHeader)) { + if (le64toh(m->header_size) < sizeof(FSSHeader)) { r = -EBADMSG; goto finish; } - if (le64toh(m->state_size) != FSPRG_stateinbytes(m->secpar)) { + if (le64toh(m->fsprg_state_size) != FSPRG_stateinbytes(m->fsprg_secpar)) { r = -EBADMSG; goto finish; } - f->fsprg_file_size = le64toh(m->header_size) + le64toh(m->state_size); - if ((uint64_t) st.st_size < f->fsprg_file_size) { + f->fss_file_size = le64toh(m->header_size) + le64toh(m->fsprg_state_size); + if ((uint64_t) st.st_size < f->fss_file_size) { r = -ENODATA; goto finish; } @@ -380,30 +380,30 @@ int journal_file_load_fsprg(JournalFile *f) { goto finish; } - if (le64toh(m->fsprg_start_usec) <= 0 || - le64toh(m->fsprg_interval_usec) <= 0) { + if (le64toh(m->start_usec) <= 0 || + le64toh(m->interval_usec) <= 0) { r = -EBADMSG; goto finish; } - f->fsprg_file = mmap(NULL, PAGE_ALIGN(f->fsprg_file_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - if (f->fsprg_file == MAP_FAILED) { - f->fsprg_file = NULL; + f->fss_file = mmap(NULL, PAGE_ALIGN(f->fss_file_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (f->fss_file == MAP_FAILED) { + f->fss_file = NULL; r = -errno; goto finish; } - f->fsprg_start_usec = le64toh(f->fsprg_file->fsprg_start_usec); - f->fsprg_interval_usec = le64toh(f->fsprg_file->fsprg_interval_usec); + 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->fsprg_file + le64toh(f->fsprg_file->header_size); - f->fsprg_state_size = le64toh(f->fsprg_file->state_size); + 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); r = 0; finish: if (m) - munmap(m, PAGE_ALIGN(sizeof(FSPRGHeader))); + munmap(m, PAGE_ALIGN(sizeof(FSSHeader))); if (fd >= 0) close_nointr_nofail(fd); @@ -412,10 +412,10 @@ finish: return r; } -int journal_file_setup_hmac(JournalFile *f) { +int journal_file_hmac_setup(JournalFile *f) { gcry_error_t e; - if (!f->authenticate) + if (!f->seal) return 0; e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); @@ -429,7 +429,7 @@ int journal_file_append_first_tag(JournalFile *f) { int r; uint64_t p; - if (!f->authenticate) + if (!f->seal) return 0; log_debug("Calculating first tag..."); @@ -463,8 +463,8 @@ int journal_file_append_first_tag(JournalFile *f) { return 0; } -bool journal_file_fsprg_enabled(JournalFile *f) { +bool journal_file_fss_enabled(JournalFile *f) { assert(f); - return !!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED); + return !!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED); } diff --git a/src/journal/journal-authenticate.h b/src/journal/journal-authenticate.h index 282c73f68c..4f4f45b114 100644 --- a/src/journal/journal-authenticate.h +++ b/src/journal/journal-authenticate.h @@ -30,14 +30,13 @@ 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, int type, uint64_t p); -int journal_file_load_fsprg(JournalFile *f); - -int journal_file_setup_hmac(JournalFile *f); - -bool journal_file_fsprg_enabled(JournalFile *f); +int journal_file_fss_load(JournalFile *f); +bool journal_file_fss_enabled(JournalFile *f); +int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime); int journal_file_fsprg_seek(JournalFile *f, uint64_t epoch); diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h index ab4988037c..52c55ab4bb 100644 --- a/src/journal/journal-def.h +++ b/src/journal/journal-def.h @@ -42,7 +42,7 @@ typedef struct TagObject TagObject; typedef struct EntryItem EntryItem; typedef struct HashItem HashItem; -typedef struct FSPRGHeader FSPRGHeader; +typedef struct FSSHeader FSSHeader; /* Object types */ enum { @@ -151,7 +151,7 @@ enum { }; enum { - HEADER_COMPATIBLE_AUTHENTICATED = 1 + HEADER_COMPATIBLE_SEALED = 1 }; #define HEADER_SIGNATURE ((char[]) { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' }) @@ -189,18 +189,18 @@ _packed_ struct Header { le64_t n_entry_arrays; }; -#define FSPRG_HEADER_SIGNATURE ((char[]) { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' }) +#define FSS_HEADER_SIGNATURE ((char[]) { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' }) -_packed_ struct FSPRGHeader { +_packed_ struct FSSHeader { uint8_t signature[8]; /* "KSHHRHLP" */ le32_t compatible_flags; le32_t incompatible_flags; sd_id128_t machine_id; sd_id128_t boot_id; /* last writer */ le64_t header_size; - le64_t fsprg_start_usec; - le64_t fsprg_interval_usec; - le16_t secpar; + le64_t start_usec; + le64_t interval_usec; + le16_t fsprg_secpar; le16_t reserved[3]; - le64_t state_size; + le64_t fsprg_state_size; }; diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 274f22db17..76bf0e53ca 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -65,7 +65,7 @@ void journal_file_close(JournalFile *f) { assert(f); /* Write the final tag */ - if (f->authenticate) + if (f->seal) journal_file_append_tag(f); /* Sync everything to disk, before we mark the file offline */ @@ -96,8 +96,8 @@ void journal_file_close(JournalFile *f) { #endif #ifdef HAVE_GCRYPT - if (f->fsprg_file) - munmap(f->fsprg_file, PAGE_ALIGN(f->fsprg_file_size)); + if (f->fss_file) + munmap(f->fss_file, PAGE_ALIGN(f->fss_file_size)); else if (f->fsprg_state) free(f->fsprg_state); @@ -125,7 +125,7 @@ static int journal_file_init_header(JournalFile *f, JournalFile *template) { htole32(f->compress ? HEADER_INCOMPATIBLE_COMPRESSED : 0); h.compatible_flags = - htole32(f->authenticate ? HEADER_COMPATIBLE_AUTHENTICATED : 0); + htole32(f->seal ? HEADER_COMPATIBLE_SEALED : 0); r = sd_id128_randomize(&h.file_id); if (r < 0) @@ -195,7 +195,7 @@ static int journal_file_verify_header(JournalFile *f) { * compatible flags, too */ if (f->writable) { #ifdef HAVE_GCRYPT - if ((le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_AUTHENTICATED) != 0) + if ((le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) != 0) return -EPROTONOSUPPORT; #else if (f->header->compatible_flags != 0) @@ -207,8 +207,8 @@ static int journal_file_verify_header(JournalFile *f) { if (le64toh(f->header->header_size) < HEADER_SIZE_MIN) return -EBADMSG; - if ((le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED) && - !JOURNAL_HEADER_CONTAINS(f->header, n_tags)) + if ((le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED) && + !JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays)) return -EBADMSG; if ((uint64_t) f->last_stat.st_size < (le64toh(f->header->header_size) + le64toh(f->header->arena_size))) @@ -240,7 +240,7 @@ static int journal_file_verify_header(JournalFile *f) { } f->compress = !!(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED); - f->authenticate = !!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED); + f->seal = !!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED); return 0; } @@ -1900,8 +1900,8 @@ void journal_file_print_header(JournalFile *f) { f->header->state == STATE_OFFLINE ? "offline" : f->header->state == STATE_ONLINE ? "online" : f->header->state == STATE_ARCHIVED ? "archived" : "unknown", - (f->header->compatible_flags & HEADER_COMPATIBLE_AUTHENTICATED) ? " AUTHENTICATED" : "", - (f->header->compatible_flags & ~HEADER_COMPATIBLE_AUTHENTICATED) ? " ???" : "", + (f->header->compatible_flags & HEADER_COMPATIBLE_SEALED) ? " SEALED" : "", + (f->header->compatible_flags & ~HEADER_COMPATIBLE_SEALED) ? " ???" : "", (f->header->incompatible_flags & HEADER_INCOMPATIBLE_COMPRESSED) ? " COMPRESSED" : "", (f->header->incompatible_flags & ~HEADER_INCOMPATIBLE_COMPRESSED) ? " ???" : "", (unsigned long long) le64toh(f->header->header_size), @@ -1934,7 +1934,7 @@ int journal_file_open( int flags, mode_t mode, bool compress, - bool authenticate, + bool seal, JournalMetrics *metrics, MMapCache *mmap_cache, JournalFile *template, @@ -1964,7 +1964,7 @@ int journal_file_open( f->prot = prot_from_flags(flags); f->writable = (flags & O_ACCMODE) != O_RDONLY; f->compress = compress; - f->authenticate = authenticate; + f->seal = seal; if (mmap_cache) f->mmap = mmap_cache_ref(mmap_cache); @@ -2000,10 +2000,10 @@ int journal_file_open( newly_created = true; /* Try to load the FSPRG state, and if we can't, then - * just don't do authentication */ - r = journal_file_load_fsprg(f); + * just don't do sealing */ + r = journal_file_fss_load(f); if (r < 0) - f->authenticate = false; + f->seal = false; r = journal_file_init_header(f, template); if (r < 0) @@ -2034,7 +2034,7 @@ int journal_file_open( } if (!newly_created && f->writable) { - r = journal_file_load_fsprg(f); + r = journal_file_fss_load(f); if (r < 0) goto fail; } @@ -2051,7 +2051,7 @@ int journal_file_open( goto fail; } - r = journal_file_setup_hmac(f); + r = journal_file_hmac_setup(f); if (r < 0) goto fail; @@ -2088,7 +2088,7 @@ fail: return r; } -int journal_file_rotate(JournalFile **f, bool compress, bool authenticate) { +int journal_file_rotate(JournalFile **f, bool compress, bool seal) { char *p; size_t l; JournalFile *old_file, *new_file = NULL; @@ -2127,7 +2127,7 @@ int journal_file_rotate(JournalFile **f, bool compress, bool authenticate) { old_file->header->state = STATE_ARCHIVED; - r = journal_file_open(old_file->path, old_file->flags, old_file->mode, compress, authenticate, NULL, old_file->mmap, old_file, &new_file); + r = journal_file_open(old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, old_file, &new_file); journal_file_close(old_file); *f = new_file; @@ -2139,7 +2139,7 @@ int journal_file_open_reliably( int flags, mode_t mode, bool compress, - bool authenticate, + bool seal, JournalMetrics *metrics, MMapCache *mmap_cache, JournalFile *template, @@ -2149,7 +2149,7 @@ int journal_file_open_reliably( size_t l; char *p; - r = journal_file_open(fname, flags, mode, compress, authenticate, + r = journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, template, ret); if (r != -EBADMSG && /* corrupted */ r != -ENODATA && /* truncated */ @@ -2184,7 +2184,7 @@ int journal_file_open_reliably( log_warning("File %s corrupted or uncleanly shut down, renaming and replacing.", fname); - return journal_file_open(fname, flags, mode, compress, authenticate, + return journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, template, ret); } diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index 9d437ae791..58de214359 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -51,7 +51,7 @@ typedef struct JournalFile { int prot; bool writable; bool compress; - bool authenticate; + bool seal; bool tail_entry_monotonic_valid; @@ -73,17 +73,17 @@ typedef struct JournalFile { gcry_md_hd_t hmac; bool hmac_running; - FSPRGHeader *fsprg_file; - size_t fsprg_file_size; + FSSHeader *fss_file; + size_t fss_file_size; + + 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; - - uint64_t fsprg_start_usec; - uint64_t fsprg_interval_usec; #endif } JournalFile; @@ -97,7 +97,7 @@ int journal_file_open( int flags, mode_t mode, bool compress, - bool authenticate, + bool seal, JournalMetrics *metrics, MMapCache *mmap_cache, JournalFile *template, @@ -110,7 +110,7 @@ int journal_file_open_reliably( int flags, mode_t mode, bool compress, - bool authenticate, + bool seal, JournalMetrics *metrics, MMapCache *mmap_cache, JournalFile *template, @@ -152,7 +152,7 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6 void journal_file_dump(JournalFile *f); void journal_file_print_header(JournalFile *f); -int journal_file_rotate(JournalFile **f, bool compress, bool authenticate); +int journal_file_rotate(JournalFile **f, bool compress, bool seal); void journal_file_post_change(JournalFile *f); diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index e646e38ddc..b7097e7b01 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -38,6 +38,7 @@ * - write tag only if non-tag objects have been written * - change terms * - write bit mucking test + * - tag timestamps should be between entry timestamps * * - Allow building without libgcrypt * - check with sparse @@ -595,7 +596,7 @@ static int verify_entry_array( return 0; } -static int journal_file_parse_seed(JournalFile *f, const char *s) { +static int journal_file_parse_verification_key(JournalFile *f, const char *key) { uint8_t *seed; size_t seed_size, c; const char *k; @@ -607,7 +608,7 @@ static int journal_file_parse_seed(JournalFile *f, const char *s) { if (!seed) return -ENOMEM; - k = s; + k = key; for (c = 0; c < seed_size; c++) { int x, y; @@ -644,13 +645,14 @@ static int journal_file_parse_seed(JournalFile *f, const char *s) { f->fsprg_seed = seed; f->fsprg_seed_size = seed_size; - f->fsprg_start_usec = start; - f->fsprg_interval_usec = interval; + + f->fss_start_usec = start; + f->fss_interval_usec = interval; return 0; } -int journal_file_verify(JournalFile *f, const char *seed) { +int journal_file_verify(JournalFile *f, const char *key) { int r; Object *o; uint64_t p = 0, last_tag = 0, last_epoch = 0; @@ -666,8 +668,8 @@ int journal_file_verify(JournalFile *f, const char *seed) { assert(f); - if (seed) { - r = journal_file_parse_seed(f, seed); + if (key) { + r = journal_file_parse_verification_key(f, key); if (r < 0) { log_error("Failed to parse seed."); return r; @@ -848,8 +850,8 @@ int journal_file_verify(JournalFile *f, const char *seed) { case OBJECT_TAG: { uint64_t q; - if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) { - log_error("Tag object without authentication at %llu", (unsigned long long) p); + if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED)) { + log_error("Tag object without sealing at %llu", (unsigned long long) p); r = -EBADMSG; goto fail; } @@ -904,7 +906,7 @@ int journal_file_verify(JournalFile *f, const char *seed) { goto fail; if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) { - log_error("Tag did not authenticate at %llu", (unsigned long long) p); + log_error("Tag failed verification at %llu", (unsigned long long) p); r = -EBADMSG; goto fail; } diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index f0654fe4e8..0df8ce57fd 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -46,7 +46,7 @@ #include "journal-authenticate.h" #include "fsprg.h" -#define DEFAULT_FSPRG_INTERVAL_USEC (15*USEC_PER_MINUTE) +#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE) static OutputMode arg_output = OUTPUT_SHORT; static bool arg_follow = false; @@ -59,8 +59,8 @@ static bool arg_local = false; static bool arg_this_boot = false; static const char *arg_directory = NULL; static int arg_priorities = 0xFF; -static const char *arg_verify_seed = NULL; -static usec_t arg_evolve = DEFAULT_FSPRG_INTERVAL_USEC; +static const char *arg_verify_key = NULL; +static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC; static enum { ACTION_SHOW, @@ -74,27 +74,27 @@ static int help(void) { printf("%s [OPTIONS...] [MATCH]\n\n" "Send control commands to or query the journal.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --no-pager Do not pipe output into a pager\n" - " -a --all Show all fields, including long and unprintable\n" - " -f --follow Follow journal\n" - " -n --lines=INTEGER Journal entries to show\n" - " --no-tail Show all lines, even in follow mode\n" - " -o --output=STRING Change journal output mode (short, short-monotonic,\n" - " verbose, export, json, cat)\n" - " -q --quiet Don't show privilege warning\n" - " -l --local Only local entries\n" - " -b --this-boot Show data only from current boot\n" - " -D --directory=PATH Show journal files from directory\n" - " -p --priority=RANGE Show only messages within the specified priority range\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " -a --all Show all fields, including long and unprintable\n" + " -f --follow Follow journal\n" + " -n --lines=INTEGER Journal entries to show\n" + " --no-tail Show all lines, even in follow mode\n" + " -o --output=STRING Change journal output mode (short, short-monotonic,\n" + " verbose, export, json, cat)\n" + " -q --quiet Don't show privilege warning\n" + " -l --local Only local entries\n" + " -b --this-boot Show data only from current boot\n" + " -D --directory=PATH Show journal files from directory\n" + " -p --priority=RANGE Show only messages within the specified priority range\n\n" "Commands:\n" - " --new-id128 Generate a new 128 Bit ID\n" - " --header Show journal header information\n" - " --verify Verify journal file consistency\n" - " --verify-seed=SEED Specify FSPRG seed for verification\n" - " --setup-keys Generate new FSPRG key and seed\n" - " --evolve=TIME How of to evolve FSPRG keys\n", + " --new-id128 Generate a new 128 Bit ID\n" + " --header Show journal header information\n" + " --setup-keys Generate new FSS key pair\n" + " --interval=TIME Time interval for changing the FSS sealing key\n" + " --verify Verify journal file consistency\n" + " --verify-key=KEY Specify FSS verification key\n", program_invocation_short_name); return 0; @@ -109,32 +109,32 @@ static int parse_argv(int argc, char *argv[]) { ARG_NEW_ID128, ARG_HEADER, ARG_SETUP_KEYS, + ARG_INTERVAL, ARG_VERIFY, - ARG_VERIFY_SEED, - ARG_EVOLVE + ARG_VERIFY_KEY }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version" , no_argument, NULL, ARG_VERSION }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "follow", no_argument, NULL, 'f' }, - { "output", required_argument, NULL, 'o' }, - { "all", no_argument, NULL, 'a' }, - { "lines", required_argument, NULL, 'n' }, - { "no-tail", no_argument, NULL, ARG_NO_TAIL }, - { "new-id128", no_argument, NULL, ARG_NEW_ID128 }, - { "quiet", no_argument, NULL, 'q' }, - { "local", no_argument, NULL, 'l' }, - { "this-boot", no_argument, NULL, 'b' }, - { "directory", required_argument, NULL, 'D' }, - { "header", no_argument, NULL, ARG_HEADER }, - { "priority", no_argument, NULL, 'p' }, - { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS }, - { "verify", no_argument, NULL, ARG_VERIFY }, - { "verify-seed", required_argument, NULL, ARG_VERIFY_SEED }, - { "evolve", required_argument, NULL, ARG_EVOLVE }, - { NULL, 0, NULL, 0 } + { "help", no_argument, NULL, 'h' }, + { "version" , no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "follow", no_argument, NULL, 'f' }, + { "output", required_argument, NULL, 'o' }, + { "all", no_argument, NULL, 'a' }, + { "lines", required_argument, NULL, 'n' }, + { "no-tail", no_argument, NULL, ARG_NO_TAIL }, + { "new-id128", no_argument, NULL, ARG_NEW_ID128 }, + { "quiet", no_argument, NULL, 'q' }, + { "local", no_argument, NULL, 'l' }, + { "this-boot", no_argument, NULL, 'b' }, + { "directory", required_argument, NULL, 'D' }, + { "header", no_argument, NULL, ARG_HEADER }, + { "priority", no_argument, NULL, 'p' }, + { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS }, + { "interval", required_argument, NULL, ARG_INTERVAL }, + { "verify", no_argument, NULL, ARG_VERIFY }, + { "verify-key", required_argument, NULL, ARG_VERIFY_KEY }, + { NULL, 0, NULL, 0 } }; int c, r; @@ -221,15 +221,15 @@ static int parse_argv(int argc, char *argv[]) { arg_action = ACTION_VERIFY; break; - case ARG_VERIFY_SEED: + case ARG_VERIFY_KEY: arg_action = ACTION_VERIFY; - arg_verify_seed = optarg; + arg_verify_key = optarg; break; - case ARG_EVOLVE: - r = parse_usec(optarg, &arg_evolve); - if (r < 0 || arg_evolve <= 0) { - log_error("Failed to parse evolve interval: %s", optarg); + case ARG_INTERVAL: + r = parse_usec(optarg, &arg_interval); + if (r < 0 || arg_interval <= 0) { + log_error("Failed to parse sealing key change interval: %s", optarg); return -EINVAL; } break; @@ -456,7 +456,7 @@ static int setup_keys(void) { int fd = -1, r; sd_id128_t machine, boot; char *p = NULL, *k = NULL; - struct FSPRGHeader h; + struct FSSHeader h; uint64_t n; r = sd_id128_get_machine(&machine); @@ -471,7 +471,7 @@ static int setup_keys(void) { return r; } - if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg", + if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss", SD_ID128_FORMAT_VAL(machine)) < 0) return log_oom(); @@ -481,7 +481,7 @@ static int setup_keys(void) { goto finish; } - if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg.tmp.XXXXXX", + if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX", SD_ID128_FORMAT_VAL(machine)) < 0) { r = log_oom(); goto finish; @@ -514,11 +514,13 @@ static int setup_keys(void) { log_info("Generating key pair..."); FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR); - log_info("Generating evolving key..."); + log_info("Generating sealing key..."); FSPRG_GenState0(state, mpk, seed, seed_size); + assert(arg_interval > 0); + n = now(CLOCK_REALTIME); - n /= arg_evolve; + n /= arg_interval; close_nointr_nofail(fd); fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY); @@ -533,10 +535,10 @@ static int setup_keys(void) { h.machine_id = machine; h.boot_id = boot; h.header_size = htole64(sizeof(h)); - h.fsprg_start_usec = htole64(n * arg_evolve); - h.fsprg_interval_usec = htole64(arg_evolve); - h.secpar = htole16(FSPRG_RECOMMENDED_SECPAR); - h.state_size = htole64(state_size); + h.start_usec = htole64(n * arg_interval); + h.interval_usec = htole64(arg_interval); + h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR); + h.fsprg_state_size = htole64(state_size); l = loop_write(fd, &h, sizeof(h), false); if (l < 0 || (size_t) l != sizeof(h)) { @@ -561,14 +563,13 @@ static int setup_keys(void) { if (isatty(STDOUT_FILENO)) { fprintf(stderr, "\n" - "The new key pair has been generated. The evolving key has been written to the\n" - "following file. It will be used to protect local journal files. This file\n" - "should be kept secret. It should not be used on multiple hosts.\n" + "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n" + "the following local file. It should not be used on multiple hosts.\n" "\n" "\t%s\n" "\n" - "Please write down the following " ANSI_HIGHLIGHT_ON "secret" ANSI_HIGHLIGHT_OFF " seed value. It should not be stored\n" - "locally on disk, and may be used to verify journal files from this host.\n" + "Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n" + "at a safe location and should not be saved locally on disk.\n" "\n\t" ANSI_HIGHLIGHT_RED_ON, p); fflush(stderr); } @@ -578,10 +579,16 @@ static int setup_keys(void) { printf("%02x", ((uint8_t*) seed)[i]); } - printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_evolve); + printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval); + + if (isatty(STDOUT_FILENO)) { + char tsb[FORMAT_TIMESPAN_MAX]; - if (isatty(STDOUT_FILENO)) - fputs(ANSI_HIGHLIGHT_OFF "\n", stderr); + fprintf(stderr, + ANSI_HIGHLIGHT_OFF "\n" + "The sealing key is automatically changed every %s.\n", + format_timespan(tsb, sizeof(tsb), arg_interval)); + } r = 0; @@ -613,13 +620,13 @@ static int verify(sd_journal *j) { int k; #ifdef HAVE_GCRYPT - if (!arg_verify_seed && journal_file_fsprg_enabled(f)) - log_warning("Journal file %s has authentication enabled but verification seed has not been passed using --verify-seed=.", f->path); + if (!arg_verify_key && journal_file_fss_enabled(f)) + log_warning("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_seed); + k = journal_file_verify(f, arg_verify_key); if (k == -EINVAL) { - /* If the seed was invalid give up right-away. */ + /* If the key was invalid give up right-away. */ return k; } else if (k < 0) { log_warning("FAIL: %s (%s)", f->path, strerror(-k)); -- cgit v1.2.3-54-g00ecf From 31094aae09dd5a773e1634334bcd12fc8834a030 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 17 Aug 2012 01:09:43 +0200 Subject: man: add man pages for new FSS stuff --- man/journalctl.xml | 100 ++++++++++++++++++++++++++++++++----------- src/journal/journal-verify.c | 3 +- 2 files changed, 77 insertions(+), 26 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/man/journalctl.xml b/man/journalctl.xml index 3cfda5b84b..1ea004fc81 100644 --- a/man/journalctl.xml +++ b/man/journalctl.xml @@ -254,31 +254,6 @@ paths. - - - - Instead of showing - journal contents generate a new 128 - bit ID suitable for identifying - messages. This is intended for usage - by developers who need a new - identifier for a new message they - introduce and want to make - recognizable. Will print the new ID in - three different formats which can be - copied into source code or - similar. - - - - - - Instead of showing - journal contents show internal header - information of the journal fiels - accessed. - - @@ -311,6 +286,81 @@ value of the range. + + + + Instead of showing + journal contents generate a new 128 + bit ID suitable for identifying + messages. This is intended for usage + by developers who need a new + identifier for a new message they + introduce and want to make + recognizable. Will print the new ID in + three different formats which can be + copied into source code or + similar. + + + + + + Instead of showing + journal contents show internal header + information of the journal fiels + accessed. + + + + + + Instead of showing + journal contents generate a new key + pair for Forward Secure Sealing + (FSS). This will generate a sealing + key and a verification key. The + sealing key is stored in the journal + data directory and shall remain on the + host. The verification key should be + stored externally. + + + + + + Specifies the change + interval for the sealing key, when + generating an FSS key pair with + . Shorter + intervals increase CPU consumption but + shorten the time range of + undetectable journal + alterations. Defaults to + 15min. + + + + + + Check the journal file + for internal consistency. If the + file has been generated with FSS + enabled, and the FSS verification key + has been specified with + + authenticity of the journal file is + verified. + + + + + + Specifies the FSS + verification key to use for the + + operation. + + diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index b7097e7b01..8eefb841b2 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -36,9 +36,10 @@ /* FIXME: * * - write tag only if non-tag objects have been written - * - change terms * - write bit mucking test * - tag timestamps should be between entry timestamps + * - output validated time ranges + * - add missing fields to journal header dump * * - Allow building without libgcrypt * - check with sparse -- cgit v1.2.3-54-g00ecf From e627440b41bb0284e4892f7aa9d84c77972487e2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 17 Aug 2012 01:19:32 +0200 Subject: journal: don't write tag objects if nothing has been written since the last time --- src/journal/journal-authenticate.c | 10 ++++------ src/journal/journal-verify.c | 3 ++- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c index ae64d3c8e0..9853edb58c 100644 --- a/src/journal/journal-authenticate.c +++ b/src/journal/journal-authenticate.c @@ -51,8 +51,6 @@ int journal_file_append_tag(JournalFile *f) { if (!f->hmac_running) return 0; - log_debug("Writing tag for epoch %llu\n", (unsigned long long) FSPRG_GetEpoch(f->fsprg_state)); - assert(f->hmac); r = journal_file_append_object(f, OBJECT_TAG, sizeof(struct TagObject), &o, &p); @@ -62,6 +60,10 @@ int journal_file_append_tag(JournalFile *f) { o->tag.seqnum = htole64(journal_file_tag_seqnum(f)); o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state)); + log_debug("Writing tag %llu for epoch %llu\n", + (unsigned long long) le64toh(o->tag.seqnum), + (unsigned long long) FSPRG_GetEpoch(f->fsprg_state)); + /* 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, p); @@ -221,10 +223,6 @@ int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) { if (r < 0) return r; - r = journal_file_hmac_start(f); - if (r < 0) - return r; - return 0; } diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 8eefb841b2..9907c5fe26 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -35,7 +35,6 @@ /* FIXME: * - * - write tag only if non-tag objects have been written * - write bit mucking test * - tag timestamps should be between entry timestamps * - output validated time ranges @@ -857,6 +856,8 @@ int journal_file_verify(JournalFile *f, const char *key) { goto fail; } + log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum)); + if (le64toh(o->tag.seqnum) != n_tags + 1) { log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p); r = -EBADMSG; -- cgit v1.2.3-54-g00ecf From 3223f44f2312d01113a9c51f898528041cc7bd8d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 17 Aug 2012 02:29:20 +0200 Subject: journal: show new header fields in header dump --- src/journal/journal-file.c | 25 ++++++++++++++++--------- src/journal/journal-verify.c | 2 +- src/journal/test-journal-verify.c | 3 +++ 3 files changed, 20 insertions(+), 10 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 76bf0e53ca..4d7a6ff17d 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -1885,21 +1885,21 @@ void journal_file_print_header(JournalFile *f) { "Arena size: %llu\n" "Data Hash Table Size: %llu\n" "Field Hash Table Size: %llu\n" - "Objects: %llu\n" - "Entry Objects: %llu\n" "Rotate Suggested: %s\n" "Head Sequential Number: %llu\n" "Tail Sequential Number: %llu\n" "Head Realtime Timestamp: %s\n" - "Tail Realtime Timestamp: %s\n", + "Tail Realtime Timestamp: %s\n" + "Objects: %llu\n" + "Entry Objects: %llu\n", f->path, sd_id128_to_string(f->header->file_id, a), sd_id128_to_string(f->header->machine_id, b), sd_id128_to_string(f->header->boot_id, c), sd_id128_to_string(f->header->seqnum_id, c), - f->header->state == STATE_OFFLINE ? "offline" : - f->header->state == STATE_ONLINE ? "online" : - f->header->state == STATE_ARCHIVED ? "archived" : "unknown", + f->header->state == STATE_OFFLINE ? "OFFLINE" : + f->header->state == STATE_ONLINE ? "ONLINE" : + f->header->state == STATE_ARCHIVED ? "ARCHIVED" : "UNKNOWN", (f->header->compatible_flags & HEADER_COMPATIBLE_SEALED) ? " SEALED" : "", (f->header->compatible_flags & ~HEADER_COMPATIBLE_SEALED) ? " ???" : "", (f->header->incompatible_flags & HEADER_INCOMPATIBLE_COMPRESSED) ? " COMPRESSED" : "", @@ -1908,13 +1908,13 @@ void journal_file_print_header(JournalFile *f) { (unsigned long long) le64toh(f->header->arena_size), (unsigned long long) le64toh(f->header->data_hash_table_size) / sizeof(HashItem), (unsigned long long) le64toh(f->header->field_hash_table_size) / sizeof(HashItem), - (unsigned long long) le64toh(f->header->n_objects), - (unsigned long long) le64toh(f->header->n_entries), yes_no(journal_file_rotate_suggested(f)), (unsigned long long) le64toh(f->header->head_entry_seqnum), (unsigned long long) le64toh(f->header->tail_entry_seqnum), format_timestamp(x, sizeof(x), le64toh(f->header->head_entry_realtime)), - format_timestamp(y, sizeof(y), le64toh(f->header->tail_entry_realtime))); + format_timestamp(y, sizeof(y), le64toh(f->header->tail_entry_realtime)), + (unsigned long long) le64toh(f->header->n_objects), + (unsigned long long) le64toh(f->header->n_entries)); if (JOURNAL_HEADER_CONTAINS(f->header, n_data)) printf("Data Objects: %llu\n" @@ -1927,6 +1927,13 @@ void journal_file_print_header(JournalFile *f) { "Field Hash Table Fill: %.1f%%\n", (unsigned long long) le64toh(f->header->n_fields), 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)))); + + if (JOURNAL_HEADER_CONTAINS(f->header, n_tags)) + printf("Tag Objects: %llu\n", + (unsigned long long) le64toh(f->header->n_tags)); + if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays)) + printf("Entry Array Objects: %llu\n", + (unsigned long long) le64toh(f->header->n_entry_arrays)); } int journal_file_open( diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 9907c5fe26..cfd6d40932 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -38,7 +38,7 @@ * - write bit mucking test * - tag timestamps should be between entry timestamps * - output validated time ranges - * - add missing fields to journal header dump + * - evolve key even if nothing happened in regular intervals * * - Allow building without libgcrypt * - check with sparse diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c index 8a7d998ff9..9a99bcba4b 100644 --- a/src/journal/test-journal-verify.c +++ b/src/journal/test-journal-verify.c @@ -68,6 +68,9 @@ int main(int argc, char *argv[]) { log_info("Verifying..."); assert_se(journal_file_open("test.journal", O_RDONLY, 0666, false, false, NULL, NULL, NULL, &f) == 0); + + journal_file_print_header(f); + assert_se(journal_file_verify(f, verification_key) >= 0); journal_file_close(f); -- cgit v1.2.3-54-g00ecf From 7b5fd91c543fe2d62d9decfa1737f8e17c0976b2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 17 Aug 2012 03:00:09 +0200 Subject: journal: ensure that entries and tags are properly ordered --- src/journal/journal-verify.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index cfd6d40932..8b71d03c34 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -646,7 +646,7 @@ static int journal_file_parse_verification_key(JournalFile *f, const char *key) f->fsprg_seed = seed; f->fsprg_seed_size = seed_size; - f->fss_start_usec = start; + f->fss_start_usec = start * interval; f->fss_interval_usec = interval; return 0; @@ -655,7 +655,7 @@ static int journal_file_parse_verification_key(JournalFile *f, const char *key) int journal_file_verify(JournalFile *f, const char *key) { int r; Object *o; - uint64_t p = 0, last_tag = 0, last_epoch = 0; + uint64_t p = 0, last_tag = 0, last_epoch = 0, last_tag_realtime = 0; uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0; sd_id128_t entry_boot_id; bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false; @@ -753,6 +753,12 @@ int journal_file_verify(JournalFile *f, const char *key) { if (r < 0) goto fail; + if (last_tag_realtime > le64toh(o->entry.realtime)) { + log_error("Older entry after newer tag at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + if (!entry_seqnum_set && le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) { log_error("Head entry sequence number incorrect"); @@ -870,6 +876,13 @@ int journal_file_verify(JournalFile *f, const char *key) { goto fail; } + last_tag_realtime = (o->tag.epoch + 1) * f->fss_interval_usec + f->fss_start_usec; + if (entry_realtime_set && entry_realtime >= last_tag_realtime) { + log_error("Tag/entry realtime timestamp out of synchronization at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + /* OK, now we know the epoch. So let's now set * it, and calculate the HMAC for everything * since the last tag. */ -- cgit v1.2.3-54-g00ecf From 356fe3e6c623c831050c835782aec9e50f6262ba Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 17 Aug 2012 03:01:07 +0200 Subject: journal: reword verification messages a bit --- src/journal/journal-verify.c | 48 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 8b71d03c34..598fc9bead 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -289,7 +289,7 @@ static int entry_points_to_data( if (le64toh(o->entry_array.items[j]) == entry_p) return 0; - a = le64toh(o->entry_array.next_entry_array_offset);; + a = le64toh(o->entry_array.next_entry_array_offset); } return 0; @@ -325,12 +325,12 @@ static int verify_data( uint64_t next, m, j; if (a == 0) { - log_error("Array chain too short at %llu.", (unsigned long long) p); + log_error("Array chain too short at %llu", (unsigned long long) p); return -EBADMSG; } if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) { - log_error("Invalid array at %llu.", (unsigned long long) p); + log_error("Invalid array at %llu", (unsigned long long) p); return -EBADMSG; } @@ -340,7 +340,7 @@ static int verify_data( next = le64toh(o->entry_array.next_entry_array_offset); if (next != 0 && next <= a) { - log_error("Array chain has cycle at %llu.", (unsigned long long) p); + log_error("Array chain has cycle at %llu", (unsigned long long) p); return -EBADMSG; } @@ -349,7 +349,7 @@ static int verify_data( q = le64toh(o->entry_array.items[j]); if (q <= last) { - log_error("Data object's entry array not sorted at %llu.", (unsigned long long) p); + log_error("Data object's entry array not sorted at %llu", (unsigned long long) p); return -EBADMSG; } last = q; @@ -398,7 +398,7 @@ static int verify_hash_table( uint64_t next; if (!contains_uint64(f->mmap, data_fd, n_data, p)) { - log_error("Invalid data object at hash entry %llu of %llu.", + log_error("Invalid data object at hash entry %llu of %llu", (unsigned long long) i, (unsigned long long) n); return -EBADMSG; } @@ -409,13 +409,13 @@ static int verify_hash_table( next = le64toh(o->data.next_hash_offset); if (next != 0 && next <= p) { - log_error("Hash chain has a cycle in hash entry %llu of %llu.", + log_error("Hash chain has a cycle in hash entry %llu of %llu", (unsigned long long) i, (unsigned long long) n); return -EBADMSG; } if (le64toh(o->data.hash) % n != i) { - log_error("Hash value mismatch in hash entry %llu of %llu.", + log_error("Hash value mismatch in hash entry %llu of %llu", (unsigned long long) i, (unsigned long long) n); return -EBADMSG; } @@ -429,7 +429,7 @@ static int verify_hash_table( } if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) { - log_error("Tail hash pointer mismatch in hash table."); + log_error("Tail hash pointer mismatch in hash table"); return -EBADMSG; } } @@ -483,8 +483,8 @@ static int verify_entry( h = le64toh(o->entry.items[i].hash); if (!contains_uint64(f->mmap, data_fd, n_data, q)) { - log_error("Invalid data object at entry %llu.", - (unsigned long long) o); + log_error("Invalid data object at entry %llu", + (unsigned long long) p); return -EBADMSG; } @@ -493,7 +493,7 @@ static int verify_entry( return r; if (le64toh(u->data.hash) != h) { - log_error("Hash mismatch for data object at entry %llu.", + log_error("Hash mismatch for data object at entry %llu", (unsigned long long) p); return -EBADMSG; } @@ -502,7 +502,7 @@ static int verify_entry( if (r < 0) return r; if (r == 0) { - log_error("Data object missing from hash at entry %llu.", + log_error("Data object missing from hash at entry %llu", (unsigned long long) p); return -EBADMSG; } @@ -536,13 +536,13 @@ static int verify_entry_array( draw_progress(0x8000 + (0x3FFF * i / n), last_usec); if (a == 0) { - log_error("Array chain too short at %llu of %llu.", + log_error("Array chain too short at %llu of %llu", (unsigned long long) i, (unsigned long long) n); return -EBADMSG; } if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) { - log_error("Invalid array at %llu of %llu.", + log_error("Invalid array at %llu of %llu", (unsigned long long) i, (unsigned long long) n); return -EBADMSG; } @@ -553,7 +553,7 @@ static int verify_entry_array( next = le64toh(o->entry_array.next_entry_array_offset); if (next != 0 && next <= a) { - log_error("Array chain has cycle at %llu of %llu.", + log_error("Array chain has cycle at %llu of %llu", (unsigned long long) i, (unsigned long long) n); return -EBADMSG; } @@ -564,14 +564,14 @@ static int verify_entry_array( p = le64toh(o->entry_array.items[j]); if (p <= last) { - log_error("Entry array not sorted at %llu of %llu.", + log_error("Entry array not sorted at %llu of %llu", (unsigned long long) i, (unsigned long long) n); return -EBADMSG; } last = p; if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) { - log_error("Invalid array entry at %llu of %llu.", + log_error("Invalid array entry at %llu of %llu", (unsigned long long) i, (unsigned long long) n); return -EBADMSG; } @@ -714,7 +714,7 @@ int journal_file_verify(JournalFile *f, const char *key) { } if (le64toh(f->header->tail_object_offset) < p) { - log_error("Invalid tail object pointer."); + log_error("Invalid tail object pointer"); r = -EBADMSG; goto fail; } @@ -729,7 +729,7 @@ int journal_file_verify(JournalFile *f, const char *key) { if (o->object.flags & OBJECT_COMPRESSED && !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) { - log_error("Compressed object without compression at %llu", (unsigned long long) p); + log_error("Compressed object in file without compression at %llu", (unsigned long long) p); r = -EBADMSG; goto fail; } @@ -761,7 +761,7 @@ int journal_file_verify(JournalFile *f, const char *key) { if (!entry_seqnum_set && le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) { - log_error("Head entry sequence number incorrect"); + log_error("Head entry sequence number incorrect at %llu", (unsigned long long) p); r = -EBADMSG; goto fail; } @@ -810,7 +810,7 @@ int journal_file_verify(JournalFile *f, const char *key) { if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) || le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) { - log_error("Header fields for data hash table invalid."); + log_error("Header fields for data hash table invalid"); r = -EBADMSG; goto fail; } @@ -827,7 +827,7 @@ int journal_file_verify(JournalFile *f, const char *key) { if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) || le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) { - log_error("Header fields for field hash table invalid."); + log_error("Header fields for field hash table invalid"); r = -EBADMSG; goto fail; } @@ -857,7 +857,7 @@ int journal_file_verify(JournalFile *f, const char *key) { uint64_t q; if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED)) { - log_error("Tag object without sealing at %llu", (unsigned long long) p); + log_error("Tag object in file without sealing at %llu", (unsigned long long) p); r = -EBADMSG; goto fail; } -- cgit v1.2.3-54-g00ecf From 6c7be122acd666d4e93541179d89747aa12efb67 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 17 Aug 2012 03:30:22 +0200 Subject: journal: after verification output validated time range --- src/journal/journal-verify.c | 28 ++++++++++++++++++++++------ src/journal/journal-verify.h | 2 +- src/journal/journalctl.c | 13 +++++++++++-- src/journal/test-journal-verify.c | 12 +++++++++++- 4 files changed, 45 insertions(+), 10 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 598fc9bead..f9a930e42e 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -36,8 +36,6 @@ /* FIXME: * * - write bit mucking test - * - tag timestamps should be between entry timestamps - * - output validated time ranges * - evolve key even if nothing happened in regular intervals * * - Allow building without libgcrypt @@ -652,7 +650,10 @@ static int journal_file_parse_verification_key(JournalFile *f, const char *key) return 0; } -int journal_file_verify(JournalFile *f, const char *key) { +int journal_file_verify( + JournalFile *f, + const char *key, + usec_t *first_validated, usec_t *last_validated, usec_t *last_contained) { int r; Object *o; uint64_t p = 0, last_tag = 0, last_epoch = 0, last_tag_realtime = 0; @@ -749,6 +750,12 @@ int journal_file_verify(JournalFile *f, const char *key) { break; case OBJECT_ENTRY: + if ((le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED) && n_tags <= 0) { + log_error("First entry before first tag at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + r = write_uint64(entry_fd, p); if (r < 0) goto fail; @@ -854,7 +861,7 @@ int journal_file_verify(JournalFile *f, const char *key) { break; case OBJECT_TAG: { - uint64_t q; + uint64_t q, rt; if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED)) { log_error("Tag object in file without sealing at %llu", (unsigned long long) p); @@ -876,8 +883,8 @@ int journal_file_verify(JournalFile *f, const char *key) { goto fail; } - last_tag_realtime = (o->tag.epoch + 1) * f->fss_interval_usec + f->fss_start_usec; - if (entry_realtime_set && entry_realtime >= last_tag_realtime) { + rt = (o->tag.epoch + 1) * f->fss_interval_usec + f->fss_start_usec; + if (entry_realtime_set && entry_realtime >= rt) { log_error("Tag/entry realtime timestamp out of synchronization at %llu", (unsigned long long) p); r = -EBADMSG; goto fail; @@ -929,6 +936,8 @@ int journal_file_verify(JournalFile *f, const char *key) { f->hmac_running = false; last_tag = p + ALIGN64(le64toh(o->object.size)); + last_tag_realtime = rt; + n_tags ++; break; } @@ -1056,6 +1065,13 @@ int journal_file_verify(JournalFile *f, const char *key) { close_nointr_nofail(entry_fd); close_nointr_nofail(entry_array_fd); + if (first_validated) + *first_validated = le64toh(f->header->head_entry_realtime); + if (last_validated) + *last_validated = last_tag_realtime; + if (last_contained) + *last_contained = le64toh(f->header->tail_entry_realtime); + return 0; fail: diff --git a/src/journal/journal-verify.h b/src/journal/journal-verify.h index 3ebdd5e7f2..e4449c6ad7 100644 --- a/src/journal/journal-verify.h +++ b/src/journal/journal-verify.h @@ -23,4 +23,4 @@ #include "journal-file.h" -int journal_file_verify(JournalFile *f, const char *key); +int journal_file_verify(JournalFile *f, const char *key, usec_t *first_validated, usec_t *last_validated, usec_t *last_contained); diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 0df8ce57fd..5c21ab0adf 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -618,21 +618,30 @@ static int verify(sd_journal *j) { HASHMAP_FOREACH(f, j->files, i) { int k; + usec_t from, to, total; #ifdef HAVE_GCRYPT if (!arg_verify_key && journal_file_fss_enabled(f)) log_warning("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); + k = journal_file_verify(f, arg_verify_key, &from, &to, &total); if (k == -EINVAL) { /* If the key was invalid give up right-away. */ return k; } else if (k < 0) { log_warning("FAIL: %s (%s)", f->path, strerror(-k)); r = k; - } else + } else { + char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX]; log_info("PASS: %s", f->path); + + if (journal_file_fss_enabled(f)) + log_info("=> Validated from %s to %s, %s missing", + format_timestamp(a, sizeof(a), from), + format_timestamp(b, sizeof(b), to), + format_timespan(c, sizeof(c), total > to ? total - to : 0)); + } } return r; diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c index 9a99bcba4b..df0a5ddef9 100644 --- a/src/journal/test-journal-verify.c +++ b/src/journal/test-journal-verify.c @@ -36,6 +36,10 @@ int main(int argc, char *argv[]) { unsigned n; JournalFile *f; const char *verification_key = argv[1]; + usec_t from, to, total; + char a[FORMAT_TIMESTAMP_MAX]; + char b[FORMAT_TIMESTAMP_MAX]; + char c[FORMAT_TIMESPAN_MAX]; log_set_max_level(LOG_DEBUG); @@ -71,7 +75,13 @@ int main(int argc, char *argv[]) { journal_file_print_header(f); - assert_se(journal_file_verify(f, verification_key) >= 0); + assert_se(journal_file_verify(f, verification_key, &from, &to, &total) >= 0); + + log_info("=> Validated from %s to %s, %s missing", + format_timestamp(a, sizeof(a), from), + format_timestamp(b, sizeof(b), to), + format_timespan(c, sizeof(c), total > to ? total - to : 0)); + journal_file_close(f); log_info("Exiting..."); -- cgit v1.2.3-54-g00ecf From db11ac1ab56bc13514a029e7d126c5efe2c68bc2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 18 Aug 2012 00:37:21 +0200 Subject: journald: add additional simple static tests to verifier --- src/journal/journal-def.h | 7 ++++--- src/journal/journal-file.c | 18 +++++++++++++++++- src/journal/journal-file.h | 1 + src/journal/journal-verify.c | 21 +++++++++++++++++++++ 4 files changed, 43 insertions(+), 4 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h index 52c55ab4bb..45c3cde9f0 100644 --- a/src/journal/journal-def.h +++ b/src/journal/journal-def.h @@ -140,9 +140,10 @@ union Object { }; enum { - STATE_OFFLINE, - STATE_ONLINE, - STATE_ARCHIVED + STATE_OFFLINE = 0, + STATE_ONLINE = 1, + STATE_ARCHIVED = 2, + _STATE_MAX }; /* Header flags */ diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 08d4285d0e..3cf28a7f3c 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -203,6 +203,9 @@ static int journal_file_verify_header(JournalFile *f) { #endif } + if (f->header->state >= _STATE_MAX) + return -EBADMSG; + /* The first addition was n_data, so check that we are at least this large */ if (le64toh(f->header->header_size) < HEADER_SIZE_MIN) return -EBADMSG; @@ -211,7 +214,16 @@ static int journal_file_verify_header(JournalFile *f) { !JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays)) return -EBADMSG; - if ((uint64_t) f->last_stat.st_size < (le64toh(f->header->header_size) + le64toh(f->header->arena_size))) + if ((le64toh(f->header->header_size) + le64toh(f->header->arena_size)) > (uint64_t) f->last_stat.st_size) + return -ENODATA; + + if (le64toh(f->header->tail_object_offset) > (le64toh(f->header->header_size) + le64toh(f->header->arena_size))) + return -ENODATA; + + if (!VALID64(f->header->data_hash_table_offset) || + !VALID64(f->header->field_hash_table_offset) || + !VALID64(f->header->tail_object_offset) || + !VALID64(f->header->entry_array_offset)) return -ENODATA; if (f->writable) { @@ -351,6 +363,10 @@ int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Objec assert(f); assert(ret); + /* Objects may only be located at multiple of 64 bit */ + if (!VALID64(offset)) + return -EFAULT; + /* One context for each type, plus one catch-all for the rest */ context = type > 0 && type < _OBJECT_TYPE_MAX ? type : 0; diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index 58de214359..2d2bf319a0 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -117,6 +117,7 @@ int journal_file_open_reliably( JournalFile **ret); #define ALIGN64(x) (((x) + 7ULL) & ~7ULL) +#define VALID64(x) (((x) & 7ULL) == 0ULL) #define JOURNAL_HEADER_CONTAINS(h, field) \ (le64toh((h)->header_size) >= offsetof(Header, field) + sizeof((h)->field)) diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index f9a930e42e..7be0d2e5d7 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -45,6 +45,8 @@ * */ static int journal_file_object_verify(JournalFile *f, Object *o) { + uint64_t i; + assert(f); assert(o); @@ -87,12 +89,22 @@ static int journal_file_object_verify(JournalFile *f, Object *o) { if (h1 != h2) return -EBADMSG; + if (!VALID64(o->data.next_hash_offset) || + !VALID64(o->data.next_field_offset) || + !VALID64(o->data.entry_offset) || + !VALID64(o->data.entry_array_offset)) + return -EBADMSG; + break; } case OBJECT_FIELD: if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) return -EBADMSG; + + if (!VALID64(o->field.next_hash_offset) || + !VALID64(o->field.head_data_offset)) + return -EBADMSG; break; case OBJECT_ENTRY: @@ -106,6 +118,12 @@ static int journal_file_object_verify(JournalFile *f, Object *o) { le64toh(o->entry.realtime) <= 0) return -EBADMSG; + for (i = 0; i < journal_file_entry_n_items(o); i++) { + if (o->entry.items[i].object_offset == 0 || + !VALID64(o->entry.items[i].object_offset)) + return -EBADMSG; + } + break; case OBJECT_DATA_HASH_TABLE: @@ -125,6 +143,9 @@ static int journal_file_object_verify(JournalFile *f, Object *o) { if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) return -EBADMSG; + if (!VALID64(o->entry_array.next_entry_array_offset)) + return -EBADMSG; + break; case OBJECT_TAG: -- cgit v1.2.3-54-g00ecf From c586dbf110abdbf0317bdd0f0a5900d709194409 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 18 Aug 2012 00:38:57 +0200 Subject: journal: fix verification without key --- src/journal/journal-file.c | 6 ++- src/journal/journal-verify.c | 88 +++++++++++++++++++++++--------------------- 2 files changed, 51 insertions(+), 43 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 3cf28a7f3c..e04ffd0452 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -65,7 +65,7 @@ void journal_file_close(JournalFile *f) { assert(f); /* Write the final tag */ - if (f->seal) + if (f->seal && f->writable) journal_file_append_tag(f); /* Sync everything to disk, before we mark the file offline */ @@ -252,7 +252,9 @@ static int journal_file_verify_header(JournalFile *f) { } f->compress = !!(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED); - f->seal = !!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED); + + if (f->writable) + f->seal = !!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED); return 0; } diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 7be0d2e5d7..39cf3a3313 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -696,7 +696,8 @@ int journal_file_verify( log_error("Failed to parse seed."); return r; } - } + } else if (f->seal) + return -ENOKEY; data_fd = mkostemp(data_path, O_CLOEXEC); if (data_fd < 0) { @@ -904,60 +905,65 @@ int journal_file_verify( goto fail; } - rt = (o->tag.epoch + 1) * f->fss_interval_usec + f->fss_start_usec; - if (entry_realtime_set && entry_realtime >= rt) { - log_error("Tag/entry realtime timestamp out of synchronization at %llu", (unsigned long long) p); - r = -EBADMSG; - goto fail; - } - - /* 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)); - if (r < 0) - goto fail; + if (f->seal) { + log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum)); - r = journal_file_hmac_start(f); - if (r < 0) - goto fail; + rt = (o->tag.epoch + 1) * f->fss_interval_usec + f->fss_start_usec; + if (entry_realtime_set && entry_realtime >= rt) { + log_error("Tag/entry realtime timestamp out of synchronization at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } - if (last_tag == 0) { - r = journal_file_hmac_put_header(f); + /* 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)); if (r < 0) goto fail; - q = le64toh(f->header->header_size); - } else - q = last_tag; - - while (q <= p) { - r = journal_file_move_to_object(f, -1, q, &o); + r = journal_file_hmac_start(f); if (r < 0) goto fail; - r = journal_file_hmac_put_object(f, -1, q); + if (last_tag == 0) { + r = journal_file_hmac_put_header(f); + if (r < 0) + goto fail; + + q = le64toh(f->header->header_size); + } else + q = last_tag; + + while (q <= p) { + r = journal_file_move_to_object(f, -1, q, &o); + if (r < 0) + goto fail; + + r = journal_file_hmac_put_object(f, -1, q); + if (r < 0) + goto fail; + + q = q + ALIGN64(le64toh(o->object.size)); + } + + /* Position might have changed, let's reposition things */ + r = journal_file_move_to_object(f, -1, p, &o); if (r < 0) goto fail; - q = q + ALIGN64(le64toh(o->object.size)); - } - - /* Position might have changed, let's reposition things */ - r = journal_file_move_to_object(f, -1, p, &o); - if (r < 0) - goto fail; + if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) { + log_error("Tag failed verification at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } - if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) { - log_error("Tag failed verification at %llu", (unsigned long long) p); - r = -EBADMSG; - goto fail; + f->hmac_running = false; + last_tag_realtime = rt; } - f->hmac_running = false; - last_tag = p + ALIGN64(le64toh(o->object.size)); - last_tag_realtime = rt; + last_epoch = le64toh(o->tag.epoch); n_tags ++; break; @@ -1087,7 +1093,7 @@ int journal_file_verify( close_nointr_nofail(entry_array_fd); if (first_validated) - *first_validated = le64toh(f->header->head_entry_realtime); + *first_validated = last_tag_realtime ? le64toh(f->header->head_entry_realtime) : 0; if (last_validated) *last_validated = last_tag_realtime; if (last_contained) -- cgit v1.2.3-54-g00ecf From b72631e59c1b9f62bcfaf1ce3f7e72e4a3beee89 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 18 Aug 2012 00:40:03 +0200 Subject: jounral: write bit fiddling test This test goes through every single bit in a journal file, toggles it, and checks if this change is detected by the verification. --- src/journal/journal-verify.c | 32 +++++++++++------- src/journal/journal-verify.h | 2 +- src/journal/journalctl.c | 4 +-- src/journal/test-journal-verify.c | 71 ++++++++++++++++++++++++++++++++++----- 4 files changed, 85 insertions(+), 24 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 39cf3a3313..f66b23556b 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -394,7 +394,8 @@ static int verify_hash_table( int data_fd, uint64_t n_data, int entry_fd, uint64_t n_entries, int entry_array_fd, uint64_t n_entry_arrays, - usec_t *last_usec) { + usec_t *last_usec, + bool show_progress) { uint64_t i, n; int r; @@ -409,7 +410,8 @@ static int verify_hash_table( for (i = 0; i < n; i++) { uint64_t last = 0, p; - draw_progress(0xC000 + (0x3FFF * i / n), last_usec); + if (show_progress) + draw_progress(0xC000 + (0x3FFF * i / n), last_usec); p = le64toh(f->data_hash_table[i].head_hash_offset); while (p != 0) { @@ -535,7 +537,8 @@ static int verify_entry_array( int data_fd, uint64_t n_data, int entry_fd, uint64_t n_entries, int entry_array_fd, uint64_t n_entry_arrays, - usec_t *last_usec) { + usec_t *last_usec, + bool show_progress) { uint64_t i = 0, a, n, last = 0; int r; @@ -552,7 +555,8 @@ static int verify_entry_array( uint64_t next, m, j; Object *o; - draw_progress(0x8000 + (0x3FFF * i / n), last_usec); + if (show_progress) + draw_progress(0x8000 + (0x3FFF * i / n), last_usec); if (a == 0) { log_error("Array chain too short at %llu of %llu", @@ -674,7 +678,8 @@ static int journal_file_parse_verification_key(JournalFile *f, const char *key) int journal_file_verify( JournalFile *f, const char *key, - usec_t *first_validated, usec_t *last_validated, usec_t *last_contained) { + usec_t *first_validated, usec_t *last_validated, usec_t *last_contained, + bool show_progress) { int r; Object *o; uint64_t p = 0, last_tag = 0, last_epoch = 0, last_tag_realtime = 0; @@ -728,7 +733,8 @@ int journal_file_verify( p = le64toh(f->header->header_size); while (p != 0) { - draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec); + if (show_progress) + draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec); r = journal_file_move_to_object(f, -1, p, &o); if (r < 0) { @@ -891,8 +897,6 @@ int journal_file_verify( goto fail; } - log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum)); - if (le64toh(o->tag.seqnum) != n_tags + 1) { log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p); r = -EBADMSG; @@ -1070,7 +1074,8 @@ int journal_file_verify( data_fd, n_data, entry_fd, n_entries, entry_array_fd, n_entry_arrays, - &last_usec); + &last_usec, + show_progress); if (r < 0) goto fail; @@ -1078,11 +1083,13 @@ int journal_file_verify( data_fd, n_data, entry_fd, n_entries, entry_array_fd, n_entry_arrays, - &last_usec); + &last_usec, + show_progress); if (r < 0) goto fail; - flush_progress(); + if (show_progress) + flush_progress(); mmap_cache_close_fd(f->mmap, data_fd); mmap_cache_close_fd(f->mmap, entry_fd); @@ -1102,7 +1109,8 @@ int journal_file_verify( return 0; fail: - flush_progress(); + if (show_progress) + flush_progress(); log_error("File corruption detected at %s:%llu (of %llu, %llu%%).", f->path, diff --git a/src/journal/journal-verify.h b/src/journal/journal-verify.h index e4449c6ad7..a4633adea9 100644 --- a/src/journal/journal-verify.h +++ b/src/journal/journal-verify.h @@ -23,4 +23,4 @@ #include "journal-file.h" -int journal_file_verify(JournalFile *f, const char *key, usec_t *first_validated, usec_t *last_validated, usec_t *last_contained); +int journal_file_verify(JournalFile *f, const char *key, usec_t *first_validated, usec_t *last_validated, usec_t *last_contained, bool show_progress); diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 25f41f6322..ba678a289f 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -637,7 +637,7 @@ static int verify(sd_journal *j) { log_warning("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, &from, &to, &total); + k = journal_file_verify(f, arg_verify_key, &from, &to, &total, true); if (k == -EINVAL) { /* If the key was invalid give up right-away. */ return k; @@ -648,7 +648,7 @@ static int verify(sd_journal *j) { char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX]; log_info("PASS: %s", f->path); - if (journal_file_fss_enabled(f)) + if (arg_verify_key && journal_file_fss_enabled(f)) log_info("=> Validated from %s to %s, %s missing", format_timestamp(a, sizeof(a), from), format_timestamp(b, sizeof(b), to), diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c index df0a5ddef9..4e6c119b6d 100644 --- a/src/journal/test-journal-verify.c +++ b/src/journal/test-journal-verify.c @@ -27,19 +27,55 @@ #include "log.h" #include "journal-file.h" #include "journal-verify.h" +#include "journal-authenticate.h" #define N_ENTRIES 6000 #define RANDOM_RANGE 77 +static void bit_toggle(const char *fn, uint64_t p) { + uint8_t b; + ssize_t r; + int fd; + + fd = open(fn, O_RDWR|O_CLOEXEC); + assert(fd >= 0); + + r = pread(fd, &b, 1, p/8); + assert(r == 1); + + b ^= 1 << (p % 8); + + r = pwrite(fd, &b, 1, p/8); + assert(r == 1); + + close_nointr_nofail(fd); +} + +static int raw_verify(const char *fn, const char *verification_key) { + JournalFile *f; + int r; + + r = journal_file_open(fn, O_RDONLY, 0666, true, true, NULL, NULL, NULL, &f); + if (r < 0) + return r; + + r = journal_file_verify(f, verification_key, NULL, NULL, NULL, false); + journal_file_close(f); + + return r; +} + int main(int argc, char *argv[]) { char t[] = "/tmp/journal-XXXXXX"; unsigned n; JournalFile *f; const char *verification_key = argv[1]; - usec_t from, to, total; + usec_t from = 0, to = 0, total = 0; char a[FORMAT_TIMESTAMP_MAX]; char b[FORMAT_TIMESTAMP_MAX]; char c[FORMAT_TIMESPAN_MAX]; + struct stat st; + uint64_t p; log_set_max_level(LOG_DEBUG); @@ -71,19 +107,36 @@ int main(int argc, char *argv[]) { log_info("Verifying..."); - assert_se(journal_file_open("test.journal", O_RDONLY, 0666, false, false, NULL, NULL, NULL, &f) == 0); - + assert_se(journal_file_open("test.journal", O_RDONLY, 0666, true, true, NULL, NULL, NULL, &f) == 0); journal_file_print_header(f); - assert_se(journal_file_verify(f, verification_key, &from, &to, &total) >= 0); - - log_info("=> Validated from %s to %s, %s missing", - format_timestamp(a, sizeof(a), from), - format_timestamp(b, sizeof(b), to), - format_timespan(c, sizeof(c), total > to ? total - to : 0)); + assert_se(journal_file_verify(f, verification_key, &from, &to, &total, true) >= 0); + if (verification_key && journal_file_fss_enabled(f)) { + log_info("=> Validated from %s to %s, %s missing", + format_timestamp(a, sizeof(a), from), + format_timestamp(b, sizeof(b), to), + format_timespan(c, sizeof(c), total > to ? total - to : 0)); + } journal_file_close(f); + log_info("Toggling bits..."); + + assert_se(stat("test.journal", &st) >= 0); + + for (p = 240*8; p < ((uint64_t) st.st_size * 8); p ++) { + bit_toggle("test.journal", p); + + log_info("[ %llu+%llu]", (unsigned long long) p / 8, (unsigned long long) p % 8); + + if (raw_verify("test.journal", verification_key) >= 0) { + log_notice(ANSI_HIGHLIGHT_RED_ON ">>>> %llu (bit %llu) can be toggled without detection." ANSI_HIGHLIGHT_OFF, (unsigned long long) p / 8, (unsigned long long) p % 8); + sleep(1); + } + + bit_toggle("test.journal", p); + } + log_info("Exiting..."); assert_se(rm_rf_dangerous(t, false, true, false) >= 0); -- cgit v1.2.3-54-g00ecf From 97147f8c1f63234470a7062d2ed0f999c0996d42 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 18 Aug 2012 00:40:48 +0200 Subject: journal: refuse verification of files with unknown flags --- src/journal/journal-verify.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index f66b23556b..e3bd8ffbd7 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -692,6 +692,8 @@ int journal_file_verify( char data_path[] = "/var/tmp/journal-data-XXXXXX", entry_path[] = "/var/tmp/journal-entry-XXXXXX", entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX"; + unsigned i; + bool found_last; assert(f); @@ -728,6 +730,24 @@ int journal_file_verify( } unlink(entry_array_path); +#ifdef HAVE_GCRYPT + if ((le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) != 0) +#else + if (f->header->compatible_flags != 0) +#endif + { + log_error("Cannot verify file with unknown extensions."); + r = -ENOTSUP; + goto fail; + } + + for (i = 0; i < sizeof(f->header->reserved); i++) + if (f->header->reserved[i] != 0) { + log_error("Reserved field in non-zero."); + r = -EBADMSG; + goto fail; + } + /* First iteration: we go through all objects, verify the * superficial structure, headers, hashes. */ @@ -742,12 +762,15 @@ int journal_file_verify( goto fail; } - if (le64toh(f->header->tail_object_offset) < p) { + if (p > le64toh(f->header->tail_object_offset)) { log_error("Invalid tail object pointer"); r = -EBADMSG; goto fail; } + if (p == le64toh(f->header->tail_object_offset)) + found_last = true; + n_objects ++; r = journal_file_object_verify(f, o); @@ -983,6 +1006,12 @@ int journal_file_verify( p = p + ALIGN64(le64toh(o->object.size)); } + if (!found_last) { + log_error("Tail object pointer dead"); + r = -EBADMSG; + goto fail; + } + if (n_objects != le64toh(f->header->n_objects)) { log_error("Object number mismatch"); r = -EBADMSG; -- cgit v1.2.3-54-g00ecf From fb9a24b6b1ed5b1f42e6e350ccdb7e11800a83bd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 18 Aug 2012 01:45:39 +0200 Subject: journal: even more simple static object tests --- src/journal/journal-def.h | 2 ++ src/journal/journal-file.c | 8 ++++++++ src/journal/journal-file.h | 1 + src/journal/journal-verify.c | 18 ++++++++++++++++++ 4 files changed, 29 insertions(+) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h index 45c3cde9f0..8f00176bd7 100644 --- a/src/journal/journal-def.h +++ b/src/journal/journal-def.h @@ -188,6 +188,8 @@ _packed_ struct Header { /* Added in 189 */ le64_t n_tags; le64_t n_entry_arrays; + + /* Size: 224 */ }; #define FSS_HEADER_SIGNATURE ((char[]) { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' }) diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index e04ffd0452..df991a4a07 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -798,6 +798,14 @@ uint64_t journal_file_entry_array_n_items(Object *o) { return (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / sizeof(uint64_t); } +uint64_t journal_file_hash_table_n_items(Object *o) { + assert(o); + assert(o->object.type == OBJECT_DATA_HASH_TABLE || + o->object.type == OBJECT_FIELD_HASH_TABLE); + + return (le64toh(o->object.size) - offsetof(Object, hash_table.items)) / sizeof(HashItem); +} + static int link_entry_into_array(JournalFile *f, le64_t *first, le64_t *idx, diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index 2d2bf319a0..e2ef03347c 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -126,6 +126,7 @@ int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Objec uint64_t journal_file_entry_n_items(Object *o); uint64_t journal_file_entry_array_n_items(Object *o); +uint64_t journal_file_hash_table_n_items(Object *o); int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset); int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqno, Object **ret, uint64_t *offset); diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index e3bd8ffbd7..a31817671b 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -134,6 +134,19 @@ static int journal_file_object_verify(JournalFile *f, Object *o) { if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) return -EBADMSG; + for (i = 0; i < journal_file_hash_table_n_items(o); i++) { + if (o->hash_table.items[i].head_hash_offset != 0 && + !VALID64(le64toh(o->hash_table.items[i].head_hash_offset))) + return -EBADMSG; + if (o->hash_table.items[i].tail_hash_offset != 0 && + !VALID64(le64toh(o->hash_table.items[i].tail_hash_offset))) + return -EBADMSG; + + if ((o->hash_table.items[i].head_hash_offset != 0) != + (o->hash_table.items[i].tail_hash_offset != 0)) + return -EBADMSG; + } + break; case OBJECT_ENTRY_ARRAY: @@ -146,6 +159,11 @@ static int journal_file_object_verify(JournalFile *f, Object *o) { if (!VALID64(o->entry_array.next_entry_array_offset)) return -EBADMSG; + for (i = 0; i < journal_file_entry_array_n_items(o); i++) + if (o->entry_array.items[i] != 0 && + !VALID64(o->entry_array.items[i])) + return -EBADMSG; + break; case OBJECT_TAG: -- cgit v1.2.3-54-g00ecf From fc89a13992384ab8d8fb0c937b021434123bbc49 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 19 Aug 2012 15:15:59 +0200 Subject: journal: validate timestamps as well --- src/journal/fsprg.c | 2 +- src/journal/journal-file.h | 15 +++++++++++++++ src/journal/journal-verify.c | 9 +++++++-- 3 files changed, 23 insertions(+), 3 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/fsprg.c b/src/journal/fsprg.c index 34ce3be96b..2190b7c796 100644 --- a/src/journal/fsprg.c +++ b/src/journal/fsprg.c @@ -160,7 +160,7 @@ static gcry_mpi_t twopowmodphi(uint64_t m, const gcry_mpi_t p) { gcry_mpi_sub_ui(phi, p, 1); /* count number of used bits in m */ - for (n = 0; ((uint64_t)1 << n) <= m; n++) + for (n = 0; (1ULL << n) <= m; n++) ; r = gcry_mpi_new(0); diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index e2ef03347c..7358173e7a 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -119,6 +119,21 @@ int journal_file_open_reliably( #define ALIGN64(x) (((x) + 7ULL) & ~7ULL) #define VALID64(x) (((x) & 7ULL) == 0ULL) +static inline bool VALID_REALTIME(uint64_t u) { + /* This considers timestamps until the year 3112 valid. That should be plenty room... */ + return u > 0 && u < (1ULL << 55); +} + +static inline bool VALID_MONOTONIC(uint64_t u) { + /* This considers timestamps until 1142 years of runtime valid. */ + return u < (1ULL << 55); +} + +static inline bool VALID_EPOCH(uint64_t u) { + /* This allows changing the key for 1142 years, every usec. */ + return u < (1ULL << 55); +} + #define JOURNAL_HEADER_CONTAINS(h, field) \ (le64toh((h)->header_size) >= offsetof(Header, field) + sizeof((h)->field)) diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index a31817671b..535b2727ac 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -35,8 +35,8 @@ /* FIXME: * - * - write bit mucking test * - evolve key even if nothing happened in regular intervals + * - add macro for accessing flags * * - Allow building without libgcrypt * - check with sparse @@ -115,7 +115,8 @@ static int journal_file_object_verify(JournalFile *f, Object *o) { return -EBADMSG; if (le64toh(o->entry.seqnum) <= 0 || - le64toh(o->entry.realtime) <= 0) + !VALID_REALTIME(le64toh(o->entry.realtime)) || + !VALID_MONOTONIC(le64toh(o->entry.monotonic))) return -EBADMSG; for (i = 0; i < journal_file_entry_n_items(o); i++) { @@ -169,6 +170,10 @@ static int journal_file_object_verify(JournalFile *f, Object *o) { case OBJECT_TAG: if (le64toh(o->object.size) != sizeof(TagObject)) return -EBADMSG; + + if (!VALID_EPOCH(o->tag.epoch)) + return -EBADMSG; + break; } -- cgit v1.2.3-54-g00ecf From a2e99cdf94a8a0350ff13b241de07f34c015b1fc Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 19 Aug 2012 15:16:32 +0200 Subject: journal: fix bisection algorithm --- src/journal/journal-verify.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 535b2727ac..e3c3ccdb20 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -266,10 +266,14 @@ static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) { if (*z == p) return 1; + if (a + 1 >= b) + return 0; + if (p < *z) b = c; - else + else { a = c; + } } return 0; -- cgit v1.2.3-54-g00ecf From f7fab8a5ae7a3b378040203821383f5a8fc91126 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 20 Aug 2012 15:59:33 +0200 Subject: journal: fix tag ordering check --- src/journal/journal-file.c | 7 ++++--- src/journal/journal-verify.c | 11 ++++++----- src/journal/journalctl.c | 2 +- src/journal/test-journal-verify.c | 23 ++++++++++++----------- 4 files changed, 23 insertions(+), 20 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index c6b25ce54b..f419cf6b65 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -1855,7 +1855,7 @@ void journal_file_dump(JournalFile *f) { break; case OBJECT_ENTRY: - printf("Type: OBJECT_ENTRY %llu %llu %llu\n", + printf("Type: OBJECT_ENTRY seqnum=%llu monotonic=%llu realtime=%llu\n", (unsigned long long) le64toh(o->entry.seqnum), (unsigned long long) le64toh(o->entry.monotonic), (unsigned long long) le64toh(o->entry.realtime)); @@ -1874,8 +1874,9 @@ void journal_file_dump(JournalFile *f) { break; case OBJECT_TAG: - printf("Type: OBJECT_TAG %llu\n", - (unsigned long long) le64toh(o->tag.seqnum)); + printf("Type: OBJECT_TAG seqnum=%llu epoch=%llu\n", + (unsigned long long) le64toh(o->tag.seqnum), + (unsigned long long) le64toh(o->tag.epoch)); break; } diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index e3c3ccdb20..08f3e16aa9 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -709,7 +709,7 @@ int journal_file_verify( bool show_progress) { int r; Object *o; - uint64_t p = 0, last_tag = 0, last_epoch = 0, last_tag_realtime = 0; + uint64_t p = 0, last_tag = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0; uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0; sd_id128_t entry_boot_id; bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false; @@ -838,7 +838,7 @@ int journal_file_verify( if (r < 0) goto fail; - if (last_tag_realtime > le64toh(o->entry.realtime)) { + if (le64toh(o->entry.realtime) < last_tag_realtime) { log_error("Older entry after newer tag at %llu", (unsigned long long) p); r = -EBADMSG; goto fail; @@ -962,8 +962,8 @@ int journal_file_verify( if (f->seal) { log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum)); - rt = (o->tag.epoch + 1) * f->fss_interval_usec + f->fss_start_usec; - if (entry_realtime_set && entry_realtime >= rt) { + rt = f->fss_start_usec + o->tag.epoch * f->fss_interval_usec; + if (entry_realtime_set && entry_realtime >= rt + f->fss_interval_usec) { log_error("Tag/entry realtime timestamp out of synchronization at %llu", (unsigned long long) p); r = -EBADMSG; goto fail; @@ -1014,6 +1014,7 @@ int journal_file_verify( f->hmac_running = false; last_tag_realtime = rt; + last_sealed_realtime = entry_realtime; } last_tag = p + ALIGN64(le64toh(o->object.size)); @@ -1158,7 +1159,7 @@ int journal_file_verify( if (first_validated) *first_validated = last_tag_realtime ? le64toh(f->header->head_entry_realtime) : 0; if (last_validated) - *last_validated = last_tag_realtime; + *last_validated = last_sealed_realtime; if (last_contained) *last_contained = le64toh(f->header->tail_entry_realtime); diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index ba678a289f..e61ddf6d92 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -478,7 +478,7 @@ static int setup_keys(void) { return log_oom(); if (access(p, F_OK) >= 0) { - log_error("Evolving key file %s exists already.", p); + log_error("Sealing key file %s exists already.", p); r = -EEXIST; goto finish; } diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c index 4e6c119b6d..ed6e21dc0c 100644 --- a/src/journal/test-journal-verify.c +++ b/src/journal/test-journal-verify.c @@ -108,7 +108,8 @@ int main(int argc, char *argv[]) { log_info("Verifying..."); assert_se(journal_file_open("test.journal", O_RDONLY, 0666, true, true, NULL, NULL, NULL, &f) == 0); - journal_file_print_header(f); + /* journal_file_print_header(f); */ + journal_file_dump(f); assert_se(journal_file_verify(f, verification_key, &from, &to, &total, true) >= 0); @@ -120,21 +121,21 @@ int main(int argc, char *argv[]) { } journal_file_close(f); - log_info("Toggling bits..."); + if (verification_key) { + log_info("Toggling bits..."); - assert_se(stat("test.journal", &st) >= 0); + assert_se(stat("test.journal", &st) >= 0); - for (p = 240*8; p < ((uint64_t) st.st_size * 8); p ++) { - bit_toggle("test.journal", p); + for (p = 38448*8+0; p < ((uint64_t) st.st_size * 8); p ++) { + bit_toggle("test.journal", p); - log_info("[ %llu+%llu]", (unsigned long long) p / 8, (unsigned long long) p % 8); + log_info("[ %llu+%llu]", (unsigned long long) p / 8, (unsigned long long) p % 8); - if (raw_verify("test.journal", verification_key) >= 0) { - log_notice(ANSI_HIGHLIGHT_RED_ON ">>>> %llu (bit %llu) can be toggled without detection." ANSI_HIGHLIGHT_OFF, (unsigned long long) p / 8, (unsigned long long) p % 8); - sleep(1); - } + if (raw_verify("test.journal", verification_key) >= 0) + log_notice(ANSI_HIGHLIGHT_RED_ON ">>>> %llu (bit %llu) can be toggled without detection." ANSI_HIGHLIGHT_OFF, (unsigned long long) p / 8, (unsigned long long) p % 8); - bit_toggle("test.journal", p); + bit_toggle("test.journal", p); + } } log_info("Exiting..."); -- cgit v1.2.3-54-g00ecf From 8088cbd3cfcf539c984d8042cd2b92ebbfda6d82 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 20 Aug 2012 16:11:42 +0200 Subject: journal: use a macro to check for file header flags --- src/journal/journal-authenticate.c | 2 +- src/journal/journal-file.c | 15 +++++++-------- src/journal/journal-file.h | 6 ++++++ src/journal/journal-verify.c | 8 +++----- 4 files changed, 17 insertions(+), 14 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c index 9853edb58c..ddcf856aec 100644 --- a/src/journal/journal-authenticate.c +++ b/src/journal/journal-authenticate.c @@ -464,5 +464,5 @@ int journal_file_append_first_tag(JournalFile *f) { bool journal_file_fss_enabled(JournalFile *f) { assert(f); - return !!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED); + return JOURNAL_HEADER_SEALED(f->header); } diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index f419cf6b65..760efaebbb 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -210,8 +210,7 @@ static int journal_file_verify_header(JournalFile *f) { if (le64toh(f->header->header_size) < HEADER_SIZE_MIN) return -EBADMSG; - if ((le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED) && - !JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays)) + if (JOURNAL_HEADER_SEALED(f->header) && !JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays)) return -EBADMSG; if ((le64toh(f->header->header_size) + le64toh(f->header->arena_size)) > (uint64_t) f->last_stat.st_size) @@ -251,10 +250,10 @@ static int journal_file_verify_header(JournalFile *f) { } } - f->compress = !!(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED); + f->compress = JOURNAL_HEADER_COMPRESSED(f->header); if (f->writable) - f->seal = !!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED); + f->seal = JOURNAL_HEADER_SEALED(f->header); return 0; } @@ -1927,10 +1926,10 @@ void journal_file_print_header(JournalFile *f) { f->header->state == STATE_OFFLINE ? "OFFLINE" : f->header->state == STATE_ONLINE ? "ONLINE" : f->header->state == STATE_ARCHIVED ? "ARCHIVED" : "UNKNOWN", - (f->header->compatible_flags & HEADER_COMPATIBLE_SEALED) ? " SEALED" : "", - (f->header->compatible_flags & ~HEADER_COMPATIBLE_SEALED) ? " ???" : "", - (f->header->incompatible_flags & HEADER_INCOMPATIBLE_COMPRESSED) ? " COMPRESSED" : "", - (f->header->incompatible_flags & ~HEADER_INCOMPATIBLE_COMPRESSED) ? " ???" : "", + JOURNAL_HEADER_SEALED(f->header) ? " SEALED" : "", + (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) ? " ???" : "", + JOURNAL_HEADER_COMPRESSED(f->header) ? " COMPRESSED" : "", + (le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_COMPRESSED) ? " ???" : "", (unsigned long long) le64toh(f->header->header_size), (unsigned long long) le64toh(f->header->arena_size), (unsigned long long) le64toh(f->header->data_hash_table_size) / sizeof(HashItem), diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index 7358173e7a..5b1530e7a7 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -137,6 +137,12 @@ static inline bool VALID_EPOCH(uint64_t u) { #define JOURNAL_HEADER_CONTAINS(h, field) \ (le64toh((h)->header_size) >= offsetof(Header, field) + sizeof((h)->field)) +#define JOURNAL_HEADER_SEALED(h) \ + (!!(le32toh((h)->compatible_flags) & HEADER_COMPATIBLE_SEALED)) + +#define JOURNAL_HEADER_COMPRESSED(h) \ + (!!(le32toh((h)->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) + int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret); uint64_t journal_file_entry_n_items(Object *o); diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 08f3e16aa9..6afeab9a80 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -36,7 +36,6 @@ /* FIXME: * * - evolve key even if nothing happened in regular intervals - * - add macro for accessing flags * * - Allow building without libgcrypt * - check with sparse @@ -806,8 +805,7 @@ int journal_file_verify( goto fail; } - if (o->object.flags & OBJECT_COMPRESSED && - !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) { + if ((o->object.flags & OBJECT_COMPRESSED) && !JOURNAL_HEADER_COMPRESSED(f->header)) { log_error("Compressed object in file without compression at %llu", (unsigned long long) p); r = -EBADMSG; goto fail; @@ -828,7 +826,7 @@ int journal_file_verify( break; case OBJECT_ENTRY: - if ((le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED) && n_tags <= 0) { + if (JOURNAL_HEADER_SEALED(f->header) && n_tags <= 0) { log_error("First entry before first tag at %llu", (unsigned long long) p); r = -EBADMSG; goto fail; @@ -941,7 +939,7 @@ int journal_file_verify( case OBJECT_TAG: { uint64_t q, rt; - if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED)) { + if (!JOURNAL_HEADER_SEALED(f->header)) { log_error("Tag object in file without sealing at %llu", (unsigned long long) p); r = -EBADMSG; goto fail; -- cgit v1.2.3-54-g00ecf From feb12d3ed2c7f9132c64773c7c41b9e3a608a814 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 20 Aug 2012 16:51:46 +0200 Subject: journal: make libgcrypt dependency optional --- Makefile.am | 4 +-- README | 1 + TODO | 6 ---- configure.ac | 2 +- src/core/build.h | 20 ++++++++++- src/journal/journal-authenticate.c | 57 ++++++++++++++++++++++++++++-- src/journal/journal-authenticate.h | 2 +- src/journal/journal-file.c | 18 ++++++++++ src/journal/journal-verify.c | 71 ++++++-------------------------------- src/journal/journalctl.c | 30 +++++++++++----- src/journal/test-journal-verify.c | 7 ++-- src/journal/test-journal.c | 2 ++ 12 files changed, 134 insertions(+), 86 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/Makefile.am b/Makefile.am index 3eeb842afe..166c357db2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2402,8 +2402,6 @@ libsystemd_journal_la_SOURCES = \ src/journal/journal-vacuum.h \ src/journal/journal-verify.c \ src/journal/journal-verify.h \ - src/journal/journal-authenticate.c \ - src/journal/journal-authenticate.h \ src/journal/lookup3.c \ src/journal/lookup3.h \ src/journal/journal-send.c \ @@ -2455,6 +2453,8 @@ endif if HAVE_GCRYPT libsystemd_journal_la_SOURCES += \ + src/journal/journal-authenticate.c \ + src/journal/journal-authenticate.h \ src/journal/fsprg.c \ src/journal/fsprg.h diff --git a/README b/README index a5d569028d..334c597213 100644 --- a/README +++ b/README @@ -42,6 +42,7 @@ REQUIREMENTS: libcap PAM >= 1.1.2 (optional) libcryptsetup (optional) + libgcrypt (optional) libaudit (optional) libacl (optional) libselinux (optional) diff --git a/TODO b/TODO index 875a3db4d6..e16db6fa4d 100644 --- a/TODO +++ b/TODO @@ -254,8 +254,6 @@ Features: * cleanup syslog 'priority' vs. 'level' wording -* journal: if mmap() fails for mapping window try to unmap a a few older maps - * dbus upstream still refers to dbus.target and shouldn't * when a service has the same env var set twice we actually store it twice and return that in systemctl show -p... We should only show the last setting @@ -300,8 +298,6 @@ Features: * journal: message catalog -* journal: forward-secure signatures - * document the exit codes when services fail before they are exec()ed * systemctl journal command @@ -503,6 +499,4 @@ Scheduled for removal (or fixing): * xxxOverridable dependencies -* journald.conf: ImportKernel= - * prefdm.service diff --git a/configure.ac b/configure.ac index c1e88daa3f..3df43b91fd 100644 --- a/configure.ac +++ b/configure.ac @@ -302,7 +302,7 @@ AC_SUBST(ACL_LIBS) AM_CONDITIONAL([HAVE_ACL], [test "x$have_acl" != xno]) # ------------------------------------------------------------------------------ -AC_ARG_ENABLE([], +AC_ARG_ENABLE([gcrypt], AS_HELP_STRING([--disable-gcrypt],[Disable optional GCRYPT support]), [case "${enableval}" in yes) have_gcrypt=yes ;; diff --git a/src/core/build.h b/src/core/build.h index 0b38050bd4..4513a0bad7 100644 --- a/src/core/build.h +++ b/src/core/build.h @@ -63,4 +63,22 @@ #define _LIBCRYPTSETUP_FEATURE_ "-LIBCRYPTSETUP" #endif -#define SYSTEMD_FEATURES _PAM_FEATURE_ " " _LIBWRAP_FEATURE_ " " _AUDIT_FEATURE_ " " _SELINUX_FEATURE_ " " _IMA_FEATURE_ " " _SYSVINIT_FEATURE_ " " _LIBCRYPTSETUP_FEATURE_ +#ifdef HAVE_GCRYPT +#define _GCRYPT_FEATURE_ "+GCRYPT" +#else +#define _GCRYPT_FEATURE_ "-GCRYPT" +#endif + +#ifdef HAVE_ACL +#define _ACL_FEATURE_ "+ACL" +#else +#define _ACL_FEATURE_ "-ACL" +#endif + +#ifdef HAVE_XZ +#define _XZ_FEATURE_ "+XZ" +#else +#define _XZ_FEATURE_ "-XZ" +#endif + +#define SYSTEMD_FEATURES _PAM_FEATURE_ " " _LIBWRAP_FEATURE_ " " _AUDIT_FEATURE_ " " _SELINUX_FEATURE_ " " _IMA_FEATURE_ " " _SYSVINIT_FEATURE_ " " _LIBCRYPTSETUP_FEATURE_ " " _GCRYPT_FEATURE_ " " _ACL_FEATURE_ " " _XZ_FEATURE_ diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c index ddcf856aec..93cc9d94a1 100644 --- a/src/journal/journal-authenticate.c +++ b/src/journal/journal-authenticate.c @@ -461,8 +461,59 @@ int journal_file_append_first_tag(JournalFile *f) { return 0; } -bool journal_file_fss_enabled(JournalFile *f) { - assert(f); - return JOURNAL_HEADER_SEALED(f->header); +int journal_file_parse_verification_key(JournalFile *f, const char *key) { + uint8_t *seed; + size_t seed_size, c; + const char *k; + int r; + unsigned long long start, interval; + + seed_size = FSPRG_RECOMMENDED_SEEDLEN; + seed = malloc(seed_size); + if (!seed) + return -ENOMEM; + + k = key; + for (c = 0; c < seed_size; c++) { + int x, y; + + while (*k == '-') + k++; + + x = unhexchar(*k); + if (x < 0) { + free(seed); + return -EINVAL; + } + k++; + y = unhexchar(*k); + if (y < 0) { + free(seed); + return -EINVAL; + } + k++; + + seed[c] = (uint8_t) (x * 16 + y); + } + + if (*k != '/') { + free(seed); + return -EINVAL; + } + k++; + + r = sscanf(k, "%llx-%llx", &start, &interval); + if (r != 2) { + free(seed); + return -EINVAL; + } + + f->fsprg_seed = seed; + f->fsprg_seed_size = seed_size; + + f->fss_start_usec = start * interval; + f->fss_interval_usec = interval; + + return 0; } diff --git a/src/journal/journal-authenticate.h b/src/journal/journal-authenticate.h index 4f4f45b114..447c7b4657 100644 --- a/src/journal/journal-authenticate.h +++ b/src/journal/journal-authenticate.h @@ -36,7 +36,7 @@ int journal_file_hmac_put_header(JournalFile *f); int journal_file_hmac_put_object(JournalFile *f, int type, uint64_t p); int journal_file_fss_load(JournalFile *f); -bool journal_file_fss_enabled(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 epoch); diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 760efaebbb..f01f12496c 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -64,9 +64,11 @@ void journal_file_close(JournalFile *f) { assert(f); +#ifdef HAVE_GCRYPT /* Write the final tag */ if (f->seal && f->writable) journal_file_append_tag(f); +#endif /* Sync everything to disk, before we mark the file offline */ if (f->mmap && f->fd >= 0) @@ -764,9 +766,11 @@ static int journal_file_append_data( if (r < 0) return r; +#ifdef HAVE_GCRYPT r = journal_file_hmac_put_object(f, OBJECT_DATA, p); if (r < 0) return r; +#endif /* The linking might have altered the window, so let's * refresh our pointer */ @@ -852,9 +856,11 @@ static int link_entry_into_array(JournalFile *f, if (r < 0) return r; +#ifdef HAVE_GCRYPT r = journal_file_hmac_put_object(f, OBJECT_ENTRY_ARRAY, q); if (r < 0) return r; +#endif o->entry_array.items[i] = htole64(p); @@ -996,9 +1002,11 @@ static int journal_file_append_entry_internal( o->entry.xor_hash = htole64(xor_hash); o->entry.boot_id = f->header->boot_id; +#ifdef HAVE_GCRYPT r = journal_file_hmac_put_object(f, OBJECT_ENTRY, np); if (r < 0) return r; +#endif r = journal_file_link_entry(f, o, np); if (r < 0) @@ -1049,9 +1057,11 @@ int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const st ts->monotonic < le64toh(f->header->tail_entry_monotonic)) return -EINVAL; +#ifdef HAVE_GCRYPT r = journal_file_maybe_append_tag(f, ts->realtime); if (r < 0) return r; +#endif /* alloca() can't take 0, hence let's allocate at least one */ items = alloca(sizeof(EntryItem) * MAX(1, n_iovec)); @@ -2030,11 +2040,13 @@ int journal_file_open( if (f->last_stat.st_size == 0 && f->writable) { newly_created = true; +#ifdef HAVE_GCRYPT /* Try to load the FSPRG state, and if we can't, then * just don't do sealing */ r = journal_file_fss_load(f); if (r < 0) f->seal = false; +#endif r = journal_file_init_header(f, template); if (r < 0) @@ -2064,11 +2076,13 @@ int journal_file_open( goto fail; } +#ifdef HAVE_GCRYPT if (!newly_created && f->writable) { r = journal_file_fss_load(f); if (r < 0) goto fail; } +#endif if (f->writable) { if (metrics) { @@ -2082,9 +2096,11 @@ int journal_file_open( goto fail; } +#ifdef 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); @@ -2095,9 +2111,11 @@ int journal_file_open( if (r < 0) goto fail; +#ifdef HAVE_GCRYPT r = journal_file_append_first_tag(f); if (r < 0) goto fail; +#endif } r = journal_file_map_field_hash_table(f); diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 6afeab9a80..a76384bdb2 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "util.h" #include "macro.h" @@ -37,7 +38,6 @@ * * - evolve key even if nothing happened in regular intervals * - * - Allow building without libgcrypt * - check with sparse * - 64bit conversions * @@ -645,62 +645,6 @@ static int verify_entry_array( return 0; } -static int journal_file_parse_verification_key(JournalFile *f, const char *key) { - uint8_t *seed; - size_t seed_size, c; - const char *k; - int r; - unsigned long long start, interval; - - seed_size = FSPRG_RECOMMENDED_SEEDLEN; - seed = malloc(seed_size); - if (!seed) - return -ENOMEM; - - k = key; - for (c = 0; c < seed_size; c++) { - int x, y; - - while (*k == '-') - k++; - - x = unhexchar(*k); - if (x < 0) { - free(seed); - return -EINVAL; - } - k++; - y = unhexchar(*k); - if (y < 0) { - free(seed); - return -EINVAL; - } - k++; - - seed[c] = (uint8_t) (x * 16 + y); - } - - if (*k != '/') { - free(seed); - return -EINVAL; - } - k++; - - r = sscanf(k, "%llx-%llx", &start, &interval); - if (r != 2) { - free(seed); - return -EINVAL; - } - - f->fsprg_seed = seed; - f->fsprg_seed_size = seed_size; - - f->fss_start_usec = start * interval; - f->fss_interval_usec = interval; - - return 0; -} - int journal_file_verify( JournalFile *f, const char *key, @@ -724,11 +668,15 @@ int journal_file_verify( assert(f); if (key) { +#ifdef HAVE_GCRYPT r = journal_file_parse_verification_key(f, key); if (r < 0) { log_error("Failed to parse seed."); return r; } +#else + return -ENOTSUP; +#endif } else if (f->seal) return -ENOKEY; @@ -936,9 +884,7 @@ int journal_file_verify( n_entry_arrays++; break; - case OBJECT_TAG: { - uint64_t q, rt; - + case OBJECT_TAG: if (!JOURNAL_HEADER_SEALED(f->header)) { log_error("Tag object in file without sealing at %llu", (unsigned long long) p); r = -EBADMSG; @@ -957,7 +903,10 @@ int journal_file_verify( goto fail; } +#ifdef HAVE_GCRYPT if (f->seal) { + uint64_t q, rt; + log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum)); rt = f->fss_start_usec + o->tag.epoch * f->fss_interval_usec; @@ -1014,13 +963,13 @@ int journal_file_verify( last_tag_realtime = rt; last_sealed_realtime = entry_realtime; } +#endif last_tag = p + ALIGN64(le64toh(o->object.size)); last_epoch = le64toh(o->tag.epoch); n_tags ++; break; - } default: n_weird ++; diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index e61ddf6d92..551cb311b5 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -62,7 +62,9 @@ static bool arg_this_boot = false; static const char *arg_directory = NULL; static int arg_priorities = 0xFF; static const char *arg_verify_key = NULL; +#ifdef HAVE_GCRYPT static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC; +#endif static enum { ACTION_SHOW, @@ -93,11 +95,13 @@ static int help(void) { "Commands:\n" " --new-id128 Generate a new 128 Bit ID\n" " --header Show journal header information\n" +#ifdef HAVE_GCRYPT " --setup-keys Generate new FSS key pair\n" " --interval=TIME Time interval for changing the FSS sealing key\n" " --verify Verify journal file consistency\n" - " --verify-key=KEY Specify FSS verification key\n", - program_invocation_short_name); + " --verify-key=KEY Specify FSS verification key\n" +#endif + , program_invocation_short_name); return 0; } @@ -215,13 +219,15 @@ static int parse_argv(int argc, char *argv[]) { arg_action = ACTION_PRINT_HEADER; break; + case ARG_VERIFY: + arg_action = ACTION_VERIFY; + break; + +#ifdef HAVE_GCRYPT case ARG_SETUP_KEYS: arg_action = ACTION_SETUP_KEYS; break; - case ARG_VERIFY: - arg_action = ACTION_VERIFY; - break; case ARG_VERIFY_KEY: arg_action = ACTION_VERIFY; @@ -235,6 +241,13 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } break; +#else + case ARG_SETUP_KEYS: + case ARG_VERIFY_KEY: + case ARG_INTERVAL: + log_error("Forward-secure sealing not available."); + return -ENOTSUP; +#endif case 'p': { const char *dots; @@ -617,7 +630,8 @@ finish: return r; #else - log_error("Forward-secure journal verification not available."); + log_error("Forward-secure sealing not available."); + return -ENOTSUP; #endif } @@ -633,7 +647,7 @@ static int verify(sd_journal *j) { usec_t from, to, total; #ifdef HAVE_GCRYPT - if (!arg_verify_key && journal_file_fss_enabled(f)) + if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) log_warning("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path); #endif @@ -648,7 +662,7 @@ static int verify(sd_journal *j) { char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX]; log_info("PASS: %s", f->path); - if (arg_verify_key && journal_file_fss_enabled(f)) + if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) log_info("=> Validated from %s to %s, %s missing", format_timestamp(a, sizeof(a), from), format_timestamp(b, sizeof(b), to), diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c index ed6e21dc0c..b6677215c0 100644 --- a/src/journal/test-journal-verify.c +++ b/src/journal/test-journal-verify.c @@ -55,7 +55,7 @@ static int raw_verify(const char *fn, const char *verification_key) { JournalFile *f; int r; - r = journal_file_open(fn, O_RDONLY, 0666, true, true, NULL, NULL, NULL, &f); + r = journal_file_open(fn, O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, &f); if (r < 0) return r; @@ -107,18 +107,19 @@ int main(int argc, char *argv[]) { log_info("Verifying..."); - assert_se(journal_file_open("test.journal", O_RDONLY, 0666, true, true, NULL, NULL, NULL, &f) == 0); + assert_se(journal_file_open("test.journal", O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, &f) == 0); /* journal_file_print_header(f); */ journal_file_dump(f); assert_se(journal_file_verify(f, verification_key, &from, &to, &total, true) >= 0); - if (verification_key && journal_file_fss_enabled(f)) { + if (verification_key && JOURNAL_HEADER_SEALED(f->header)) { log_info("=> Validated from %s to %s, %s missing", format_timestamp(a, sizeof(a), from), format_timestamp(b, sizeof(b), to), format_timespan(c, sizeof(c), total > to ? total - to : 0)); } + journal_file_close(f); if (verification_key) { diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c index 05bb2ea8ed..2273500100 100644 --- a/src/journal/test-journal.c +++ b/src/journal/test-journal.c @@ -59,7 +59,9 @@ int main(int argc, char *argv[]) { iovec.iov_len = strlen(test); assert_se(journal_file_append_entry(f, &ts, &iovec, 1, NULL, NULL, NULL) == 0); +#ifdef HAVE_GCRYPT journal_file_append_tag(f); +#endif journal_file_dump(f); assert(journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p) == 1); -- cgit v1.2.3-54-g00ecf From 3e4b9b506d676d1cb8692306b38c05f8529e5cdb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 20 Aug 2012 19:21:19 +0200 Subject: journal: add missing endianess conversion --- src/journal/journal-authenticate.c | 2 +- src/journal/journal-verify.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c index 93cc9d94a1..435481000d 100644 --- a/src/journal/journal-authenticate.c +++ b/src/journal/journal-authenticate.c @@ -362,7 +362,7 @@ int journal_file_fss_load(JournalFile *f) { goto finish; } - if (le64toh(m->fsprg_state_size) != FSPRG_stateinbytes(m->fsprg_secpar)) { + if (le64toh(m->fsprg_state_size) != FSPRG_stateinbytes(le16toh(m->fsprg_secpar))) { r = -EBADMSG; goto finish; } diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index a76384bdb2..29a9229e53 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -39,7 +39,6 @@ * - evolve key even if nothing happened in regular intervals * * - check with sparse - * - 64bit conversions * * */ -- cgit v1.2.3-54-g00ecf From 89fef99014662a5a63e7deaedd6292b7fb4ab2f8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Aug 2012 01:29:17 +0200 Subject: journal: automatically evolve FSS key even when nothing is logged --- src/journal/journal-authenticate.c | 19 ++++++++++++++++++ src/journal/journal-authenticate.h | 2 ++ src/journal/journal-verify.c | 8 -------- src/journal/journalctl.c | 1 + src/journal/journald.c | 41 ++++++++++++++++++++++++++++++-------- 5 files changed, 55 insertions(+), 16 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c index 435481000d..fd81797cf9 100644 --- a/src/journal/journal-authenticate.c +++ b/src/journal/journal-authenticate.c @@ -211,6 +211,9 @@ int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) { if (!f->seal) return 0; + if (realtime <= 0) + realtime = now(CLOCK_MONOTONIC); + r = journal_file_fsprg_need_evolve(f, realtime); if (r <= 0) return 0; @@ -517,3 +520,19 @@ int journal_file_parse_verification_key(JournalFile *f, const char *key) { return 0; } + +bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u) { + uint64_t epoch; + + assert(f); + assert(u); + + if (!f->seal) + return false; + + epoch = FSPRG_GetEpoch(f->fsprg_state); + + *u = (usec_t) (f->fss_start_usec + f->fss_interval_usec * epoch + f->fss_interval_usec); + + return true; +} diff --git a/src/journal/journal-authenticate.h b/src/journal/journal-authenticate.h index 447c7b4657..3586464d1e 100644 --- a/src/journal/journal-authenticate.h +++ b/src/journal/journal-authenticate.h @@ -40,3 +40,5 @@ 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 epoch); + +bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u); diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 29a9229e53..8604b6e7cb 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -34,14 +34,6 @@ #include "compress.h" #include "fsprg.h" -/* FIXME: - * - * - evolve key even if nothing happened in regular intervals - * - * - check with sparse - * - * */ - static int journal_file_object_verify(JournalFile *f, Object *o) { uint64_t i; diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 1dee74a935..0bbf4a045d 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -233,6 +233,7 @@ static int parse_argv(int argc, char *argv[]) { case ARG_VERIFY_KEY: arg_action = ACTION_VERIFY; arg_verify_key = optarg; + arg_local = true; break; case ARG_INTERVAL: diff --git a/src/journal/journald.c b/src/journal/journald.c index 7b3b6471de..f2dd4050b6 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -48,6 +48,7 @@ #include "journal-rate-limit.h" #include "journal-internal.h" #include "journal-vacuum.h" +#include "journal-authenticate.h" #include "conf-parser.h" #include "journald.h" #include "virt.h" @@ -2969,8 +2970,26 @@ int main(int argc, char *argv[]) { for (;;) { struct epoll_event event; + int t; - r = epoll_wait(server.epoll_fd, &event, 1, -1); +#ifdef HAVE_GCRYPT + usec_t u; + + if (server.system_journal && + journal_file_next_evolve_usec(server.system_journal, &u)) { + usec_t n; + + n = now(CLOCK_MONOTONIC); + + if (n >= u) + t = 0; + else + t = (int) ((u - n + USEC_PER_MSEC - 1) / USEC_PER_MSEC); + } else +#endif + t = -1; + + r = epoll_wait(server.epoll_fd, &event, 1, t); if (r < 0) { if (errno == EINTR) @@ -2979,14 +2998,20 @@ int main(int argc, char *argv[]) { log_error("epoll_wait() failed: %m"); r = -errno; goto finish; - } else if (r == 0) - break; + } - r = process_event(&server, &event); - if (r < 0) - goto finish; - else if (r == 0) - break; + if (r > 0) { + r = process_event(&server, &event); + if (r < 0) + goto finish; + else if (r == 0) + break; + } + +#ifdef HAVE_GCRYPT + if (server.system_journal) + journal_file_maybe_append_tag(server.system_journal, 0); +#endif } log_debug("systemd-journald stopped as pid %lu", (unsigned long) getpid()); -- cgit v1.2.3-54-g00ecf From 369f0589218a874a88bc69c5481d8f90f987b7dd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Aug 2012 15:32:51 +0200 Subject: verify: optimize entry search a bit by using bisection --- src/journal/journal-verify.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 8604b6e7cb..90739a66c2 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -309,22 +309,46 @@ static int entry_points_to_data( * main entry array has already been verified we can rely on * its consistency.*/ + i = 0; n = le64toh(f->header->n_entries); a = le64toh(f->header->entry_array_offset); - i = 0; while (i < n) { - uint64_t m, j; + uint64_t m, u; r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); if (r < 0) return r; m = journal_file_entry_array_n_items(o); - for (j = 0; i < n && j < m; i++, j++) - if (le64toh(o->entry_array.items[j]) == entry_p) - return 0; + u = MIN(n - i, m); + + if (entry_p <= le64toh(o->entry_array.items[u-1])) { + uint64_t x, y, z; + + x = 0; + y = u; + + while (x < y) { + z = (x + y) / 2; + + if (le64toh(o->entry_array.items[z]) == entry_p) + return 0; + + if (x + 1 >= y) + break; + + if (entry_p < le64toh(o->entry_array.items[z])) + y = z; + else + x = z; + } + + log_error("Entry object doesn't exist in main entry array at %llu", (unsigned long long) entry_p); + return -EBADMSG; + } + i += u; a = le64toh(o->entry_array.next_entry_array_offset); } -- cgit v1.2.3-54-g00ecf From fcde238921b857679363a95488a5a5af1dc1f243 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Aug 2012 15:33:21 +0200 Subject: journal: be more careful when keeping around mmaps we still need --- src/journal/journal-file.c | 14 ++++----- src/journal/journal-verify.c | 5 ++- src/journal/mmap-cache.c | 73 +++++++++++++++----------------------------- src/journal/mmap-cache.h | 3 +- 4 files changed, 35 insertions(+), 60 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index f01f12496c..79f759897a 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -311,8 +311,6 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) if (r != 0) return -r; - mmap_cache_close_fd_range(f->mmap, f->fd, old_size); - if (fstat(f->fd, &f->last_stat) < 0) return -errno; @@ -321,7 +319,7 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) return 0; } -static int journal_file_move_to(JournalFile *f, int context, uint64_t offset, uint64_t size, void **ret) { +static int journal_file_move_to(JournalFile *f, int context, bool keep_always, uint64_t offset, uint64_t size, void **ret) { assert(f); assert(ret); @@ -335,7 +333,7 @@ static int journal_file_move_to(JournalFile *f, int context, uint64_t offset, ui return -EADDRNOTAVAIL; } - return mmap_cache_get(f->mmap, f->fd, f->prot, context, offset, size, ret); + return mmap_cache_get(f->mmap, f->fd, f->prot, context, keep_always, offset, size, &f->last_stat, ret); } static uint64_t minimum_header_size(Object *o) { @@ -373,7 +371,7 @@ int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Objec /* One context for each type, plus one catch-all for the rest */ context = type > 0 && type < _OBJECT_TYPE_MAX ? type : 0; - r = journal_file_move_to(f, context, offset, sizeof(ObjectHeader), &t); + r = journal_file_move_to(f, context, false, offset, sizeof(ObjectHeader), &t); if (r < 0) return r; @@ -393,7 +391,7 @@ int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Objec return -EBADMSG; if (s > sizeof(ObjectHeader)) { - r = journal_file_move_to(f, o->object.type, offset, s, &t); + r = journal_file_move_to(f, o->object.type, false, offset, s, &t); if (r < 0) return r; @@ -457,7 +455,7 @@ int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object * if (r < 0) return r; - r = journal_file_move_to(f, type, p, size, &t); + r = journal_file_move_to(f, type, false, p, size, &t); if (r < 0) return r; @@ -544,6 +542,7 @@ static int journal_file_map_data_hash_table(JournalFile *f) { r = journal_file_move_to(f, OBJECT_DATA_HASH_TABLE, + true, p, s, &t); if (r < 0) @@ -565,6 +564,7 @@ static int journal_file_map_field_hash_table(JournalFile *f) { r = journal_file_move_to(f, OBJECT_FIELD_HASH_TABLE, + true, p, s, &t); if (r < 0) diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 90739a66c2..9156fd5dc0 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -249,7 +249,7 @@ static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) { c = (a + b) / 2; - r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z); + r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z); if (r < 0) return r; @@ -261,9 +261,8 @@ static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) { if (p < *z) b = c; - else { + else a = c; - } } return 0; diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c index 69efb20ade..2b45e375c5 100644 --- a/src/journal/mmap-cache.c +++ b/src/journal/mmap-cache.c @@ -336,6 +336,7 @@ static int mmap_cache_make_room(MMapCache *m) { Window *v; v = m->windows + w; + assert(v->n_ref == 0); if (v->ptr) { mmap_cache_window_unmap(m, w); @@ -354,8 +355,10 @@ static int mmap_cache_put( unsigned fd_index, int prot, unsigned context, + bool keep_always, uint64_t offset, uint64_t size, + struct stat *st, void **ret) { unsigned w; @@ -387,6 +390,18 @@ static int mmap_cache_put( wsize = WINDOW_SIZE; } + if (st) { + /* Memory maps that are larger then the files + underneath have undefined behaviour. Hence, clamp + things to the file size if we know it */ + + if (woffset >= (uint64_t) st->st_size) + return -EADDRNOTAVAIL; + + if (woffset + wsize > (uint64_t) st->st_size) + wsize = PAGE_ALIGN(st->st_size - woffset); + } + for (;;) { d = mmap(NULL, wsize, prot, MAP_SHARED, fd, woffset); if (d != MAP_FAILED) @@ -413,8 +428,13 @@ static int mmap_cache_put( v->offset = woffset; v->size = wsize; - v->n_ref = 0; - mmap_cache_window_add_lru(m, w); + if (keep_always) + v->n_ref = 1; + else { + v->n_ref = 0; + mmap_cache_window_add_lru(m, w); + } + mmap_cache_fd_add(m, fd_index, w); mmap_cache_context_set(m, context, w); @@ -581,8 +601,10 @@ int mmap_cache_get( int fd, int prot, unsigned context, + bool keep_always, uint64_t offset, uint64_t size, + struct stat *st, void **ret) { unsigned fd_index; @@ -638,7 +660,7 @@ int mmap_cache_get( return r; /* Not found? Then, let's add it */ - return mmap_cache_put(m, fd, fd_index, prot, context, offset, size, ret); + return mmap_cache_put(m, fd, fd_index, prot, context, keep_always, offset, size, st, ret); } void mmap_cache_close_fd(MMapCache *m, int fd) { @@ -679,51 +701,6 @@ void mmap_cache_close_fd(MMapCache *m, int fd) { m->n_fds --; } -void mmap_cache_close_fd_range(MMapCache *m, int fd, uint64_t p) { - unsigned fd_index, c, w; - int r; - - assert(m); - assert(fd > 0); - - /* This drops all windows that include space right of the - * specified offset. This is useful to ensure that after the - * file size is extended we drop our mappings of the end and - * create it anew, since otherwise it is undefined whether - * mapping will continue to work as intended. */ - - r = mmap_cache_peek_fd_index(m, fd, &fd_index); - if (r <= 0) - return; - - for (c = 0; c < m->contexts_max; c++) { - w = m->by_context[c]; - - if (w != (unsigned) -1 && m->windows[w].fd == fd) - mmap_cache_context_unset(m, c); - } - - w = m->by_fd[fd_index].windows; - while (w != (unsigned) -1) { - Window *v; - - v = m->windows + w; - assert(v->fd == fd); - assert(v->by_fd_next == (unsigned) -1 || - m->windows[v->by_fd_next].fd == fd); - - if (v->offset + v->size > p) { - - mmap_cache_window_unmap(m, w); - mmap_cache_fd_remove(m, fd_index, w); - v->fd = -1; - - w = m->by_fd[fd_index].windows; - } else - w = v->by_fd_next; - } -} - void mmap_cache_close_context(MMapCache *m, unsigned context) { mmap_cache_context_unset(m, context); } diff --git a/src/journal/mmap-cache.h b/src/journal/mmap-cache.h index aae4544357..98a1a731f8 100644 --- a/src/journal/mmap-cache.h +++ b/src/journal/mmap-cache.h @@ -29,7 +29,6 @@ MMapCache* mmap_cache_new(void); MMapCache* mmap_cache_ref(MMapCache *m); MMapCache* mmap_cache_unref(MMapCache *m); -int mmap_cache_get(MMapCache *m, int fd, int prot, unsigned context, uint64_t offset, uint64_t size, void **ret); +int mmap_cache_get(MMapCache *m, int fd, int prot, unsigned context, bool keep_always, uint64_t offset, uint64_t size, struct stat *st, void **ret); void mmap_cache_close_fd(MMapCache *m, int fd); -void mmap_cache_close_fd_range(MMapCache *m, int fd, uint64_t range); void mmap_cache_close_context(MMapCache *m, unsigned context); -- cgit v1.2.3-54-g00ecf From c0ca7aeec963207b6fa5ee39bd204cb26cba4023 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Aug 2012 23:03:20 +0200 Subject: journalctl: be more friendly when informing about seal verification --- TODO | 6 ++++++ src/journal/journal-verify.c | 2 +- src/journal/journalctl.c | 17 ++++++++++++----- 3 files changed, 19 insertions(+), 6 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/TODO b/TODO index 56e264ce42..7f53535401 100644 --- a/TODO +++ b/TODO @@ -49,6 +49,12 @@ Bugfixes: Features: +* cleanup ellipsation for log output in journalctl and systemctl status: have a sane way to disable ellipsation, and disable it by default when invoked in less/more + +* enforce limits on fds openened by socket units + +* proper service failure code for services which hit the restart limit + * explore multiple service instances per listening socket idea * testing tool for socket activation: some binary that listens on a socket and passes it on using the usual socket activation protocol to some server. diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 9156fd5dc0..24012931d4 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -1118,7 +1118,7 @@ int journal_file_verify( close_nointr_nofail(entry_array_fd); if (first_validated) - *first_validated = last_tag_realtime ? le64toh(f->header->head_entry_realtime) : 0; + *first_validated = last_sealed_realtime > 0 ? le64toh(f->header->head_entry_realtime) : 0; if (last_validated) *last_validated = last_sealed_realtime; if (last_contained) diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 36c3d2b688..bee26fd769 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -681,11 +681,18 @@ static int verify(sd_journal *j) { char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX]; log_info("PASS: %s", f->path); - if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) - log_info("=> Validated from %s to %s, %s missing", - format_timestamp(a, sizeof(a), from), - format_timestamp(b, sizeof(b), to), - format_timespan(c, sizeof(c), total > to ? total - to : 0)); + if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) { + if (from > 0) { + log_info("=> Validated from %s to %s, final %s entries not sealed.", + format_timestamp(a, sizeof(a), from), + format_timestamp(b, sizeof(b), to), + format_timespan(c, sizeof(c), total > to ? total - to : 0)); + } else if (total > 0) + log_info("=> No sealing yet, %s of entries not sealed.", + format_timespan(c, sizeof(c), total)); + else + log_info("=> No sealing yet, no entries in file."); + } } } -- cgit v1.2.3-54-g00ecf From 70f09301d5d47577cfa4fe70c7b3cba336529cdf Mon Sep 17 00:00:00 2001 From: Yin Kangkai Date: Fri, 24 Aug 2012 16:52:58 +0800 Subject: journal: add HAVE_XZ check to avoid build failure Without this, build fail with --disable-xz or xz not installed. --- src/journal/journal-verify.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 24012931d4..5d134bdb97 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -63,6 +63,7 @@ static int journal_file_object_verify(JournalFile *f, Object *o) { h1 = le64toh(o->data.hash); if (o->object.flags & OBJECT_COMPRESSED) { +#ifdef HAVE_XZ void *b = NULL; uint64_t alloc = 0, b_size; @@ -73,6 +74,9 @@ static int journal_file_object_verify(JournalFile *f, Object *o) { h2 = hash64(b, b_size); free(b); +#else + return -EPROTONOSUPPORT; +#endif } else h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload)); -- cgit v1.2.3-54-g00ecf From 5996c7c295e073ce21d41305169132c8aa993ad0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 13 Sep 2012 17:06:04 +0200 Subject: journald: don't reposition window if we don't have to --- src/journal/journal-authenticate.c | 20 ++++++++++++-------- src/journal/journal-authenticate.h | 2 +- src/journal/journal-file.c | 16 ++++++++-------- src/journal/journal-verify.c | 2 +- 4 files changed, 22 insertions(+), 18 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c index 593bf7eb2a..674f81218f 100644 --- a/src/journal/journal-authenticate.c +++ b/src/journal/journal-authenticate.c @@ -66,7 +66,7 @@ int journal_file_append_tag(JournalFile *f) { /* 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, p); + r = journal_file_hmac_put_object(f, OBJECT_TAG, o, p); if (r < 0) return r; @@ -229,9 +229,8 @@ int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) { return 0; } -int journal_file_hmac_put_object(JournalFile *f, int type, uint64_t p) { +int journal_file_hmac_put_object(JournalFile *f, int type, Object *o, uint64_t p) { int r; - Object *o; assert(f); @@ -242,9 +241,14 @@ int journal_file_hmac_put_object(JournalFile *f, int type, uint64_t p) { if (r < 0) return r; - r = journal_file_move_to_object(f, type, p, &o); - 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 >= 0 && o->object.type != type) + return -EBADMSG; + } gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload)); @@ -460,7 +464,7 @@ int journal_file_append_first_tag(JournalFile *f) { return -EINVAL; p -= offsetof(Object, hash_table.items); - r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, p); + r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, NULL, p); if (r < 0) return r; @@ -469,7 +473,7 @@ int journal_file_append_first_tag(JournalFile *f) { return -EINVAL; p -= offsetof(Object, hash_table.items); - r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, p); + r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, NULL, p); if (r < 0) return r; diff --git a/src/journal/journal-authenticate.h b/src/journal/journal-authenticate.h index 3586464d1e..0aaf836721 100644 --- a/src/journal/journal-authenticate.h +++ b/src/journal/journal-authenticate.h @@ -33,7 +33,7 @@ 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, int type, uint64_t p); +int journal_file_hmac_put_object(JournalFile *f, int type, Object *o, uint64_t p); int journal_file_fss_load(JournalFile *f); int journal_file_parse_verification_key(JournalFile *f, const char *key); diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 8016852b91..e55162ab2c 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -775,18 +775,18 @@ static int journal_file_append_data( if (r < 0) return r; -#ifdef HAVE_GCRYPT - r = journal_file_hmac_put_object(f, OBJECT_DATA, p); - if (r < 0) - return r; -#endif - /* The linking might have altered the window, so let's * refresh our pointer */ r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); if (r < 0) return r; +#ifdef HAVE_GCRYPT + r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p); + if (r < 0) + return r; +#endif + if (ret) *ret = o; @@ -866,7 +866,7 @@ static int link_entry_into_array(JournalFile *f, return r; #ifdef HAVE_GCRYPT - r = journal_file_hmac_put_object(f, OBJECT_ENTRY_ARRAY, q); + r = journal_file_hmac_put_object(f, OBJECT_ENTRY_ARRAY, o, q); if (r < 0) return r; #endif @@ -1012,7 +1012,7 @@ static int journal_file_append_entry_internal( o->entry.boot_id = f->header->boot_id; #ifdef HAVE_GCRYPT - r = journal_file_hmac_put_object(f, OBJECT_ENTRY, np); + r = journal_file_hmac_put_object(f, OBJECT_ENTRY, o, np); if (r < 0) return r; #endif diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 5d134bdb97..629b2389bc 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -959,7 +959,7 @@ int journal_file_verify( if (r < 0) goto fail; - r = journal_file_hmac_put_object(f, -1, q); + r = journal_file_hmac_put_object(f, -1, o, q); if (r < 0) goto fail; -- cgit v1.2.3-54-g00ecf From 2a7b539a82a45202c0e990c45da25ccdc487d633 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Sep 2012 15:02:43 +0200 Subject: journald: always pass first entry timestamp back from journal_file_verify() --- src/journal/journal-verify.c | 6 +++--- src/journal/journal-verify.h | 2 +- src/journal/journalctl.c | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 629b2389bc..7d2e5eb1c2 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -666,7 +666,7 @@ static int verify_entry_array( int journal_file_verify( JournalFile *f, const char *key, - usec_t *first_validated, usec_t *last_validated, usec_t *last_contained, + usec_t *first_contained, usec_t *last_validated, usec_t *last_contained, bool show_progress) { int r; Object *o; @@ -1121,8 +1121,8 @@ int journal_file_verify( close_nointr_nofail(entry_fd); close_nointr_nofail(entry_array_fd); - if (first_validated) - *first_validated = last_sealed_realtime > 0 ? le64toh(f->header->head_entry_realtime) : 0; + if (first_contained) + *first_contained = le64toh(f->header->head_entry_realtime); if (last_validated) *last_validated = last_sealed_realtime; if (last_contained) diff --git a/src/journal/journal-verify.h b/src/journal/journal-verify.h index a4633adea9..e392ab61d7 100644 --- a/src/journal/journal-verify.h +++ b/src/journal/journal-verify.h @@ -23,4 +23,4 @@ #include "journal-file.h" -int journal_file_verify(JournalFile *f, const char *key, usec_t *first_validated, usec_t *last_validated, usec_t *last_contained, bool show_progress); +int journal_file_verify(JournalFile *f, const char *key, usec_t *first_contained, usec_t *last_validated, usec_t *last_contained, bool show_progress); diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index b9be0c6ef7..62bdcb7c68 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -684,14 +684,14 @@ static int verify(sd_journal *j) { HASHMAP_FOREACH(f, j->files, i) { int k; - usec_t from, to, total; + usec_t first, validated, last; #ifdef 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, &from, &to, &total, true); + k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true); if (k == -EINVAL) { /* If the key was invalid give up right-away. */ return k; @@ -703,14 +703,14 @@ static int verify(sd_journal *j) { log_info("PASS: %s", f->path); if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) { - if (from > 0) { + if (validated > 0) { log_info("=> Validated from %s to %s, final %s entries not sealed.", - format_timestamp(a, sizeof(a), from), - format_timestamp(b, sizeof(b), to), - format_timespan(c, sizeof(c), total > to ? total - to : 0)); - } else if (total > 0) + format_timestamp(a, sizeof(a), first), + format_timestamp(b, sizeof(b), validated), + format_timespan(c, sizeof(c), last > validated ? last - validated : 0)); + } else if (last > 0) log_info("=> No sealing yet, %s of entries not sealed.", - format_timespan(c, sizeof(c), total - f->header->head_entry_realtime)); + format_timespan(c, sizeof(c), last - first)); else log_info("=> No sealing yet, no entries in file."); } -- cgit v1.2.3-54-g00ecf From 0ab5c3ed8750734124c7a1f7fb8a7944f71d6e25 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Sat, 13 Oct 2012 17:50:53 +0000 Subject: journal-verify: get rid of an unused variable When compiling without gcrypt, gcc emits an annoying warning. --- src/journal/journal-verify.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 7d2e5eb1c2..551f04af35 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -670,7 +670,8 @@ int journal_file_verify( bool show_progress) { int r; Object *o; - uint64_t p = 0, last_tag = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0; + uint64_t p = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0; + uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0; sd_id128_t entry_boot_id; bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false; @@ -682,7 +683,9 @@ int journal_file_verify( entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX"; unsigned i; bool found_last; - +#ifdef HAVE_GCRYPT + uint64_t last_tag = 0; +#endif assert(f); if (key) { @@ -981,9 +984,10 @@ int journal_file_verify( last_tag_realtime = rt; last_sealed_realtime = entry_realtime; } -#endif last_tag = p + ALIGN64(le64toh(o->object.size)); +#endif + last_epoch = le64toh(o->tag.epoch); n_tags ++; -- cgit v1.2.3-54-g00ecf From 8481248b9fbddc6d5e6ff26eb23505ef13dc85f7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Oct 2012 23:59:41 +0200 Subject: util: unify usage of on_tty() in util.c --- TODO | 2 ++ src/journal/journal-verify.c | 4 ++-- src/journal/journalctl.c | 21 ++------------------- src/login/loginctl.c | 17 ----------------- src/shared/pager.c | 2 +- src/shared/util.c | 16 +++++++++++----- src/shared/util.h | 1 + src/systemctl/systemctl.c | 18 ------------------ 8 files changed, 19 insertions(+), 62 deletions(-) (limited to 'src/journal/journal-verify.c') diff --git a/TODO b/TODO index bb7d8eea21..1c33ead227 100644 --- a/TODO +++ b/TODO @@ -19,6 +19,8 @@ F18: Features: +* drop --follow from systemctl + * don't show cgroup in "systemctl status" if empty/non-existant, especially for foreign .mount units * timedated: export boolean that clarifies whether NTP is even available diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 551f04af35..1a67d5a04b 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -178,7 +178,7 @@ static void draw_progress(uint64_t p, usec_t *last_usec) { unsigned n, i, j, k; usec_t z, x; - if (!isatty(STDOUT_FILENO)) + if (!on_tty()) return; z = now(CLOCK_MONOTONIC); @@ -212,7 +212,7 @@ static void draw_progress(uint64_t p, usec_t *last_usec) { static void flush_progress(void) { unsigned n, i; - if (!isatty(STDOUT_FILENO)) + if (!on_tty()) return; n = (3 * columns()) / 4; diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index d6f19e900a..f4b6518557 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -400,21 +400,6 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -static bool on_tty(void) { - static int t = -1; - - /* Note that this is invoked relatively early, before we start - * the pager. That means the value we return reflects whether - * we originally were started on a tty, not if we currently - * are. But this is intended, since we want colour and so on - * when run in our own pager. */ - - if (_unlikely_(t < 0)) - t = isatty(STDOUT_FILENO) > 0; - - return t; -} - static int generate_new_id128(void) { sd_id128_t id; int r; @@ -697,7 +682,7 @@ static int setup_keys(void) { goto finish; } - if (isatty(STDOUT_FILENO)) { + if (on_tty()) { fprintf(stderr, "\n" "The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n" @@ -719,7 +704,7 @@ static int setup_keys(void) { printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval); - if (isatty(STDOUT_FILENO)) { + if (on_tty()) { char tsb[FORMAT_TIMESPAN_MAX], *hn; fprintf(stderr, @@ -982,8 +967,6 @@ int main(int argc, char *argv[]) { goto finish; } - on_tty(); - if (!arg_no_pager && !arg_follow) pager_open(); diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 97c6617d7d..24941fef34 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -50,26 +50,9 @@ static enum transport { static bool arg_ask_password = true; static const char *arg_host = NULL; -static bool on_tty(void) { - static int t = -1; - - /* Note that this is invoked relatively early, before we start - * the pager. That means the value we return reflects whether - * we originally were started on a tty, not if we currently - * are. But this is intended, since we want colour and so on - * when run in our own pager. */ - - if (_unlikely_(t < 0)) - t = isatty(STDOUT_FILENO) > 0; - - return t; -} - static void pager_open_if_enabled(void) { /* Cache result before we open the pager */ - on_tty(); - if (arg_no_pager) return; diff --git a/src/shared/pager.c b/src/shared/pager.c index 6799787e85..488a12c763 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -57,7 +57,7 @@ int pager_open(void) { if (!*pager || streq(pager, "cat")) return 0; - if (isatty(STDOUT_FILENO) <= 0) + if (!on_tty()) return 0; /* Determine and cache number of columns before we spawn the diff --git a/src/shared/util.c b/src/shared/util.c index 462b541b41..527a5800fe 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -2175,28 +2175,25 @@ int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) { } int ask(char *ret, const char *replies, const char *text, ...) { - bool on_tty; assert(ret); assert(replies); assert(text); - on_tty = isatty(STDOUT_FILENO); - for (;;) { va_list ap; char c; int r; bool need_nl = true; - if (on_tty) + if (on_tty()) fputs(ANSI_HIGHLIGHT_ON, stdout); va_start(ap, text); vprintf(text, ap); va_end(ap); - if (on_tty) + if (on_tty()) fputs(ANSI_HIGHLIGHT_OFF, stdout); fflush(stdout); @@ -3820,6 +3817,15 @@ void columns_cache_reset(int signum) { cached_columns = 0; } +bool on_tty(void) { + static int cached_on_tty = -1; + + if (_unlikely_(cached_on_tty < 0)) + cached_on_tty = isatty(STDOUT_FILENO) > 0; + + return cached_on_tty; +} + int fd_lines(int fd) { struct winsize ws; zero(ws); diff --git a/src/shared/util.h b/src/shared/util.h index 662c3d1f39..e19f76c1ea 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -385,6 +385,7 @@ int status_welcome(void); int fd_columns(int fd); unsigned columns(void); void columns_cache_reset(int _unused_ signum); +bool on_tty(void); int fd_lines(int fd); unsigned lines(void); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 82c801e4d9..d77d534e0b 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -132,26 +132,8 @@ static bool private_bus = false; static int daemon_reload(DBusConnection *bus, char **args); static void halt_now(enum action a); -static bool on_tty(void) { - static int t = -1; - - /* Note that this is invoked relatively early, before we start - * the pager. That means the value we return reflects whether - * we originally were started on a tty, not if we currently - * are. But this is intended, since we want colour and so on - * when run in our own pager. */ - - if (_unlikely_(t < 0)) - t = isatty(STDOUT_FILENO) > 0; - - return t; -} - static void pager_open_if_enabled(void) { - /* Cache result before we open the pager */ - on_tty(); - if (arg_no_pager) return; -- cgit v1.2.3-54-g00ecf