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