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) {
68 uint64_t alloc = 0, b_size;
70 if (!uncompress_blob(o->data.payload,
71 le64toh(o->object.size) - offsetof(Object, data.payload),
72 &b, &alloc, &b_size, 0))
75 h2 = hash64(b, b_size);
78 return -EPROTONOSUPPORT;
81 h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
86 if (!VALID64(o->data.next_hash_offset) ||
87 !VALID64(o->data.next_field_offset) ||
88 !VALID64(o->data.entry_offset) ||
89 !VALID64(o->data.entry_array_offset))
96 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
99 if (!VALID64(o->field.next_hash_offset) ||
100 !VALID64(o->field.head_data_offset))
105 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
108 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
111 if (le64toh(o->entry.seqnum) <= 0 ||
112 !VALID_REALTIME(le64toh(o->entry.realtime)) ||
113 !VALID_MONOTONIC(le64toh(o->entry.monotonic)))
116 for (i = 0; i < journal_file_entry_n_items(o); i++) {
117 if (o->entry.items[i].object_offset == 0 ||
118 !VALID64(o->entry.items[i].object_offset))
124 case OBJECT_DATA_HASH_TABLE:
125 case OBJECT_FIELD_HASH_TABLE:
126 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0)
129 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
132 for (i = 0; i < journal_file_hash_table_n_items(o); i++) {
133 if (o->hash_table.items[i].head_hash_offset != 0 &&
134 !VALID64(le64toh(o->hash_table.items[i].head_hash_offset)))
136 if (o->hash_table.items[i].tail_hash_offset != 0 &&
137 !VALID64(le64toh(o->hash_table.items[i].tail_hash_offset)))
140 if ((o->hash_table.items[i].head_hash_offset != 0) !=
141 (o->hash_table.items[i].tail_hash_offset != 0))
147 case OBJECT_ENTRY_ARRAY:
148 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0)
151 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
154 if (!VALID64(o->entry_array.next_entry_array_offset))
157 for (i = 0; i < journal_file_entry_array_n_items(o); i++)
158 if (o->entry_array.items[i] != 0 &&
159 !VALID64(o->entry_array.items[i]))
165 if (le64toh(o->object.size) != sizeof(TagObject))
168 if (!VALID_EPOCH(o->tag.epoch))
177 static void draw_progress(uint64_t p, usec_t *last_usec) {
184 z = now(CLOCK_MONOTONIC);
187 if (x != 0 && x + 40 * USEC_PER_MSEC > z)
192 n = (3 * columns()) / 4;
193 j = (n * (unsigned) p) / 65535ULL;
196 fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
198 for (i = 0; i < j; i++)
199 fputs("\xe2\x96\x88", stdout);
201 fputs(ANSI_HIGHLIGHT_OFF, stdout);
203 for (i = 0; i < k; i++)
204 fputs("\xe2\x96\x91", stdout);
206 printf(" %3"PRIu64"%%", 100U * p / 65535U);
208 fputs("\r\x1B[?25h", stdout);
212 static void flush_progress(void) {
218 n = (3 * columns()) / 4;
222 for (i = 0; i < n + 5; i++)
229 static int write_uint64(int fd, uint64_t p) {
232 k = write(fd, &p, sizeof(p));
241 static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
256 r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z);
275 static int entry_points_to_data(
288 assert(entry_fd >= 0);
290 if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
291 log_error("Data object references invalid entry at %"PRIu64, data_p);
295 r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
299 n = journal_file_entry_n_items(o);
300 for (i = 0; i < n; i++)
301 if (le64toh(o->entry.items[i].object_offset) == data_p) {
307 log_error("Data object not referenced by linked entry at %"PRIu64, data_p);
311 /* Check if this entry is also in main entry array. Since the
312 * main entry array has already been verified we can rely on
316 n = le64toh(f->header->n_entries);
317 a = le64toh(f->header->entry_array_offset);
322 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
326 m = journal_file_entry_array_n_items(o);
329 if (entry_p <= le64toh(o->entry_array.items[u-1])) {
338 if (le64toh(o->entry_array.items[z]) == entry_p)
344 if (entry_p < le64toh(o->entry_array.items[z]))
350 log_error("Entry object doesn't exist in main entry array at %"PRIu64, entry_p);
355 a = le64toh(o->entry_array.next_entry_array_offset);
361 static int verify_data(
363 Object *o, uint64_t p,
364 int entry_fd, uint64_t n_entries,
365 int entry_array_fd, uint64_t n_entry_arrays) {
367 uint64_t i, n, a, last, q;
372 assert(entry_fd >= 0);
373 assert(entry_array_fd >= 0);
375 n = le64toh(o->data.n_entries);
376 a = le64toh(o->data.entry_array_offset);
378 /* We already checked this earlier */
381 last = q = le64toh(o->data.entry_offset);
382 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
391 log_error("Array chain too short at %"PRIu64, p);
395 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
396 log_error("Invalid array at %"PRIu64, p);
400 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
404 next = le64toh(o->entry_array.next_entry_array_offset);
405 if (next != 0 && next <= a) {
406 log_error("Array chain has cycle at %"PRIu64, p);
410 m = journal_file_entry_array_n_items(o);
411 for (j = 0; i < n && j < m; i++, j++) {
413 q = le64toh(o->entry_array.items[j]);
415 log_error("Data object's entry array not sorted at %"PRIu64, p);
420 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
424 /* Pointer might have moved, reposition */
425 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
436 static int verify_hash_table(
438 int data_fd, uint64_t n_data,
439 int entry_fd, uint64_t n_entries,
440 int entry_array_fd, uint64_t n_entry_arrays,
442 bool show_progress) {
448 assert(data_fd >= 0);
449 assert(entry_fd >= 0);
450 assert(entry_array_fd >= 0);
453 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
454 for (i = 0; i < n; i++) {
455 uint64_t last = 0, p;
458 draw_progress(0xC000 + (0x3FFF * i / n), last_usec);
460 p = le64toh(f->data_hash_table[i].head_hash_offset);
465 if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
466 log_error("Invalid data object at hash entry %"PRIu64" of %"PRIu64,
471 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
475 next = le64toh(o->data.next_hash_offset);
476 if (next != 0 && next <= p) {
477 log_error("Hash chain has a cycle in hash entry %"PRIu64" of %"PRIu64,
482 if (le64toh(o->data.hash) % n != i) {
483 log_error("Hash value mismatch in hash entry %"PRIu64" of %"PRIu64,
488 r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
496 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
497 log_error("Tail hash pointer mismatch in hash table");
505 static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
510 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
513 q = le64toh(f->data_hash_table[h].head_hash_offset);
520 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
524 q = le64toh(o->data.next_hash_offset);
530 static int verify_entry(
532 Object *o, uint64_t p,
533 int data_fd, uint64_t n_data) {
540 assert(data_fd >= 0);
542 n = journal_file_entry_n_items(o);
543 for (i = 0; i < n; i++) {
547 q = le64toh(o->entry.items[i].object_offset);
548 h = le64toh(o->entry.items[i].hash);
550 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
551 log_error("Invalid data object at entry %"PRIu64, p);
555 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
559 if (le64toh(u->data.hash) != h) {
560 log_error("Hash mismatch for data object at entry %"PRIu64, p);
564 r = data_object_in_hash_table(f, h, q);
568 log_error("Data object missing from hash at entry %"PRIu64, p);
576 static int verify_entry_array(
578 int data_fd, uint64_t n_data,
579 int entry_fd, uint64_t n_entries,
580 int entry_array_fd, uint64_t n_entry_arrays,
582 bool show_progress) {
584 uint64_t i = 0, a, n, last = 0;
588 assert(data_fd >= 0);
589 assert(entry_fd >= 0);
590 assert(entry_array_fd >= 0);
593 n = le64toh(f->header->n_entries);
594 a = le64toh(f->header->entry_array_offset);
600 draw_progress(0x8000 + (0x3FFF * i / n), last_usec);
603 log_error("Array chain too short at %"PRIu64" of %"PRIu64, i, n);
607 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
608 log_error("Invalid array at %"PRIu64" of %"PRIu64, i, n);
612 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
616 next = le64toh(o->entry_array.next_entry_array_offset);
617 if (next != 0 && next <= a) {
618 log_error("Array chain has cycle at %"PRIu64" of %"PRIu64, i, n);
622 m = journal_file_entry_array_n_items(o);
623 for (j = 0; i < n && j < m; i++, j++) {
626 p = le64toh(o->entry_array.items[j]);
628 log_error("Entry array not sorted at %"PRIu64" of %"PRIu64,
634 if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
635 log_error("Invalid array entry at %"PRIu64" of %"PRIu64,
640 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
644 r = verify_entry(f, o, p, data_fd, n_data);
648 /* Pointer might have moved, reposition */
649 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
660 int journal_file_verify(
663 usec_t *first_contained, usec_t *last_validated, usec_t *last_contained,
664 bool show_progress) {
667 uint64_t p = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0;
669 uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
670 sd_id128_t entry_boot_id;
671 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
672 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;
673 usec_t last_usec = 0;
674 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
675 char data_path[] = "/var/tmp/journal-data-XXXXXX",
676 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
677 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
681 uint64_t last_tag = 0;
687 r = journal_file_parse_verification_key(f, key);
689 log_error("Failed to parse seed.");
698 data_fd = mkostemp(data_path, O_CLOEXEC);
700 log_error("Failed to create data file: %m");
706 entry_fd = mkostemp(entry_path, O_CLOEXEC);
708 log_error("Failed to create entry file: %m");
714 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
715 if (entry_array_fd < 0) {
716 log_error("Failed to create entry array file: %m");
720 unlink(entry_array_path);
723 if ((le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) != 0)
725 if (f->header->compatible_flags != 0)
728 log_error("Cannot verify file with unknown extensions.");
733 for (i = 0; i < sizeof(f->header->reserved); i++)
734 if (f->header->reserved[i] != 0) {
735 log_error("Reserved field in non-zero.");
740 /* First iteration: we go through all objects, verify the
741 * superficial structure, headers, hashes. */
743 p = le64toh(f->header->header_size);
746 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
748 r = journal_file_move_to_object(f, -1, p, &o);
750 log_error("Invalid object at %"PRIu64, p);
754 if (p > le64toh(f->header->tail_object_offset)) {
755 log_error("Invalid tail object pointer");
760 if (p == le64toh(f->header->tail_object_offset))
765 r = journal_file_object_verify(f, o);
767 log_error("Invalid object contents at %"PRIu64, p);
771 if ((o->object.flags & OBJECT_COMPRESSED) && !JOURNAL_HEADER_COMPRESSED(f->header)) {
772 log_error("Compressed object in file without compression at %"PRIu64, p);
777 switch (o->object.type) {
780 r = write_uint64(data_fd, p);
792 if (JOURNAL_HEADER_SEALED(f->header) && n_tags <= 0) {
793 log_error("First entry before first tag at %"PRIu64, p);
798 r = write_uint64(entry_fd, p);
802 if (le64toh(o->entry.realtime) < last_tag_realtime) {
803 log_error("Older entry after newer tag at %"PRIu64, p);
808 if (!entry_seqnum_set &&
809 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
810 log_error("Head entry sequence number incorrect at %"PRIu64, p);
815 if (entry_seqnum_set &&
816 entry_seqnum >= le64toh(o->entry.seqnum)) {
817 log_error("Entry sequence number out of synchronization at %"PRIu64, p);
822 entry_seqnum = le64toh(o->entry.seqnum);
823 entry_seqnum_set = true;
825 if (entry_monotonic_set &&
826 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
827 entry_monotonic > le64toh(o->entry.monotonic)) {
828 log_error("Entry timestamp out of synchronization at %"PRIu64, p);
833 entry_monotonic = le64toh(o->entry.monotonic);
834 entry_boot_id = o->entry.boot_id;
835 entry_monotonic_set = true;
837 if (!entry_realtime_set &&
838 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
839 log_error("Head entry realtime timestamp incorrect");
844 entry_realtime = le64toh(o->entry.realtime);
845 entry_realtime_set = true;
850 case OBJECT_DATA_HASH_TABLE:
851 if (n_data_hash_tables > 1) {
852 log_error("More than one data hash table at %"PRIu64, p);
857 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
858 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
859 log_error("Header fields for data hash table invalid");
864 n_data_hash_tables++;
867 case OBJECT_FIELD_HASH_TABLE:
868 if (n_field_hash_tables > 1) {
869 log_error("More than one field hash table at %"PRIu64, p);
874 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
875 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
876 log_error("Header fields for field hash table invalid");
881 n_field_hash_tables++;
884 case OBJECT_ENTRY_ARRAY:
885 r = write_uint64(entry_array_fd, p);
889 if (p == le64toh(f->header->entry_array_offset)) {
890 if (found_main_entry_array) {
891 log_error("More than one main entry array at %"PRIu64, p);
896 found_main_entry_array = true;
903 if (!JOURNAL_HEADER_SEALED(f->header)) {
904 log_error("Tag object in file without sealing at %"PRIu64, p);
909 if (le64toh(o->tag.seqnum) != n_tags + 1) {
910 log_error("Tag sequence number out of synchronization at %"PRIu64, p);
915 if (le64toh(o->tag.epoch) < last_epoch) {
916 log_error("Epoch sequence out of synchronization at %"PRIu64, p);
925 log_debug("Checking tag %"PRIu64"...", le64toh(o->tag.seqnum));
927 rt = f->fss_start_usec + o->tag.epoch * f->fss_interval_usec;
928 if (entry_realtime_set && entry_realtime >= rt + f->fss_interval_usec) {
929 log_error("Tag/entry realtime timestamp out of synchronization at %"PRIu64, p);
934 /* OK, now we know the epoch. So let's now set
935 * it, and calculate the HMAC for everything
936 * since the last tag. */
937 r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
941 r = journal_file_hmac_start(f);
946 r = journal_file_hmac_put_header(f);
950 q = le64toh(f->header->header_size);
955 r = journal_file_move_to_object(f, -1, q, &o);
959 r = journal_file_hmac_put_object(f, -1, o, q);
963 q = q + ALIGN64(le64toh(o->object.size));
966 /* Position might have changed, let's reposition things */
967 r = journal_file_move_to_object(f, -1, p, &o);
971 if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
972 log_error("Tag failed verification at %"PRIu64, p);
977 f->hmac_running = false;
978 last_tag_realtime = rt;
979 last_sealed_realtime = entry_realtime;
982 last_tag = p + ALIGN64(le64toh(o->object.size));
985 last_epoch = le64toh(o->tag.epoch);
994 if (p == le64toh(f->header->tail_object_offset))
997 p = p + ALIGN64(le64toh(o->object.size));
1001 log_error("Tail object pointer dead");
1006 if (n_objects != le64toh(f->header->n_objects)) {
1007 log_error("Object number mismatch");
1012 if (n_entries != le64toh(f->header->n_entries)) {
1013 log_error("Entry number mismatch");
1018 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
1019 n_data != le64toh(f->header->n_data)) {
1020 log_error("Data number mismatch");
1025 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
1026 n_fields != le64toh(f->header->n_fields)) {
1027 log_error("Field number mismatch");
1032 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
1033 n_tags != le64toh(f->header->n_tags)) {
1034 log_error("Tag number mismatch");
1039 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
1040 n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
1041 log_error("Entry array number mismatch");
1046 if (n_data_hash_tables != 1) {
1047 log_error("Missing data hash table");
1052 if (n_field_hash_tables != 1) {
1053 log_error("Missing field hash table");
1058 if (!found_main_entry_array) {
1059 log_error("Missing entry array");
1064 if (entry_seqnum_set &&
1065 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
1066 log_error("Invalid tail seqnum");
1071 if (entry_monotonic_set &&
1072 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
1073 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
1074 log_error("Invalid tail monotonic timestamp");
1079 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
1080 log_error("Invalid tail realtime timestamp");
1085 /* Second iteration: we follow all objects referenced from the
1086 * two entry points: the object hash table and the entry
1087 * array. We also check that everything referenced (directly
1088 * or indirectly) in the data hash table also exists in the
1089 * entry array, and vice versa. Note that we do not care for
1090 * unreferenced objects. We only care that everything that is
1091 * referenced is consistent. */
1093 r = verify_entry_array(f,
1095 entry_fd, n_entries,
1096 entry_array_fd, n_entry_arrays,
1102 r = verify_hash_table(f,
1104 entry_fd, n_entries,
1105 entry_array_fd, n_entry_arrays,
1114 mmap_cache_close_fd(f->mmap, data_fd);
1115 mmap_cache_close_fd(f->mmap, entry_fd);
1116 mmap_cache_close_fd(f->mmap, entry_array_fd);
1118 close_nointr_nofail(data_fd);
1119 close_nointr_nofail(entry_fd);
1120 close_nointr_nofail(entry_array_fd);
1122 if (first_contained)
1123 *first_contained = le64toh(f->header->head_entry_realtime);
1125 *last_validated = last_sealed_realtime;
1127 *last_contained = le64toh(f->header->tail_entry_realtime);
1135 log_error("File corruption detected at %s:%"PRIu64" (of %llu bytes, %"PRIu64"%%).",
1138 (unsigned long long) f->last_stat.st_size,
1139 100 * p / f->last_stat.st_size);
1142 mmap_cache_close_fd(f->mmap, data_fd);
1143 close_nointr_nofail(data_fd);
1146 if (entry_fd >= 0) {
1147 mmap_cache_close_fd(f->mmap, entry_fd);
1148 close_nointr_nofail(entry_fd);
1151 if (entry_array_fd >= 0) {
1152 mmap_cache_close_fd(f->mmap, entry_array_fd);
1153 close_nointr_nofail(entry_array_fd);