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