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