1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
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.
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.
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/>.
28 #include "journal-def.h"
29 #include "journal-file.h"
30 #include "journal-authenticate.h"
31 #include "journal-verify.h"
36 * - verify hashes of compressed objects
38 * - check for unreferenced objects
43 static int journal_file_object_verify(JournalFile *f, Object *o) {
47 /* This does various superficial tests about the length an
48 * possible field values. It does not follow any references to
51 if ((o->object.flags & OBJECT_COMPRESSED) &&
52 o->object.type != OBJECT_DATA)
55 switch (o->object.type) {
58 if (le64toh(o->data.entry_offset) <= 0 ||
59 le64toh(o->data.n_entries) <= 0)
62 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
65 if (!(o->object.flags & OBJECT_COMPRESSED)) {
68 h1 = le64toh(o->data.hash);
69 h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
78 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
83 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
86 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
89 if (le64toh(o->entry.seqnum) <= 0 ||
90 le64toh(o->entry.realtime) <= 0)
95 case OBJECT_DATA_HASH_TABLE:
96 case OBJECT_FIELD_HASH_TABLE:
97 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0)
102 case OBJECT_ENTRY_ARRAY:
103 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0)
109 if (le64toh(o->object.size) != sizeof(TagObject))
117 static void draw_progress(uint64_t p, usec_t *last_usec) {
121 if (!isatty(STDOUT_FILENO))
124 z = now(CLOCK_MONOTONIC);
127 if (x != 0 && x + 40 * USEC_PER_MSEC > z)
132 n = (3 * columns()) / 4;
133 j = (n * (unsigned) p) / 65535ULL;
136 fputs("\r\x1B[?25l", stdout);
138 for (i = 0; i < j; i++)
139 fputs("\xe2\x96\x88", stdout);
141 for (i = 0; i < k; i++)
142 fputs("\xe2\x96\x91", stdout);
144 printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU);
146 fputs("\r\x1B[?25h", stdout);
150 static void flush_progress(void) {
153 if (!isatty(STDOUT_FILENO))
156 n = (3 * columns()) / 4;
160 for (i = 0; i < n + 5; i++)
167 static int write_uint64(int fd, uint64_t p) {
170 k = write(fd, &p, sizeof(p));
179 static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
194 r = mmap_cache_get(m, fd, PROT_READ, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z);
210 int journal_file_verify(JournalFile *f, const char *key) {
214 uint64_t tag_seqnum = 0, entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
215 sd_id128_t entry_boot_id;
216 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
217 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;
218 usec_t last_usec = 0;
219 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
220 char data_path[] = "/var/tmp/journal-data-XXXXXX",
221 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
222 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
226 data_fd = mkostemp(data_path, O_CLOEXEC);
228 log_error("Failed to create data file: %m");
233 entry_fd = mkostemp(entry_path, O_CLOEXEC);
235 log_error("Failed to create entry file: %m");
240 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
241 if (entry_array_fd < 0) {
242 log_error("Failed to create entry array file: %m");
245 unlink(entry_array_path);
247 /* First iteration: we go through all objects, verify the
248 * superficial structure, headers, hashes. */
250 r = journal_file_hmac_put_header(f);
252 log_error("Failed to calculate HMAC of header.");
256 p = le64toh(f->header->header_size);
258 draw_progress((0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec);
260 r = journal_file_move_to_object(f, -1, p, &o);
262 log_error("Invalid object at %llu", (unsigned long long) p);
266 if (le64toh(f->header->tail_object_offset) < p) {
267 log_error("Invalid tail object pointer.");
274 r = journal_file_object_verify(f, o);
276 log_error("Invalid object contents at %llu", (unsigned long long) p);
280 if (o->object.flags & OBJECT_COMPRESSED &&
281 !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
282 log_error("Compressed object without compression at %llu", (unsigned long long) p);
287 r = journal_file_hmac_put_object(f, -1, p);
289 log_error("Failed to calculate HMAC at %llu", (unsigned long long) p);
293 if (o->object.type == OBJECT_TAG) {
295 if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) {
296 log_error("Tag object without authentication at %llu", (unsigned long long) p);
301 if (le64toh(o->tag.seqnum) != tag_seqnum) {
302 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
307 } else if (o->object.type == OBJECT_ENTRY) {
309 r = write_uint64(entry_fd, p);
313 if (!entry_seqnum_set &&
314 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
315 log_error("Head entry sequence number incorrect");
320 if (entry_seqnum_set &&
321 entry_seqnum >= le64toh(o->entry.seqnum)) {
322 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
327 entry_seqnum = le64toh(o->entry.seqnum);
328 entry_seqnum_set = true;
330 if (entry_monotonic_set &&
331 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
332 entry_monotonic > le64toh(o->entry.monotonic)) {
333 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
338 entry_monotonic = le64toh(o->entry.monotonic);
339 entry_boot_id = o->entry.boot_id;
340 entry_monotonic_set = true;
342 if (!entry_realtime_set &&
343 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
344 log_error("Head entry realtime timestamp incorrect");
349 entry_realtime = le64toh(o->entry.realtime);
350 entry_realtime_set = true;
353 } else if (o->object.type == OBJECT_ENTRY_ARRAY) {
355 r = write_uint64(entry_array_fd, p);
359 if (p == le64toh(f->header->entry_array_offset)) {
360 if (found_main_entry_array) {
361 log_error("More than one main entry array at %llu", (unsigned long long) p);
366 found_main_entry_array = true;
371 } else if (o->object.type == OBJECT_DATA) {
373 r = write_uint64(data_fd, p);
379 } else if (o->object.type == OBJECT_FIELD)
381 else if (o->object.type == OBJECT_DATA_HASH_TABLE) {
382 n_data_hash_tables++;
384 if (n_data_hash_tables > 1) {
385 log_error("More than one data hash table at %llu", (unsigned long long) p);
390 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
391 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
392 log_error("Header fields for data hash table invalid.");
396 } else if (o->object.type == OBJECT_FIELD_HASH_TABLE) {
397 n_field_hash_tables++;
399 if (n_field_hash_tables > 1) {
400 log_error("More than one field hash table at %llu", (unsigned long long) p);
405 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
406 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
407 log_error("Header fields for field hash table invalid.");
411 } else if (o->object.type >= _OBJECT_TYPE_MAX)
414 if (p == le64toh(f->header->tail_object_offset))
417 p = p + ALIGN64(le64toh(o->object.size));
420 if (n_objects != le64toh(f->header->n_objects)) {
421 log_error("Object number mismatch");
426 if (n_entries != le64toh(f->header->n_entries)) {
427 log_error("Entry number mismatch");
432 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
433 n_data != le64toh(f->header->n_data)) {
434 log_error("Data number mismatch");
439 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
440 n_fields != le64toh(f->header->n_fields)) {
441 log_error("Field number mismatch");
446 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
447 tag_seqnum != le64toh(f->header->n_tags)) {
448 log_error("Tag number mismatch");
453 if (n_data_hash_tables != 1) {
454 log_error("Missing data hash table");
459 if (n_field_hash_tables != 1) {
460 log_error("Missing field hash table");
465 if (!found_main_entry_array) {
466 log_error("Missing entry array");
471 if (entry_seqnum_set &&
472 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
473 log_error("Invalid tail seqnum");
478 if (entry_monotonic_set &&
479 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
480 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
481 log_error("Invalid tail monotonic timestamp");
486 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
487 log_error("Invalid tail realtime timestamp");
492 /* Second iteration: we go through all objects again, this
493 * time verify all pointers. */
495 p = le64toh(f->header->header_size);
497 draw_progress(0x8000 + (0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec);
499 r = journal_file_move_to_object(f, -1, p, &o);
501 log_error("Invalid object at %llu", (unsigned long long) p);
505 if (o->object.type == OBJECT_ENTRY_ARRAY) {
508 if (le64toh(o->entry_array.next_entry_array_offset) != 0 &&
509 !contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, le64toh(o->entry_array.next_entry_array_offset))) {
510 log_error("Entry array chains up to invalid next array at %llu", (unsigned long long) p);
515 n = journal_file_entry_array_n_items(o);
516 for (i = 0; i < n; i++) {
517 if (le64toh(o->entry_array.items[i]) != 0 &&
518 !contains_uint64(f->mmap, entry_fd, n_entries, le64toh(o->entry_array.items[i]))) {
520 log_error("Entry array points to invalid next array at %llu", (unsigned long long) p);
528 r = journal_file_move_to_object(f, -1, p, &o);
530 log_error("Invalid object at %llu", (unsigned long long) p);
534 if (p == le64toh(f->header->tail_object_offset))
537 p = p + ALIGN64(le64toh(o->object.size));
542 mmap_cache_close_fd(f->mmap, data_fd);
543 mmap_cache_close_fd(f->mmap, entry_fd);
544 mmap_cache_close_fd(f->mmap, entry_array_fd);
546 close_nointr_nofail(data_fd);
547 close_nointr_nofail(entry_fd);
548 close_nointr_nofail(entry_array_fd);
555 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
557 (unsigned long long) p,
558 (unsigned long long) f->last_stat.st_size,
559 (unsigned long long) (100 * p / f->last_stat.st_size));
562 mmap_cache_close_fd(f->mmap, data_fd);
563 close_nointr_nofail(data_fd);
567 mmap_cache_close_fd(f->mmap, entry_fd);
568 close_nointr_nofail(entry_fd);
571 if (entry_array_fd >= 0) {
572 mmap_cache_close_fd(f->mmap, entry_array_fd);
573 close_nointr_nofail(entry_array_fd);