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