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/>.
29 #include "journal-def.h"
30 #include "journal-file.h"
31 #include "journal-authenticate.h"
32 #include "journal-verify.h"
37 static int journal_file_object_verify(JournalFile *f, Object *o) {
43 /* This does various superficial tests about the length an
44 * possible field values. It does not follow any references to
47 if ((o->object.flags & OBJECT_COMPRESSED) &&
48 o->object.type != OBJECT_DATA)
51 switch (o->object.type) {
56 if (le64toh(o->data.entry_offset) <= 0 ||
57 le64toh(o->data.n_entries) <= 0)
60 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
63 h1 = le64toh(o->data.hash);
65 if (o->object.flags & OBJECT_COMPRESSED) {
67 uint64_t alloc = 0, b_size;
69 if (!uncompress_blob(o->data.payload,
70 le64toh(o->object.size) - offsetof(Object, data.payload),
74 h2 = hash64(b, b_size);
77 h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
82 if (!VALID64(o->data.next_hash_offset) ||
83 !VALID64(o->data.next_field_offset) ||
84 !VALID64(o->data.entry_offset) ||
85 !VALID64(o->data.entry_array_offset))
92 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
95 if (!VALID64(o->field.next_hash_offset) ||
96 !VALID64(o->field.head_data_offset))
101 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
104 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
107 if (le64toh(o->entry.seqnum) <= 0 ||
108 !VALID_REALTIME(le64toh(o->entry.realtime)) ||
109 !VALID_MONOTONIC(le64toh(o->entry.monotonic)))
112 for (i = 0; i < journal_file_entry_n_items(o); i++) {
113 if (o->entry.items[i].object_offset == 0 ||
114 !VALID64(o->entry.items[i].object_offset))
120 case OBJECT_DATA_HASH_TABLE:
121 case OBJECT_FIELD_HASH_TABLE:
122 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0)
125 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
128 for (i = 0; i < journal_file_hash_table_n_items(o); i++) {
129 if (o->hash_table.items[i].head_hash_offset != 0 &&
130 !VALID64(le64toh(o->hash_table.items[i].head_hash_offset)))
132 if (o->hash_table.items[i].tail_hash_offset != 0 &&
133 !VALID64(le64toh(o->hash_table.items[i].tail_hash_offset)))
136 if ((o->hash_table.items[i].head_hash_offset != 0) !=
137 (o->hash_table.items[i].tail_hash_offset != 0))
143 case OBJECT_ENTRY_ARRAY:
144 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0)
147 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
150 if (!VALID64(o->entry_array.next_entry_array_offset))
153 for (i = 0; i < journal_file_entry_array_n_items(o); i++)
154 if (o->entry_array.items[i] != 0 &&
155 !VALID64(o->entry_array.items[i]))
161 if (le64toh(o->object.size) != sizeof(TagObject))
164 if (!VALID_EPOCH(o->tag.epoch))
173 static void draw_progress(uint64_t p, usec_t *last_usec) {
177 if (!isatty(STDOUT_FILENO))
180 z = now(CLOCK_MONOTONIC);
183 if (x != 0 && x + 40 * USEC_PER_MSEC > z)
188 n = (3 * columns()) / 4;
189 j = (n * (unsigned) p) / 65535ULL;
192 fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
194 for (i = 0; i < j; i++)
195 fputs("\xe2\x96\x88", stdout);
197 fputs(ANSI_HIGHLIGHT_OFF, stdout);
199 for (i = 0; i < k; i++)
200 fputs("\xe2\x96\x91", stdout);
202 printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU);
204 fputs("\r\x1B[?25h", stdout);
208 static void flush_progress(void) {
211 if (!isatty(STDOUT_FILENO))
214 n = (3 * columns()) / 4;
218 for (i = 0; i < n + 5; i++)
225 static int write_uint64(int fd, uint64_t p) {
228 k = write(fd, &p, sizeof(p));
237 static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
252 r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z);
272 static int entry_points_to_data(
285 assert(entry_fd >= 0);
287 if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
288 log_error("Data object references invalid entry at %llu", (unsigned long long) data_p);
292 r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
296 n = journal_file_entry_n_items(o);
297 for (i = 0; i < n; i++)
298 if (le64toh(o->entry.items[i].object_offset) == data_p) {
304 log_error("Data object not referenced by linked entry at %llu", (unsigned long long) data_p);
308 /* Check if this entry is also in main entry array. Since the
309 * main entry array has already been verified we can rely on
312 n = le64toh(f->header->n_entries);
313 a = le64toh(f->header->entry_array_offset);
319 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
323 m = journal_file_entry_array_n_items(o);
324 for (j = 0; i < n && j < m; i++, j++)
325 if (le64toh(o->entry_array.items[j]) == entry_p)
328 a = le64toh(o->entry_array.next_entry_array_offset);
334 static int verify_data(
336 Object *o, uint64_t p,
337 int entry_fd, uint64_t n_entries,
338 int entry_array_fd, uint64_t n_entry_arrays) {
340 uint64_t i, n, a, last, q;
345 assert(entry_fd >= 0);
346 assert(entry_array_fd >= 0);
348 n = le64toh(o->data.n_entries);
349 a = le64toh(o->data.entry_array_offset);
351 /* We already checked this earlier */
354 last = q = le64toh(o->data.entry_offset);
355 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
364 log_error("Array chain too short at %llu", (unsigned long long) p);
368 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
369 log_error("Invalid array at %llu", (unsigned long long) p);
373 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
377 next = le64toh(o->entry_array.next_entry_array_offset);
378 if (next != 0 && next <= a) {
379 log_error("Array chain has cycle at %llu", (unsigned long long) p);
383 m = journal_file_entry_array_n_items(o);
384 for (j = 0; i < n && j < m; i++, j++) {
386 q = le64toh(o->entry_array.items[j]);
388 log_error("Data object's entry array not sorted at %llu", (unsigned long long) p);
393 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
397 /* Pointer might have moved, reposition */
398 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
409 static int verify_hash_table(
411 int data_fd, uint64_t n_data,
412 int entry_fd, uint64_t n_entries,
413 int entry_array_fd, uint64_t n_entry_arrays,
415 bool show_progress) {
421 assert(data_fd >= 0);
422 assert(entry_fd >= 0);
423 assert(entry_array_fd >= 0);
426 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
427 for (i = 0; i < n; i++) {
428 uint64_t last = 0, p;
431 draw_progress(0xC000 + (0x3FFF * i / n), last_usec);
433 p = le64toh(f->data_hash_table[i].head_hash_offset);
438 if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
439 log_error("Invalid data object at hash entry %llu of %llu",
440 (unsigned long long) i, (unsigned long long) n);
444 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
448 next = le64toh(o->data.next_hash_offset);
449 if (next != 0 && next <= p) {
450 log_error("Hash chain has a cycle in hash entry %llu of %llu",
451 (unsigned long long) i, (unsigned long long) n);
455 if (le64toh(o->data.hash) % n != i) {
456 log_error("Hash value mismatch in hash entry %llu of %llu",
457 (unsigned long long) i, (unsigned long long) n);
461 r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
469 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
470 log_error("Tail hash pointer mismatch in hash table");
478 static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
483 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
486 q = le64toh(f->data_hash_table[h].head_hash_offset);
493 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
497 q = le64toh(o->data.next_hash_offset);
503 static int verify_entry(
505 Object *o, uint64_t p,
506 int data_fd, uint64_t n_data) {
513 assert(data_fd >= 0);
515 n = journal_file_entry_n_items(o);
516 for (i = 0; i < n; i++) {
520 q = le64toh(o->entry.items[i].object_offset);
521 h = le64toh(o->entry.items[i].hash);
523 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
524 log_error("Invalid data object at entry %llu",
525 (unsigned long long) p);
529 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
533 if (le64toh(u->data.hash) != h) {
534 log_error("Hash mismatch for data object at entry %llu",
535 (unsigned long long) p);
539 r = data_object_in_hash_table(f, h, q);
543 log_error("Data object missing from hash at entry %llu",
544 (unsigned long long) p);
552 static int verify_entry_array(
554 int data_fd, uint64_t n_data,
555 int entry_fd, uint64_t n_entries,
556 int entry_array_fd, uint64_t n_entry_arrays,
558 bool show_progress) {
560 uint64_t i = 0, a, n, last = 0;
564 assert(data_fd >= 0);
565 assert(entry_fd >= 0);
566 assert(entry_array_fd >= 0);
569 n = le64toh(f->header->n_entries);
570 a = le64toh(f->header->entry_array_offset);
576 draw_progress(0x8000 + (0x3FFF * i / n), last_usec);
579 log_error("Array chain too short at %llu of %llu",
580 (unsigned long long) i, (unsigned long long) n);
584 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
585 log_error("Invalid array at %llu of %llu",
586 (unsigned long long) i, (unsigned long long) n);
590 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
594 next = le64toh(o->entry_array.next_entry_array_offset);
595 if (next != 0 && next <= a) {
596 log_error("Array chain has cycle at %llu of %llu",
597 (unsigned long long) i, (unsigned long long) n);
601 m = journal_file_entry_array_n_items(o);
602 for (j = 0; i < n && j < m; i++, j++) {
605 p = le64toh(o->entry_array.items[j]);
607 log_error("Entry array not sorted at %llu of %llu",
608 (unsigned long long) i, (unsigned long long) n);
613 if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
614 log_error("Invalid array entry at %llu of %llu",
615 (unsigned long long) i, (unsigned long long) n);
619 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
623 r = verify_entry(f, o, p, data_fd, n_data);
627 /* Pointer might have moved, reposition */
628 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
639 int journal_file_verify(
642 usec_t *first_validated, usec_t *last_validated, usec_t *last_contained,
643 bool show_progress) {
646 uint64_t p = 0, last_tag = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0;
647 uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
648 sd_id128_t entry_boot_id;
649 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
650 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;
651 usec_t last_usec = 0;
652 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
653 char data_path[] = "/var/tmp/journal-data-XXXXXX",
654 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
655 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
663 r = journal_file_parse_verification_key(f, key);
665 log_error("Failed to parse seed.");
674 data_fd = mkostemp(data_path, O_CLOEXEC);
676 log_error("Failed to create data file: %m");
682 entry_fd = mkostemp(entry_path, O_CLOEXEC);
684 log_error("Failed to create entry file: %m");
690 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
691 if (entry_array_fd < 0) {
692 log_error("Failed to create entry array file: %m");
696 unlink(entry_array_path);
699 if ((le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) != 0)
701 if (f->header->compatible_flags != 0)
704 log_error("Cannot verify file with unknown extensions.");
709 for (i = 0; i < sizeof(f->header->reserved); i++)
710 if (f->header->reserved[i] != 0) {
711 log_error("Reserved field in non-zero.");
716 /* First iteration: we go through all objects, verify the
717 * superficial structure, headers, hashes. */
719 p = le64toh(f->header->header_size);
722 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
724 r = journal_file_move_to_object(f, -1, p, &o);
726 log_error("Invalid object at %llu", (unsigned long long) p);
730 if (p > le64toh(f->header->tail_object_offset)) {
731 log_error("Invalid tail object pointer");
736 if (p == le64toh(f->header->tail_object_offset))
741 r = journal_file_object_verify(f, o);
743 log_error("Invalid object contents at %llu", (unsigned long long) p);
747 if ((o->object.flags & OBJECT_COMPRESSED) && !JOURNAL_HEADER_COMPRESSED(f->header)) {
748 log_error("Compressed object in file without compression at %llu", (unsigned long long) p);
753 switch (o->object.type) {
756 r = write_uint64(data_fd, p);
768 if (JOURNAL_HEADER_SEALED(f->header) && n_tags <= 0) {
769 log_error("First entry before first tag at %llu", (unsigned long long) p);
774 r = write_uint64(entry_fd, p);
778 if (le64toh(o->entry.realtime) < last_tag_realtime) {
779 log_error("Older entry after newer tag at %llu", (unsigned long long) p);
784 if (!entry_seqnum_set &&
785 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
786 log_error("Head entry sequence number incorrect at %llu", (unsigned long long) p);
791 if (entry_seqnum_set &&
792 entry_seqnum >= le64toh(o->entry.seqnum)) {
793 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
798 entry_seqnum = le64toh(o->entry.seqnum);
799 entry_seqnum_set = true;
801 if (entry_monotonic_set &&
802 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
803 entry_monotonic > le64toh(o->entry.monotonic)) {
804 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
809 entry_monotonic = le64toh(o->entry.monotonic);
810 entry_boot_id = o->entry.boot_id;
811 entry_monotonic_set = true;
813 if (!entry_realtime_set &&
814 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
815 log_error("Head entry realtime timestamp incorrect");
820 entry_realtime = le64toh(o->entry.realtime);
821 entry_realtime_set = true;
826 case OBJECT_DATA_HASH_TABLE:
827 if (n_data_hash_tables > 1) {
828 log_error("More than one data hash table at %llu", (unsigned long long) p);
833 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
834 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
835 log_error("Header fields for data hash table invalid");
840 n_data_hash_tables++;
843 case OBJECT_FIELD_HASH_TABLE:
844 if (n_field_hash_tables > 1) {
845 log_error("More than one field hash table at %llu", (unsigned long long) p);
850 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
851 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
852 log_error("Header fields for field hash table invalid");
857 n_field_hash_tables++;
860 case OBJECT_ENTRY_ARRAY:
861 r = write_uint64(entry_array_fd, p);
865 if (p == le64toh(f->header->entry_array_offset)) {
866 if (found_main_entry_array) {
867 log_error("More than one main entry array at %llu", (unsigned long long) p);
872 found_main_entry_array = true;
879 if (!JOURNAL_HEADER_SEALED(f->header)) {
880 log_error("Tag object in file without sealing at %llu", (unsigned long long) p);
885 if (le64toh(o->tag.seqnum) != n_tags + 1) {
886 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
891 if (le64toh(o->tag.epoch) < last_epoch) {
892 log_error("Epoch sequence out of synchronization at %llu", (unsigned long long) p);
901 log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum));
903 rt = f->fss_start_usec + o->tag.epoch * f->fss_interval_usec;
904 if (entry_realtime_set && entry_realtime >= rt + f->fss_interval_usec) {
905 log_error("Tag/entry realtime timestamp out of synchronization at %llu", (unsigned long long) p);
910 /* OK, now we know the epoch. So let's now set
911 * it, and calculate the HMAC for everything
912 * since the last tag. */
913 r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
917 r = journal_file_hmac_start(f);
922 r = journal_file_hmac_put_header(f);
926 q = le64toh(f->header->header_size);
931 r = journal_file_move_to_object(f, -1, q, &o);
935 r = journal_file_hmac_put_object(f, -1, q);
939 q = q + ALIGN64(le64toh(o->object.size));
942 /* Position might have changed, let's reposition things */
943 r = journal_file_move_to_object(f, -1, p, &o);
947 if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
948 log_error("Tag failed verification at %llu", (unsigned long long) p);
953 f->hmac_running = false;
954 last_tag_realtime = rt;
955 last_sealed_realtime = entry_realtime;
959 last_tag = p + ALIGN64(le64toh(o->object.size));
960 last_epoch = le64toh(o->tag.epoch);
969 if (p == le64toh(f->header->tail_object_offset))
972 p = p + ALIGN64(le64toh(o->object.size));
976 log_error("Tail object pointer dead");
981 if (n_objects != le64toh(f->header->n_objects)) {
982 log_error("Object number mismatch");
987 if (n_entries != le64toh(f->header->n_entries)) {
988 log_error("Entry number mismatch");
993 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
994 n_data != le64toh(f->header->n_data)) {
995 log_error("Data number mismatch");
1000 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
1001 n_fields != le64toh(f->header->n_fields)) {
1002 log_error("Field number mismatch");
1007 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
1008 n_tags != le64toh(f->header->n_tags)) {
1009 log_error("Tag number mismatch");
1014 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
1015 n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
1016 log_error("Entry array number mismatch");
1021 if (n_data_hash_tables != 1) {
1022 log_error("Missing data hash table");
1027 if (n_field_hash_tables != 1) {
1028 log_error("Missing field hash table");
1033 if (!found_main_entry_array) {
1034 log_error("Missing entry array");
1039 if (entry_seqnum_set &&
1040 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
1041 log_error("Invalid tail seqnum");
1046 if (entry_monotonic_set &&
1047 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
1048 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
1049 log_error("Invalid tail monotonic timestamp");
1054 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
1055 log_error("Invalid tail realtime timestamp");
1060 /* Second iteration: we follow all objects referenced from the
1061 * two entry points: the object hash table and the entry
1062 * array. We also check that everything referenced (directly
1063 * or indirectly) in the data hash table also exists in the
1064 * entry array, and vice versa. Note that we do not care for
1065 * unreferenced objects. We only care that everything that is
1066 * referenced is consistent. */
1068 r = verify_entry_array(f,
1070 entry_fd, n_entries,
1071 entry_array_fd, n_entry_arrays,
1077 r = verify_hash_table(f,
1079 entry_fd, n_entries,
1080 entry_array_fd, n_entry_arrays,
1089 mmap_cache_close_fd(f->mmap, data_fd);
1090 mmap_cache_close_fd(f->mmap, entry_fd);
1091 mmap_cache_close_fd(f->mmap, entry_array_fd);
1093 close_nointr_nofail(data_fd);
1094 close_nointr_nofail(entry_fd);
1095 close_nointr_nofail(entry_array_fd);
1097 if (first_validated)
1098 *first_validated = last_tag_realtime ? le64toh(f->header->head_entry_realtime) : 0;
1100 *last_validated = last_sealed_realtime;
1102 *last_contained = le64toh(f->header->tail_entry_realtime);
1110 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
1112 (unsigned long long) p,
1113 (unsigned long long) f->last_stat.st_size,
1114 (unsigned long long) (100 * p / f->last_stat.st_size));
1117 mmap_cache_close_fd(f->mmap, data_fd);
1118 close_nointr_nofail(data_fd);
1121 if (entry_fd >= 0) {
1122 mmap_cache_close_fd(f->mmap, entry_fd);
1123 close_nointr_nofail(entry_fd);
1126 if (entry_array_fd >= 0) {
1127 mmap_cache_close_fd(f->mmap, entry_array_fd);
1128 close_nointr_nofail(entry_array_fd);