]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journal-verify.c
journal: fix verification without key
[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
399 uint64_t i, n;
400 int r;
401
402 assert(f);
403 assert(data_fd >= 0);
404 assert(entry_fd >= 0);
405 assert(entry_array_fd >= 0);
406 assert(last_usec);
407
408 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
409 for (i = 0; i < n; i++) {
410 uint64_t last = 0, p;
411
412 draw_progress(0xC000 + (0x3FFF * i / n), last_usec);
413
414 p = le64toh(f->data_hash_table[i].head_hash_offset);
415 while (p != 0) {
416 Object *o;
417 uint64_t next;
418
419 if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
420 log_error("Invalid data object at hash entry %llu of %llu",
421 (unsigned long long) i, (unsigned long long) n);
422 return -EBADMSG;
423 }
424
425 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
426 if (r < 0)
427 return r;
428
429 next = le64toh(o->data.next_hash_offset);
430 if (next != 0 && next <= p) {
431 log_error("Hash chain has a cycle in hash entry %llu of %llu",
432 (unsigned long long) i, (unsigned long long) n);
433 return -EBADMSG;
434 }
435
436 if (le64toh(o->data.hash) % n != i) {
437 log_error("Hash value mismatch in hash entry %llu of %llu",
438 (unsigned long long) i, (unsigned long long) n);
439 return -EBADMSG;
440 }
441
442 r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
443 if (r < 0)
444 return r;
445
446 last = p;
447 p = next;
448 }
449
450 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
451 log_error("Tail hash pointer mismatch in hash table");
452 return -EBADMSG;
453 }
454 }
455
456 return 0;
457 }
458
459 static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
460 uint64_t n, h, q;
461 int r;
462 assert(f);
463
464 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
465 h = hash % n;
466
467 q = le64toh(f->data_hash_table[h].head_hash_offset);
468 while (q != 0) {
469 Object *o;
470
471 if (p == q)
472 return 1;
473
474 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
475 if (r < 0)
476 return r;
477
478 q = le64toh(o->data.next_hash_offset);
479 }
480
481 return 0;
482 }
483
484 static int verify_entry(
485 JournalFile *f,
486 Object *o, uint64_t p,
487 int data_fd, uint64_t n_data) {
488
489 uint64_t i, n;
490 int r;
491
492 assert(f);
493 assert(o);
494 assert(data_fd >= 0);
495
496 n = journal_file_entry_n_items(o);
497 for (i = 0; i < n; i++) {
498 uint64_t q, h;
499 Object *u;
500
501 q = le64toh(o->entry.items[i].object_offset);
502 h = le64toh(o->entry.items[i].hash);
503
504 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
505 log_error("Invalid data object at entry %llu",
506 (unsigned long long) p);
507 return -EBADMSG;
508 }
509
510 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
511 if (r < 0)
512 return r;
513
514 if (le64toh(u->data.hash) != h) {
515 log_error("Hash mismatch for data object at entry %llu",
516 (unsigned long long) p);
517 return -EBADMSG;
518 }
519
520 r = data_object_in_hash_table(f, h, q);
521 if (r < 0)
522 return r;
523 if (r == 0) {
524 log_error("Data object missing from hash at entry %llu",
525 (unsigned long long) p);
526 return -EBADMSG;
527 }
528 }
529
530 return 0;
531 }
532
533 static int verify_entry_array(
534 JournalFile *f,
535 int data_fd, uint64_t n_data,
536 int entry_fd, uint64_t n_entries,
537 int entry_array_fd, uint64_t n_entry_arrays,
538 usec_t *last_usec) {
539
540 uint64_t i = 0, a, n, last = 0;
541 int r;
542
543 assert(f);
544 assert(data_fd >= 0);
545 assert(entry_fd >= 0);
546 assert(entry_array_fd >= 0);
547 assert(last_usec);
548
549 n = le64toh(f->header->n_entries);
550 a = le64toh(f->header->entry_array_offset);
551 while (i < n) {
552 uint64_t next, m, j;
553 Object *o;
554
555 draw_progress(0x8000 + (0x3FFF * i / n), last_usec);
556
557 if (a == 0) {
558 log_error("Array chain too short at %llu of %llu",
559 (unsigned long long) i, (unsigned long long) n);
560 return -EBADMSG;
561 }
562
563 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
564 log_error("Invalid array at %llu of %llu",
565 (unsigned long long) i, (unsigned long long) n);
566 return -EBADMSG;
567 }
568
569 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
570 if (r < 0)
571 return r;
572
573 next = le64toh(o->entry_array.next_entry_array_offset);
574 if (next != 0 && next <= a) {
575 log_error("Array chain has cycle at %llu of %llu",
576 (unsigned long long) i, (unsigned long long) n);
577 return -EBADMSG;
578 }
579
580 m = journal_file_entry_array_n_items(o);
581 for (j = 0; i < n && j < m; i++, j++) {
582 uint64_t p;
583
584 p = le64toh(o->entry_array.items[j]);
585 if (p <= last) {
586 log_error("Entry array not sorted at %llu of %llu",
587 (unsigned long long) i, (unsigned long long) n);
588 return -EBADMSG;
589 }
590 last = p;
591
592 if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
593 log_error("Invalid array entry 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, p, &o);
599 if (r < 0)
600 return r;
601
602 r = verify_entry(f, o, p, data_fd, n_data);
603 if (r < 0)
604 return r;
605
606 /* Pointer might have moved, reposition */
607 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
608 if (r < 0)
609 return r;
610 }
611
612 a = next;
613 }
614
615 return 0;
616 }
617
618 static int journal_file_parse_verification_key(JournalFile *f, const char *key) {
619 uint8_t *seed;
620 size_t seed_size, c;
621 const char *k;
622 int r;
623 unsigned long long start, interval;
624
625 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
626 seed = malloc(seed_size);
627 if (!seed)
628 return -ENOMEM;
629
630 k = key;
631 for (c = 0; c < seed_size; c++) {
632 int x, y;
633
634 while (*k == '-')
635 k++;
636
637 x = unhexchar(*k);
638 if (x < 0) {
639 free(seed);
640 return -EINVAL;
641 }
642 k++;
643 y = unhexchar(*k);
644 if (y < 0) {
645 free(seed);
646 return -EINVAL;
647 }
648 k++;
649
650 seed[c] = (uint8_t) (x * 16 + y);
651 }
652
653 if (*k != '/') {
654 free(seed);
655 return -EINVAL;
656 }
657 k++;
658
659 r = sscanf(k, "%llx-%llx", &start, &interval);
660 if (r != 2) {
661 free(seed);
662 return -EINVAL;
663 }
664
665 f->fsprg_seed = seed;
666 f->fsprg_seed_size = seed_size;
667
668 f->fss_start_usec = start * interval;
669 f->fss_interval_usec = interval;
670
671 return 0;
672 }
673
674 int journal_file_verify(
675 JournalFile *f,
676 const char *key,
677 usec_t *first_validated, usec_t *last_validated, usec_t *last_contained) {
678 int r;
679 Object *o;
680 uint64_t p = 0, last_tag = 0, last_epoch = 0, last_tag_realtime = 0;
681 uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
682 sd_id128_t entry_boot_id;
683 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
684 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;
685 usec_t last_usec = 0;
686 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
687 char data_path[] = "/var/tmp/journal-data-XXXXXX",
688 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
689 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
690
691 assert(f);
692
693 if (key) {
694 r = journal_file_parse_verification_key(f, key);
695 if (r < 0) {
696 log_error("Failed to parse seed.");
697 return r;
698 }
699 } else if (f->seal)
700 return -ENOKEY;
701
702 data_fd = mkostemp(data_path, O_CLOEXEC);
703 if (data_fd < 0) {
704 log_error("Failed to create data file: %m");
705 r = -errno;
706 goto fail;
707 }
708 unlink(data_path);
709
710 entry_fd = mkostemp(entry_path, O_CLOEXEC);
711 if (entry_fd < 0) {
712 log_error("Failed to create entry file: %m");
713 r = -errno;
714 goto fail;
715 }
716 unlink(entry_path);
717
718 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
719 if (entry_array_fd < 0) {
720 log_error("Failed to create entry array file: %m");
721 r = -errno;
722 goto fail;
723 }
724 unlink(entry_array_path);
725
726 /* First iteration: we go through all objects, verify the
727 * superficial structure, headers, hashes. */
728
729 p = le64toh(f->header->header_size);
730 while (p != 0) {
731 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
732
733 r = journal_file_move_to_object(f, -1, p, &o);
734 if (r < 0) {
735 log_error("Invalid object at %llu", (unsigned long long) p);
736 goto fail;
737 }
738
739 if (le64toh(f->header->tail_object_offset) < p) {
740 log_error("Invalid tail object pointer");
741 r = -EBADMSG;
742 goto fail;
743 }
744
745 n_objects ++;
746
747 r = journal_file_object_verify(f, o);
748 if (r < 0) {
749 log_error("Invalid object contents at %llu", (unsigned long long) p);
750 goto fail;
751 }
752
753 if (o->object.flags & OBJECT_COMPRESSED &&
754 !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
755 log_error("Compressed object in file without compression at %llu", (unsigned long long) p);
756 r = -EBADMSG;
757 goto fail;
758 }
759
760 switch (o->object.type) {
761
762 case OBJECT_DATA:
763 r = write_uint64(data_fd, p);
764 if (r < 0)
765 goto fail;
766
767 n_data++;
768 break;
769
770 case OBJECT_FIELD:
771 n_fields++;
772 break;
773
774 case OBJECT_ENTRY:
775 if ((le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED) && n_tags <= 0) {
776 log_error("First entry before first tag at %llu", (unsigned long long) p);
777 r = -EBADMSG;
778 goto fail;
779 }
780
781 r = write_uint64(entry_fd, p);
782 if (r < 0)
783 goto fail;
784
785 if (last_tag_realtime > le64toh(o->entry.realtime)) {
786 log_error("Older entry after newer tag at %llu", (unsigned long long) p);
787 r = -EBADMSG;
788 goto fail;
789 }
790
791 if (!entry_seqnum_set &&
792 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
793 log_error("Head entry sequence number incorrect at %llu", (unsigned long long) p);
794 r = -EBADMSG;
795 goto fail;
796 }
797
798 if (entry_seqnum_set &&
799 entry_seqnum >= le64toh(o->entry.seqnum)) {
800 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
801 r = -EBADMSG;
802 goto fail;
803 }
804
805 entry_seqnum = le64toh(o->entry.seqnum);
806 entry_seqnum_set = true;
807
808 if (entry_monotonic_set &&
809 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
810 entry_monotonic > le64toh(o->entry.monotonic)) {
811 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
812 r = -EBADMSG;
813 goto fail;
814 }
815
816 entry_monotonic = le64toh(o->entry.monotonic);
817 entry_boot_id = o->entry.boot_id;
818 entry_monotonic_set = true;
819
820 if (!entry_realtime_set &&
821 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
822 log_error("Head entry realtime timestamp incorrect");
823 r = -EBADMSG;
824 goto fail;
825 }
826
827 entry_realtime = le64toh(o->entry.realtime);
828 entry_realtime_set = true;
829
830 n_entries ++;
831 break;
832
833 case OBJECT_DATA_HASH_TABLE:
834 if (n_data_hash_tables > 1) {
835 log_error("More than one data hash table at %llu", (unsigned long long) p);
836 r = -EBADMSG;
837 goto fail;
838 }
839
840 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
841 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
842 log_error("Header fields for data hash table invalid");
843 r = -EBADMSG;
844 goto fail;
845 }
846
847 n_data_hash_tables++;
848 break;
849
850 case OBJECT_FIELD_HASH_TABLE:
851 if (n_field_hash_tables > 1) {
852 log_error("More than one field hash table at %llu", (unsigned long long) p);
853 r = -EBADMSG;
854 goto fail;
855 }
856
857 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
858 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
859 log_error("Header fields for field hash table invalid");
860 r = -EBADMSG;
861 goto fail;
862 }
863
864 n_field_hash_tables++;
865 break;
866
867 case OBJECT_ENTRY_ARRAY:
868 r = write_uint64(entry_array_fd, p);
869 if (r < 0)
870 goto fail;
871
872 if (p == le64toh(f->header->entry_array_offset)) {
873 if (found_main_entry_array) {
874 log_error("More than one main entry array at %llu", (unsigned long long) p);
875 r = -EBADMSG;
876 goto fail;
877 }
878
879 found_main_entry_array = true;
880 }
881
882 n_entry_arrays++;
883 break;
884
885 case OBJECT_TAG: {
886 uint64_t q, rt;
887
888 if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED)) {
889 log_error("Tag object in file without sealing at %llu", (unsigned long long) p);
890 r = -EBADMSG;
891 goto fail;
892 }
893
894 log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum));
895
896 if (le64toh(o->tag.seqnum) != n_tags + 1) {
897 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
898 r = -EBADMSG;
899 goto fail;
900 }
901
902 if (le64toh(o->tag.epoch) < last_epoch) {
903 log_error("Epoch sequence out of synchronization at %llu", (unsigned long long) p);
904 r = -EBADMSG;
905 goto fail;
906 }
907
908 if (f->seal) {
909 log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum));
910
911 rt = (o->tag.epoch + 1) * f->fss_interval_usec + f->fss_start_usec;
912 if (entry_realtime_set && entry_realtime >= rt) {
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 }
964
965 last_tag = p + ALIGN64(le64toh(o->object.size));
966 last_epoch = le64toh(o->tag.epoch);
967
968 n_tags ++;
969 break;
970 }
971
972 default:
973 n_weird ++;
974 }
975
976 if (p == le64toh(f->header->tail_object_offset))
977 p = 0;
978 else
979 p = p + ALIGN64(le64toh(o->object.size));
980 }
981
982 if (n_objects != le64toh(f->header->n_objects)) {
983 log_error("Object number mismatch");
984 r = -EBADMSG;
985 goto fail;
986 }
987
988 if (n_entries != le64toh(f->header->n_entries)) {
989 log_error("Entry number mismatch");
990 r = -EBADMSG;
991 goto fail;
992 }
993
994 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
995 n_data != le64toh(f->header->n_data)) {
996 log_error("Data number mismatch");
997 r = -EBADMSG;
998 goto fail;
999 }
1000
1001 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
1002 n_fields != le64toh(f->header->n_fields)) {
1003 log_error("Field number mismatch");
1004 r = -EBADMSG;
1005 goto fail;
1006 }
1007
1008 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
1009 n_tags != le64toh(f->header->n_tags)) {
1010 log_error("Tag number mismatch");
1011 r = -EBADMSG;
1012 goto fail;
1013 }
1014
1015 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
1016 n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
1017 log_error("Entry array number mismatch");
1018 r = -EBADMSG;
1019 goto fail;
1020 }
1021
1022 if (n_data_hash_tables != 1) {
1023 log_error("Missing data hash table");
1024 r = -EBADMSG;
1025 goto fail;
1026 }
1027
1028 if (n_field_hash_tables != 1) {
1029 log_error("Missing field hash table");
1030 r = -EBADMSG;
1031 goto fail;
1032 }
1033
1034 if (!found_main_entry_array) {
1035 log_error("Missing entry array");
1036 r = -EBADMSG;
1037 goto fail;
1038 }
1039
1040 if (entry_seqnum_set &&
1041 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
1042 log_error("Invalid tail seqnum");
1043 r = -EBADMSG;
1044 goto fail;
1045 }
1046
1047 if (entry_monotonic_set &&
1048 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
1049 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
1050 log_error("Invalid tail monotonic timestamp");
1051 r = -EBADMSG;
1052 goto fail;
1053 }
1054
1055 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
1056 log_error("Invalid tail realtime timestamp");
1057 r = -EBADMSG;
1058 goto fail;
1059 }
1060
1061 /* Second iteration: we follow all objects referenced from the
1062 * two entry points: the object hash table and the entry
1063 * array. We also check that everything referenced (directly
1064 * or indirectly) in the data hash table also exists in the
1065 * entry array, and vice versa. Note that we do not care for
1066 * unreferenced objects. We only care that everything that is
1067 * referenced is consistent. */
1068
1069 r = verify_entry_array(f,
1070 data_fd, n_data,
1071 entry_fd, n_entries,
1072 entry_array_fd, n_entry_arrays,
1073 &last_usec);
1074 if (r < 0)
1075 goto fail;
1076
1077 r = verify_hash_table(f,
1078 data_fd, n_data,
1079 entry_fd, n_entries,
1080 entry_array_fd, n_entry_arrays,
1081 &last_usec);
1082 if (r < 0)
1083 goto fail;
1084
1085 flush_progress();
1086
1087 mmap_cache_close_fd(f->mmap, data_fd);
1088 mmap_cache_close_fd(f->mmap, entry_fd);
1089 mmap_cache_close_fd(f->mmap, entry_array_fd);
1090
1091 close_nointr_nofail(data_fd);
1092 close_nointr_nofail(entry_fd);
1093 close_nointr_nofail(entry_array_fd);
1094
1095 if (first_validated)
1096 *first_validated = last_tag_realtime ? le64toh(f->header->head_entry_realtime) : 0;
1097 if (last_validated)
1098 *last_validated = last_tag_realtime;
1099 if (last_contained)
1100 *last_contained = le64toh(f->header->tail_entry_realtime);
1101
1102 return 0;
1103
1104 fail:
1105 flush_progress();
1106
1107 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
1108 f->path,
1109 (unsigned long long) p,
1110 (unsigned long long) f->last_stat.st_size,
1111 (unsigned long long) (100 * p / f->last_stat.st_size));
1112
1113 if (data_fd >= 0) {
1114 mmap_cache_close_fd(f->mmap, data_fd);
1115 close_nointr_nofail(data_fd);
1116 }
1117
1118 if (entry_fd >= 0) {
1119 mmap_cache_close_fd(f->mmap, entry_fd);
1120 close_nointr_nofail(entry_fd);
1121 }
1122
1123 if (entry_array_fd >= 0) {
1124 mmap_cache_close_fd(f->mmap, entry_array_fd);
1125 close_nointr_nofail(entry_array_fd);
1126 }
1127
1128 return r;
1129 }