chiark / gitweb /
journal: refuse verification of files with unknown flags
[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         unsigned i;
696         bool found_last;
697
698         assert(f);
699
700         if (key) {
701                 r = journal_file_parse_verification_key(f, key);
702                 if (r < 0) {
703                         log_error("Failed to parse seed.");
704                         return r;
705                 }
706         } else if (f->seal)
707                 return -ENOKEY;
708
709         data_fd = mkostemp(data_path, O_CLOEXEC);
710         if (data_fd < 0) {
711                 log_error("Failed to create data file: %m");
712                 r = -errno;
713                 goto fail;
714         }
715         unlink(data_path);
716
717         entry_fd = mkostemp(entry_path, O_CLOEXEC);
718         if (entry_fd < 0) {
719                 log_error("Failed to create entry file: %m");
720                 r = -errno;
721                 goto fail;
722         }
723         unlink(entry_path);
724
725         entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
726         if (entry_array_fd < 0) {
727                 log_error("Failed to create entry array file: %m");
728                 r = -errno;
729                 goto fail;
730         }
731         unlink(entry_array_path);
732
733 #ifdef HAVE_GCRYPT
734         if ((le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) != 0)
735 #else
736         if (f->header->compatible_flags != 0)
737 #endif
738         {
739                 log_error("Cannot verify file with unknown extensions.");
740                 r = -ENOTSUP;
741                 goto fail;
742         }
743
744         for (i = 0; i < sizeof(f->header->reserved); i++)
745                 if (f->header->reserved[i] != 0) {
746                         log_error("Reserved field in non-zero.");
747                         r = -EBADMSG;
748                         goto fail;
749                 }
750
751         /* First iteration: we go through all objects, verify the
752          * superficial structure, headers, hashes. */
753
754         p = le64toh(f->header->header_size);
755         while (p != 0) {
756                 if (show_progress)
757                         draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
758
759                 r = journal_file_move_to_object(f, -1, p, &o);
760                 if (r < 0) {
761                         log_error("Invalid object at %llu", (unsigned long long) p);
762                         goto fail;
763                 }
764
765                 if (p > le64toh(f->header->tail_object_offset)) {
766                         log_error("Invalid tail object pointer");
767                         r = -EBADMSG;
768                         goto fail;
769                 }
770
771                 if (p == le64toh(f->header->tail_object_offset))
772                         found_last = true;
773
774                 n_objects ++;
775
776                 r = journal_file_object_verify(f, o);
777                 if (r < 0) {
778                         log_error("Invalid object contents at %llu", (unsigned long long) p);
779                         goto fail;
780                 }
781
782                 if (o->object.flags & OBJECT_COMPRESSED &&
783                     !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
784                         log_error("Compressed object in file without compression at %llu", (unsigned long long) p);
785                         r = -EBADMSG;
786                         goto fail;
787                 }
788
789                 switch (o->object.type) {
790
791                 case OBJECT_DATA:
792                         r = write_uint64(data_fd, p);
793                         if (r < 0)
794                                 goto fail;
795
796                         n_data++;
797                         break;
798
799                 case OBJECT_FIELD:
800                         n_fields++;
801                         break;
802
803                 case OBJECT_ENTRY:
804                         if ((le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED) && n_tags <= 0) {
805                                 log_error("First entry before first tag at %llu", (unsigned long long) p);
806                                 r = -EBADMSG;
807                                 goto fail;
808                         }
809
810                         r = write_uint64(entry_fd, p);
811                         if (r < 0)
812                                 goto fail;
813
814                         if (last_tag_realtime > le64toh(o->entry.realtime)) {
815                                 log_error("Older entry after newer tag at %llu", (unsigned long long) p);
816                                 r = -EBADMSG;
817                                 goto fail;
818                         }
819
820                         if (!entry_seqnum_set &&
821                             le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
822                                 log_error("Head entry sequence number incorrect at %llu", (unsigned long long) p);
823                                 r = -EBADMSG;
824                                 goto fail;
825                         }
826
827                         if (entry_seqnum_set &&
828                             entry_seqnum >= le64toh(o->entry.seqnum)) {
829                                 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
830                                 r = -EBADMSG;
831                                 goto fail;
832                         }
833
834                         entry_seqnum = le64toh(o->entry.seqnum);
835                         entry_seqnum_set = true;
836
837                         if (entry_monotonic_set &&
838                             sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
839                             entry_monotonic > le64toh(o->entry.monotonic)) {
840                                 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
841                                 r = -EBADMSG;
842                                 goto fail;
843                         }
844
845                         entry_monotonic = le64toh(o->entry.monotonic);
846                         entry_boot_id = o->entry.boot_id;
847                         entry_monotonic_set = true;
848
849                         if (!entry_realtime_set &&
850                             le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
851                                 log_error("Head entry realtime timestamp incorrect");
852                                 r = -EBADMSG;
853                                 goto fail;
854                         }
855
856                         entry_realtime = le64toh(o->entry.realtime);
857                         entry_realtime_set = true;
858
859                         n_entries ++;
860                         break;
861
862                 case OBJECT_DATA_HASH_TABLE:
863                         if (n_data_hash_tables > 1) {
864                                 log_error("More than one data hash table at %llu", (unsigned long long) p);
865                                 r = -EBADMSG;
866                                 goto fail;
867                         }
868
869                         if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
870                             le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
871                                 log_error("Header fields for data hash table invalid");
872                                 r = -EBADMSG;
873                                 goto fail;
874                         }
875
876                         n_data_hash_tables++;
877                         break;
878
879                 case OBJECT_FIELD_HASH_TABLE:
880                         if (n_field_hash_tables > 1) {
881                                 log_error("More than one field hash table at %llu", (unsigned long long) p);
882                                 r = -EBADMSG;
883                                 goto fail;
884                         }
885
886                         if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
887                             le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
888                                 log_error("Header fields for field hash table invalid");
889                                 r = -EBADMSG;
890                                 goto fail;
891                         }
892
893                         n_field_hash_tables++;
894                         break;
895
896                 case OBJECT_ENTRY_ARRAY:
897                         r = write_uint64(entry_array_fd, p);
898                         if (r < 0)
899                                 goto fail;
900
901                         if (p == le64toh(f->header->entry_array_offset)) {
902                                 if (found_main_entry_array) {
903                                         log_error("More than one main entry array at %llu", (unsigned long long) p);
904                                         r = -EBADMSG;
905                                         goto fail;
906                                 }
907
908                                 found_main_entry_array = true;
909                         }
910
911                         n_entry_arrays++;
912                         break;
913
914                 case OBJECT_TAG: {
915                         uint64_t q, rt;
916
917                         if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED)) {
918                                 log_error("Tag object in file without sealing at %llu", (unsigned long long) p);
919                                 r = -EBADMSG;
920                                 goto fail;
921                         }
922
923                         if (le64toh(o->tag.seqnum) != n_tags + 1) {
924                                 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
925                                 r = -EBADMSG;
926                                 goto fail;
927                         }
928
929                         if (le64toh(o->tag.epoch) < last_epoch) {
930                                 log_error("Epoch sequence out of synchronization at %llu", (unsigned long long) p);
931                                 r = -EBADMSG;
932                                 goto fail;
933                         }
934
935                         if (f->seal) {
936                                 log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum));
937
938                                 rt = (o->tag.epoch + 1) * f->fss_interval_usec + f->fss_start_usec;
939                                 if (entry_realtime_set && entry_realtime >= rt) {
940                                         log_error("Tag/entry realtime timestamp out of synchronization at %llu", (unsigned long long) p);
941                                         r = -EBADMSG;
942                                         goto fail;
943                                 }
944
945                                 /* OK, now we know the epoch. So let's now set
946                                  * it, and calculate the HMAC for everything
947                                  * since the last tag. */
948                                 r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
949                                 if (r < 0)
950                                         goto fail;
951
952                                 r = journal_file_hmac_start(f);
953                                 if (r < 0)
954                                         goto fail;
955
956                                 if (last_tag == 0) {
957                                         r = journal_file_hmac_put_header(f);
958                                         if (r < 0)
959                                                 goto fail;
960
961                                         q = le64toh(f->header->header_size);
962                                 } else
963                                         q = last_tag;
964
965                                 while (q <= p) {
966                                         r = journal_file_move_to_object(f, -1, q, &o);
967                                         if (r < 0)
968                                                 goto fail;
969
970                                         r = journal_file_hmac_put_object(f, -1, q);
971                                         if (r < 0)
972                                                 goto fail;
973
974                                         q = q + ALIGN64(le64toh(o->object.size));
975                                 }
976
977                                 /* Position might have changed, let's reposition things */
978                                 r = journal_file_move_to_object(f, -1, p, &o);
979                                 if (r < 0)
980                                         goto fail;
981
982                                 if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
983                                         log_error("Tag failed verification at %llu", (unsigned long long) p);
984                                         r = -EBADMSG;
985                                         goto fail;
986                                 }
987
988                                 f->hmac_running = false;
989                                 last_tag_realtime = rt;
990                         }
991
992                         last_tag = p + ALIGN64(le64toh(o->object.size));
993                         last_epoch = le64toh(o->tag.epoch);
994
995                         n_tags ++;
996                         break;
997                 }
998
999                 default:
1000                         n_weird ++;
1001                 }
1002
1003                 if (p == le64toh(f->header->tail_object_offset))
1004                         p = 0;
1005                 else
1006                         p = p + ALIGN64(le64toh(o->object.size));
1007         }
1008
1009         if (!found_last) {
1010                 log_error("Tail object pointer dead");
1011                 r = -EBADMSG;
1012                 goto fail;
1013         }
1014
1015         if (n_objects != le64toh(f->header->n_objects)) {
1016                 log_error("Object number mismatch");
1017                 r = -EBADMSG;
1018                 goto fail;
1019         }
1020
1021         if (n_entries != le64toh(f->header->n_entries)) {
1022                 log_error("Entry number mismatch");
1023                 r = -EBADMSG;
1024                 goto fail;
1025         }
1026
1027         if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
1028             n_data != le64toh(f->header->n_data)) {
1029                 log_error("Data number mismatch");
1030                 r = -EBADMSG;
1031                 goto fail;
1032         }
1033
1034         if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
1035             n_fields != le64toh(f->header->n_fields)) {
1036                 log_error("Field number mismatch");
1037                 r = -EBADMSG;
1038                 goto fail;
1039         }
1040
1041         if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
1042             n_tags != le64toh(f->header->n_tags)) {
1043                 log_error("Tag number mismatch");
1044                 r = -EBADMSG;
1045                 goto fail;
1046         }
1047
1048         if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
1049             n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
1050                 log_error("Entry array number mismatch");
1051                 r = -EBADMSG;
1052                 goto fail;
1053         }
1054
1055         if (n_data_hash_tables != 1) {
1056                 log_error("Missing data hash table");
1057                 r = -EBADMSG;
1058                 goto fail;
1059         }
1060
1061         if (n_field_hash_tables != 1) {
1062                 log_error("Missing field hash table");
1063                 r = -EBADMSG;
1064                 goto fail;
1065         }
1066
1067         if (!found_main_entry_array) {
1068                 log_error("Missing entry array");
1069                 r = -EBADMSG;
1070                 goto fail;
1071         }
1072
1073         if (entry_seqnum_set &&
1074             entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
1075                 log_error("Invalid tail seqnum");
1076                 r = -EBADMSG;
1077                 goto fail;
1078         }
1079
1080         if (entry_monotonic_set &&
1081             (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
1082              entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
1083                 log_error("Invalid tail monotonic timestamp");
1084                 r = -EBADMSG;
1085                 goto fail;
1086         }
1087
1088         if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
1089                 log_error("Invalid tail realtime timestamp");
1090                 r = -EBADMSG;
1091                 goto fail;
1092         }
1093
1094         /* Second iteration: we follow all objects referenced from the
1095          * two entry points: the object hash table and the entry
1096          * array. We also check that everything referenced (directly
1097          * or indirectly) in the data hash table also exists in the
1098          * entry array, and vice versa. Note that we do not care for
1099          * unreferenced objects. We only care that everything that is
1100          * referenced is consistent. */
1101
1102         r = verify_entry_array(f,
1103                                data_fd, n_data,
1104                                entry_fd, n_entries,
1105                                entry_array_fd, n_entry_arrays,
1106                                &last_usec,
1107                                show_progress);
1108         if (r < 0)
1109                 goto fail;
1110
1111         r = verify_hash_table(f,
1112                               data_fd, n_data,
1113                               entry_fd, n_entries,
1114                               entry_array_fd, n_entry_arrays,
1115                               &last_usec,
1116                               show_progress);
1117         if (r < 0)
1118                 goto fail;
1119
1120         if (show_progress)
1121                 flush_progress();
1122
1123         mmap_cache_close_fd(f->mmap, data_fd);
1124         mmap_cache_close_fd(f->mmap, entry_fd);
1125         mmap_cache_close_fd(f->mmap, entry_array_fd);
1126
1127         close_nointr_nofail(data_fd);
1128         close_nointr_nofail(entry_fd);
1129         close_nointr_nofail(entry_array_fd);
1130
1131         if (first_validated)
1132                 *first_validated = last_tag_realtime ? le64toh(f->header->head_entry_realtime) : 0;
1133         if (last_validated)
1134                 *last_validated = last_tag_realtime;
1135         if (last_contained)
1136                 *last_contained = le64toh(f->header->tail_entry_realtime);
1137
1138         return 0;
1139
1140 fail:
1141         if (show_progress)
1142                 flush_progress();
1143
1144         log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
1145                   f->path,
1146                   (unsigned long long) p,
1147                   (unsigned long long) f->last_stat.st_size,
1148                   (unsigned long long) (100 * p / f->last_stat.st_size));
1149
1150         if (data_fd >= 0) {
1151                 mmap_cache_close_fd(f->mmap, data_fd);
1152                 close_nointr_nofail(data_fd);
1153         }
1154
1155         if (entry_fd >= 0) {
1156                 mmap_cache_close_fd(f->mmap, entry_fd);
1157                 close_nointr_nofail(entry_fd);
1158         }
1159
1160         if (entry_array_fd >= 0) {
1161                 mmap_cache_close_fd(f->mmap, entry_array_fd);
1162                 close_nointr_nofail(entry_array_fd);
1163         }
1164
1165         return r;
1166 }