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