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);
323 log_error("Array chain too short at %llu.", (unsigned long long) p);
327 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
328 log_error("Invalid array at %llu.", (unsigned long long) p);
332 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
336 next = le64toh(o->entry_array.next_entry_array_offset);
337 if (next != 0 && next <= a) {
338 log_error("Array chain has cycle at %llu.", (unsigned long long) p);
342 m = journal_file_entry_array_n_items(o);
343 for (j = 0; i < n && j < m; i++, j++) {
345 q = le64toh(o->entry_array.items[j]);
347 log_error("Data object's entry array not sorted at %llu.", (unsigned long long) p);
352 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
356 /* Pointer might have moved, reposition */
357 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
368 static int verify_hash_table(
370 int data_fd, uint64_t n_data,
371 int entry_fd, uint64_t n_entries,
372 int entry_array_fd, uint64_t n_entry_arrays,
379 assert(data_fd >= 0);
380 assert(entry_fd >= 0);
381 assert(entry_array_fd >= 0);
384 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
385 for (i = 0; i < n; i++) {
386 uint64_t last = 0, p;
388 draw_progress(0xC000 + (0x3FFF * i / n), last_usec);
390 p = le64toh(f->data_hash_table[i].head_hash_offset);
395 if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
396 log_error("Invalid data object at hash entry %llu of %llu.",
397 (unsigned long long) i, (unsigned long long) n);
401 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
405 next = le64toh(o->data.next_hash_offset);
406 if (next != 0 && next <= p) {
407 log_error("Hash chain has a cycle in hash entry %llu of %llu.",
408 (unsigned long long) i, (unsigned long long) n);
412 if (le64toh(o->data.hash) % n != i) {
413 log_error("Hash value mismatch in hash entry %llu of %llu.",
414 (unsigned long long) i, (unsigned long long) n);
418 r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
426 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
427 log_error("Tail hash pointer mismatch in hash table.");
435 static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
440 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
443 q = le64toh(f->data_hash_table[h].head_hash_offset);
450 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
454 q = le64toh(o->data.next_hash_offset);
460 static int verify_entry(
462 Object *o, uint64_t p,
463 int data_fd, uint64_t n_data) {
470 assert(data_fd >= 0);
472 n = journal_file_entry_n_items(o);
473 for (i = 0; i < n; i++) {
477 q = le64toh(o->entry.items[i].object_offset);
478 h = le64toh(o->entry.items[i].hash);
480 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
481 log_error("Invalid data object at entry %llu.",
482 (unsigned long long) o);
486 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
490 if (le64toh(u->data.hash) != h) {
491 log_error("Hash mismatch for data object at entry %llu.",
492 (unsigned long long) p);
496 r = data_object_in_hash_table(f, h, q);
500 log_error("Data object missing from hash at entry %llu.",
501 (unsigned long long) p);
509 static int verify_entry_array(
511 int data_fd, uint64_t n_data,
512 int entry_fd, uint64_t n_entries,
513 int entry_array_fd, uint64_t n_entry_arrays,
516 uint64_t i = 0, a, n, last = 0;
520 assert(data_fd >= 0);
521 assert(entry_fd >= 0);
522 assert(entry_array_fd >= 0);
525 n = le64toh(f->header->n_entries);
526 a = le64toh(f->header->entry_array_offset);
531 draw_progress(0x8000 + (0x3FFF * i / n), last_usec);
534 log_error("Array chain too short at %llu of %llu.",
535 (unsigned long long) i, (unsigned long long) n);
539 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
540 log_error("Invalid array at %llu of %llu.",
541 (unsigned long long) i, (unsigned long long) n);
545 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
549 next = le64toh(o->entry_array.next_entry_array_offset);
550 if (next != 0 && next <= a) {
551 log_error("Array chain has cycle at %llu of %llu.",
552 (unsigned long long) i, (unsigned long long) n);
556 m = journal_file_entry_array_n_items(o);
557 for (j = 0; i < n && j < m; i++, j++) {
560 p = le64toh(o->entry_array.items[j]);
562 log_error("Entry array not sorted at %llu of %llu.",
563 (unsigned long long) i, (unsigned long long) n);
568 if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
569 log_error("Invalid array entry at %llu of %llu.",
570 (unsigned long long) i, (unsigned long long) n);
574 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
578 r = verify_entry(f, o, p, data_fd, n_data);
582 /* Pointer might have moved, reposition */
583 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
594 int journal_file_verify(JournalFile *f, const char *key) {
598 uint64_t tag_seqnum = 0, entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
599 sd_id128_t entry_boot_id;
600 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
601 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;
602 usec_t last_usec = 0;
603 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
604 char data_path[] = "/var/tmp/journal-data-XXXXXX",
605 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
606 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
610 data_fd = mkostemp(data_path, O_CLOEXEC);
612 log_error("Failed to create data file: %m");
618 entry_fd = mkostemp(entry_path, O_CLOEXEC);
620 log_error("Failed to create entry file: %m");
626 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
627 if (entry_array_fd < 0) {
628 log_error("Failed to create entry array file: %m");
632 unlink(entry_array_path);
634 /* First iteration: we go through all objects, verify the
635 * superficial structure, headers, hashes. */
637 r = journal_file_hmac_put_header(f);
639 log_error("Failed to calculate HMAC of header.");
643 p = le64toh(f->header->header_size);
645 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
647 r = journal_file_move_to_object(f, -1, p, &o);
649 log_error("Invalid object at %llu", (unsigned long long) p);
653 if (le64toh(f->header->tail_object_offset) < p) {
654 log_error("Invalid tail object pointer.");
661 r = journal_file_object_verify(f, o);
663 log_error("Invalid object contents at %llu", (unsigned long long) p);
667 if (o->object.flags & OBJECT_COMPRESSED &&
668 !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
669 log_error("Compressed object without compression at %llu", (unsigned long long) p);
674 r = journal_file_hmac_put_object(f, -1, p);
676 log_error("Failed to calculate HMAC at %llu", (unsigned long long) p);
680 if (o->object.type == OBJECT_TAG) {
682 if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) {
683 log_error("Tag object without authentication at %llu", (unsigned long long) p);
688 if (le64toh(o->tag.seqnum) != tag_seqnum) {
689 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
694 } else if (o->object.type == OBJECT_ENTRY) {
696 r = write_uint64(entry_fd, p);
700 if (!entry_seqnum_set &&
701 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
702 log_error("Head entry sequence number incorrect");
707 if (entry_seqnum_set &&
708 entry_seqnum >= le64toh(o->entry.seqnum)) {
709 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
714 entry_seqnum = le64toh(o->entry.seqnum);
715 entry_seqnum_set = true;
717 if (entry_monotonic_set &&
718 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
719 entry_monotonic > le64toh(o->entry.monotonic)) {
720 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
725 entry_monotonic = le64toh(o->entry.monotonic);
726 entry_boot_id = o->entry.boot_id;
727 entry_monotonic_set = true;
729 if (!entry_realtime_set &&
730 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
731 log_error("Head entry realtime timestamp incorrect");
736 entry_realtime = le64toh(o->entry.realtime);
737 entry_realtime_set = true;
740 } else if (o->object.type == OBJECT_ENTRY_ARRAY) {
742 r = write_uint64(entry_array_fd, p);
746 if (p == le64toh(f->header->entry_array_offset)) {
747 if (found_main_entry_array) {
748 log_error("More than one main entry array at %llu", (unsigned long long) p);
753 found_main_entry_array = true;
758 } else if (o->object.type == OBJECT_DATA) {
760 r = write_uint64(data_fd, p);
766 } else if (o->object.type == OBJECT_FIELD)
768 else if (o->object.type == OBJECT_DATA_HASH_TABLE) {
769 n_data_hash_tables++;
771 if (n_data_hash_tables > 1) {
772 log_error("More than one data hash table at %llu", (unsigned long long) p);
777 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
778 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
779 log_error("Header fields for data hash table invalid.");
783 } else if (o->object.type == OBJECT_FIELD_HASH_TABLE) {
784 n_field_hash_tables++;
786 if (n_field_hash_tables > 1) {
787 log_error("More than one field hash table at %llu", (unsigned long long) p);
792 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
793 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
794 log_error("Header fields for field hash table invalid.");
798 } else if (o->object.type >= _OBJECT_TYPE_MAX)
801 if (p == le64toh(f->header->tail_object_offset))
804 p = p + ALIGN64(le64toh(o->object.size));
807 if (n_objects != le64toh(f->header->n_objects)) {
808 log_error("Object number mismatch");
813 if (n_entries != le64toh(f->header->n_entries)) {
814 log_error("Entry number mismatch");
819 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
820 n_data != le64toh(f->header->n_data)) {
821 log_error("Data number mismatch");
826 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
827 n_fields != le64toh(f->header->n_fields)) {
828 log_error("Field number mismatch");
833 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
834 tag_seqnum != le64toh(f->header->n_tags)) {
835 log_error("Tag number mismatch");
840 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
841 n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
842 log_error("Entry array number mismatch");
847 if (n_data_hash_tables != 1) {
848 log_error("Missing data hash table");
853 if (n_field_hash_tables != 1) {
854 log_error("Missing field hash table");
859 if (!found_main_entry_array) {
860 log_error("Missing entry array");
865 if (entry_seqnum_set &&
866 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
867 log_error("Invalid tail seqnum");
872 if (entry_monotonic_set &&
873 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
874 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
875 log_error("Invalid tail monotonic timestamp");
880 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
881 log_error("Invalid tail realtime timestamp");
886 /* Second iteration: we follow all objects referenced from the
887 * two entry points: the object hash table and the entry
888 * array. We also check that everything referenced (directly
889 * or indirectly) in the data hash table also exists in the
890 * entry array, and vice versa. Note that we do not care for
891 * unreferenced objects. We only care that everything that is
892 * referenced is consistent. */
894 r = verify_entry_array(f,
897 entry_array_fd, n_entry_arrays,
902 r = verify_hash_table(f,
905 entry_array_fd, n_entry_arrays,
912 mmap_cache_close_fd(f->mmap, data_fd);
913 mmap_cache_close_fd(f->mmap, entry_fd);
914 mmap_cache_close_fd(f->mmap, entry_array_fd);
916 close_nointr_nofail(data_fd);
917 close_nointr_nofail(entry_fd);
918 close_nointr_nofail(entry_array_fd);
925 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
927 (unsigned long long) p,
928 (unsigned long long) f->last_stat.st_size,
929 (unsigned long long) (100 * p / f->last_stat.st_size));
932 mmap_cache_close_fd(f->mmap, data_fd);
933 close_nointr_nofail(data_fd);
937 mmap_cache_close_fd(f->mmap, entry_fd);
938 close_nointr_nofail(entry_fd);
941 if (entry_array_fd >= 0) {
942 mmap_cache_close_fd(f->mmap, entry_array_fd);
943 close_nointr_nofail(entry_array_fd);