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) {
51 /* This does various superficial tests about the length an
52 * possible field values. It does not follow any references to
55 if ((o->object.flags & OBJECT_COMPRESSED) &&
56 o->object.type != OBJECT_DATA)
59 switch (o->object.type) {
64 if (le64toh(o->data.entry_offset) <= 0 ||
65 le64toh(o->data.n_entries) <= 0)
68 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
71 h1 = le64toh(o->data.hash);
73 if (o->object.flags & OBJECT_COMPRESSED) {
75 uint64_t alloc = 0, b_size;
77 if (!uncompress_blob(o->data.payload,
78 le64toh(o->object.size) - offsetof(Object, data.payload),
82 h2 = hash64(b, b_size);
85 h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
94 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
99 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
102 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
105 if (le64toh(o->entry.seqnum) <= 0 ||
106 le64toh(o->entry.realtime) <= 0)
111 case OBJECT_DATA_HASH_TABLE:
112 case OBJECT_FIELD_HASH_TABLE:
113 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0)
116 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
121 case OBJECT_ENTRY_ARRAY:
122 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0)
125 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
131 if (le64toh(o->object.size) != sizeof(TagObject))
139 static void draw_progress(uint64_t p, usec_t *last_usec) {
143 if (!isatty(STDOUT_FILENO))
146 z = now(CLOCK_MONOTONIC);
149 if (x != 0 && x + 40 * USEC_PER_MSEC > z)
154 n = (3 * columns()) / 4;
155 j = (n * (unsigned) p) / 65535ULL;
158 fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
160 for (i = 0; i < j; i++)
161 fputs("\xe2\x96\x88", stdout);
163 fputs(ANSI_HIGHLIGHT_OFF, stdout);
165 for (i = 0; i < k; i++)
166 fputs("\xe2\x96\x91", stdout);
168 printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU);
170 fputs("\r\x1B[?25h", stdout);
174 static void flush_progress(void) {
177 if (!isatty(STDOUT_FILENO))
180 n = (3 * columns()) / 4;
184 for (i = 0; i < n + 5; i++)
191 static int write_uint64(int fd, uint64_t p) {
194 k = write(fd, &p, sizeof(p));
203 static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
218 r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z);
234 static int entry_points_to_data(
247 assert(entry_fd >= 0);
249 if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
250 log_error("Data object references invalid entry at %llu", (unsigned long long) data_p);
254 r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
258 n = journal_file_entry_n_items(o);
259 for (i = 0; i < n; i++)
260 if (le64toh(o->entry.items[i].object_offset) == data_p) {
266 log_error("Data object not referenced by linked entry at %llu", (unsigned long long) data_p);
270 /* Check if this entry is also in main entry array. Since the
271 * main entry array has already been verified we can rely on
274 n = le64toh(f->header->n_entries);
275 a = le64toh(f->header->entry_array_offset);
281 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
285 m = journal_file_entry_array_n_items(o);
286 for (j = 0; i < n && j < m; i++, j++)
287 if (le64toh(o->entry_array.items[j]) == entry_p)
290 a = le64toh(o->entry_array.next_entry_array_offset);
296 static int verify_data(
298 Object *o, uint64_t p,
299 int entry_fd, uint64_t n_entries,
300 int entry_array_fd, uint64_t n_entry_arrays) {
302 uint64_t i, n, a, last, q;
307 assert(entry_fd >= 0);
308 assert(entry_array_fd >= 0);
310 n = le64toh(o->data.n_entries);
311 a = le64toh(o->data.entry_array_offset);
313 /* We already checked this earlier */
316 last = q = le64toh(o->data.entry_offset);
317 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
326 log_error("Array chain too short at %llu", (unsigned long long) p);
330 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
331 log_error("Invalid array at %llu", (unsigned long long) p);
335 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
339 next = le64toh(o->entry_array.next_entry_array_offset);
340 if (next != 0 && next <= a) {
341 log_error("Array chain has cycle at %llu", (unsigned long long) p);
345 m = journal_file_entry_array_n_items(o);
346 for (j = 0; i < n && j < m; i++, j++) {
348 q = le64toh(o->entry_array.items[j]);
350 log_error("Data object's entry array not sorted at %llu", (unsigned long long) p);
355 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
359 /* Pointer might have moved, reposition */
360 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
371 static int verify_hash_table(
373 int data_fd, uint64_t n_data,
374 int entry_fd, uint64_t n_entries,
375 int entry_array_fd, uint64_t n_entry_arrays,
382 assert(data_fd >= 0);
383 assert(entry_fd >= 0);
384 assert(entry_array_fd >= 0);
387 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
388 for (i = 0; i < n; i++) {
389 uint64_t last = 0, p;
391 draw_progress(0xC000 + (0x3FFF * i / n), last_usec);
393 p = le64toh(f->data_hash_table[i].head_hash_offset);
398 if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
399 log_error("Invalid data object at hash entry %llu of %llu",
400 (unsigned long long) i, (unsigned long long) n);
404 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
408 next = le64toh(o->data.next_hash_offset);
409 if (next != 0 && next <= p) {
410 log_error("Hash chain has a cycle in hash entry %llu of %llu",
411 (unsigned long long) i, (unsigned long long) n);
415 if (le64toh(o->data.hash) % n != i) {
416 log_error("Hash value mismatch in hash entry %llu of %llu",
417 (unsigned long long) i, (unsigned long long) n);
421 r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
429 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
430 log_error("Tail hash pointer mismatch in hash table");
438 static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
443 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
446 q = le64toh(f->data_hash_table[h].head_hash_offset);
453 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
457 q = le64toh(o->data.next_hash_offset);
463 static int verify_entry(
465 Object *o, uint64_t p,
466 int data_fd, uint64_t n_data) {
473 assert(data_fd >= 0);
475 n = journal_file_entry_n_items(o);
476 for (i = 0; i < n; i++) {
480 q = le64toh(o->entry.items[i].object_offset);
481 h = le64toh(o->entry.items[i].hash);
483 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
484 log_error("Invalid data object at entry %llu",
485 (unsigned long long) p);
489 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
493 if (le64toh(u->data.hash) != h) {
494 log_error("Hash mismatch for data object at entry %llu",
495 (unsigned long long) p);
499 r = data_object_in_hash_table(f, h, q);
503 log_error("Data object missing from hash at entry %llu",
504 (unsigned long long) p);
512 static int verify_entry_array(
514 int data_fd, uint64_t n_data,
515 int entry_fd, uint64_t n_entries,
516 int entry_array_fd, uint64_t n_entry_arrays,
519 uint64_t i = 0, a, n, last = 0;
523 assert(data_fd >= 0);
524 assert(entry_fd >= 0);
525 assert(entry_array_fd >= 0);
528 n = le64toh(f->header->n_entries);
529 a = le64toh(f->header->entry_array_offset);
534 draw_progress(0x8000 + (0x3FFF * i / n), last_usec);
537 log_error("Array chain too short at %llu of %llu",
538 (unsigned long long) i, (unsigned long long) n);
542 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
543 log_error("Invalid array at %llu of %llu",
544 (unsigned long long) i, (unsigned long long) n);
548 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
552 next = le64toh(o->entry_array.next_entry_array_offset);
553 if (next != 0 && next <= a) {
554 log_error("Array chain has cycle at %llu of %llu",
555 (unsigned long long) i, (unsigned long long) n);
559 m = journal_file_entry_array_n_items(o);
560 for (j = 0; i < n && j < m; i++, j++) {
563 p = le64toh(o->entry_array.items[j]);
565 log_error("Entry array not sorted at %llu of %llu",
566 (unsigned long long) i, (unsigned long long) n);
571 if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
572 log_error("Invalid array entry at %llu of %llu",
573 (unsigned long long) i, (unsigned long long) n);
577 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
581 r = verify_entry(f, o, p, data_fd, n_data);
585 /* Pointer might have moved, reposition */
586 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
597 static int journal_file_parse_verification_key(JournalFile *f, const char *key) {
602 unsigned long long start, interval;
604 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
605 seed = malloc(seed_size);
610 for (c = 0; c < seed_size; c++) {
629 seed[c] = (uint8_t) (x * 16 + y);
638 r = sscanf(k, "%llx-%llx", &start, &interval);
644 f->fsprg_seed = seed;
645 f->fsprg_seed_size = seed_size;
647 f->fss_start_usec = start * interval;
648 f->fss_interval_usec = interval;
653 int journal_file_verify(
656 usec_t *first_validated, usec_t *last_validated, usec_t *last_contained) {
659 uint64_t p = 0, last_tag = 0, last_epoch = 0, last_tag_realtime = 0;
660 uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
661 sd_id128_t entry_boot_id;
662 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
663 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;
664 usec_t last_usec = 0;
665 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
666 char data_path[] = "/var/tmp/journal-data-XXXXXX",
667 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
668 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
673 r = journal_file_parse_verification_key(f, key);
675 log_error("Failed to parse seed.");
680 data_fd = mkostemp(data_path, O_CLOEXEC);
682 log_error("Failed to create data file: %m");
688 entry_fd = mkostemp(entry_path, O_CLOEXEC);
690 log_error("Failed to create entry file: %m");
696 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
697 if (entry_array_fd < 0) {
698 log_error("Failed to create entry array file: %m");
702 unlink(entry_array_path);
704 /* First iteration: we go through all objects, verify the
705 * superficial structure, headers, hashes. */
707 p = le64toh(f->header->header_size);
709 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
711 r = journal_file_move_to_object(f, -1, p, &o);
713 log_error("Invalid object at %llu", (unsigned long long) p);
717 if (le64toh(f->header->tail_object_offset) < p) {
718 log_error("Invalid tail object pointer");
725 r = journal_file_object_verify(f, o);
727 log_error("Invalid object contents at %llu", (unsigned long long) p);
731 if (o->object.flags & OBJECT_COMPRESSED &&
732 !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
733 log_error("Compressed object in file without compression at %llu", (unsigned long long) p);
738 switch (o->object.type) {
741 r = write_uint64(data_fd, p);
753 if ((le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED) && n_tags <= 0) {
754 log_error("First entry before first tag at %llu", (unsigned long long) p);
759 r = write_uint64(entry_fd, p);
763 if (last_tag_realtime > le64toh(o->entry.realtime)) {
764 log_error("Older entry after newer tag at %llu", (unsigned long long) p);
769 if (!entry_seqnum_set &&
770 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
771 log_error("Head entry sequence number incorrect at %llu", (unsigned long long) p);
776 if (entry_seqnum_set &&
777 entry_seqnum >= le64toh(o->entry.seqnum)) {
778 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
783 entry_seqnum = le64toh(o->entry.seqnum);
784 entry_seqnum_set = true;
786 if (entry_monotonic_set &&
787 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
788 entry_monotonic > le64toh(o->entry.monotonic)) {
789 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
794 entry_monotonic = le64toh(o->entry.monotonic);
795 entry_boot_id = o->entry.boot_id;
796 entry_monotonic_set = true;
798 if (!entry_realtime_set &&
799 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
800 log_error("Head entry realtime timestamp incorrect");
805 entry_realtime = le64toh(o->entry.realtime);
806 entry_realtime_set = true;
811 case OBJECT_DATA_HASH_TABLE:
812 if (n_data_hash_tables > 1) {
813 log_error("More than one data hash table at %llu", (unsigned long long) p);
818 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
819 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
820 log_error("Header fields for data hash table invalid");
825 n_data_hash_tables++;
828 case OBJECT_FIELD_HASH_TABLE:
829 if (n_field_hash_tables > 1) {
830 log_error("More than one field hash table at %llu", (unsigned long long) p);
835 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
836 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
837 log_error("Header fields for field hash table invalid");
842 n_field_hash_tables++;
845 case OBJECT_ENTRY_ARRAY:
846 r = write_uint64(entry_array_fd, p);
850 if (p == le64toh(f->header->entry_array_offset)) {
851 if (found_main_entry_array) {
852 log_error("More than one main entry array at %llu", (unsigned long long) p);
857 found_main_entry_array = true;
866 if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED)) {
867 log_error("Tag object in file without sealing at %llu", (unsigned long long) p);
872 log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum));
874 if (le64toh(o->tag.seqnum) != n_tags + 1) {
875 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
880 if (le64toh(o->tag.epoch) < last_epoch) {
881 log_error("Epoch sequence out of synchronization at %llu", (unsigned long long) p);
886 rt = (o->tag.epoch + 1) * f->fss_interval_usec + f->fss_start_usec;
887 if (entry_realtime_set && entry_realtime >= rt) {
888 log_error("Tag/entry realtime timestamp out of synchronization at %llu", (unsigned long long) p);
893 /* OK, now we know the epoch. So let's now set
894 * it, and calculate the HMAC for everything
895 * since the last tag. */
896 r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
900 r = journal_file_hmac_start(f);
905 r = journal_file_hmac_put_header(f);
909 q = le64toh(f->header->header_size);
914 r = journal_file_move_to_object(f, -1, q, &o);
918 r = journal_file_hmac_put_object(f, -1, q);
922 q = q + ALIGN64(le64toh(o->object.size));
925 /* Position might have changed, let's reposition things */
926 r = journal_file_move_to_object(f, -1, p, &o);
930 if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
931 log_error("Tag failed verification at %llu", (unsigned long long) p);
936 f->hmac_running = false;
938 last_tag = p + ALIGN64(le64toh(o->object.size));
939 last_tag_realtime = rt;
949 if (p == le64toh(f->header->tail_object_offset))
952 p = p + ALIGN64(le64toh(o->object.size));
955 if (n_objects != le64toh(f->header->n_objects)) {
956 log_error("Object number mismatch");
961 if (n_entries != le64toh(f->header->n_entries)) {
962 log_error("Entry number mismatch");
967 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
968 n_data != le64toh(f->header->n_data)) {
969 log_error("Data number mismatch");
974 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
975 n_fields != le64toh(f->header->n_fields)) {
976 log_error("Field number mismatch");
981 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
982 n_tags != le64toh(f->header->n_tags)) {
983 log_error("Tag number mismatch");
988 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
989 n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
990 log_error("Entry array number mismatch");
995 if (n_data_hash_tables != 1) {
996 log_error("Missing data hash table");
1001 if (n_field_hash_tables != 1) {
1002 log_error("Missing field hash table");
1007 if (!found_main_entry_array) {
1008 log_error("Missing entry array");
1013 if (entry_seqnum_set &&
1014 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
1015 log_error("Invalid tail seqnum");
1020 if (entry_monotonic_set &&
1021 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
1022 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
1023 log_error("Invalid tail monotonic timestamp");
1028 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
1029 log_error("Invalid tail realtime timestamp");
1034 /* Second iteration: we follow all objects referenced from the
1035 * two entry points: the object hash table and the entry
1036 * array. We also check that everything referenced (directly
1037 * or indirectly) in the data hash table also exists in the
1038 * entry array, and vice versa. Note that we do not care for
1039 * unreferenced objects. We only care that everything that is
1040 * referenced is consistent. */
1042 r = verify_entry_array(f,
1044 entry_fd, n_entries,
1045 entry_array_fd, n_entry_arrays,
1050 r = verify_hash_table(f,
1052 entry_fd, n_entries,
1053 entry_array_fd, n_entry_arrays,
1060 mmap_cache_close_fd(f->mmap, data_fd);
1061 mmap_cache_close_fd(f->mmap, entry_fd);
1062 mmap_cache_close_fd(f->mmap, entry_array_fd);
1064 close_nointr_nofail(data_fd);
1065 close_nointr_nofail(entry_fd);
1066 close_nointr_nofail(entry_array_fd);
1068 if (first_validated)
1069 *first_validated = le64toh(f->header->head_entry_realtime);
1071 *last_validated = last_tag_realtime;
1073 *last_contained = le64toh(f->header->tail_entry_realtime);
1080 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
1082 (unsigned long long) p,
1083 (unsigned long long) f->last_stat.st_size,
1084 (unsigned long long) (100 * p / f->last_stat.st_size));
1087 mmap_cache_close_fd(f->mmap, data_fd);
1088 close_nointr_nofail(data_fd);
1091 if (entry_fd >= 0) {
1092 mmap_cache_close_fd(f->mmap, entry_fd);
1093 close_nointr_nofail(entry_fd);
1096 if (entry_array_fd >= 0) {
1097 mmap_cache_close_fd(f->mmap, entry_array_fd);
1098 close_nointr_nofail(entry_array_fd);