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