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