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
41 * - tag timestamps should be between entry timestamps
43 * - Allow building without libgcrypt
49 static int journal_file_object_verify(JournalFile *f, Object *o) {
53 /* This does various superficial tests about the length an
54 * possible field values. It does not follow any references to
57 if ((o->object.flags & OBJECT_COMPRESSED) &&
58 o->object.type != OBJECT_DATA)
61 switch (o->object.type) {
66 if (le64toh(o->data.entry_offset) <= 0 ||
67 le64toh(o->data.n_entries) <= 0)
70 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
73 h1 = le64toh(o->data.hash);
75 if (o->object.flags & OBJECT_COMPRESSED) {
77 uint64_t alloc = 0, b_size;
79 if (!uncompress_blob(o->data.payload,
80 le64toh(o->object.size) - offsetof(Object, data.payload),
84 h2 = hash64(b, b_size);
87 h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
96 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
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 le64toh(o->entry.realtime) <= 0)
113 case OBJECT_DATA_HASH_TABLE:
114 case OBJECT_FIELD_HASH_TABLE:
115 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0)
118 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
123 case OBJECT_ENTRY_ARRAY:
124 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0)
127 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
133 if (le64toh(o->object.size) != sizeof(TagObject))
141 static void draw_progress(uint64_t p, usec_t *last_usec) {
145 if (!isatty(STDOUT_FILENO))
148 z = now(CLOCK_MONOTONIC);
151 if (x != 0 && x + 40 * USEC_PER_MSEC > z)
156 n = (3 * columns()) / 4;
157 j = (n * (unsigned) p) / 65535ULL;
160 fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
162 for (i = 0; i < j; i++)
163 fputs("\xe2\x96\x88", stdout);
165 fputs(ANSI_HIGHLIGHT_OFF, stdout);
167 for (i = 0; i < k; i++)
168 fputs("\xe2\x96\x91", stdout);
170 printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU);
172 fputs("\r\x1B[?25h", stdout);
176 static void flush_progress(void) {
179 if (!isatty(STDOUT_FILENO))
182 n = (3 * columns()) / 4;
186 for (i = 0; i < n + 5; i++)
193 static int write_uint64(int fd, uint64_t p) {
196 k = write(fd, &p, sizeof(p));
205 static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
220 r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z);
236 static int entry_points_to_data(
249 assert(entry_fd >= 0);
251 if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
252 log_error("Data object references invalid entry at %llu", (unsigned long long) data_p);
256 r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
260 n = journal_file_entry_n_items(o);
261 for (i = 0; i < n; i++)
262 if (le64toh(o->entry.items[i].object_offset) == data_p) {
268 log_error("Data object not referenced by linked entry at %llu", (unsigned long long) data_p);
272 /* Check if this entry is also in main entry array. Since the
273 * main entry array has already been verified we can rely on
276 n = le64toh(f->header->n_entries);
277 a = le64toh(f->header->entry_array_offset);
283 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
287 m = journal_file_entry_array_n_items(o);
288 for (j = 0; i < n && j < m; i++, j++)
289 if (le64toh(o->entry_array.items[j]) == entry_p)
292 a = le64toh(o->entry_array.next_entry_array_offset);;
298 static int verify_data(
300 Object *o, uint64_t p,
301 int entry_fd, uint64_t n_entries,
302 int entry_array_fd, uint64_t n_entry_arrays) {
304 uint64_t i, n, a, last, q;
309 assert(entry_fd >= 0);
310 assert(entry_array_fd >= 0);
312 n = le64toh(o->data.n_entries);
313 a = le64toh(o->data.entry_array_offset);
315 /* We already checked this earlier */
318 last = q = le64toh(o->data.entry_offset);
319 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
328 log_error("Array chain too short at %llu.", (unsigned long long) p);
332 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
333 log_error("Invalid array at %llu.", (unsigned long long) p);
337 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
341 next = le64toh(o->entry_array.next_entry_array_offset);
342 if (next != 0 && next <= a) {
343 log_error("Array chain has cycle at %llu.", (unsigned long long) p);
347 m = journal_file_entry_array_n_items(o);
348 for (j = 0; i < n && j < m; i++, j++) {
350 q = le64toh(o->entry_array.items[j]);
352 log_error("Data object's entry array not sorted at %llu.", (unsigned long long) p);
357 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
361 /* Pointer might have moved, reposition */
362 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
373 static int verify_hash_table(
375 int data_fd, uint64_t n_data,
376 int entry_fd, uint64_t n_entries,
377 int entry_array_fd, uint64_t n_entry_arrays,
384 assert(data_fd >= 0);
385 assert(entry_fd >= 0);
386 assert(entry_array_fd >= 0);
389 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
390 for (i = 0; i < n; i++) {
391 uint64_t last = 0, p;
393 draw_progress(0xC000 + (0x3FFF * i / n), last_usec);
395 p = le64toh(f->data_hash_table[i].head_hash_offset);
400 if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
401 log_error("Invalid data object at hash entry %llu of %llu.",
402 (unsigned long long) i, (unsigned long long) n);
406 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
410 next = le64toh(o->data.next_hash_offset);
411 if (next != 0 && next <= p) {
412 log_error("Hash chain has a cycle in hash entry %llu of %llu.",
413 (unsigned long long) i, (unsigned long long) n);
417 if (le64toh(o->data.hash) % n != i) {
418 log_error("Hash value mismatch in hash entry %llu of %llu.",
419 (unsigned long long) i, (unsigned long long) n);
423 r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
431 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
432 log_error("Tail hash pointer mismatch in hash table.");
440 static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
445 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
448 q = le64toh(f->data_hash_table[h].head_hash_offset);
455 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
459 q = le64toh(o->data.next_hash_offset);
465 static int verify_entry(
467 Object *o, uint64_t p,
468 int data_fd, uint64_t n_data) {
475 assert(data_fd >= 0);
477 n = journal_file_entry_n_items(o);
478 for (i = 0; i < n; i++) {
482 q = le64toh(o->entry.items[i].object_offset);
483 h = le64toh(o->entry.items[i].hash);
485 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
486 log_error("Invalid data object at entry %llu.",
487 (unsigned long long) o);
491 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
495 if (le64toh(u->data.hash) != h) {
496 log_error("Hash mismatch for data object at entry %llu.",
497 (unsigned long long) p);
501 r = data_object_in_hash_table(f, h, q);
505 log_error("Data object missing from hash at entry %llu.",
506 (unsigned long long) p);
514 static int verify_entry_array(
516 int data_fd, uint64_t n_data,
517 int entry_fd, uint64_t n_entries,
518 int entry_array_fd, uint64_t n_entry_arrays,
521 uint64_t i = 0, a, n, last = 0;
525 assert(data_fd >= 0);
526 assert(entry_fd >= 0);
527 assert(entry_array_fd >= 0);
530 n = le64toh(f->header->n_entries);
531 a = le64toh(f->header->entry_array_offset);
536 draw_progress(0x8000 + (0x3FFF * i / n), last_usec);
539 log_error("Array chain too short at %llu of %llu.",
540 (unsigned long long) i, (unsigned long long) n);
544 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
545 log_error("Invalid array at %llu of %llu.",
546 (unsigned long long) i, (unsigned long long) n);
550 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
554 next = le64toh(o->entry_array.next_entry_array_offset);
555 if (next != 0 && next <= a) {
556 log_error("Array chain has cycle at %llu of %llu.",
557 (unsigned long long) i, (unsigned long long) n);
561 m = journal_file_entry_array_n_items(o);
562 for (j = 0; i < n && j < m; i++, j++) {
565 p = le64toh(o->entry_array.items[j]);
567 log_error("Entry array not sorted at %llu of %llu.",
568 (unsigned long long) i, (unsigned long long) n);
573 if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
574 log_error("Invalid array entry at %llu of %llu.",
575 (unsigned long long) i, (unsigned long long) n);
579 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
583 r = verify_entry(f, o, p, data_fd, n_data);
587 /* Pointer might have moved, reposition */
588 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
599 static int journal_file_parse_verification_key(JournalFile *f, const char *key) {
604 unsigned long long start, interval;
606 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
607 seed = malloc(seed_size);
612 for (c = 0; c < seed_size; c++) {
631 seed[c] = (uint8_t) (x * 16 + y);
640 r = sscanf(k, "%llx-%llx", &start, &interval);
646 f->fsprg_seed = seed;
647 f->fsprg_seed_size = seed_size;
649 f->fss_start_usec = start;
650 f->fss_interval_usec = interval;
655 int journal_file_verify(JournalFile *f, const char *key) {
658 uint64_t p = 0, last_tag = 0, last_epoch = 0;
659 uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
660 sd_id128_t entry_boot_id;
661 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
662 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;
663 usec_t last_usec = 0;
664 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
665 char data_path[] = "/var/tmp/journal-data-XXXXXX",
666 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
667 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
672 r = journal_file_parse_verification_key(f, key);
674 log_error("Failed to parse seed.");
679 data_fd = mkostemp(data_path, O_CLOEXEC);
681 log_error("Failed to create data file: %m");
687 entry_fd = mkostemp(entry_path, O_CLOEXEC);
689 log_error("Failed to create entry file: %m");
695 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
696 if (entry_array_fd < 0) {
697 log_error("Failed to create entry array file: %m");
701 unlink(entry_array_path);
703 /* First iteration: we go through all objects, verify the
704 * superficial structure, headers, hashes. */
706 p = le64toh(f->header->header_size);
708 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
710 r = journal_file_move_to_object(f, -1, p, &o);
712 log_error("Invalid object at %llu", (unsigned long long) p);
716 if (le64toh(f->header->tail_object_offset) < p) {
717 log_error("Invalid tail object pointer.");
724 r = journal_file_object_verify(f, o);
726 log_error("Invalid object contents at %llu", (unsigned long long) p);
730 if (o->object.flags & OBJECT_COMPRESSED &&
731 !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
732 log_error("Compressed object without compression at %llu", (unsigned long long) p);
737 switch (o->object.type) {
740 r = write_uint64(data_fd, p);
752 r = write_uint64(entry_fd, p);
756 if (!entry_seqnum_set &&
757 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
758 log_error("Head entry sequence number incorrect");
763 if (entry_seqnum_set &&
764 entry_seqnum >= le64toh(o->entry.seqnum)) {
765 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
770 entry_seqnum = le64toh(o->entry.seqnum);
771 entry_seqnum_set = true;
773 if (entry_monotonic_set &&
774 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
775 entry_monotonic > le64toh(o->entry.monotonic)) {
776 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
781 entry_monotonic = le64toh(o->entry.monotonic);
782 entry_boot_id = o->entry.boot_id;
783 entry_monotonic_set = true;
785 if (!entry_realtime_set &&
786 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
787 log_error("Head entry realtime timestamp incorrect");
792 entry_realtime = le64toh(o->entry.realtime);
793 entry_realtime_set = true;
798 case OBJECT_DATA_HASH_TABLE:
799 if (n_data_hash_tables > 1) {
800 log_error("More than one data hash table at %llu", (unsigned long long) p);
805 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
806 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
807 log_error("Header fields for data hash table invalid.");
812 n_data_hash_tables++;
815 case OBJECT_FIELD_HASH_TABLE:
816 if (n_field_hash_tables > 1) {
817 log_error("More than one field hash table at %llu", (unsigned long long) p);
822 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
823 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
824 log_error("Header fields for field hash table invalid.");
829 n_field_hash_tables++;
832 case OBJECT_ENTRY_ARRAY:
833 r = write_uint64(entry_array_fd, p);
837 if (p == le64toh(f->header->entry_array_offset)) {
838 if (found_main_entry_array) {
839 log_error("More than one main entry array at %llu", (unsigned long long) p);
844 found_main_entry_array = true;
853 if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED)) {
854 log_error("Tag object without sealing at %llu", (unsigned long long) p);
859 if (le64toh(o->tag.seqnum) != n_tags + 1) {
860 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
865 if (le64toh(o->tag.epoch) < last_epoch) {
866 log_error("Epoch sequence out of synchronization at %llu", (unsigned long long) p);
871 /* OK, now we know the epoch. So let's now set
872 * it, and calculate the HMAC for everything
873 * since the last tag. */
874 r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
878 r = journal_file_hmac_start(f);
883 r = journal_file_hmac_put_header(f);
887 q = le64toh(f->header->header_size);
892 r = journal_file_move_to_object(f, -1, q, &o);
896 r = journal_file_hmac_put_object(f, -1, q);
900 q = q + ALIGN64(le64toh(o->object.size));
903 /* Position might have changed, let's reposition things */
904 r = journal_file_move_to_object(f, -1, p, &o);
908 if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
909 log_error("Tag failed verification at %llu", (unsigned long long) p);
914 f->hmac_running = false;
916 last_tag = p + ALIGN64(le64toh(o->object.size));
925 if (p == le64toh(f->header->tail_object_offset))
928 p = p + ALIGN64(le64toh(o->object.size));
931 if (n_objects != le64toh(f->header->n_objects)) {
932 log_error("Object number mismatch");
937 if (n_entries != le64toh(f->header->n_entries)) {
938 log_error("Entry number mismatch");
943 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
944 n_data != le64toh(f->header->n_data)) {
945 log_error("Data number mismatch");
950 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
951 n_fields != le64toh(f->header->n_fields)) {
952 log_error("Field number mismatch");
957 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
958 n_tags != le64toh(f->header->n_tags)) {
959 log_error("Tag number mismatch");
964 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
965 n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
966 log_error("Entry array number mismatch");
971 if (n_data_hash_tables != 1) {
972 log_error("Missing data hash table");
977 if (n_field_hash_tables != 1) {
978 log_error("Missing field hash table");
983 if (!found_main_entry_array) {
984 log_error("Missing entry array");
989 if (entry_seqnum_set &&
990 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
991 log_error("Invalid tail seqnum");
996 if (entry_monotonic_set &&
997 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
998 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
999 log_error("Invalid tail monotonic timestamp");
1004 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
1005 log_error("Invalid tail realtime timestamp");
1010 /* Second iteration: we follow all objects referenced from the
1011 * two entry points: the object hash table and the entry
1012 * array. We also check that everything referenced (directly
1013 * or indirectly) in the data hash table also exists in the
1014 * entry array, and vice versa. Note that we do not care for
1015 * unreferenced objects. We only care that everything that is
1016 * referenced is consistent. */
1018 r = verify_entry_array(f,
1020 entry_fd, n_entries,
1021 entry_array_fd, n_entry_arrays,
1026 r = verify_hash_table(f,
1028 entry_fd, n_entries,
1029 entry_array_fd, n_entry_arrays,
1036 mmap_cache_close_fd(f->mmap, data_fd);
1037 mmap_cache_close_fd(f->mmap, entry_fd);
1038 mmap_cache_close_fd(f->mmap, entry_array_fd);
1040 close_nointr_nofail(data_fd);
1041 close_nointr_nofail(entry_fd);
1042 close_nointr_nofail(entry_array_fd);
1049 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
1051 (unsigned long long) p,
1052 (unsigned long long) f->last_stat.st_size,
1053 (unsigned long long) (100 * p / f->last_stat.st_size));
1056 mmap_cache_close_fd(f->mmap, data_fd);
1057 close_nointr_nofail(data_fd);
1060 if (entry_fd >= 0) {
1061 mmap_cache_close_fd(f->mmap, entry_fd);
1062 close_nointr_nofail(entry_fd);
1065 if (entry_array_fd >= 0) {
1066 mmap_cache_close_fd(f->mmap, entry_array_fd);
1067 close_nointr_nofail(entry_array_fd);