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