]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journal-verify.c
Properly check for overflow in offsets
[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),
93b73b06 72 &b, &alloc, &b_size, 0))
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
8481248b 181 if (!on_tty())
0284adc6
LP
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
8481248b 215 if (!on_tty())
0284adc6
LP
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,
2a7b539a 669 usec_t *first_contained, usec_t *last_validated, usec_t *last_contained,
b72631e5 670 bool show_progress) {
0284adc6
LP
671 int r;
672 Object *o;
0ab5c3ed
ZJS
673 uint64_t p = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0;
674
14d10188 675 uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
0284adc6
LP
676 sd_id128_t entry_boot_id;
677 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
14d10188 678 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
679 usec_t last_usec = 0;
680 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
681 char data_path[] = "/var/tmp/journal-data-XXXXXX",
682 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
683 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
97147f8c
LP
684 unsigned i;
685 bool found_last;
0ab5c3ed
ZJS
686#ifdef HAVE_GCRYPT
687 uint64_t last_tag = 0;
688#endif
0284adc6
LP
689 assert(f);
690
baed47c3 691 if (key) {
feb12d3e 692#ifdef HAVE_GCRYPT
baed47c3 693 r = journal_file_parse_verification_key(f, key);
b7c9ae91
LP
694 if (r < 0) {
695 log_error("Failed to parse seed.");
56e81f7c 696 return r;
b7c9ae91 697 }
feb12d3e
LP
698#else
699 return -ENOTSUP;
700#endif
c586dbf1
LP
701 } else if (f->seal)
702 return -ENOKEY;
b7c9ae91 703
0284adc6
LP
704 data_fd = mkostemp(data_path, O_CLOEXEC);
705 if (data_fd < 0) {
706 log_error("Failed to create data file: %m");
1137e6c7 707 r = -errno;
0284adc6
LP
708 goto fail;
709 }
710 unlink(data_path);
711
712 entry_fd = mkostemp(entry_path, O_CLOEXEC);
713 if (entry_fd < 0) {
714 log_error("Failed to create entry file: %m");
1137e6c7 715 r = -errno;
0284adc6
LP
716 goto fail;
717 }
718 unlink(entry_path);
719
720 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
721 if (entry_array_fd < 0) {
722 log_error("Failed to create entry array file: %m");
1137e6c7 723 r = -errno;
0284adc6
LP
724 goto fail;
725 }
726 unlink(entry_array_path);
727
97147f8c
LP
728#ifdef HAVE_GCRYPT
729 if ((le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) != 0)
730#else
731 if (f->header->compatible_flags != 0)
732#endif
733 {
734 log_error("Cannot verify file with unknown extensions.");
735 r = -ENOTSUP;
736 goto fail;
737 }
738
739 for (i = 0; i < sizeof(f->header->reserved); i++)
740 if (f->header->reserved[i] != 0) {
741 log_error("Reserved field in non-zero.");
742 r = -EBADMSG;
743 goto fail;
744 }
745
0284adc6
LP
746 /* First iteration: we go through all objects, verify the
747 * superficial structure, headers, hashes. */
748
0284adc6
LP
749 p = le64toh(f->header->header_size);
750 while (p != 0) {
b72631e5
LP
751 if (show_progress)
752 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
0284adc6
LP
753
754 r = journal_file_move_to_object(f, -1, p, &o);
755 if (r < 0) {
756 log_error("Invalid object at %llu", (unsigned long long) p);
757 goto fail;
758 }
759
97147f8c 760 if (p > le64toh(f->header->tail_object_offset)) {
356fe3e6 761 log_error("Invalid tail object pointer");
0284adc6
LP
762 r = -EBADMSG;
763 goto fail;
764 }
765
97147f8c
LP
766 if (p == le64toh(f->header->tail_object_offset))
767 found_last = true;
768
0284adc6
LP
769 n_objects ++;
770
771 r = journal_file_object_verify(f, o);
772 if (r < 0) {
773 log_error("Invalid object contents at %llu", (unsigned long long) p);
774 goto fail;
775 }
776
8088cbd3 777 if ((o->object.flags & OBJECT_COMPRESSED) && !JOURNAL_HEADER_COMPRESSED(f->header)) {
356fe3e6 778 log_error("Compressed object in file without compression at %llu", (unsigned long long) p);
0284adc6
LP
779 r = -EBADMSG;
780 goto fail;
781 }
782
a8e5f514 783 switch (o->object.type) {
0284adc6 784
a8e5f514
LP
785 case OBJECT_DATA:
786 r = write_uint64(data_fd, p);
787 if (r < 0)
0284adc6 788 goto fail;
0284adc6 789
a8e5f514
LP
790 n_data++;
791 break;
0284adc6 792
a8e5f514
LP
793 case OBJECT_FIELD:
794 n_fields++;
795 break;
0284adc6 796
a8e5f514 797 case OBJECT_ENTRY:
8088cbd3 798 if (JOURNAL_HEADER_SEALED(f->header) && n_tags <= 0) {
6c7be122
LP
799 log_error("First entry before first tag at %llu", (unsigned long long) p);
800 r = -EBADMSG;
801 goto fail;
802 }
803
0284adc6
LP
804 r = write_uint64(entry_fd, p);
805 if (r < 0)
806 goto fail;
807
f7fab8a5 808 if (le64toh(o->entry.realtime) < last_tag_realtime) {
7b5fd91c
LP
809 log_error("Older entry after newer tag at %llu", (unsigned long long) p);
810 r = -EBADMSG;
811 goto fail;
812 }
813
0284adc6
LP
814 if (!entry_seqnum_set &&
815 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
356fe3e6 816 log_error("Head entry sequence number incorrect at %llu", (unsigned long long) p);
0284adc6
LP
817 r = -EBADMSG;
818 goto fail;
819 }
820
821 if (entry_seqnum_set &&
822 entry_seqnum >= le64toh(o->entry.seqnum)) {
823 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
824 r = -EBADMSG;
825 goto fail;
826 }
827
828 entry_seqnum = le64toh(o->entry.seqnum);
829 entry_seqnum_set = true;
830
831 if (entry_monotonic_set &&
832 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
833 entry_monotonic > le64toh(o->entry.monotonic)) {
834 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
835 r = -EBADMSG;
836 goto fail;
837 }
838
839 entry_monotonic = le64toh(o->entry.monotonic);
840 entry_boot_id = o->entry.boot_id;
841 entry_monotonic_set = true;
842
843 if (!entry_realtime_set &&
844 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
845 log_error("Head entry realtime timestamp incorrect");
846 r = -EBADMSG;
847 goto fail;
848 }
849
850 entry_realtime = le64toh(o->entry.realtime);
851 entry_realtime_set = true;
852
853 n_entries ++;
a8e5f514 854 break;
0284adc6 855
a8e5f514 856 case OBJECT_DATA_HASH_TABLE:
0284adc6
LP
857 if (n_data_hash_tables > 1) {
858 log_error("More than one data hash table at %llu", (unsigned long long) p);
859 r = -EBADMSG;
860 goto fail;
861 }
862
863 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
864 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
356fe3e6 865 log_error("Header fields for data hash table invalid");
0284adc6
LP
866 r = -EBADMSG;
867 goto fail;
868 }
0284adc6 869
a8e5f514
LP
870 n_data_hash_tables++;
871 break;
872
873 case OBJECT_FIELD_HASH_TABLE:
0284adc6
LP
874 if (n_field_hash_tables > 1) {
875 log_error("More than one field hash table at %llu", (unsigned long long) p);
876 r = -EBADMSG;
877 goto fail;
878 }
879
880 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
881 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
356fe3e6 882 log_error("Header fields for field hash table invalid");
0284adc6
LP
883 r = -EBADMSG;
884 goto fail;
885 }
a8e5f514
LP
886
887 n_field_hash_tables++;
888 break;
889
890 case OBJECT_ENTRY_ARRAY:
891 r = write_uint64(entry_array_fd, p);
892 if (r < 0)
893 goto fail;
894
895 if (p == le64toh(f->header->entry_array_offset)) {
896 if (found_main_entry_array) {
897 log_error("More than one main entry array at %llu", (unsigned long long) p);
898 r = -EBADMSG;
899 goto fail;
900 }
901
902 found_main_entry_array = true;
903 }
904
905 n_entry_arrays++;
906 break;
907
feb12d3e 908 case OBJECT_TAG:
8088cbd3 909 if (!JOURNAL_HEADER_SEALED(f->header)) {
356fe3e6 910 log_error("Tag object in file without sealing at %llu", (unsigned long long) p);
a8e5f514
LP
911 r = -EBADMSG;
912 goto fail;
913 }
914
915 if (le64toh(o->tag.seqnum) != n_tags + 1) {
916 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
917 r = -EBADMSG;
918 goto fail;
919 }
920
14d10188
LP
921 if (le64toh(o->tag.epoch) < last_epoch) {
922 log_error("Epoch sequence out of synchronization at %llu", (unsigned long long) p);
7b5fd91c
LP
923 r = -EBADMSG;
924 goto fail;
925 }
926
feb12d3e 927#ifdef HAVE_GCRYPT
c586dbf1 928 if (f->seal) {
feb12d3e
LP
929 uint64_t q, rt;
930
c586dbf1 931 log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum));
14d10188 932
f7fab8a5
LP
933 rt = f->fss_start_usec + o->tag.epoch * f->fss_interval_usec;
934 if (entry_realtime_set && entry_realtime >= rt + f->fss_interval_usec) {
c586dbf1
LP
935 log_error("Tag/entry realtime timestamp out of synchronization at %llu", (unsigned long long) p);
936 r = -EBADMSG;
937 goto fail;
938 }
14d10188 939
c586dbf1
LP
940 /* OK, now we know the epoch. So let's now set
941 * it, and calculate the HMAC for everything
942 * since the last tag. */
943 r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
14d10188
LP
944 if (r < 0)
945 goto fail;
946
c586dbf1 947 r = journal_file_hmac_start(f);
14d10188
LP
948 if (r < 0)
949 goto fail;
950
c586dbf1
LP
951 if (last_tag == 0) {
952 r = journal_file_hmac_put_header(f);
953 if (r < 0)
954 goto fail;
955
956 q = le64toh(f->header->header_size);
957 } else
958 q = last_tag;
959
960 while (q <= p) {
961 r = journal_file_move_to_object(f, -1, q, &o);
962 if (r < 0)
963 goto fail;
964
5996c7c2 965 r = journal_file_hmac_put_object(f, -1, o, q);
c586dbf1
LP
966 if (r < 0)
967 goto fail;
968
969 q = q + ALIGN64(le64toh(o->object.size));
970 }
971
972 /* Position might have changed, let's reposition things */
973 r = journal_file_move_to_object(f, -1, p, &o);
14d10188
LP
974 if (r < 0)
975 goto fail;
976
c586dbf1
LP
977 if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
978 log_error("Tag failed verification at %llu", (unsigned long long) p);
979 r = -EBADMSG;
980 goto fail;
981 }
14d10188 982
c586dbf1
LP
983 f->hmac_running = false;
984 last_tag_realtime = rt;
f7fab8a5 985 last_sealed_realtime = entry_realtime;
14d10188
LP
986 }
987
14d10188 988 last_tag = p + ALIGN64(le64toh(o->object.size));
0ab5c3ed
ZJS
989#endif
990
c586dbf1 991 last_epoch = le64toh(o->tag.epoch);
6c7be122 992
a8e5f514
LP
993 n_tags ++;
994 break;
995
996 default:
0284adc6 997 n_weird ++;
a8e5f514 998 }
0284adc6
LP
999
1000 if (p == le64toh(f->header->tail_object_offset))
1001 p = 0;
1002 else
1003 p = p + ALIGN64(le64toh(o->object.size));
1004 }
1005
97147f8c
LP
1006 if (!found_last) {
1007 log_error("Tail object pointer dead");
1008 r = -EBADMSG;
1009 goto fail;
1010 }
1011
0284adc6
LP
1012 if (n_objects != le64toh(f->header->n_objects)) {
1013 log_error("Object number mismatch");
1014 r = -EBADMSG;
1015 goto fail;
1016 }
1017
1018 if (n_entries != le64toh(f->header->n_entries)) {
1019 log_error("Entry number mismatch");
1020 r = -EBADMSG;
1021 goto fail;
1022 }
1023
1024 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
1025 n_data != le64toh(f->header->n_data)) {
1026 log_error("Data number mismatch");
1027 r = -EBADMSG;
1028 goto fail;
1029 }
1030
1031 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
1032 n_fields != le64toh(f->header->n_fields)) {
1033 log_error("Field number mismatch");
1034 r = -EBADMSG;
1035 goto fail;
1036 }
1037
1038 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
a8e5f514 1039 n_tags != le64toh(f->header->n_tags)) {
0284adc6
LP
1040 log_error("Tag number mismatch");
1041 r = -EBADMSG;
1042 goto fail;
1043 }
1044
2dee23eb
LP
1045 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
1046 n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
1047 log_error("Entry array number mismatch");
1048 r = -EBADMSG;
1049 goto fail;
1050 }
1051
0284adc6
LP
1052 if (n_data_hash_tables != 1) {
1053 log_error("Missing data hash table");
1054 r = -EBADMSG;
1055 goto fail;
1056 }
1057
1058 if (n_field_hash_tables != 1) {
1059 log_error("Missing field hash table");
1060 r = -EBADMSG;
1061 goto fail;
1062 }
1063
1064 if (!found_main_entry_array) {
1065 log_error("Missing entry array");
1066 r = -EBADMSG;
1067 goto fail;
1068 }
1069
1070 if (entry_seqnum_set &&
1071 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
1072 log_error("Invalid tail seqnum");
1073 r = -EBADMSG;
1074 goto fail;
1075 }
1076
1077 if (entry_monotonic_set &&
1078 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
1079 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
1080 log_error("Invalid tail monotonic timestamp");
1081 r = -EBADMSG;
1082 goto fail;
1083 }
1084
1085 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
1086 log_error("Invalid tail realtime timestamp");
1087 r = -EBADMSG;
1088 goto fail;
1089 }
1090
86adf873
LP
1091 /* Second iteration: we follow all objects referenced from the
1092 * two entry points: the object hash table and the entry
1093 * array. We also check that everything referenced (directly
1094 * or indirectly) in the data hash table also exists in the
1095 * entry array, and vice versa. Note that we do not care for
1096 * unreferenced objects. We only care that everything that is
1097 * referenced is consistent. */
1098
1099 r = verify_entry_array(f,
1100 data_fd, n_data,
1101 entry_fd, n_entries,
1102 entry_array_fd, n_entry_arrays,
b72631e5
LP
1103 &last_usec,
1104 show_progress);
86adf873
LP
1105 if (r < 0)
1106 goto fail;
0284adc6 1107
86adf873
LP
1108 r = verify_hash_table(f,
1109 data_fd, n_data,
1110 entry_fd, n_entries,
1111 entry_array_fd, n_entry_arrays,
b72631e5
LP
1112 &last_usec,
1113 show_progress);
86adf873
LP
1114 if (r < 0)
1115 goto fail;
0284adc6 1116
b72631e5
LP
1117 if (show_progress)
1118 flush_progress();
0284adc6
LP
1119
1120 mmap_cache_close_fd(f->mmap, data_fd);
1121 mmap_cache_close_fd(f->mmap, entry_fd);
1122 mmap_cache_close_fd(f->mmap, entry_array_fd);
1123
1124 close_nointr_nofail(data_fd);
1125 close_nointr_nofail(entry_fd);
1126 close_nointr_nofail(entry_array_fd);
1127
2a7b539a
LP
1128 if (first_contained)
1129 *first_contained = le64toh(f->header->head_entry_realtime);
6c7be122 1130 if (last_validated)
f7fab8a5 1131 *last_validated = last_sealed_realtime;
6c7be122
LP
1132 if (last_contained)
1133 *last_contained = le64toh(f->header->tail_entry_realtime);
1134
0284adc6
LP
1135 return 0;
1136
1137fail:
b72631e5
LP
1138 if (show_progress)
1139 flush_progress();
0284adc6
LP
1140
1141 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
1142 f->path,
1143 (unsigned long long) p,
1144 (unsigned long long) f->last_stat.st_size,
1145 (unsigned long long) (100 * p / f->last_stat.st_size));
1146
1147 if (data_fd >= 0) {
1148 mmap_cache_close_fd(f->mmap, data_fd);
1149 close_nointr_nofail(data_fd);
1150 }
1151
1152 if (entry_fd >= 0) {
1153 mmap_cache_close_fd(f->mmap, entry_fd);
1154 close_nointr_nofail(entry_fd);
1155 }
1156
1157 if (entry_array_fd >= 0) {
1158 mmap_cache_close_fd(f->mmap, entry_array_fd);
1159 close_nointr_nofail(entry_array_fd);
1160 }
1161
1162 return r;
1163}