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