1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include "journal-def.h"
29 #include "journal-file.h"
30 #include "journal-authenticate.h"
31 #include "journal-verify.h"
38 * - Allow building without libgcrypt
44 static int journal_file_object_verify(JournalFile *f, Object *o) {
48 /* This does various superficial tests about the length an
49 * possible field values. It does not follow any references to
52 if ((o->object.flags & OBJECT_COMPRESSED) &&
53 o->object.type != OBJECT_DATA)
56 switch (o->object.type) {
61 if (le64toh(o->data.entry_offset) <= 0 ||
62 le64toh(o->data.n_entries) <= 0)
65 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
68 h1 = le64toh(o->data.hash);
70 if (o->object.flags & OBJECT_COMPRESSED) {
72 uint64_t alloc = 0, b_size;
74 if (!uncompress_blob(o->data.payload,
75 le64toh(o->object.size) - offsetof(Object, data.payload),
79 h2 = hash64(b, b_size);
82 h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
91 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
96 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
99 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
102 if (le64toh(o->entry.seqnum) <= 0 ||
103 le64toh(o->entry.realtime) <= 0)
108 case OBJECT_DATA_HASH_TABLE:
109 case OBJECT_FIELD_HASH_TABLE:
110 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0)
113 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
118 case OBJECT_ENTRY_ARRAY:
119 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0)
122 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
128 if (le64toh(o->object.size) != sizeof(TagObject))
136 static void draw_progress(uint64_t p, usec_t *last_usec) {
140 if (!isatty(STDOUT_FILENO))
143 z = now(CLOCK_MONOTONIC);
146 if (x != 0 && x + 40 * USEC_PER_MSEC > z)
151 n = (3 * columns()) / 4;
152 j = (n * (unsigned) p) / 65535ULL;
155 fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
157 for (i = 0; i < j; i++)
158 fputs("\xe2\x96\x88", stdout);
160 fputs(ANSI_HIGHLIGHT_OFF, stdout);
162 for (i = 0; i < k; i++)
163 fputs("\xe2\x96\x91", stdout);
165 printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU);
167 fputs("\r\x1B[?25h", stdout);
171 static void flush_progress(void) {
174 if (!isatty(STDOUT_FILENO))
177 n = (3 * columns()) / 4;
181 for (i = 0; i < n + 5; i++)
188 static int write_uint64(int fd, uint64_t p) {
191 k = write(fd, &p, sizeof(p));
200 static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
215 r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z);
231 static int entry_points_to_data(
244 assert(entry_fd >= 0);
246 if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
247 log_error("Data object references invalid entry at %llu", (unsigned long long) data_p);
251 r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
255 n = journal_file_entry_n_items(o);
256 for (i = 0; i < n; i++)
257 if (le64toh(o->entry.items[i].object_offset) == data_p) {
263 log_error("Data object not referenced by linked entry at %llu", (unsigned long long) data_p);
267 /* Check if this entry is also in main entry array. Since the
268 * main entry array has already been verified we can rely on
271 n = le64toh(f->header->n_entries);
272 a = le64toh(f->header->entry_array_offset);
278 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
282 m = journal_file_entry_array_n_items(o);
283 for (j = 0; i < n && j < m; i++, j++)
284 if (le64toh(o->entry_array.items[j]) == entry_p)
287 a = le64toh(o->entry_array.next_entry_array_offset);;
293 static int verify_data(
295 Object *o, uint64_t p,
296 int entry_fd, uint64_t n_entries,
297 int entry_array_fd, uint64_t n_entry_arrays) {
299 uint64_t i, n, a, last, q;
304 assert(entry_fd >= 0);
305 assert(entry_array_fd >= 0);
307 n = le64toh(o->data.n_entries);
308 a = le64toh(o->data.entry_array_offset);
310 /* We already checked this earlier */
313 last = q = le64toh(o->data.entry_offset);
314 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
322 log_error("Array chain too short at %llu.", (unsigned long long) p);
326 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
327 log_error("Invalid array at %llu.", (unsigned long long) p);
331 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
335 next = le64toh(o->entry_array.next_entry_array_offset);
336 if (next != 0 && next <= a) {
337 log_error("Array chain has cycle at %llu.", (unsigned long long) p);
341 m = journal_file_entry_array_n_items(o);
342 for (j = 0; i < n && j < m; i++, j++) {
344 q = le64toh(o->entry_array.items[j]);
346 log_error("Data object's entry array not sorted at %llu.", (unsigned long long) p);
351 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
355 /* Pointer might have moved, reposition */
356 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
367 static int verify_hash_table(
369 int data_fd, uint64_t n_data,
370 int entry_fd, uint64_t n_entries,
371 int entry_array_fd, uint64_t n_entry_arrays,
378 assert(data_fd >= 0);
379 assert(entry_fd >= 0);
380 assert(entry_array_fd >= 0);
383 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
384 for (i = 0; i < n; i++) {
385 uint64_t last = 0, p;
387 draw_progress(0xC000 + (0x3FFF * i / n), last_usec);
389 p = le64toh(f->data_hash_table[i].head_hash_offset);
394 if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
395 log_error("Invalid data object at hash entry %llu of %llu.",
396 (unsigned long long) i, (unsigned long long) n);
400 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
404 next = le64toh(o->data.next_hash_offset);
405 if (next != 0 && next <= p) {
406 log_error("Hash chain has a cycle in hash entry %llu of %llu.",
407 (unsigned long long) i, (unsigned long long) n);
411 if (le64toh(o->data.hash) % n != i) {
412 log_error("Hash value mismatch in hash entry %llu of %llu.",
413 (unsigned long long) i, (unsigned long long) n);
417 r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
425 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
426 log_error("Tail hash pointer mismatch in hash table.");
434 static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
439 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
442 q = le64toh(f->data_hash_table[h].head_hash_offset);
449 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
453 q = le64toh(o->data.next_hash_offset);
459 static int verify_entry(
461 Object *o, uint64_t p,
462 int data_fd, uint64_t n_data) {
469 assert(data_fd >= 0);
471 n = journal_file_entry_n_items(o);
472 for (i = 0; i < n; i++) {
476 q = le64toh(o->entry.items[i].object_offset);
477 h = le64toh(o->entry.items[i].hash);
479 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
480 log_error("Invalid data object at entry %llu.",
481 (unsigned long long) o);
485 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
489 if (le64toh(u->data.hash) != h) {
490 log_error("Hash mismatch for data object at entry %llu.",
491 (unsigned long long) p);
495 r = data_object_in_hash_table(f, h, q);
499 log_error("Data object missing from hash at entry %llu.",
500 (unsigned long long) p);
508 static int verify_entry_array(
510 int data_fd, uint64_t n_data,
511 int entry_fd, uint64_t n_entries,
512 int entry_array_fd, uint64_t n_entry_arrays,
515 uint64_t i = 0, a, n, last = 0;
519 assert(data_fd >= 0);
520 assert(entry_fd >= 0);
521 assert(entry_array_fd >= 0);
524 n = le64toh(f->header->n_entries);
525 a = le64toh(f->header->entry_array_offset);
530 draw_progress(0x8000 + (0x3FFF * i / n), last_usec);
533 log_error("Array chain too short at %llu of %llu.",
534 (unsigned long long) i, (unsigned long long) n);
538 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
539 log_error("Invalid array at %llu of %llu.",
540 (unsigned long long) i, (unsigned long long) n);
544 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
548 next = le64toh(o->entry_array.next_entry_array_offset);
549 if (next != 0 && next <= a) {
550 log_error("Array chain has cycle at %llu of %llu.",
551 (unsigned long long) i, (unsigned long long) n);
555 m = journal_file_entry_array_n_items(o);
556 for (j = 0; i < n && j < m; i++, j++) {
559 p = le64toh(o->entry_array.items[j]);
561 log_error("Entry array not sorted at %llu of %llu.",
562 (unsigned long long) i, (unsigned long long) n);
567 if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
568 log_error("Invalid array entry at %llu of %llu.",
569 (unsigned long long) i, (unsigned long long) n);
573 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
577 r = verify_entry(f, o, p, data_fd, n_data);
581 /* Pointer might have moved, reposition */
582 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
593 int journal_file_verify(JournalFile *f, const char *key) {
597 uint64_t tag_seqnum = 0, entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
598 sd_id128_t entry_boot_id;
599 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
600 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;
601 usec_t last_usec = 0;
602 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
603 char data_path[] = "/var/tmp/journal-data-XXXXXX",
604 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
605 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
609 data_fd = mkostemp(data_path, O_CLOEXEC);
611 log_error("Failed to create data file: %m");
616 entry_fd = mkostemp(entry_path, O_CLOEXEC);
618 log_error("Failed to create entry file: %m");
623 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
624 if (entry_array_fd < 0) {
625 log_error("Failed to create entry array file: %m");
628 unlink(entry_array_path);
630 /* First iteration: we go through all objects, verify the
631 * superficial structure, headers, hashes. */
633 r = journal_file_hmac_put_header(f);
635 log_error("Failed to calculate HMAC of header.");
639 p = le64toh(f->header->header_size);
641 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
643 r = journal_file_move_to_object(f, -1, p, &o);
645 log_error("Invalid object at %llu", (unsigned long long) p);
649 if (le64toh(f->header->tail_object_offset) < p) {
650 log_error("Invalid tail object pointer.");
657 r = journal_file_object_verify(f, o);
659 log_error("Invalid object contents at %llu", (unsigned long long) p);
663 if (o->object.flags & OBJECT_COMPRESSED &&
664 !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
665 log_error("Compressed object without compression at %llu", (unsigned long long) p);
670 r = journal_file_hmac_put_object(f, -1, p);
672 log_error("Failed to calculate HMAC at %llu", (unsigned long long) p);
676 if (o->object.type == OBJECT_TAG) {
678 if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) {
679 log_error("Tag object without authentication at %llu", (unsigned long long) p);
684 if (le64toh(o->tag.seqnum) != tag_seqnum) {
685 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
690 } else if (o->object.type == OBJECT_ENTRY) {
692 r = write_uint64(entry_fd, p);
696 if (!entry_seqnum_set &&
697 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
698 log_error("Head entry sequence number incorrect");
703 if (entry_seqnum_set &&
704 entry_seqnum >= le64toh(o->entry.seqnum)) {
705 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
710 entry_seqnum = le64toh(o->entry.seqnum);
711 entry_seqnum_set = true;
713 if (entry_monotonic_set &&
714 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
715 entry_monotonic > le64toh(o->entry.monotonic)) {
716 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
721 entry_monotonic = le64toh(o->entry.monotonic);
722 entry_boot_id = o->entry.boot_id;
723 entry_monotonic_set = true;
725 if (!entry_realtime_set &&
726 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
727 log_error("Head entry realtime timestamp incorrect");
732 entry_realtime = le64toh(o->entry.realtime);
733 entry_realtime_set = true;
736 } else if (o->object.type == OBJECT_ENTRY_ARRAY) {
738 r = write_uint64(entry_array_fd, p);
742 if (p == le64toh(f->header->entry_array_offset)) {
743 if (found_main_entry_array) {
744 log_error("More than one main entry array at %llu", (unsigned long long) p);
749 found_main_entry_array = true;
754 } else if (o->object.type == OBJECT_DATA) {
756 r = write_uint64(data_fd, p);
762 } else if (o->object.type == OBJECT_FIELD)
764 else if (o->object.type == OBJECT_DATA_HASH_TABLE) {
765 n_data_hash_tables++;
767 if (n_data_hash_tables > 1) {
768 log_error("More than one data hash table at %llu", (unsigned long long) p);
773 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
774 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
775 log_error("Header fields for data hash table invalid.");
779 } else if (o->object.type == OBJECT_FIELD_HASH_TABLE) {
780 n_field_hash_tables++;
782 if (n_field_hash_tables > 1) {
783 log_error("More than one field hash table at %llu", (unsigned long long) p);
788 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
789 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
790 log_error("Header fields for field hash table invalid.");
794 } else if (o->object.type >= _OBJECT_TYPE_MAX)
797 if (p == le64toh(f->header->tail_object_offset))
800 p = p + ALIGN64(le64toh(o->object.size));
803 if (n_objects != le64toh(f->header->n_objects)) {
804 log_error("Object number mismatch");
809 if (n_entries != le64toh(f->header->n_entries)) {
810 log_error("Entry number mismatch");
815 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
816 n_data != le64toh(f->header->n_data)) {
817 log_error("Data number mismatch");
822 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
823 n_fields != le64toh(f->header->n_fields)) {
824 log_error("Field number mismatch");
829 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
830 tag_seqnum != le64toh(f->header->n_tags)) {
831 log_error("Tag number mismatch");
836 if (n_data_hash_tables != 1) {
837 log_error("Missing data hash table");
842 if (n_field_hash_tables != 1) {
843 log_error("Missing field hash table");
848 if (!found_main_entry_array) {
849 log_error("Missing entry array");
854 if (entry_seqnum_set &&
855 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
856 log_error("Invalid tail seqnum");
861 if (entry_monotonic_set &&
862 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
863 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
864 log_error("Invalid tail monotonic timestamp");
869 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
870 log_error("Invalid tail realtime timestamp");
875 /* Second iteration: we follow all objects referenced from the
876 * two entry points: the object hash table and the entry
877 * array. We also check that everything referenced (directly
878 * or indirectly) in the data hash table also exists in the
879 * entry array, and vice versa. Note that we do not care for
880 * unreferenced objects. We only care that everything that is
881 * referenced is consistent. */
883 r = verify_entry_array(f,
886 entry_array_fd, n_entry_arrays,
891 r = verify_hash_table(f,
894 entry_array_fd, n_entry_arrays,
901 mmap_cache_close_fd(f->mmap, data_fd);
902 mmap_cache_close_fd(f->mmap, entry_fd);
903 mmap_cache_close_fd(f->mmap, entry_array_fd);
905 close_nointr_nofail(data_fd);
906 close_nointr_nofail(entry_fd);
907 close_nointr_nofail(entry_array_fd);
914 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
916 (unsigned long long) p,
917 (unsigned long long) f->last_stat.st_size,
918 (unsigned long long) (100 * p / f->last_stat.st_size));
921 mmap_cache_close_fd(f->mmap, data_fd);
922 close_nointr_nofail(data_fd);
926 mmap_cache_close_fd(f->mmap, entry_fd);
927 close_nointr_nofail(entry_fd);
930 if (entry_array_fd >= 0) {
931 mmap_cache_close_fd(f->mmap, entry_array_fd);
932 close_nointr_nofail(entry_array_fd);