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"
38 * - check for unreferenced objects
40 * - Allow building without libgcrypt
44 static int journal_file_object_verify(JournalFile *f, Object *o) {
48 /* This does various superficial tests about the length an
49 * possible field values. It does not follow any references to
52 if ((o->object.flags & OBJECT_COMPRESSED) &&
53 o->object.type != OBJECT_DATA)
56 switch (o->object.type) {
61 if (le64toh(o->data.entry_offset) <= 0 ||
62 le64toh(o->data.n_entries) <= 0)
65 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
68 h1 = le64toh(o->data.hash);
70 if (o->object.flags & OBJECT_COMPRESSED) {
72 uint64_t alloc = 0, b_size;
74 if (!uncompress_blob(o->data.payload,
75 le64toh(o->object.size) - offsetof(Object, data.payload),
79 h2 = hash64(b, b_size);
82 h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
91 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
96 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
99 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
102 if (le64toh(o->entry.seqnum) <= 0 ||
103 le64toh(o->entry.realtime) <= 0)
108 case OBJECT_DATA_HASH_TABLE:
109 case OBJECT_FIELD_HASH_TABLE:
110 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0)
115 case OBJECT_ENTRY_ARRAY:
116 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0)
122 if (le64toh(o->object.size) != sizeof(TagObject))
130 static void draw_progress(uint64_t p, usec_t *last_usec) {
134 if (!isatty(STDOUT_FILENO))
137 z = now(CLOCK_MONOTONIC);
140 if (x != 0 && x + 40 * USEC_PER_MSEC > z)
145 n = (3 * columns()) / 4;
146 j = (n * (unsigned) p) / 65535ULL;
149 fputs("\r\x1B[?25l", stdout);
151 for (i = 0; i < j; i++)
152 fputs("\xe2\x96\x88", stdout);
154 for (i = 0; i < k; i++)
155 fputs("\xe2\x96\x91", stdout);
157 printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU);
159 fputs("\r\x1B[?25h", stdout);
163 static void flush_progress(void) {
166 if (!isatty(STDOUT_FILENO))
169 n = (3 * columns()) / 4;
173 for (i = 0; i < n + 5; i++)
180 static int write_uint64(int fd, uint64_t p) {
183 k = write(fd, &p, sizeof(p));
192 static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
207 r = mmap_cache_get(m, fd, PROT_READ, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z);
223 int journal_file_verify(JournalFile *f, const char *key) {
227 uint64_t tag_seqnum = 0, entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
228 sd_id128_t entry_boot_id;
229 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
230 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;
231 usec_t last_usec = 0;
232 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
233 char data_path[] = "/var/tmp/journal-data-XXXXXX",
234 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
235 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
239 data_fd = mkostemp(data_path, O_CLOEXEC);
241 log_error("Failed to create data file: %m");
246 entry_fd = mkostemp(entry_path, O_CLOEXEC);
248 log_error("Failed to create entry file: %m");
253 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
254 if (entry_array_fd < 0) {
255 log_error("Failed to create entry array file: %m");
258 unlink(entry_array_path);
260 /* First iteration: we go through all objects, verify the
261 * superficial structure, headers, hashes. */
263 r = journal_file_hmac_put_header(f);
265 log_error("Failed to calculate HMAC of header.");
269 p = le64toh(f->header->header_size);
271 draw_progress((0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec);
273 r = journal_file_move_to_object(f, -1, p, &o);
275 log_error("Invalid object at %llu", (unsigned long long) p);
279 if (le64toh(f->header->tail_object_offset) < p) {
280 log_error("Invalid tail object pointer.");
287 r = journal_file_object_verify(f, o);
289 log_error("Invalid object contents at %llu", (unsigned long long) p);
293 if (o->object.flags & OBJECT_COMPRESSED &&
294 !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
295 log_error("Compressed object without compression at %llu", (unsigned long long) p);
300 r = journal_file_hmac_put_object(f, -1, p);
302 log_error("Failed to calculate HMAC at %llu", (unsigned long long) p);
306 if (o->object.type == OBJECT_TAG) {
308 if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) {
309 log_error("Tag object without authentication at %llu", (unsigned long long) p);
314 if (le64toh(o->tag.seqnum) != tag_seqnum) {
315 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
320 } else if (o->object.type == OBJECT_ENTRY) {
322 r = write_uint64(entry_fd, p);
326 if (!entry_seqnum_set &&
327 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
328 log_error("Head entry sequence number incorrect");
333 if (entry_seqnum_set &&
334 entry_seqnum >= le64toh(o->entry.seqnum)) {
335 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
340 entry_seqnum = le64toh(o->entry.seqnum);
341 entry_seqnum_set = true;
343 if (entry_monotonic_set &&
344 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
345 entry_monotonic > le64toh(o->entry.monotonic)) {
346 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
351 entry_monotonic = le64toh(o->entry.monotonic);
352 entry_boot_id = o->entry.boot_id;
353 entry_monotonic_set = true;
355 if (!entry_realtime_set &&
356 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
357 log_error("Head entry realtime timestamp incorrect");
362 entry_realtime = le64toh(o->entry.realtime);
363 entry_realtime_set = true;
366 } else if (o->object.type == OBJECT_ENTRY_ARRAY) {
368 r = write_uint64(entry_array_fd, p);
372 if (p == le64toh(f->header->entry_array_offset)) {
373 if (found_main_entry_array) {
374 log_error("More than one main entry array at %llu", (unsigned long long) p);
379 found_main_entry_array = true;
384 } else if (o->object.type == OBJECT_DATA) {
386 r = write_uint64(data_fd, p);
392 } else if (o->object.type == OBJECT_FIELD)
394 else if (o->object.type == OBJECT_DATA_HASH_TABLE) {
395 n_data_hash_tables++;
397 if (n_data_hash_tables > 1) {
398 log_error("More than one data hash table at %llu", (unsigned long long) p);
403 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
404 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
405 log_error("Header fields for data hash table invalid.");
409 } else if (o->object.type == OBJECT_FIELD_HASH_TABLE) {
410 n_field_hash_tables++;
412 if (n_field_hash_tables > 1) {
413 log_error("More than one field hash table at %llu", (unsigned long long) p);
418 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
419 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
420 log_error("Header fields for field hash table invalid.");
424 } else if (o->object.type >= _OBJECT_TYPE_MAX)
427 if (p == le64toh(f->header->tail_object_offset))
430 p = p + ALIGN64(le64toh(o->object.size));
433 if (n_objects != le64toh(f->header->n_objects)) {
434 log_error("Object number mismatch");
439 if (n_entries != le64toh(f->header->n_entries)) {
440 log_error("Entry number mismatch");
445 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
446 n_data != le64toh(f->header->n_data)) {
447 log_error("Data number mismatch");
452 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
453 n_fields != le64toh(f->header->n_fields)) {
454 log_error("Field number mismatch");
459 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
460 tag_seqnum != le64toh(f->header->n_tags)) {
461 log_error("Tag number mismatch");
466 if (n_data_hash_tables != 1) {
467 log_error("Missing data hash table");
472 if (n_field_hash_tables != 1) {
473 log_error("Missing field hash table");
478 if (!found_main_entry_array) {
479 log_error("Missing entry array");
484 if (entry_seqnum_set &&
485 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
486 log_error("Invalid tail seqnum");
491 if (entry_monotonic_set &&
492 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
493 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
494 log_error("Invalid tail monotonic timestamp");
499 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
500 log_error("Invalid tail realtime timestamp");
505 /* Second iteration: we go through all objects again, this
506 * time verify all pointers. */
508 p = le64toh(f->header->header_size);
510 draw_progress(0x8000 + (0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec);
512 r = journal_file_move_to_object(f, -1, p, &o);
514 log_error("Invalid object at %llu", (unsigned long long) p);
518 if (o->object.type == OBJECT_ENTRY_ARRAY) {
521 if (le64toh(o->entry_array.next_entry_array_offset) != 0 &&
522 !contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, le64toh(o->entry_array.next_entry_array_offset))) {
523 log_error("Entry array chains up to invalid next array at %llu", (unsigned long long) p);
528 n = journal_file_entry_array_n_items(o);
529 for (i = 0; i < n; i++) {
530 if (le64toh(o->entry_array.items[i]) != 0 &&
531 !contains_uint64(f->mmap, entry_fd, n_entries, le64toh(o->entry_array.items[i]))) {
533 log_error("Entry array points to invalid next array at %llu", (unsigned long long) p);
541 r = journal_file_move_to_object(f, -1, p, &o);
543 log_error("Invalid object at %llu", (unsigned long long) p);
547 if (p == le64toh(f->header->tail_object_offset))
550 p = p + ALIGN64(le64toh(o->object.size));
555 mmap_cache_close_fd(f->mmap, data_fd);
556 mmap_cache_close_fd(f->mmap, entry_fd);
557 mmap_cache_close_fd(f->mmap, entry_array_fd);
559 close_nointr_nofail(data_fd);
560 close_nointr_nofail(entry_fd);
561 close_nointr_nofail(entry_array_fd);
568 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
570 (unsigned long long) p,
571 (unsigned long long) f->last_stat.st_size,
572 (unsigned long long) (100 * p / f->last_stat.st_size));
575 mmap_cache_close_fd(f->mmap, data_fd);
576 close_nointr_nofail(data_fd);
580 mmap_cache_close_fd(f->mmap, entry_fd);
581 close_nointr_nofail(entry_fd);
584 if (entry_array_fd >= 0) {
585 mmap_cache_close_fd(f->mmap, entry_array_fd);
586 close_nointr_nofail(entry_array_fd);