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