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 * - write tag only if non-tag objects have been written
40 * - write bit mucking test
42 * - Allow building without libgcrypt
48 static int journal_file_object_verify(JournalFile *f, Object *o) {
52 /* This does various superficial tests about the length an
53 * possible field values. It does not follow any references to
56 if ((o->object.flags & OBJECT_COMPRESSED) &&
57 o->object.type != OBJECT_DATA)
60 switch (o->object.type) {
65 if (le64toh(o->data.entry_offset) <= 0 ||
66 le64toh(o->data.n_entries) <= 0)
69 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
72 h1 = le64toh(o->data.hash);
74 if (o->object.flags & OBJECT_COMPRESSED) {
76 uint64_t alloc = 0, b_size;
78 if (!uncompress_blob(o->data.payload,
79 le64toh(o->object.size) - offsetof(Object, data.payload),
83 h2 = hash64(b, b_size);
86 h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
95 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
100 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
103 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
106 if (le64toh(o->entry.seqnum) <= 0 ||
107 le64toh(o->entry.realtime) <= 0)
112 case OBJECT_DATA_HASH_TABLE:
113 case OBJECT_FIELD_HASH_TABLE:
114 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0)
117 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
122 case OBJECT_ENTRY_ARRAY:
123 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0)
126 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
132 if (le64toh(o->object.size) != sizeof(TagObject))
140 static void draw_progress(uint64_t p, usec_t *last_usec) {
144 if (!isatty(STDOUT_FILENO))
147 z = now(CLOCK_MONOTONIC);
150 if (x != 0 && x + 40 * USEC_PER_MSEC > z)
155 n = (3 * columns()) / 4;
156 j = (n * (unsigned) p) / 65535ULL;
159 fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
161 for (i = 0; i < j; i++)
162 fputs("\xe2\x96\x88", stdout);
164 fputs(ANSI_HIGHLIGHT_OFF, stdout);
166 for (i = 0; i < k; i++)
167 fputs("\xe2\x96\x91", stdout);
169 printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU);
171 fputs("\r\x1B[?25h", stdout);
175 static void flush_progress(void) {
178 if (!isatty(STDOUT_FILENO))
181 n = (3 * columns()) / 4;
185 for (i = 0; i < n + 5; i++)
192 static int write_uint64(int fd, uint64_t p) {
195 k = write(fd, &p, sizeof(p));
204 static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
219 r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z);
235 static int entry_points_to_data(
248 assert(entry_fd >= 0);
250 if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
251 log_error("Data object references invalid entry at %llu", (unsigned long long) data_p);
255 r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
259 n = journal_file_entry_n_items(o);
260 for (i = 0; i < n; i++)
261 if (le64toh(o->entry.items[i].object_offset) == data_p) {
267 log_error("Data object not referenced by linked entry at %llu", (unsigned long long) data_p);
271 /* Check if this entry is also in main entry array. Since the
272 * main entry array has already been verified we can rely on
275 n = le64toh(f->header->n_entries);
276 a = le64toh(f->header->entry_array_offset);
282 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
286 m = journal_file_entry_array_n_items(o);
287 for (j = 0; i < n && j < m; i++, j++)
288 if (le64toh(o->entry_array.items[j]) == entry_p)
291 a = le64toh(o->entry_array.next_entry_array_offset);;
297 static int verify_data(
299 Object *o, uint64_t p,
300 int entry_fd, uint64_t n_entries,
301 int entry_array_fd, uint64_t n_entry_arrays) {
303 uint64_t i, n, a, last, q;
308 assert(entry_fd >= 0);
309 assert(entry_array_fd >= 0);
311 n = le64toh(o->data.n_entries);
312 a = le64toh(o->data.entry_array_offset);
314 /* We already checked this earlier */
317 last = q = le64toh(o->data.entry_offset);
318 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
327 log_error("Array chain too short at %llu.", (unsigned long long) p);
331 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
332 log_error("Invalid array at %llu.", (unsigned long long) p);
336 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
340 next = le64toh(o->entry_array.next_entry_array_offset);
341 if (next != 0 && next <= a) {
342 log_error("Array chain has cycle at %llu.", (unsigned long long) p);
346 m = journal_file_entry_array_n_items(o);
347 for (j = 0; i < n && j < m; i++, j++) {
349 q = le64toh(o->entry_array.items[j]);
351 log_error("Data object's entry array not sorted at %llu.", (unsigned long long) p);
356 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
360 /* Pointer might have moved, reposition */
361 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
372 static int verify_hash_table(
374 int data_fd, uint64_t n_data,
375 int entry_fd, uint64_t n_entries,
376 int entry_array_fd, uint64_t n_entry_arrays,
383 assert(data_fd >= 0);
384 assert(entry_fd >= 0);
385 assert(entry_array_fd >= 0);
388 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
389 for (i = 0; i < n; i++) {
390 uint64_t last = 0, p;
392 draw_progress(0xC000 + (0x3FFF * i / n), last_usec);
394 p = le64toh(f->data_hash_table[i].head_hash_offset);
399 if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
400 log_error("Invalid data object at hash entry %llu of %llu.",
401 (unsigned long long) i, (unsigned long long) n);
405 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
409 next = le64toh(o->data.next_hash_offset);
410 if (next != 0 && next <= p) {
411 log_error("Hash chain has a cycle in hash entry %llu of %llu.",
412 (unsigned long long) i, (unsigned long long) n);
416 if (le64toh(o->data.hash) % n != i) {
417 log_error("Hash value mismatch in hash entry %llu of %llu.",
418 (unsigned long long) i, (unsigned long long) n);
422 r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
430 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
431 log_error("Tail hash pointer mismatch in hash table.");
439 static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
444 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
447 q = le64toh(f->data_hash_table[h].head_hash_offset);
454 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
458 q = le64toh(o->data.next_hash_offset);
464 static int verify_entry(
466 Object *o, uint64_t p,
467 int data_fd, uint64_t n_data) {
474 assert(data_fd >= 0);
476 n = journal_file_entry_n_items(o);
477 for (i = 0; i < n; i++) {
481 q = le64toh(o->entry.items[i].object_offset);
482 h = le64toh(o->entry.items[i].hash);
484 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
485 log_error("Invalid data object at entry %llu.",
486 (unsigned long long) o);
490 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
494 if (le64toh(u->data.hash) != h) {
495 log_error("Hash mismatch for data object at entry %llu.",
496 (unsigned long long) p);
500 r = data_object_in_hash_table(f, h, q);
504 log_error("Data object missing from hash at entry %llu.",
505 (unsigned long long) p);
513 static int verify_entry_array(
515 int data_fd, uint64_t n_data,
516 int entry_fd, uint64_t n_entries,
517 int entry_array_fd, uint64_t n_entry_arrays,
520 uint64_t i = 0, a, n, last = 0;
524 assert(data_fd >= 0);
525 assert(entry_fd >= 0);
526 assert(entry_array_fd >= 0);
529 n = le64toh(f->header->n_entries);
530 a = le64toh(f->header->entry_array_offset);
535 draw_progress(0x8000 + (0x3FFF * i / n), last_usec);
538 log_error("Array chain too short at %llu of %llu.",
539 (unsigned long long) i, (unsigned long long) n);
543 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
544 log_error("Invalid array at %llu of %llu.",
545 (unsigned long long) i, (unsigned long long) n);
549 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
553 next = le64toh(o->entry_array.next_entry_array_offset);
554 if (next != 0 && next <= a) {
555 log_error("Array chain has cycle at %llu of %llu.",
556 (unsigned long long) i, (unsigned long long) n);
560 m = journal_file_entry_array_n_items(o);
561 for (j = 0; i < n && j < m; i++, j++) {
564 p = le64toh(o->entry_array.items[j]);
566 log_error("Entry array not sorted at %llu of %llu.",
567 (unsigned long long) i, (unsigned long long) n);
572 if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
573 log_error("Invalid array entry at %llu of %llu.",
574 (unsigned long long) i, (unsigned long long) n);
578 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
582 r = verify_entry(f, o, p, data_fd, n_data);
586 /* Pointer might have moved, reposition */
587 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
598 static int journal_file_parse_seed(JournalFile *f, const char *s) {
603 unsigned long long start, interval;
605 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
606 seed = malloc(seed_size);
611 for (c = 0; c < seed_size; c++) {
630 seed[c] = (uint8_t) (x * 16 + y);
639 r = sscanf(k, "%llx-%llx", &start, &interval);
645 f->fsprg_seed = seed;
646 f->fsprg_seed_size = seed_size;
647 f->fsprg_start_usec = start;
648 f->fsprg_interval_usec = interval;
653 int journal_file_verify(JournalFile *f, const char *seed) {
656 uint64_t p = 0, last_tag = 0, last_epoch = 0;
657 uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
658 sd_id128_t entry_boot_id;
659 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
660 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;
661 usec_t last_usec = 0;
662 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
663 char data_path[] = "/var/tmp/journal-data-XXXXXX",
664 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
665 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
670 r = journal_file_parse_seed(f, seed);
672 log_error("Failed to parse seed.");
677 data_fd = mkostemp(data_path, O_CLOEXEC);
679 log_error("Failed to create data file: %m");
685 entry_fd = mkostemp(entry_path, O_CLOEXEC);
687 log_error("Failed to create entry file: %m");
693 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
694 if (entry_array_fd < 0) {
695 log_error("Failed to create entry array file: %m");
699 unlink(entry_array_path);
701 /* First iteration: we go through all objects, verify the
702 * superficial structure, headers, hashes. */
704 p = le64toh(f->header->header_size);
706 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
708 r = journal_file_move_to_object(f, -1, p, &o);
710 log_error("Invalid object at %llu", (unsigned long long) p);
714 if (le64toh(f->header->tail_object_offset) < p) {
715 log_error("Invalid tail object pointer.");
722 r = journal_file_object_verify(f, o);
724 log_error("Invalid object contents at %llu", (unsigned long long) p);
728 if (o->object.flags & OBJECT_COMPRESSED &&
729 !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
730 log_error("Compressed object without compression at %llu", (unsigned long long) p);
735 switch (o->object.type) {
738 r = write_uint64(data_fd, p);
750 r = write_uint64(entry_fd, p);
754 if (!entry_seqnum_set &&
755 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
756 log_error("Head entry sequence number incorrect");
761 if (entry_seqnum_set &&
762 entry_seqnum >= le64toh(o->entry.seqnum)) {
763 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
768 entry_seqnum = le64toh(o->entry.seqnum);
769 entry_seqnum_set = true;
771 if (entry_monotonic_set &&
772 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
773 entry_monotonic > le64toh(o->entry.monotonic)) {
774 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
779 entry_monotonic = le64toh(o->entry.monotonic);
780 entry_boot_id = o->entry.boot_id;
781 entry_monotonic_set = true;
783 if (!entry_realtime_set &&
784 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
785 log_error("Head entry realtime timestamp incorrect");
790 entry_realtime = le64toh(o->entry.realtime);
791 entry_realtime_set = true;
796 case OBJECT_DATA_HASH_TABLE:
797 if (n_data_hash_tables > 1) {
798 log_error("More than one data hash table at %llu", (unsigned long long) p);
803 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
804 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
805 log_error("Header fields for data hash table invalid.");
810 n_data_hash_tables++;
813 case OBJECT_FIELD_HASH_TABLE:
814 if (n_field_hash_tables > 1) {
815 log_error("More than one field hash table at %llu", (unsigned long long) p);
820 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
821 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
822 log_error("Header fields for field hash table invalid.");
827 n_field_hash_tables++;
830 case OBJECT_ENTRY_ARRAY:
831 r = write_uint64(entry_array_fd, p);
835 if (p == le64toh(f->header->entry_array_offset)) {
836 if (found_main_entry_array) {
837 log_error("More than one main entry array at %llu", (unsigned long long) p);
842 found_main_entry_array = true;
851 if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) {
852 log_error("Tag object without authentication at %llu", (unsigned long long) p);
857 if (le64toh(o->tag.seqnum) != n_tags + 1) {
858 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
863 if (le64toh(o->tag.epoch) < last_epoch) {
864 log_error("Epoch sequence out of synchronization at %llu", (unsigned long long) p);
869 /* OK, now we know the epoch. So let's now set
870 * it, and calculate the HMAC for everything
871 * since the last tag. */
872 r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
876 r = journal_file_hmac_start(f);
881 r = journal_file_hmac_put_header(f);
885 q = le64toh(f->header->header_size);
890 r = journal_file_move_to_object(f, -1, q, &o);
894 r = journal_file_hmac_put_object(f, -1, q);
898 q = q + ALIGN64(le64toh(o->object.size));
901 /* Position might have changed, let's reposition things */
902 r = journal_file_move_to_object(f, -1, p, &o);
906 if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
907 log_error("Tag did not authenticate at %llu", (unsigned long long) p);
912 f->hmac_running = false;
914 last_tag = p + ALIGN64(le64toh(o->object.size));
923 if (p == le64toh(f->header->tail_object_offset))
926 p = p + ALIGN64(le64toh(o->object.size));
929 if (n_objects != le64toh(f->header->n_objects)) {
930 log_error("Object number mismatch");
935 if (n_entries != le64toh(f->header->n_entries)) {
936 log_error("Entry number mismatch");
941 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
942 n_data != le64toh(f->header->n_data)) {
943 log_error("Data number mismatch");
948 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
949 n_fields != le64toh(f->header->n_fields)) {
950 log_error("Field number mismatch");
955 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
956 n_tags != le64toh(f->header->n_tags)) {
957 log_error("Tag number mismatch");
962 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
963 n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
964 log_error("Entry array number mismatch");
969 if (n_data_hash_tables != 1) {
970 log_error("Missing data hash table");
975 if (n_field_hash_tables != 1) {
976 log_error("Missing field hash table");
981 if (!found_main_entry_array) {
982 log_error("Missing entry array");
987 if (entry_seqnum_set &&
988 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
989 log_error("Invalid tail seqnum");
994 if (entry_monotonic_set &&
995 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
996 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
997 log_error("Invalid tail monotonic timestamp");
1002 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
1003 log_error("Invalid tail realtime timestamp");
1008 /* Second iteration: we follow all objects referenced from the
1009 * two entry points: the object hash table and the entry
1010 * array. We also check that everything referenced (directly
1011 * or indirectly) in the data hash table also exists in the
1012 * entry array, and vice versa. Note that we do not care for
1013 * unreferenced objects. We only care that everything that is
1014 * referenced is consistent. */
1016 r = verify_entry_array(f,
1018 entry_fd, n_entries,
1019 entry_array_fd, n_entry_arrays,
1024 r = verify_hash_table(f,
1026 entry_fd, n_entries,
1027 entry_array_fd, n_entry_arrays,
1034 mmap_cache_close_fd(f->mmap, data_fd);
1035 mmap_cache_close_fd(f->mmap, entry_fd);
1036 mmap_cache_close_fd(f->mmap, entry_array_fd);
1038 close_nointr_nofail(data_fd);
1039 close_nointr_nofail(entry_fd);
1040 close_nointr_nofail(entry_array_fd);
1047 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
1049 (unsigned long long) p,
1050 (unsigned long long) f->last_stat.st_size,
1051 (unsigned long long) (100 * p / f->last_stat.st_size));
1054 mmap_cache_close_fd(f->mmap, data_fd);
1055 close_nointr_nofail(data_fd);
1058 if (entry_fd >= 0) {
1059 mmap_cache_close_fd(f->mmap, entry_fd);
1060 close_nointr_nofail(entry_fd);
1063 if (entry_array_fd >= 0) {
1064 mmap_cache_close_fd(f->mmap, entry_array_fd);
1065 close_nointr_nofail(entry_array_fd);