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