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" ANSI_HIGHLIGHT_GREEN_ON, stdout);
151 for (i = 0; i < j; i++)
152 fputs("\xe2\x96\x88", stdout);
154 fputs(ANSI_HIGHLIGHT_OFF, stdout);
156 for (i = 0; i < k; i++)
157 fputs("\xe2\x96\x91", stdout);
159 printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU);
161 fputs("\r\x1B[?25h", stdout);
165 static void flush_progress(void) {
168 if (!isatty(STDOUT_FILENO))
171 n = (3 * columns()) / 4;
175 for (i = 0; i < n + 5; i++)
182 static int write_uint64(int fd, uint64_t p) {
185 k = write(fd, &p, sizeof(p));
194 static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
209 r = mmap_cache_get(m, fd, PROT_READ, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z);
225 int journal_file_verify(JournalFile *f, const char *key) {
229 uint64_t tag_seqnum = 0, entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
230 sd_id128_t entry_boot_id;
231 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
232 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;
233 usec_t last_usec = 0;
234 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
235 char data_path[] = "/var/tmp/journal-data-XXXXXX",
236 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
237 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
241 data_fd = mkostemp(data_path, O_CLOEXEC);
243 log_error("Failed to create data file: %m");
248 entry_fd = mkostemp(entry_path, O_CLOEXEC);
250 log_error("Failed to create entry file: %m");
255 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
256 if (entry_array_fd < 0) {
257 log_error("Failed to create entry array file: %m");
260 unlink(entry_array_path);
262 /* First iteration: we go through all objects, verify the
263 * superficial structure, headers, hashes. */
265 r = journal_file_hmac_put_header(f);
267 log_error("Failed to calculate HMAC of header.");
271 p = le64toh(f->header->header_size);
273 draw_progress((0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec);
275 r = journal_file_move_to_object(f, -1, p, &o);
277 log_error("Invalid object at %llu", (unsigned long long) p);
281 if (le64toh(f->header->tail_object_offset) < p) {
282 log_error("Invalid tail object pointer.");
289 r = journal_file_object_verify(f, o);
291 log_error("Invalid object contents at %llu", (unsigned long long) p);
295 if (o->object.flags & OBJECT_COMPRESSED &&
296 !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
297 log_error("Compressed object without compression at %llu", (unsigned long long) p);
302 r = journal_file_hmac_put_object(f, -1, p);
304 log_error("Failed to calculate HMAC at %llu", (unsigned long long) p);
308 if (o->object.type == OBJECT_TAG) {
310 if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) {
311 log_error("Tag object without authentication at %llu", (unsigned long long) p);
316 if (le64toh(o->tag.seqnum) != tag_seqnum) {
317 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
322 } else if (o->object.type == OBJECT_ENTRY) {
324 r = write_uint64(entry_fd, p);
328 if (!entry_seqnum_set &&
329 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
330 log_error("Head entry sequence number incorrect");
335 if (entry_seqnum_set &&
336 entry_seqnum >= le64toh(o->entry.seqnum)) {
337 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
342 entry_seqnum = le64toh(o->entry.seqnum);
343 entry_seqnum_set = true;
345 if (entry_monotonic_set &&
346 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
347 entry_monotonic > le64toh(o->entry.monotonic)) {
348 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
353 entry_monotonic = le64toh(o->entry.monotonic);
354 entry_boot_id = o->entry.boot_id;
355 entry_monotonic_set = true;
357 if (!entry_realtime_set &&
358 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
359 log_error("Head entry realtime timestamp incorrect");
364 entry_realtime = le64toh(o->entry.realtime);
365 entry_realtime_set = true;
368 } else if (o->object.type == OBJECT_ENTRY_ARRAY) {
370 r = write_uint64(entry_array_fd, p);
374 if (p == le64toh(f->header->entry_array_offset)) {
375 if (found_main_entry_array) {
376 log_error("More than one main entry array at %llu", (unsigned long long) p);
381 found_main_entry_array = true;
386 } else if (o->object.type == OBJECT_DATA) {
388 r = write_uint64(data_fd, p);
394 } else if (o->object.type == OBJECT_FIELD)
396 else if (o->object.type == OBJECT_DATA_HASH_TABLE) {
397 n_data_hash_tables++;
399 if (n_data_hash_tables > 1) {
400 log_error("More than one data hash table at %llu", (unsigned long long) p);
405 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
406 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
407 log_error("Header fields for data hash table invalid.");
411 } else if (o->object.type == OBJECT_FIELD_HASH_TABLE) {
412 n_field_hash_tables++;
414 if (n_field_hash_tables > 1) {
415 log_error("More than one field hash table at %llu", (unsigned long long) p);
420 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
421 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
422 log_error("Header fields for field hash table invalid.");
426 } else if (o->object.type >= _OBJECT_TYPE_MAX)
429 if (p == le64toh(f->header->tail_object_offset))
432 p = p + ALIGN64(le64toh(o->object.size));
435 if (n_objects != le64toh(f->header->n_objects)) {
436 log_error("Object number mismatch");
441 if (n_entries != le64toh(f->header->n_entries)) {
442 log_error("Entry number mismatch");
447 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
448 n_data != le64toh(f->header->n_data)) {
449 log_error("Data number mismatch");
454 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
455 n_fields != le64toh(f->header->n_fields)) {
456 log_error("Field number mismatch");
461 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
462 tag_seqnum != le64toh(f->header->n_tags)) {
463 log_error("Tag number mismatch");
468 if (n_data_hash_tables != 1) {
469 log_error("Missing data hash table");
474 if (n_field_hash_tables != 1) {
475 log_error("Missing field hash table");
480 if (!found_main_entry_array) {
481 log_error("Missing entry array");
486 if (entry_seqnum_set &&
487 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
488 log_error("Invalid tail seqnum");
493 if (entry_monotonic_set &&
494 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
495 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
496 log_error("Invalid tail monotonic timestamp");
501 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
502 log_error("Invalid tail realtime timestamp");
507 /* Second iteration: we go through all objects again, this
508 * time verify all pointers. */
510 p = le64toh(f->header->header_size);
512 draw_progress(0x8000 + (0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec);
514 r = journal_file_move_to_object(f, -1, p, &o);
516 log_error("Invalid object at %llu", (unsigned long long) p);
520 if (o->object.type == OBJECT_ENTRY_ARRAY) {
523 if (le64toh(o->entry_array.next_entry_array_offset) != 0 &&
524 !contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, le64toh(o->entry_array.next_entry_array_offset))) {
525 log_error("Entry array chains up to invalid next array at %llu", (unsigned long long) p);
530 n = journal_file_entry_array_n_items(o);
531 for (i = 0; i < n; i++) {
532 if (le64toh(o->entry_array.items[i]) != 0 &&
533 !contains_uint64(f->mmap, entry_fd, n_entries, le64toh(o->entry_array.items[i]))) {
535 log_error("Entry array points to invalid next array at %llu", (unsigned long long) p);
543 r = journal_file_move_to_object(f, -1, p, &o);
545 log_error("Invalid object at %llu", (unsigned long long) p);
549 if (p == le64toh(f->header->tail_object_offset))
552 p = p + ALIGN64(le64toh(o->object.size));
557 mmap_cache_close_fd(f->mmap, data_fd);
558 mmap_cache_close_fd(f->mmap, entry_fd);
559 mmap_cache_close_fd(f->mmap, entry_array_fd);
561 close_nointr_nofail(data_fd);
562 close_nointr_nofail(entry_fd);
563 close_nointr_nofail(entry_array_fd);
570 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
572 (unsigned long long) p,
573 (unsigned long long) f->last_stat.st_size,
574 (unsigned long long) (100 * p / f->last_stat.st_size));
577 mmap_cache_close_fd(f->mmap, data_fd);
578 close_nointr_nofail(data_fd);
582 mmap_cache_close_fd(f->mmap, entry_fd);
583 close_nointr_nofail(entry_fd);
586 if (entry_array_fd >= 0) {
587 mmap_cache_close_fd(f->mmap, entry_array_fd);
588 close_nointr_nofail(entry_array_fd);