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