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