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