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 bit mucking test
39 * - evolve key even if nothing happened in regular intervals
41 * - Allow building without libgcrypt
47 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));
92 if (!VALID64(o->data.next_hash_offset) ||
93 !VALID64(o->data.next_field_offset) ||
94 !VALID64(o->data.entry_offset) ||
95 !VALID64(o->data.entry_array_offset))
102 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
105 if (!VALID64(o->field.next_hash_offset) ||
106 !VALID64(o->field.head_data_offset))
111 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
114 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
117 if (le64toh(o->entry.seqnum) <= 0 ||
118 le64toh(o->entry.realtime) <= 0)
121 for (i = 0; i < journal_file_entry_n_items(o); i++) {
122 if (o->entry.items[i].object_offset == 0 ||
123 !VALID64(o->entry.items[i].object_offset))
129 case OBJECT_DATA_HASH_TABLE:
130 case OBJECT_FIELD_HASH_TABLE:
131 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0)
134 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
139 case OBJECT_ENTRY_ARRAY:
140 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0)
143 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
146 if (!VALID64(o->entry_array.next_entry_array_offset))
152 if (le64toh(o->object.size) != sizeof(TagObject))
160 static void draw_progress(uint64_t p, usec_t *last_usec) {
164 if (!isatty(STDOUT_FILENO))
167 z = now(CLOCK_MONOTONIC);
170 if (x != 0 && x + 40 * USEC_PER_MSEC > z)
175 n = (3 * columns()) / 4;
176 j = (n * (unsigned) p) / 65535ULL;
179 fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
181 for (i = 0; i < j; i++)
182 fputs("\xe2\x96\x88", stdout);
184 fputs(ANSI_HIGHLIGHT_OFF, stdout);
186 for (i = 0; i < k; i++)
187 fputs("\xe2\x96\x91", stdout);
189 printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU);
191 fputs("\r\x1B[?25h", stdout);
195 static void flush_progress(void) {
198 if (!isatty(STDOUT_FILENO))
201 n = (3 * columns()) / 4;
205 for (i = 0; i < n + 5; i++)
212 static int write_uint64(int fd, uint64_t p) {
215 k = write(fd, &p, sizeof(p));
224 static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
239 r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z);
255 static int entry_points_to_data(
268 assert(entry_fd >= 0);
270 if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
271 log_error("Data object references invalid entry at %llu", (unsigned long long) data_p);
275 r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
279 n = journal_file_entry_n_items(o);
280 for (i = 0; i < n; i++)
281 if (le64toh(o->entry.items[i].object_offset) == data_p) {
287 log_error("Data object not referenced by linked entry at %llu", (unsigned long long) data_p);
291 /* Check if this entry is also in main entry array. Since the
292 * main entry array has already been verified we can rely on
295 n = le64toh(f->header->n_entries);
296 a = le64toh(f->header->entry_array_offset);
302 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
306 m = journal_file_entry_array_n_items(o);
307 for (j = 0; i < n && j < m; i++, j++)
308 if (le64toh(o->entry_array.items[j]) == entry_p)
311 a = le64toh(o->entry_array.next_entry_array_offset);
317 static int verify_data(
319 Object *o, uint64_t p,
320 int entry_fd, uint64_t n_entries,
321 int entry_array_fd, uint64_t n_entry_arrays) {
323 uint64_t i, n, a, last, q;
328 assert(entry_fd >= 0);
329 assert(entry_array_fd >= 0);
331 n = le64toh(o->data.n_entries);
332 a = le64toh(o->data.entry_array_offset);
334 /* We already checked this earlier */
337 last = q = le64toh(o->data.entry_offset);
338 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
347 log_error("Array chain too short at %llu", (unsigned long long) p);
351 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
352 log_error("Invalid array at %llu", (unsigned long long) p);
356 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
360 next = le64toh(o->entry_array.next_entry_array_offset);
361 if (next != 0 && next <= a) {
362 log_error("Array chain has cycle at %llu", (unsigned long long) p);
366 m = journal_file_entry_array_n_items(o);
367 for (j = 0; i < n && j < m; i++, j++) {
369 q = le64toh(o->entry_array.items[j]);
371 log_error("Data object's entry array not sorted at %llu", (unsigned long long) p);
376 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
380 /* Pointer might have moved, reposition */
381 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
392 static int verify_hash_table(
394 int data_fd, uint64_t n_data,
395 int entry_fd, uint64_t n_entries,
396 int entry_array_fd, uint64_t n_entry_arrays,
398 bool show_progress) {
404 assert(data_fd >= 0);
405 assert(entry_fd >= 0);
406 assert(entry_array_fd >= 0);
409 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
410 for (i = 0; i < n; i++) {
411 uint64_t last = 0, p;
414 draw_progress(0xC000 + (0x3FFF * i / n), last_usec);
416 p = le64toh(f->data_hash_table[i].head_hash_offset);
421 if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
422 log_error("Invalid data object at hash entry %llu of %llu",
423 (unsigned long long) i, (unsigned long long) n);
427 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
431 next = le64toh(o->data.next_hash_offset);
432 if (next != 0 && next <= p) {
433 log_error("Hash chain has a cycle in hash entry %llu of %llu",
434 (unsigned long long) i, (unsigned long long) n);
438 if (le64toh(o->data.hash) % n != i) {
439 log_error("Hash value mismatch in hash entry %llu of %llu",
440 (unsigned long long) i, (unsigned long long) n);
444 r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
452 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
453 log_error("Tail hash pointer mismatch in hash table");
461 static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
466 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
469 q = le64toh(f->data_hash_table[h].head_hash_offset);
476 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
480 q = le64toh(o->data.next_hash_offset);
486 static int verify_entry(
488 Object *o, uint64_t p,
489 int data_fd, uint64_t n_data) {
496 assert(data_fd >= 0);
498 n = journal_file_entry_n_items(o);
499 for (i = 0; i < n; i++) {
503 q = le64toh(o->entry.items[i].object_offset);
504 h = le64toh(o->entry.items[i].hash);
506 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
507 log_error("Invalid data object at entry %llu",
508 (unsigned long long) p);
512 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
516 if (le64toh(u->data.hash) != h) {
517 log_error("Hash mismatch for data object at entry %llu",
518 (unsigned long long) p);
522 r = data_object_in_hash_table(f, h, q);
526 log_error("Data object missing from hash at entry %llu",
527 (unsigned long long) p);
535 static int verify_entry_array(
537 int data_fd, uint64_t n_data,
538 int entry_fd, uint64_t n_entries,
539 int entry_array_fd, uint64_t n_entry_arrays,
541 bool show_progress) {
543 uint64_t i = 0, a, n, last = 0;
547 assert(data_fd >= 0);
548 assert(entry_fd >= 0);
549 assert(entry_array_fd >= 0);
552 n = le64toh(f->header->n_entries);
553 a = le64toh(f->header->entry_array_offset);
559 draw_progress(0x8000 + (0x3FFF * i / n), last_usec);
562 log_error("Array chain too short at %llu of %llu",
563 (unsigned long long) i, (unsigned long long) n);
567 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
568 log_error("Invalid array at %llu of %llu",
569 (unsigned long long) i, (unsigned long long) n);
573 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
577 next = le64toh(o->entry_array.next_entry_array_offset);
578 if (next != 0 && next <= a) {
579 log_error("Array chain has cycle at %llu of %llu",
580 (unsigned long long) i, (unsigned long long) n);
584 m = journal_file_entry_array_n_items(o);
585 for (j = 0; i < n && j < m; i++, j++) {
588 p = le64toh(o->entry_array.items[j]);
590 log_error("Entry array not sorted at %llu of %llu",
591 (unsigned long long) i, (unsigned long long) n);
596 if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
597 log_error("Invalid array entry at %llu of %llu",
598 (unsigned long long) i, (unsigned long long) n);
602 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
606 r = verify_entry(f, o, p, data_fd, n_data);
610 /* Pointer might have moved, reposition */
611 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
622 static int journal_file_parse_verification_key(JournalFile *f, const char *key) {
627 unsigned long long start, interval;
629 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
630 seed = malloc(seed_size);
635 for (c = 0; c < seed_size; c++) {
654 seed[c] = (uint8_t) (x * 16 + y);
663 r = sscanf(k, "%llx-%llx", &start, &interval);
669 f->fsprg_seed = seed;
670 f->fsprg_seed_size = seed_size;
672 f->fss_start_usec = start * interval;
673 f->fss_interval_usec = interval;
678 int journal_file_verify(
681 usec_t *first_validated, usec_t *last_validated, usec_t *last_contained,
682 bool show_progress) {
685 uint64_t p = 0, last_tag = 0, last_epoch = 0, last_tag_realtime = 0;
686 uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
687 sd_id128_t entry_boot_id;
688 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
689 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;
690 usec_t last_usec = 0;
691 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
692 char data_path[] = "/var/tmp/journal-data-XXXXXX",
693 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
694 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
701 r = journal_file_parse_verification_key(f, key);
703 log_error("Failed to parse seed.");
709 data_fd = mkostemp(data_path, O_CLOEXEC);
711 log_error("Failed to create data file: %m");
717 entry_fd = mkostemp(entry_path, O_CLOEXEC);
719 log_error("Failed to create entry file: %m");
725 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
726 if (entry_array_fd < 0) {
727 log_error("Failed to create entry array file: %m");
731 unlink(entry_array_path);
734 if ((le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) != 0)
736 if (f->header->compatible_flags != 0)
739 log_error("Cannot verify file with unknown extensions.");
744 for (i = 0; i < sizeof(f->header->reserved); i++)
745 if (f->header->reserved[i] != 0) {
746 log_error("Reserved field in non-zero.");
751 /* First iteration: we go through all objects, verify the
752 * superficial structure, headers, hashes. */
754 p = le64toh(f->header->header_size);
757 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
759 r = journal_file_move_to_object(f, -1, p, &o);
761 log_error("Invalid object at %llu", (unsigned long long) p);
765 if (p > le64toh(f->header->tail_object_offset)) {
766 log_error("Invalid tail object pointer");
771 if (p == le64toh(f->header->tail_object_offset))
776 r = journal_file_object_verify(f, o);
778 log_error("Invalid object contents at %llu", (unsigned long long) p);
782 if (o->object.flags & OBJECT_COMPRESSED &&
783 !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
784 log_error("Compressed object in file without compression at %llu", (unsigned long long) p);
789 switch (o->object.type) {
792 r = write_uint64(data_fd, p);
804 if ((le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED) && n_tags <= 0) {
805 log_error("First entry before first tag at %llu", (unsigned long long) p);
810 r = write_uint64(entry_fd, p);
814 if (last_tag_realtime > le64toh(o->entry.realtime)) {
815 log_error("Older entry after newer tag at %llu", (unsigned long long) p);
820 if (!entry_seqnum_set &&
821 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
822 log_error("Head entry sequence number incorrect at %llu", (unsigned long long) p);
827 if (entry_seqnum_set &&
828 entry_seqnum >= le64toh(o->entry.seqnum)) {
829 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
834 entry_seqnum = le64toh(o->entry.seqnum);
835 entry_seqnum_set = true;
837 if (entry_monotonic_set &&
838 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
839 entry_monotonic > le64toh(o->entry.monotonic)) {
840 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
845 entry_monotonic = le64toh(o->entry.monotonic);
846 entry_boot_id = o->entry.boot_id;
847 entry_monotonic_set = true;
849 if (!entry_realtime_set &&
850 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
851 log_error("Head entry realtime timestamp incorrect");
856 entry_realtime = le64toh(o->entry.realtime);
857 entry_realtime_set = true;
862 case OBJECT_DATA_HASH_TABLE:
863 if (n_data_hash_tables > 1) {
864 log_error("More than one data hash table at %llu", (unsigned long long) p);
869 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
870 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
871 log_error("Header fields for data hash table invalid");
876 n_data_hash_tables++;
879 case OBJECT_FIELD_HASH_TABLE:
880 if (n_field_hash_tables > 1) {
881 log_error("More than one field hash table at %llu", (unsigned long long) p);
886 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
887 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
888 log_error("Header fields for field hash table invalid");
893 n_field_hash_tables++;
896 case OBJECT_ENTRY_ARRAY:
897 r = write_uint64(entry_array_fd, p);
901 if (p == le64toh(f->header->entry_array_offset)) {
902 if (found_main_entry_array) {
903 log_error("More than one main entry array at %llu", (unsigned long long) p);
908 found_main_entry_array = true;
917 if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED)) {
918 log_error("Tag object in file without sealing at %llu", (unsigned long long) p);
923 if (le64toh(o->tag.seqnum) != n_tags + 1) {
924 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
929 if (le64toh(o->tag.epoch) < last_epoch) {
930 log_error("Epoch sequence out of synchronization at %llu", (unsigned long long) p);
936 log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum));
938 rt = (o->tag.epoch + 1) * f->fss_interval_usec + f->fss_start_usec;
939 if (entry_realtime_set && entry_realtime >= rt) {
940 log_error("Tag/entry realtime timestamp out of synchronization at %llu", (unsigned long long) p);
945 /* OK, now we know the epoch. So let's now set
946 * it, and calculate the HMAC for everything
947 * since the last tag. */
948 r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
952 r = journal_file_hmac_start(f);
957 r = journal_file_hmac_put_header(f);
961 q = le64toh(f->header->header_size);
966 r = journal_file_move_to_object(f, -1, q, &o);
970 r = journal_file_hmac_put_object(f, -1, q);
974 q = q + ALIGN64(le64toh(o->object.size));
977 /* Position might have changed, let's reposition things */
978 r = journal_file_move_to_object(f, -1, p, &o);
982 if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
983 log_error("Tag failed verification at %llu", (unsigned long long) p);
988 f->hmac_running = false;
989 last_tag_realtime = rt;
992 last_tag = p + ALIGN64(le64toh(o->object.size));
993 last_epoch = le64toh(o->tag.epoch);
1003 if (p == le64toh(f->header->tail_object_offset))
1006 p = p + ALIGN64(le64toh(o->object.size));
1010 log_error("Tail object pointer dead");
1015 if (n_objects != le64toh(f->header->n_objects)) {
1016 log_error("Object number mismatch");
1021 if (n_entries != le64toh(f->header->n_entries)) {
1022 log_error("Entry number mismatch");
1027 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
1028 n_data != le64toh(f->header->n_data)) {
1029 log_error("Data number mismatch");
1034 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
1035 n_fields != le64toh(f->header->n_fields)) {
1036 log_error("Field number mismatch");
1041 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
1042 n_tags != le64toh(f->header->n_tags)) {
1043 log_error("Tag number mismatch");
1048 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
1049 n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
1050 log_error("Entry array number mismatch");
1055 if (n_data_hash_tables != 1) {
1056 log_error("Missing data hash table");
1061 if (n_field_hash_tables != 1) {
1062 log_error("Missing field hash table");
1067 if (!found_main_entry_array) {
1068 log_error("Missing entry array");
1073 if (entry_seqnum_set &&
1074 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
1075 log_error("Invalid tail seqnum");
1080 if (entry_monotonic_set &&
1081 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
1082 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
1083 log_error("Invalid tail monotonic timestamp");
1088 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
1089 log_error("Invalid tail realtime timestamp");
1094 /* Second iteration: we follow all objects referenced from the
1095 * two entry points: the object hash table and the entry
1096 * array. We also check that everything referenced (directly
1097 * or indirectly) in the data hash table also exists in the
1098 * entry array, and vice versa. Note that we do not care for
1099 * unreferenced objects. We only care that everything that is
1100 * referenced is consistent. */
1102 r = verify_entry_array(f,
1104 entry_fd, n_entries,
1105 entry_array_fd, n_entry_arrays,
1111 r = verify_hash_table(f,
1113 entry_fd, n_entries,
1114 entry_array_fd, n_entry_arrays,
1123 mmap_cache_close_fd(f->mmap, data_fd);
1124 mmap_cache_close_fd(f->mmap, entry_fd);
1125 mmap_cache_close_fd(f->mmap, entry_array_fd);
1127 close_nointr_nofail(data_fd);
1128 close_nointr_nofail(entry_fd);
1129 close_nointr_nofail(entry_array_fd);
1131 if (first_validated)
1132 *first_validated = last_tag_realtime ? le64toh(f->header->head_entry_realtime) : 0;
1134 *last_validated = last_tag_realtime;
1136 *last_contained = le64toh(f->header->tail_entry_realtime);
1144 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
1146 (unsigned long long) p,
1147 (unsigned long long) f->last_stat.st_size,
1148 (unsigned long long) (100 * p / f->last_stat.st_size));
1151 mmap_cache_close_fd(f->mmap, data_fd);
1152 close_nointr_nofail(data_fd);
1155 if (entry_fd >= 0) {
1156 mmap_cache_close_fd(f->mmap, entry_fd);
1157 close_nointr_nofail(entry_fd);
1160 if (entry_array_fd >= 0) {
1161 mmap_cache_close_fd(f->mmap, entry_array_fd);
1162 close_nointr_nofail(entry_array_fd);