]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journal-verify.c
29a9229e53148812b4502e3c4c5cc329f803dd2b
[thirdparty/systemd.git] / src / journal / journal-verify.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <unistd.h>
23 #include <sys/mman.h>
24 #include <fcntl.h>
25 #include <stddef.h>
26
27 #include "util.h"
28 #include "macro.h"
29 #include "journal-def.h"
30 #include "journal-file.h"
31 #include "journal-authenticate.h"
32 #include "journal-verify.h"
33 #include "lookup3.h"
34 #include "compress.h"
35 #include "fsprg.h"
36
37 /* FIXME:
38 *
39 * - evolve key even if nothing happened in regular intervals
40 *
41 * - check with sparse
42 *
43 * */
44
45 static int journal_file_object_verify(JournalFile *f, Object *o) {
46 uint64_t i;
47
48 assert(f);
49 assert(o);
50
51 /* This does various superficial tests about the length an
52 * possible field values. It does not follow any references to
53 * other objects. */
54
55 if ((o->object.flags & OBJECT_COMPRESSED) &&
56 o->object.type != OBJECT_DATA)
57 return -EBADMSG;
58
59 switch (o->object.type) {
60
61 case OBJECT_DATA: {
62 uint64_t h1, h2;
63
64 if (le64toh(o->data.entry_offset) <= 0 ||
65 le64toh(o->data.n_entries) <= 0)
66 return -EBADMSG;
67
68 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
69 return -EBADMSG;
70
71 h1 = le64toh(o->data.hash);
72
73 if (o->object.flags & OBJECT_COMPRESSED) {
74 void *b = NULL;
75 uint64_t alloc = 0, b_size;
76
77 if (!uncompress_blob(o->data.payload,
78 le64toh(o->object.size) - offsetof(Object, data.payload),
79 &b, &alloc, &b_size))
80 return -EBADMSG;
81
82 h2 = hash64(b, b_size);
83 free(b);
84 } else
85 h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
86
87 if (h1 != h2)
88 return -EBADMSG;
89
90 if (!VALID64(o->data.next_hash_offset) ||
91 !VALID64(o->data.next_field_offset) ||
92 !VALID64(o->data.entry_offset) ||
93 !VALID64(o->data.entry_array_offset))
94 return -EBADMSG;
95
96 break;
97 }
98
99 case OBJECT_FIELD:
100 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
101 return -EBADMSG;
102
103 if (!VALID64(o->field.next_hash_offset) ||
104 !VALID64(o->field.head_data_offset))
105 return -EBADMSG;
106 break;
107
108 case OBJECT_ENTRY:
109 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
110 return -EBADMSG;
111
112 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
113 return -EBADMSG;
114
115 if (le64toh(o->entry.seqnum) <= 0 ||
116 !VALID_REALTIME(le64toh(o->entry.realtime)) ||
117 !VALID_MONOTONIC(le64toh(o->entry.monotonic)))
118 return -EBADMSG;
119
120 for (i = 0; i < journal_file_entry_n_items(o); i++) {
121 if (o->entry.items[i].object_offset == 0 ||
122 !VALID64(o->entry.items[i].object_offset))
123 return -EBADMSG;
124 }
125
126 break;
127
128 case OBJECT_DATA_HASH_TABLE:
129 case OBJECT_FIELD_HASH_TABLE:
130 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0)
131 return -EBADMSG;
132
133 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
134 return -EBADMSG;
135
136 for (i = 0; i < journal_file_hash_table_n_items(o); i++) {
137 if (o->hash_table.items[i].head_hash_offset != 0 &&
138 !VALID64(le64toh(o->hash_table.items[i].head_hash_offset)))
139 return -EBADMSG;
140 if (o->hash_table.items[i].tail_hash_offset != 0 &&
141 !VALID64(le64toh(o->hash_table.items[i].tail_hash_offset)))
142 return -EBADMSG;
143
144 if ((o->hash_table.items[i].head_hash_offset != 0) !=
145 (o->hash_table.items[i].tail_hash_offset != 0))
146 return -EBADMSG;
147 }
148
149 break;
150
151 case OBJECT_ENTRY_ARRAY:
152 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0)
153 return -EBADMSG;
154
155 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
156 return -EBADMSG;
157
158 if (!VALID64(o->entry_array.next_entry_array_offset))
159 return -EBADMSG;
160
161 for (i = 0; i < journal_file_entry_array_n_items(o); i++)
162 if (o->entry_array.items[i] != 0 &&
163 !VALID64(o->entry_array.items[i]))
164 return -EBADMSG;
165
166 break;
167
168 case OBJECT_TAG:
169 if (le64toh(o->object.size) != sizeof(TagObject))
170 return -EBADMSG;
171
172 if (!VALID_EPOCH(o->tag.epoch))
173 return -EBADMSG;
174
175 break;
176 }
177
178 return 0;
179 }
180
181 static void draw_progress(uint64_t p, usec_t *last_usec) {
182 unsigned n, i, j, k;
183 usec_t z, x;
184
185 if (!isatty(STDOUT_FILENO))
186 return;
187
188 z = now(CLOCK_MONOTONIC);
189 x = *last_usec;
190
191 if (x != 0 && x + 40 * USEC_PER_MSEC > z)
192 return;
193
194 *last_usec = z;
195
196 n = (3 * columns()) / 4;
197 j = (n * (unsigned) p) / 65535ULL;
198 k = n - j;
199
200 fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
201
202 for (i = 0; i < j; i++)
203 fputs("\xe2\x96\x88", stdout);
204
205 fputs(ANSI_HIGHLIGHT_OFF, stdout);
206
207 for (i = 0; i < k; i++)
208 fputs("\xe2\x96\x91", stdout);
209
210 printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU);
211
212 fputs("\r\x1B[?25h", stdout);
213 fflush(stdout);
214 }
215
216 static void flush_progress(void) {
217 unsigned n, i;
218
219 if (!isatty(STDOUT_FILENO))
220 return;
221
222 n = (3 * columns()) / 4;
223
224 putchar('\r');
225
226 for (i = 0; i < n + 5; i++)
227 putchar(' ');
228
229 putchar('\r');
230 fflush(stdout);
231 }
232
233 static int write_uint64(int fd, uint64_t p) {
234 ssize_t k;
235
236 k = write(fd, &p, sizeof(p));
237 if (k < 0)
238 return -errno;
239 if (k != sizeof(p))
240 return -EIO;
241
242 return 0;
243 }
244
245 static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
246 uint64_t a, b;
247 int r;
248
249 assert(m);
250 assert(fd >= 0);
251
252 /* Bisection ... */
253
254 a = 0; b = n;
255 while (a < b) {
256 uint64_t c, *z;
257
258 c = (a + b) / 2;
259
260 r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z);
261 if (r < 0)
262 return r;
263
264 if (*z == p)
265 return 1;
266
267 if (a + 1 >= b)
268 return 0;
269
270 if (p < *z)
271 b = c;
272 else {
273 a = c;
274 }
275 }
276
277 return 0;
278 }
279
280 static int entry_points_to_data(
281 JournalFile *f,
282 int entry_fd,
283 uint64_t n_entries,
284 uint64_t entry_p,
285 uint64_t data_p) {
286
287 int r;
288 uint64_t i, n, a;
289 Object *o;
290 bool found = false;
291
292 assert(f);
293 assert(entry_fd >= 0);
294
295 if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
296 log_error("Data object references invalid entry at %llu", (unsigned long long) data_p);
297 return -EBADMSG;
298 }
299
300 r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
301 if (r < 0)
302 return r;
303
304 n = journal_file_entry_n_items(o);
305 for (i = 0; i < n; i++)
306 if (le64toh(o->entry.items[i].object_offset) == data_p) {
307 found = true;
308 break;
309 }
310
311 if (!found) {
312 log_error("Data object not referenced by linked entry at %llu", (unsigned long long) data_p);
313 return -EBADMSG;
314 }
315
316 /* Check if this entry is also in main entry array. Since the
317 * main entry array has already been verified we can rely on
318 * its consistency.*/
319
320 n = le64toh(f->header->n_entries);
321 a = le64toh(f->header->entry_array_offset);
322 i = 0;
323
324 while (i < n) {
325 uint64_t m, j;
326
327 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
328 if (r < 0)
329 return r;
330
331 m = journal_file_entry_array_n_items(o);
332 for (j = 0; i < n && j < m; i++, j++)
333 if (le64toh(o->entry_array.items[j]) == entry_p)
334 return 0;
335
336 a = le64toh(o->entry_array.next_entry_array_offset);
337 }
338
339 return 0;
340 }
341
342 static int verify_data(
343 JournalFile *f,
344 Object *o, uint64_t p,
345 int entry_fd, uint64_t n_entries,
346 int entry_array_fd, uint64_t n_entry_arrays) {
347
348 uint64_t i, n, a, last, q;
349 int r;
350
351 assert(f);
352 assert(o);
353 assert(entry_fd >= 0);
354 assert(entry_array_fd >= 0);
355
356 n = le64toh(o->data.n_entries);
357 a = le64toh(o->data.entry_array_offset);
358
359 /* We already checked this earlier */
360 assert(n > 0);
361
362 last = q = le64toh(o->data.entry_offset);
363 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
364 if (r < 0)
365 return r;
366
367 i = 1;
368 while (i < n) {
369 uint64_t next, m, j;
370
371 if (a == 0) {
372 log_error("Array chain too short at %llu", (unsigned long long) p);
373 return -EBADMSG;
374 }
375
376 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
377 log_error("Invalid array at %llu", (unsigned long long) p);
378 return -EBADMSG;
379 }
380
381 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
382 if (r < 0)
383 return r;
384
385 next = le64toh(o->entry_array.next_entry_array_offset);
386 if (next != 0 && next <= a) {
387 log_error("Array chain has cycle at %llu", (unsigned long long) p);
388 return -EBADMSG;
389 }
390
391 m = journal_file_entry_array_n_items(o);
392 for (j = 0; i < n && j < m; i++, j++) {
393
394 q = le64toh(o->entry_array.items[j]);
395 if (q <= last) {
396 log_error("Data object's entry array not sorted at %llu", (unsigned long long) p);
397 return -EBADMSG;
398 }
399 last = q;
400
401 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
402 if (r < 0)
403 return r;
404
405 /* Pointer might have moved, reposition */
406 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
407 if (r < 0)
408 return r;
409 }
410
411 a = next;
412 }
413
414 return 0;
415 }
416
417 static int verify_hash_table(
418 JournalFile *f,
419 int data_fd, uint64_t n_data,
420 int entry_fd, uint64_t n_entries,
421 int entry_array_fd, uint64_t n_entry_arrays,
422 usec_t *last_usec,
423 bool show_progress) {
424
425 uint64_t i, n;
426 int r;
427
428 assert(f);
429 assert(data_fd >= 0);
430 assert(entry_fd >= 0);
431 assert(entry_array_fd >= 0);
432 assert(last_usec);
433
434 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
435 for (i = 0; i < n; i++) {
436 uint64_t last = 0, p;
437
438 if (show_progress)
439 draw_progress(0xC000 + (0x3FFF * i / n), last_usec);
440
441 p = le64toh(f->data_hash_table[i].head_hash_offset);
442 while (p != 0) {
443 Object *o;
444 uint64_t next;
445
446 if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
447 log_error("Invalid data object at hash entry %llu of %llu",
448 (unsigned long long) i, (unsigned long long) n);
449 return -EBADMSG;
450 }
451
452 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
453 if (r < 0)
454 return r;
455
456 next = le64toh(o->data.next_hash_offset);
457 if (next != 0 && next <= p) {
458 log_error("Hash chain has a cycle in hash entry %llu of %llu",
459 (unsigned long long) i, (unsigned long long) n);
460 return -EBADMSG;
461 }
462
463 if (le64toh(o->data.hash) % n != i) {
464 log_error("Hash value mismatch in hash entry %llu of %llu",
465 (unsigned long long) i, (unsigned long long) n);
466 return -EBADMSG;
467 }
468
469 r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
470 if (r < 0)
471 return r;
472
473 last = p;
474 p = next;
475 }
476
477 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
478 log_error("Tail hash pointer mismatch in hash table");
479 return -EBADMSG;
480 }
481 }
482
483 return 0;
484 }
485
486 static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
487 uint64_t n, h, q;
488 int r;
489 assert(f);
490
491 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
492 h = hash % n;
493
494 q = le64toh(f->data_hash_table[h].head_hash_offset);
495 while (q != 0) {
496 Object *o;
497
498 if (p == q)
499 return 1;
500
501 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
502 if (r < 0)
503 return r;
504
505 q = le64toh(o->data.next_hash_offset);
506 }
507
508 return 0;
509 }
510
511 static int verify_entry(
512 JournalFile *f,
513 Object *o, uint64_t p,
514 int data_fd, uint64_t n_data) {
515
516 uint64_t i, n;
517 int r;
518
519 assert(f);
520 assert(o);
521 assert(data_fd >= 0);
522
523 n = journal_file_entry_n_items(o);
524 for (i = 0; i < n; i++) {
525 uint64_t q, h;
526 Object *u;
527
528 q = le64toh(o->entry.items[i].object_offset);
529 h = le64toh(o->entry.items[i].hash);
530
531 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
532 log_error("Invalid data object at entry %llu",
533 (unsigned long long) p);
534 return -EBADMSG;
535 }
536
537 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
538 if (r < 0)
539 return r;
540
541 if (le64toh(u->data.hash) != h) {
542 log_error("Hash mismatch for data object at entry %llu",
543 (unsigned long long) p);
544 return -EBADMSG;
545 }
546
547 r = data_object_in_hash_table(f, h, q);
548 if (r < 0)
549 return r;
550 if (r == 0) {
551 log_error("Data object missing from hash at entry %llu",
552 (unsigned long long) p);
553 return -EBADMSG;
554 }
555 }
556
557 return 0;
558 }
559
560 static int verify_entry_array(
561 JournalFile *f,
562 int data_fd, uint64_t n_data,
563 int entry_fd, uint64_t n_entries,
564 int entry_array_fd, uint64_t n_entry_arrays,
565 usec_t *last_usec,
566 bool show_progress) {
567
568 uint64_t i = 0, a, n, last = 0;
569 int r;
570
571 assert(f);
572 assert(data_fd >= 0);
573 assert(entry_fd >= 0);
574 assert(entry_array_fd >= 0);
575 assert(last_usec);
576
577 n = le64toh(f->header->n_entries);
578 a = le64toh(f->header->entry_array_offset);
579 while (i < n) {
580 uint64_t next, m, j;
581 Object *o;
582
583 if (show_progress)
584 draw_progress(0x8000 + (0x3FFF * i / n), last_usec);
585
586 if (a == 0) {
587 log_error("Array chain too short at %llu of %llu",
588 (unsigned long long) i, (unsigned long long) n);
589 return -EBADMSG;
590 }
591
592 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
593 log_error("Invalid array at %llu of %llu",
594 (unsigned long long) i, (unsigned long long) n);
595 return -EBADMSG;
596 }
597
598 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
599 if (r < 0)
600 return r;
601
602 next = le64toh(o->entry_array.next_entry_array_offset);
603 if (next != 0 && next <= a) {
604 log_error("Array chain has cycle at %llu of %llu",
605 (unsigned long long) i, (unsigned long long) n);
606 return -EBADMSG;
607 }
608
609 m = journal_file_entry_array_n_items(o);
610 for (j = 0; i < n && j < m; i++, j++) {
611 uint64_t p;
612
613 p = le64toh(o->entry_array.items[j]);
614 if (p <= last) {
615 log_error("Entry array not sorted at %llu of %llu",
616 (unsigned long long) i, (unsigned long long) n);
617 return -EBADMSG;
618 }
619 last = p;
620
621 if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
622 log_error("Invalid array entry at %llu of %llu",
623 (unsigned long long) i, (unsigned long long) n);
624 return -EBADMSG;
625 }
626
627 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
628 if (r < 0)
629 return r;
630
631 r = verify_entry(f, o, p, data_fd, n_data);
632 if (r < 0)
633 return r;
634
635 /* Pointer might have moved, reposition */
636 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
637 if (r < 0)
638 return r;
639 }
640
641 a = next;
642 }
643
644 return 0;
645 }
646
647 int journal_file_verify(
648 JournalFile *f,
649 const char *key,
650 usec_t *first_validated, usec_t *last_validated, usec_t *last_contained,
651 bool show_progress) {
652 int r;
653 Object *o;
654 uint64_t p = 0, last_tag = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0;
655 uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
656 sd_id128_t entry_boot_id;
657 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
658 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;
659 usec_t last_usec = 0;
660 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
661 char data_path[] = "/var/tmp/journal-data-XXXXXX",
662 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
663 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
664 unsigned i;
665 bool found_last;
666
667 assert(f);
668
669 if (key) {
670 #ifdef HAVE_GCRYPT
671 r = journal_file_parse_verification_key(f, key);
672 if (r < 0) {
673 log_error("Failed to parse seed.");
674 return r;
675 }
676 #else
677 return -ENOTSUP;
678 #endif
679 } else if (f->seal)
680 return -ENOKEY;
681
682 data_fd = mkostemp(data_path, O_CLOEXEC);
683 if (data_fd < 0) {
684 log_error("Failed to create data file: %m");
685 r = -errno;
686 goto fail;
687 }
688 unlink(data_path);
689
690 entry_fd = mkostemp(entry_path, O_CLOEXEC);
691 if (entry_fd < 0) {
692 log_error("Failed to create entry file: %m");
693 r = -errno;
694 goto fail;
695 }
696 unlink(entry_path);
697
698 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
699 if (entry_array_fd < 0) {
700 log_error("Failed to create entry array file: %m");
701 r = -errno;
702 goto fail;
703 }
704 unlink(entry_array_path);
705
706 #ifdef HAVE_GCRYPT
707 if ((le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) != 0)
708 #else
709 if (f->header->compatible_flags != 0)
710 #endif
711 {
712 log_error("Cannot verify file with unknown extensions.");
713 r = -ENOTSUP;
714 goto fail;
715 }
716
717 for (i = 0; i < sizeof(f->header->reserved); i++)
718 if (f->header->reserved[i] != 0) {
719 log_error("Reserved field in non-zero.");
720 r = -EBADMSG;
721 goto fail;
722 }
723
724 /* First iteration: we go through all objects, verify the
725 * superficial structure, headers, hashes. */
726
727 p = le64toh(f->header->header_size);
728 while (p != 0) {
729 if (show_progress)
730 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
731
732 r = journal_file_move_to_object(f, -1, p, &o);
733 if (r < 0) {
734 log_error("Invalid object at %llu", (unsigned long long) p);
735 goto fail;
736 }
737
738 if (p > le64toh(f->header->tail_object_offset)) {
739 log_error("Invalid tail object pointer");
740 r = -EBADMSG;
741 goto fail;
742 }
743
744 if (p == le64toh(f->header->tail_object_offset))
745 found_last = true;
746
747 n_objects ++;
748
749 r = journal_file_object_verify(f, o);
750 if (r < 0) {
751 log_error("Invalid object contents at %llu", (unsigned long long) p);
752 goto fail;
753 }
754
755 if ((o->object.flags & OBJECT_COMPRESSED) && !JOURNAL_HEADER_COMPRESSED(f->header)) {
756 log_error("Compressed object in file without compression at %llu", (unsigned long long) p);
757 r = -EBADMSG;
758 goto fail;
759 }
760
761 switch (o->object.type) {
762
763 case OBJECT_DATA:
764 r = write_uint64(data_fd, p);
765 if (r < 0)
766 goto fail;
767
768 n_data++;
769 break;
770
771 case OBJECT_FIELD:
772 n_fields++;
773 break;
774
775 case OBJECT_ENTRY:
776 if (JOURNAL_HEADER_SEALED(f->header) && n_tags <= 0) {
777 log_error("First entry before first tag at %llu", (unsigned long long) p);
778 r = -EBADMSG;
779 goto fail;
780 }
781
782 r = write_uint64(entry_fd, p);
783 if (r < 0)
784 goto fail;
785
786 if (le64toh(o->entry.realtime) < last_tag_realtime) {
787 log_error("Older entry after newer tag at %llu", (unsigned long long) p);
788 r = -EBADMSG;
789 goto fail;
790 }
791
792 if (!entry_seqnum_set &&
793 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
794 log_error("Head entry sequence number incorrect at %llu", (unsigned long long) p);
795 r = -EBADMSG;
796 goto fail;
797 }
798
799 if (entry_seqnum_set &&
800 entry_seqnum >= le64toh(o->entry.seqnum)) {
801 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
802 r = -EBADMSG;
803 goto fail;
804 }
805
806 entry_seqnum = le64toh(o->entry.seqnum);
807 entry_seqnum_set = true;
808
809 if (entry_monotonic_set &&
810 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
811 entry_monotonic > le64toh(o->entry.monotonic)) {
812 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
813 r = -EBADMSG;
814 goto fail;
815 }
816
817 entry_monotonic = le64toh(o->entry.monotonic);
818 entry_boot_id = o->entry.boot_id;
819 entry_monotonic_set = true;
820
821 if (!entry_realtime_set &&
822 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
823 log_error("Head entry realtime timestamp incorrect");
824 r = -EBADMSG;
825 goto fail;
826 }
827
828 entry_realtime = le64toh(o->entry.realtime);
829 entry_realtime_set = true;
830
831 n_entries ++;
832 break;
833
834 case OBJECT_DATA_HASH_TABLE:
835 if (n_data_hash_tables > 1) {
836 log_error("More than one data hash table at %llu", (unsigned long long) p);
837 r = -EBADMSG;
838 goto fail;
839 }
840
841 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
842 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
843 log_error("Header fields for data hash table invalid");
844 r = -EBADMSG;
845 goto fail;
846 }
847
848 n_data_hash_tables++;
849 break;
850
851 case OBJECT_FIELD_HASH_TABLE:
852 if (n_field_hash_tables > 1) {
853 log_error("More than one field hash table at %llu", (unsigned long long) p);
854 r = -EBADMSG;
855 goto fail;
856 }
857
858 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
859 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
860 log_error("Header fields for field hash table invalid");
861 r = -EBADMSG;
862 goto fail;
863 }
864
865 n_field_hash_tables++;
866 break;
867
868 case OBJECT_ENTRY_ARRAY:
869 r = write_uint64(entry_array_fd, p);
870 if (r < 0)
871 goto fail;
872
873 if (p == le64toh(f->header->entry_array_offset)) {
874 if (found_main_entry_array) {
875 log_error("More than one main entry array at %llu", (unsigned long long) p);
876 r = -EBADMSG;
877 goto fail;
878 }
879
880 found_main_entry_array = true;
881 }
882
883 n_entry_arrays++;
884 break;
885
886 case OBJECT_TAG:
887 if (!JOURNAL_HEADER_SEALED(f->header)) {
888 log_error("Tag object in file without sealing at %llu", (unsigned long long) p);
889 r = -EBADMSG;
890 goto fail;
891 }
892
893 if (le64toh(o->tag.seqnum) != n_tags + 1) {
894 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
895 r = -EBADMSG;
896 goto fail;
897 }
898
899 if (le64toh(o->tag.epoch) < last_epoch) {
900 log_error("Epoch sequence out of synchronization at %llu", (unsigned long long) p);
901 r = -EBADMSG;
902 goto fail;
903 }
904
905 #ifdef HAVE_GCRYPT
906 if (f->seal) {
907 uint64_t q, rt;
908
909 log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum));
910
911 rt = f->fss_start_usec + o->tag.epoch * f->fss_interval_usec;
912 if (entry_realtime_set && entry_realtime >= rt + f->fss_interval_usec) {
913 log_error("Tag/entry realtime timestamp out of synchronization at %llu", (unsigned long long) p);
914 r = -EBADMSG;
915 goto fail;
916 }
917
918 /* OK, now we know the epoch. So let's now set
919 * it, and calculate the HMAC for everything
920 * since the last tag. */
921 r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
922 if (r < 0)
923 goto fail;
924
925 r = journal_file_hmac_start(f);
926 if (r < 0)
927 goto fail;
928
929 if (last_tag == 0) {
930 r = journal_file_hmac_put_header(f);
931 if (r < 0)
932 goto fail;
933
934 q = le64toh(f->header->header_size);
935 } else
936 q = last_tag;
937
938 while (q <= p) {
939 r = journal_file_move_to_object(f, -1, q, &o);
940 if (r < 0)
941 goto fail;
942
943 r = journal_file_hmac_put_object(f, -1, q);
944 if (r < 0)
945 goto fail;
946
947 q = q + ALIGN64(le64toh(o->object.size));
948 }
949
950 /* Position might have changed, let's reposition things */
951 r = journal_file_move_to_object(f, -1, p, &o);
952 if (r < 0)
953 goto fail;
954
955 if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
956 log_error("Tag failed verification at %llu", (unsigned long long) p);
957 r = -EBADMSG;
958 goto fail;
959 }
960
961 f->hmac_running = false;
962 last_tag_realtime = rt;
963 last_sealed_realtime = entry_realtime;
964 }
965 #endif
966
967 last_tag = p + ALIGN64(le64toh(o->object.size));
968 last_epoch = le64toh(o->tag.epoch);
969
970 n_tags ++;
971 break;
972
973 default:
974 n_weird ++;
975 }
976
977 if (p == le64toh(f->header->tail_object_offset))
978 p = 0;
979 else
980 p = p + ALIGN64(le64toh(o->object.size));
981 }
982
983 if (!found_last) {
984 log_error("Tail object pointer dead");
985 r = -EBADMSG;
986 goto fail;
987 }
988
989 if (n_objects != le64toh(f->header->n_objects)) {
990 log_error("Object number mismatch");
991 r = -EBADMSG;
992 goto fail;
993 }
994
995 if (n_entries != le64toh(f->header->n_entries)) {
996 log_error("Entry number mismatch");
997 r = -EBADMSG;
998 goto fail;
999 }
1000
1001 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
1002 n_data != le64toh(f->header->n_data)) {
1003 log_error("Data number mismatch");
1004 r = -EBADMSG;
1005 goto fail;
1006 }
1007
1008 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
1009 n_fields != le64toh(f->header->n_fields)) {
1010 log_error("Field number mismatch");
1011 r = -EBADMSG;
1012 goto fail;
1013 }
1014
1015 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
1016 n_tags != le64toh(f->header->n_tags)) {
1017 log_error("Tag number mismatch");
1018 r = -EBADMSG;
1019 goto fail;
1020 }
1021
1022 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
1023 n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
1024 log_error("Entry array number mismatch");
1025 r = -EBADMSG;
1026 goto fail;
1027 }
1028
1029 if (n_data_hash_tables != 1) {
1030 log_error("Missing data hash table");
1031 r = -EBADMSG;
1032 goto fail;
1033 }
1034
1035 if (n_field_hash_tables != 1) {
1036 log_error("Missing field hash table");
1037 r = -EBADMSG;
1038 goto fail;
1039 }
1040
1041 if (!found_main_entry_array) {
1042 log_error("Missing entry array");
1043 r = -EBADMSG;
1044 goto fail;
1045 }
1046
1047 if (entry_seqnum_set &&
1048 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
1049 log_error("Invalid tail seqnum");
1050 r = -EBADMSG;
1051 goto fail;
1052 }
1053
1054 if (entry_monotonic_set &&
1055 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
1056 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
1057 log_error("Invalid tail monotonic timestamp");
1058 r = -EBADMSG;
1059 goto fail;
1060 }
1061
1062 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
1063 log_error("Invalid tail realtime timestamp");
1064 r = -EBADMSG;
1065 goto fail;
1066 }
1067
1068 /* Second iteration: we follow all objects referenced from the
1069 * two entry points: the object hash table and the entry
1070 * array. We also check that everything referenced (directly
1071 * or indirectly) in the data hash table also exists in the
1072 * entry array, and vice versa. Note that we do not care for
1073 * unreferenced objects. We only care that everything that is
1074 * referenced is consistent. */
1075
1076 r = verify_entry_array(f,
1077 data_fd, n_data,
1078 entry_fd, n_entries,
1079 entry_array_fd, n_entry_arrays,
1080 &last_usec,
1081 show_progress);
1082 if (r < 0)
1083 goto fail;
1084
1085 r = verify_hash_table(f,
1086 data_fd, n_data,
1087 entry_fd, n_entries,
1088 entry_array_fd, n_entry_arrays,
1089 &last_usec,
1090 show_progress);
1091 if (r < 0)
1092 goto fail;
1093
1094 if (show_progress)
1095 flush_progress();
1096
1097 mmap_cache_close_fd(f->mmap, data_fd);
1098 mmap_cache_close_fd(f->mmap, entry_fd);
1099 mmap_cache_close_fd(f->mmap, entry_array_fd);
1100
1101 close_nointr_nofail(data_fd);
1102 close_nointr_nofail(entry_fd);
1103 close_nointr_nofail(entry_array_fd);
1104
1105 if (first_validated)
1106 *first_validated = last_tag_realtime ? le64toh(f->header->head_entry_realtime) : 0;
1107 if (last_validated)
1108 *last_validated = last_sealed_realtime;
1109 if (last_contained)
1110 *last_contained = le64toh(f->header->tail_entry_realtime);
1111
1112 return 0;
1113
1114 fail:
1115 if (show_progress)
1116 flush_progress();
1117
1118 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
1119 f->path,
1120 (unsigned long long) p,
1121 (unsigned long long) f->last_stat.st_size,
1122 (unsigned long long) (100 * p / f->last_stat.st_size));
1123
1124 if (data_fd >= 0) {
1125 mmap_cache_close_fd(f->mmap, data_fd);
1126 close_nointr_nofail(data_fd);
1127 }
1128
1129 if (entry_fd >= 0) {
1130 mmap_cache_close_fd(f->mmap, entry_fd);
1131 close_nointr_nofail(entry_fd);
1132 }
1133
1134 if (entry_array_fd >= 0) {
1135 mmap_cache_close_fd(f->mmap, entry_array_fd);
1136 close_nointr_nofail(entry_array_fd);
1137 }
1138
1139 return r;
1140 }