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