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");
617 entry_fd = mkostemp(entry_path, O_CLOEXEC);
619 log_error("Failed to create entry file: %m");
624 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
625 if (entry_array_fd < 0) {
626 log_error("Failed to create entry array file: %m");
629 unlink(entry_array_path);
631 /* First iteration: we go through all objects, verify the
632 * superficial structure, headers, hashes. */
634 r = journal_file_hmac_put_header(f);
636 log_error("Failed to calculate HMAC of header.");
640 p = le64toh(f->header->header_size);
642 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
644 r = journal_file_move_to_object(f, -1, p, &o);
646 log_error("Invalid object at %llu", (unsigned long long) p);
650 if (le64toh(f->header->tail_object_offset) < p) {
651 log_error("Invalid tail object pointer.");
658 r = journal_file_object_verify(f, o);
660 log_error("Invalid object contents at %llu", (unsigned long long) p);
664 if (o->object.flags & OBJECT_COMPRESSED &&
665 !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
666 log_error("Compressed object without compression at %llu", (unsigned long long) p);
671 r = journal_file_hmac_put_object(f, -1, p);
673 log_error("Failed to calculate HMAC at %llu", (unsigned long long) p);
677 if (o->object.type == OBJECT_TAG) {
679 if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) {
680 log_error("Tag object without authentication at %llu", (unsigned long long) p);
685 if (le64toh(o->tag.seqnum) != tag_seqnum) {
686 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
691 } else if (o->object.type == OBJECT_ENTRY) {
693 r = write_uint64(entry_fd, p);
697 if (!entry_seqnum_set &&
698 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
699 log_error("Head entry sequence number incorrect");
704 if (entry_seqnum_set &&
705 entry_seqnum >= le64toh(o->entry.seqnum)) {
706 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
711 entry_seqnum = le64toh(o->entry.seqnum);
712 entry_seqnum_set = true;
714 if (entry_monotonic_set &&
715 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
716 entry_monotonic > le64toh(o->entry.monotonic)) {
717 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
722 entry_monotonic = le64toh(o->entry.monotonic);
723 entry_boot_id = o->entry.boot_id;
724 entry_monotonic_set = true;
726 if (!entry_realtime_set &&
727 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
728 log_error("Head entry realtime timestamp incorrect");
733 entry_realtime = le64toh(o->entry.realtime);
734 entry_realtime_set = true;
737 } else if (o->object.type == OBJECT_ENTRY_ARRAY) {
739 r = write_uint64(entry_array_fd, p);
743 if (p == le64toh(f->header->entry_array_offset)) {
744 if (found_main_entry_array) {
745 log_error("More than one main entry array at %llu", (unsigned long long) p);
750 found_main_entry_array = true;
755 } else if (o->object.type == OBJECT_DATA) {
757 r = write_uint64(data_fd, p);
763 } else if (o->object.type == OBJECT_FIELD)
765 else if (o->object.type == OBJECT_DATA_HASH_TABLE) {
766 n_data_hash_tables++;
768 if (n_data_hash_tables > 1) {
769 log_error("More than one data hash table at %llu", (unsigned long long) p);
774 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
775 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
776 log_error("Header fields for data hash table invalid.");
780 } else if (o->object.type == OBJECT_FIELD_HASH_TABLE) {
781 n_field_hash_tables++;
783 if (n_field_hash_tables > 1) {
784 log_error("More than one field hash table at %llu", (unsigned long long) p);
789 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
790 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
791 log_error("Header fields for field hash table invalid.");
795 } else if (o->object.type >= _OBJECT_TYPE_MAX)
798 if (p == le64toh(f->header->tail_object_offset))
801 p = p + ALIGN64(le64toh(o->object.size));
804 if (n_objects != le64toh(f->header->n_objects)) {
805 log_error("Object number mismatch");
810 if (n_entries != le64toh(f->header->n_entries)) {
811 log_error("Entry number mismatch");
816 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
817 n_data != le64toh(f->header->n_data)) {
818 log_error("Data number mismatch");
823 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
824 n_fields != le64toh(f->header->n_fields)) {
825 log_error("Field number mismatch");
830 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
831 tag_seqnum != le64toh(f->header->n_tags)) {
832 log_error("Tag number mismatch");
837 if (n_data_hash_tables != 1) {
838 log_error("Missing data hash table");
843 if (n_field_hash_tables != 1) {
844 log_error("Missing field hash table");
849 if (!found_main_entry_array) {
850 log_error("Missing entry array");
855 if (entry_seqnum_set &&
856 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
857 log_error("Invalid tail seqnum");
862 if (entry_monotonic_set &&
863 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
864 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
865 log_error("Invalid tail monotonic timestamp");
870 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
871 log_error("Invalid tail realtime timestamp");
876 /* Second iteration: we follow all objects referenced from the
877 * two entry points: the object hash table and the entry
878 * array. We also check that everything referenced (directly
879 * or indirectly) in the data hash table also exists in the
880 * entry array, and vice versa. Note that we do not care for
881 * unreferenced objects. We only care that everything that is
882 * referenced is consistent. */
884 r = verify_entry_array(f,
887 entry_array_fd, n_entry_arrays,
892 r = verify_hash_table(f,
895 entry_array_fd, n_entry_arrays,
902 mmap_cache_close_fd(f->mmap, data_fd);
903 mmap_cache_close_fd(f->mmap, entry_fd);
904 mmap_cache_close_fd(f->mmap, entry_array_fd);
906 close_nointr_nofail(data_fd);
907 close_nointr_nofail(entry_fd);
908 close_nointr_nofail(entry_array_fd);
915 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
917 (unsigned long long) p,
918 (unsigned long long) f->last_stat.st_size,
919 (unsigned long long) (100 * p / f->last_stat.st_size));
922 mmap_cache_close_fd(f->mmap, data_fd);
923 close_nointr_nofail(data_fd);
927 mmap_cache_close_fd(f->mmap, entry_fd);
928 close_nointr_nofail(entry_fd);
931 if (entry_array_fd >= 0) {
932 mmap_cache_close_fd(f->mmap, entry_array_fd);
933 close_nointr_nofail(entry_array_fd);