chiark / gitweb /
journal: fix verification without key
[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
399         uint64_t i, n;
400         int r;
401
402         assert(f);
403         assert(data_fd >= 0);
404         assert(entry_fd >= 0);
405         assert(entry_array_fd >= 0);
406         assert(last_usec);
407
408         n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
409         for (i = 0; i < n; i++) {
410                 uint64_t last = 0, p;
411
412                 draw_progress(0xC000 + (0x3FFF * i / n), last_usec);
413
414                 p = le64toh(f->data_hash_table[i].head_hash_offset);
415                 while (p != 0) {
416                         Object *o;
417                         uint64_t next;
418
419                         if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
420                                 log_error("Invalid data object at hash entry %llu of %llu",
421                                           (unsigned long long) i, (unsigned long long) n);
422                                 return -EBADMSG;
423                         }
424
425                         r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
426                         if (r < 0)
427                                 return r;
428
429                         next = le64toh(o->data.next_hash_offset);
430                         if (next != 0 && next <= p) {
431                                 log_error("Hash chain has a cycle in hash entry %llu of %llu",
432                                           (unsigned long long) i, (unsigned long long) n);
433                                 return -EBADMSG;
434                         }
435
436                         if (le64toh(o->data.hash) % n != i) {
437                                 log_error("Hash value mismatch in hash entry %llu of %llu",
438                                           (unsigned long long) i, (unsigned long long) n);
439                                 return -EBADMSG;
440                         }
441
442                         r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
443                         if (r < 0)
444                                 return r;
445
446                         last = p;
447                         p = next;
448                 }
449
450                 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
451                         log_error("Tail hash pointer mismatch in hash table");
452                         return -EBADMSG;
453                 }
454         }
455
456         return 0;
457 }
458
459 static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
460         uint64_t n, h, q;
461         int r;
462         assert(f);
463
464         n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
465         h = hash % n;
466
467         q = le64toh(f->data_hash_table[h].head_hash_offset);
468         while (q != 0) {
469                 Object *o;
470
471                 if (p == q)
472                         return 1;
473
474                 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
475                 if (r < 0)
476                         return r;
477
478                 q = le64toh(o->data.next_hash_offset);
479         }
480
481         return 0;
482 }
483
484 static int verify_entry(
485                 JournalFile *f,
486                 Object *o, uint64_t p,
487                 int data_fd, uint64_t n_data) {
488
489         uint64_t i, n;
490         int r;
491
492         assert(f);
493         assert(o);
494         assert(data_fd >= 0);
495
496         n = journal_file_entry_n_items(o);
497         for (i = 0; i < n; i++) {
498                 uint64_t q, h;
499                 Object *u;
500
501                 q = le64toh(o->entry.items[i].object_offset);
502                 h = le64toh(o->entry.items[i].hash);
503
504                 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
505                         log_error("Invalid data object at entry %llu",
506                                   (unsigned long long) p);
507                                 return -EBADMSG;
508                         }
509
510                 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
511                 if (r < 0)
512                         return r;
513
514                 if (le64toh(u->data.hash) != h) {
515                         log_error("Hash mismatch for data object at entry %llu",
516                                   (unsigned long long) p);
517                         return -EBADMSG;
518                 }
519
520                 r = data_object_in_hash_table(f, h, q);
521                 if (r < 0)
522                         return r;
523                 if (r == 0) {
524                         log_error("Data object missing from hash at entry %llu",
525                                   (unsigned long long) p);
526                         return -EBADMSG;
527                 }
528         }
529
530         return 0;
531 }
532
533 static int verify_entry_array(
534                 JournalFile *f,
535                 int data_fd, uint64_t n_data,
536                 int entry_fd, uint64_t n_entries,
537                 int entry_array_fd, uint64_t n_entry_arrays,
538                 usec_t *last_usec) {
539
540         uint64_t i = 0, a, n, last = 0;
541         int r;
542
543         assert(f);
544         assert(data_fd >= 0);
545         assert(entry_fd >= 0);
546         assert(entry_array_fd >= 0);
547         assert(last_usec);
548
549         n = le64toh(f->header->n_entries);
550         a = le64toh(f->header->entry_array_offset);
551         while (i < n) {
552                 uint64_t next, m, j;
553                 Object *o;
554
555                 draw_progress(0x8000 + (0x3FFF * i / n), last_usec);
556
557                 if (a == 0) {
558                         log_error("Array chain too short at %llu of %llu",
559                                   (unsigned long long) i, (unsigned long long) n);
560                         return -EBADMSG;
561                 }
562
563                 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
564                         log_error("Invalid array at %llu of %llu",
565                                   (unsigned long long) i, (unsigned long long) n);
566                         return -EBADMSG;
567                 }
568
569                 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
570                 if (r < 0)
571                         return r;
572
573                 next = le64toh(o->entry_array.next_entry_array_offset);
574                 if (next != 0 && next <= a) {
575                         log_error("Array chain has cycle at %llu of %llu",
576                                   (unsigned long long) i, (unsigned long long) n);
577                         return -EBADMSG;
578                 }
579
580                 m = journal_file_entry_array_n_items(o);
581                 for (j = 0; i < n && j < m; i++, j++) {
582                         uint64_t p;
583
584                         p = le64toh(o->entry_array.items[j]);
585                         if (p <= last) {
586                                 log_error("Entry array not sorted at %llu of %llu",
587                                           (unsigned long long) i, (unsigned long long) n);
588                                 return -EBADMSG;
589                         }
590                         last = p;
591
592                         if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
593                                 log_error("Invalid array entry at %llu of %llu",
594                                           (unsigned long long) i, (unsigned long long) n);
595                                 return -EBADMSG;
596                         }
597
598                         r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
599                         if (r < 0)
600                                 return r;
601
602                         r = verify_entry(f, o, p, data_fd, n_data);
603                         if (r < 0)
604                                 return r;
605
606                         /* Pointer might have moved, reposition */
607                         r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
608                         if (r < 0)
609                                 return r;
610                 }
611
612                 a = next;
613         }
614
615         return 0;
616 }
617
618 static int journal_file_parse_verification_key(JournalFile *f, const char *key) {
619         uint8_t *seed;
620         size_t seed_size, c;
621         const char *k;
622         int r;
623         unsigned long long start, interval;
624
625         seed_size = FSPRG_RECOMMENDED_SEEDLEN;
626         seed = malloc(seed_size);
627         if (!seed)
628                 return -ENOMEM;
629
630         k = key;
631         for (c = 0; c < seed_size; c++) {
632                 int x, y;
633
634                 while (*k == '-')
635                         k++;
636
637                 x = unhexchar(*k);
638                 if (x < 0) {
639                         free(seed);
640                         return -EINVAL;
641                 }
642                 k++;
643                 y = unhexchar(*k);
644                 if (y < 0) {
645                         free(seed);
646                         return -EINVAL;
647                 }
648                 k++;
649
650                 seed[c] = (uint8_t) (x * 16 + y);
651         }
652
653         if (*k != '/') {
654                 free(seed);
655                 return -EINVAL;
656         }
657         k++;
658
659         r = sscanf(k, "%llx-%llx", &start, &interval);
660         if (r != 2) {
661                 free(seed);
662                 return -EINVAL;
663         }
664
665         f->fsprg_seed = seed;
666         f->fsprg_seed_size = seed_size;
667
668         f->fss_start_usec = start * interval;
669         f->fss_interval_usec = interval;
670
671         return 0;
672 }
673
674 int journal_file_verify(
675                 JournalFile *f,
676                 const char *key,
677                 usec_t *first_validated, usec_t *last_validated, usec_t *last_contained) {
678         int r;
679         Object *o;
680         uint64_t p = 0, last_tag = 0, last_epoch = 0, last_tag_realtime = 0;
681         uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
682         sd_id128_t entry_boot_id;
683         bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
684         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;
685         usec_t last_usec = 0;
686         int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
687         char data_path[] = "/var/tmp/journal-data-XXXXXX",
688                 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
689                 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
690
691         assert(f);
692
693         if (key) {
694                 r = journal_file_parse_verification_key(f, key);
695                 if (r < 0) {
696                         log_error("Failed to parse seed.");
697                         return r;
698                 }
699         } else if (f->seal)
700                 return -ENOKEY;
701
702         data_fd = mkostemp(data_path, O_CLOEXEC);
703         if (data_fd < 0) {
704                 log_error("Failed to create data file: %m");
705                 r = -errno;
706                 goto fail;
707         }
708         unlink(data_path);
709
710         entry_fd = mkostemp(entry_path, O_CLOEXEC);
711         if (entry_fd < 0) {
712                 log_error("Failed to create entry file: %m");
713                 r = -errno;
714                 goto fail;
715         }
716         unlink(entry_path);
717
718         entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
719         if (entry_array_fd < 0) {
720                 log_error("Failed to create entry array file: %m");
721                 r = -errno;
722                 goto fail;
723         }
724         unlink(entry_array_path);
725
726         /* First iteration: we go through all objects, verify the
727          * superficial structure, headers, hashes. */
728
729         p = le64toh(f->header->header_size);
730         while (p != 0) {
731                 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
732
733                 r = journal_file_move_to_object(f, -1, p, &o);
734                 if (r < 0) {
735                         log_error("Invalid object at %llu", (unsigned long long) p);
736                         goto fail;
737                 }
738
739                 if (le64toh(f->header->tail_object_offset) < p) {
740                         log_error("Invalid tail object pointer");
741                         r = -EBADMSG;
742                         goto fail;
743                 }
744
745                 n_objects ++;
746
747                 r = journal_file_object_verify(f, o);
748                 if (r < 0) {
749                         log_error("Invalid object contents at %llu", (unsigned long long) p);
750                         goto fail;
751                 }
752
753                 if (o->object.flags & OBJECT_COMPRESSED &&
754                     !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
755                         log_error("Compressed object in file without compression at %llu", (unsigned long long) p);
756                         r = -EBADMSG;
757                         goto fail;
758                 }
759
760                 switch (o->object.type) {
761
762                 case OBJECT_DATA:
763                         r = write_uint64(data_fd, p);
764                         if (r < 0)
765                                 goto fail;
766
767                         n_data++;
768                         break;
769
770                 case OBJECT_FIELD:
771                         n_fields++;
772                         break;
773
774                 case OBJECT_ENTRY:
775                         if ((le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED) && n_tags <= 0) {
776                                 log_error("First entry before first tag at %llu", (unsigned long long) p);
777                                 r = -EBADMSG;
778                                 goto fail;
779                         }
780
781                         r = write_uint64(entry_fd, p);
782                         if (r < 0)
783                                 goto fail;
784
785                         if (last_tag_realtime > le64toh(o->entry.realtime)) {
786                                 log_error("Older entry after newer tag at %llu", (unsigned long long) p);
787                                 r = -EBADMSG;
788                                 goto fail;
789                         }
790
791                         if (!entry_seqnum_set &&
792                             le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
793                                 log_error("Head entry sequence number incorrect at %llu", (unsigned long long) p);
794                                 r = -EBADMSG;
795                                 goto fail;
796                         }
797
798                         if (entry_seqnum_set &&
799                             entry_seqnum >= le64toh(o->entry.seqnum)) {
800                                 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
801                                 r = -EBADMSG;
802                                 goto fail;
803                         }
804
805                         entry_seqnum = le64toh(o->entry.seqnum);
806                         entry_seqnum_set = true;
807
808                         if (entry_monotonic_set &&
809                             sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
810                             entry_monotonic > le64toh(o->entry.monotonic)) {
811                                 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
812                                 r = -EBADMSG;
813                                 goto fail;
814                         }
815
816                         entry_monotonic = le64toh(o->entry.monotonic);
817                         entry_boot_id = o->entry.boot_id;
818                         entry_monotonic_set = true;
819
820                         if (!entry_realtime_set &&
821                             le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
822                                 log_error("Head entry realtime timestamp incorrect");
823                                 r = -EBADMSG;
824                                 goto fail;
825                         }
826
827                         entry_realtime = le64toh(o->entry.realtime);
828                         entry_realtime_set = true;
829
830                         n_entries ++;
831                         break;
832
833                 case OBJECT_DATA_HASH_TABLE:
834                         if (n_data_hash_tables > 1) {
835                                 log_error("More than one data hash table at %llu", (unsigned long long) p);
836                                 r = -EBADMSG;
837                                 goto fail;
838                         }
839
840                         if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
841                             le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
842                                 log_error("Header fields for data hash table invalid");
843                                 r = -EBADMSG;
844                                 goto fail;
845                         }
846
847                         n_data_hash_tables++;
848                         break;
849
850                 case OBJECT_FIELD_HASH_TABLE:
851                         if (n_field_hash_tables > 1) {
852                                 log_error("More than one field hash table at %llu", (unsigned long long) p);
853                                 r = -EBADMSG;
854                                 goto fail;
855                         }
856
857                         if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
858                             le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
859                                 log_error("Header fields for field hash table invalid");
860                                 r = -EBADMSG;
861                                 goto fail;
862                         }
863
864                         n_field_hash_tables++;
865                         break;
866
867                 case OBJECT_ENTRY_ARRAY:
868                         r = write_uint64(entry_array_fd, p);
869                         if (r < 0)
870                                 goto fail;
871
872                         if (p == le64toh(f->header->entry_array_offset)) {
873                                 if (found_main_entry_array) {
874                                         log_error("More than one main entry array at %llu", (unsigned long long) p);
875                                         r = -EBADMSG;
876                                         goto fail;
877                                 }
878
879                                 found_main_entry_array = true;
880                         }
881
882                         n_entry_arrays++;
883                         break;
884
885                 case OBJECT_TAG: {
886                         uint64_t q, rt;
887
888                         if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED)) {
889                                 log_error("Tag object in file without sealing at %llu", (unsigned long long) p);
890                                 r = -EBADMSG;
891                                 goto fail;
892                         }
893
894                         log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum));
895
896                         if (le64toh(o->tag.seqnum) != n_tags + 1) {
897                                 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
898                                 r = -EBADMSG;
899                                 goto fail;
900                         }
901
902                         if (le64toh(o->tag.epoch) < last_epoch) {
903                                 log_error("Epoch sequence out of synchronization at %llu", (unsigned long long) p);
904                                 r = -EBADMSG;
905                                 goto fail;
906                         }
907
908                         if (f->seal) {
909                                 log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum));
910
911                                 rt = (o->tag.epoch + 1) * f->fss_interval_usec + f->fss_start_usec;
912                                 if (entry_realtime_set && entry_realtime >= rt) {
913                                         log_error("Tag/entry realtime timestamp out of synchronization at %llu", (unsigned long long) p);
914                                         r = -EBADMSG;
915                                         goto fail;
916                                 }
917
918                                 /* OK, now we know the epoch. So let's now set
919                                  * it, and calculate the HMAC for everything
920                                  * since the last tag. */
921                                 r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
922                                 if (r < 0)
923                                         goto fail;
924
925                                 r = journal_file_hmac_start(f);
926                                 if (r < 0)
927                                         goto fail;
928
929                                 if (last_tag == 0) {
930                                         r = journal_file_hmac_put_header(f);
931                                         if (r < 0)
932                                                 goto fail;
933
934                                         q = le64toh(f->header->header_size);
935                                 } else
936                                         q = last_tag;
937
938                                 while (q <= p) {
939                                         r = journal_file_move_to_object(f, -1, q, &o);
940                                         if (r < 0)
941                                                 goto fail;
942
943                                         r = journal_file_hmac_put_object(f, -1, q);
944                                         if (r < 0)
945                                                 goto fail;
946
947                                         q = q + ALIGN64(le64toh(o->object.size));
948                                 }
949
950                                 /* Position might have changed, let's reposition things */
951                                 r = journal_file_move_to_object(f, -1, p, &o);
952                                 if (r < 0)
953                                         goto fail;
954
955                                 if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
956                                         log_error("Tag failed verification at %llu", (unsigned long long) p);
957                                         r = -EBADMSG;
958                                         goto fail;
959                                 }
960
961                                 f->hmac_running = false;
962                                 last_tag_realtime = rt;
963                         }
964
965                         last_tag = p + ALIGN64(le64toh(o->object.size));
966                         last_epoch = le64toh(o->tag.epoch);
967
968                         n_tags ++;
969                         break;
970                 }
971
972                 default:
973                         n_weird ++;
974                 }
975
976                 if (p == le64toh(f->header->tail_object_offset))
977                         p = 0;
978                 else
979                         p = p + ALIGN64(le64toh(o->object.size));
980         }
981
982         if (n_objects != le64toh(f->header->n_objects)) {
983                 log_error("Object number mismatch");
984                 r = -EBADMSG;
985                 goto fail;
986         }
987
988         if (n_entries != le64toh(f->header->n_entries)) {
989                 log_error("Entry number mismatch");
990                 r = -EBADMSG;
991                 goto fail;
992         }
993
994         if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
995             n_data != le64toh(f->header->n_data)) {
996                 log_error("Data number mismatch");
997                 r = -EBADMSG;
998                 goto fail;
999         }
1000
1001         if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
1002             n_fields != le64toh(f->header->n_fields)) {
1003                 log_error("Field number mismatch");
1004                 r = -EBADMSG;
1005                 goto fail;
1006         }
1007
1008         if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
1009             n_tags != le64toh(f->header->n_tags)) {
1010                 log_error("Tag number mismatch");
1011                 r = -EBADMSG;
1012                 goto fail;
1013         }
1014
1015         if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
1016             n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
1017                 log_error("Entry array number mismatch");
1018                 r = -EBADMSG;
1019                 goto fail;
1020         }
1021
1022         if (n_data_hash_tables != 1) {
1023                 log_error("Missing data hash table");
1024                 r = -EBADMSG;
1025                 goto fail;
1026         }
1027
1028         if (n_field_hash_tables != 1) {
1029                 log_error("Missing field hash table");
1030                 r = -EBADMSG;
1031                 goto fail;
1032         }
1033
1034         if (!found_main_entry_array) {
1035                 log_error("Missing entry array");
1036                 r = -EBADMSG;
1037                 goto fail;
1038         }
1039
1040         if (entry_seqnum_set &&
1041             entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
1042                 log_error("Invalid tail seqnum");
1043                 r = -EBADMSG;
1044                 goto fail;
1045         }
1046
1047         if (entry_monotonic_set &&
1048             (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
1049              entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
1050                 log_error("Invalid tail monotonic timestamp");
1051                 r = -EBADMSG;
1052                 goto fail;
1053         }
1054
1055         if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
1056                 log_error("Invalid tail realtime timestamp");
1057                 r = -EBADMSG;
1058                 goto fail;
1059         }
1060
1061         /* Second iteration: we follow all objects referenced from the
1062          * two entry points: the object hash table and the entry
1063          * array. We also check that everything referenced (directly
1064          * or indirectly) in the data hash table also exists in the
1065          * entry array, and vice versa. Note that we do not care for
1066          * unreferenced objects. We only care that everything that is
1067          * referenced is consistent. */
1068
1069         r = verify_entry_array(f,
1070                                data_fd, n_data,
1071                                entry_fd, n_entries,
1072                                entry_array_fd, n_entry_arrays,
1073                                &last_usec);
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         if (r < 0)
1083                 goto fail;
1084
1085         flush_progress();
1086
1087         mmap_cache_close_fd(f->mmap, data_fd);
1088         mmap_cache_close_fd(f->mmap, entry_fd);
1089         mmap_cache_close_fd(f->mmap, entry_array_fd);
1090
1091         close_nointr_nofail(data_fd);
1092         close_nointr_nofail(entry_fd);
1093         close_nointr_nofail(entry_array_fd);
1094
1095         if (first_validated)
1096                 *first_validated = last_tag_realtime ? le64toh(f->header->head_entry_realtime) : 0;
1097         if (last_validated)
1098                 *last_validated = last_tag_realtime;
1099         if (last_contained)
1100                 *last_contained = le64toh(f->header->tail_entry_realtime);
1101
1102         return 0;
1103
1104 fail:
1105         flush_progress();
1106
1107         log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
1108                   f->path,
1109                   (unsigned long long) p,
1110                   (unsigned long long) f->last_stat.st_size,
1111                   (unsigned long long) (100 * p / f->last_stat.st_size));
1112
1113         if (data_fd >= 0) {
1114                 mmap_cache_close_fd(f->mmap, data_fd);
1115                 close_nointr_nofail(data_fd);
1116         }
1117
1118         if (entry_fd >= 0) {
1119                 mmap_cache_close_fd(f->mmap, entry_fd);
1120                 close_nointr_nofail(entry_fd);
1121         }
1122
1123         if (entry_array_fd >= 0) {
1124                 mmap_cache_close_fd(f->mmap, entry_array_fd);
1125                 close_nointr_nofail(entry_array_fd);
1126         }
1127
1128         return r;
1129 }