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