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