]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journal-verify.c
8ef91ce4854fdb337c256af2771aec0421f6b5f8
[thirdparty/systemd.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
35 /* FIXME:
36 *
37 * - follow all chains
38 * - check for unreferenced objects
39 * - verify FSPRG
40 * - Allow building without libgcrypt
41 *
42 * */
43
44 static int journal_file_object_verify(JournalFile *f, Object *o) {
45 assert(f);
46 assert(o);
47
48 /* This does various superficial tests about the length an
49 * possible field values. It does not follow any references to
50 * other objects. */
51
52 if ((o->object.flags & OBJECT_COMPRESSED) &&
53 o->object.type != OBJECT_DATA)
54 return -EBADMSG;
55
56 switch (o->object.type) {
57
58 case OBJECT_DATA: {
59 uint64_t h1, h2;
60
61 if (le64toh(o->data.entry_offset) <= 0 ||
62 le64toh(o->data.n_entries) <= 0)
63 return -EBADMSG;
64
65 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
66 return -EBADMSG;
67
68 h1 = le64toh(o->data.hash);
69
70 if (o->object.flags & OBJECT_COMPRESSED) {
71 void *b = NULL;
72 uint64_t alloc = 0, b_size;
73
74 if (!uncompress_blob(o->data.payload,
75 le64toh(o->object.size) - offsetof(Object, data.payload),
76 &b, &alloc, &b_size))
77 return -EBADMSG;
78
79 h2 = hash64(b, b_size);
80 free(b);
81 } else
82 h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
83
84 if (h1 != h2)
85 return -EBADMSG;
86
87 break;
88 }
89
90 case OBJECT_FIELD:
91 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
92 return -EBADMSG;
93 break;
94
95 case OBJECT_ENTRY:
96 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
97 return -EBADMSG;
98
99 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
100 return -EBADMSG;
101
102 if (le64toh(o->entry.seqnum) <= 0 ||
103 le64toh(o->entry.realtime) <= 0)
104 return -EBADMSG;
105
106 break;
107
108 case OBJECT_DATA_HASH_TABLE:
109 case OBJECT_FIELD_HASH_TABLE:
110 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0)
111 return -EBADMSG;
112
113 break;
114
115 case OBJECT_ENTRY_ARRAY:
116 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0)
117 return -EBADMSG;
118
119 break;
120
121 case OBJECT_TAG:
122 if (le64toh(o->object.size) != sizeof(TagObject))
123 return -EBADMSG;
124 break;
125 }
126
127 return 0;
128 }
129
130 static void draw_progress(uint64_t p, usec_t *last_usec) {
131 unsigned n, i, j, k;
132 usec_t z, x;
133
134 if (!isatty(STDOUT_FILENO))
135 return;
136
137 z = now(CLOCK_MONOTONIC);
138 x = *last_usec;
139
140 if (x != 0 && x + 40 * USEC_PER_MSEC > z)
141 return;
142
143 *last_usec = z;
144
145 n = (3 * columns()) / 4;
146 j = (n * (unsigned) p) / 65535ULL;
147 k = n - j;
148
149 fputs("\r\x1B[?25l", stdout);
150
151 for (i = 0; i < j; i++)
152 fputs("\xe2\x96\x88", stdout);
153
154 for (i = 0; i < k; i++)
155 fputs("\xe2\x96\x91", stdout);
156
157 printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU);
158
159 fputs("\r\x1B[?25h", stdout);
160 fflush(stdout);
161 }
162
163 static void flush_progress(void) {
164 unsigned n, i;
165
166 if (!isatty(STDOUT_FILENO))
167 return;
168
169 n = (3 * columns()) / 4;
170
171 putchar('\r');
172
173 for (i = 0; i < n + 5; i++)
174 putchar(' ');
175
176 putchar('\r');
177 fflush(stdout);
178 }
179
180 static int write_uint64(int fd, uint64_t p) {
181 ssize_t k;
182
183 k = write(fd, &p, sizeof(p));
184 if (k < 0)
185 return -errno;
186 if (k != sizeof(p))
187 return -EIO;
188
189 return 0;
190 }
191
192 static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
193 uint64_t a, b;
194 int r;
195
196 assert(m);
197 assert(fd >= 0);
198
199 /* Bisection ... */
200
201 a = 0; b = n;
202 while (a < b) {
203 uint64_t c, *z;
204
205 c = (a + b) / 2;
206
207 r = mmap_cache_get(m, fd, PROT_READ, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z);
208 if (r < 0)
209 return r;
210
211 if (*z == p)
212 return 1;
213
214 if (p < *z)
215 b = c;
216 else
217 a = c;
218 }
219
220 return 0;
221 }
222
223 int journal_file_verify(JournalFile *f, const char *key) {
224 int r;
225 Object *o;
226 uint64_t p = 0;
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";
236
237 assert(f);
238
239 data_fd = mkostemp(data_path, O_CLOEXEC);
240 if (data_fd < 0) {
241 log_error("Failed to create data file: %m");
242 goto fail;
243 }
244 unlink(data_path);
245
246 entry_fd = mkostemp(entry_path, O_CLOEXEC);
247 if (entry_fd < 0) {
248 log_error("Failed to create entry file: %m");
249 goto fail;
250 }
251 unlink(entry_path);
252
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");
256 goto fail;
257 }
258 unlink(entry_array_path);
259
260 /* First iteration: we go through all objects, verify the
261 * superficial structure, headers, hashes. */
262
263 r = journal_file_hmac_put_header(f);
264 if (r < 0) {
265 log_error("Failed to calculate HMAC of header.");
266 goto fail;
267 }
268
269 p = le64toh(f->header->header_size);
270 while (p != 0) {
271 draw_progress((0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec);
272
273 r = journal_file_move_to_object(f, -1, p, &o);
274 if (r < 0) {
275 log_error("Invalid object at %llu", (unsigned long long) p);
276 goto fail;
277 }
278
279 if (le64toh(f->header->tail_object_offset) < p) {
280 log_error("Invalid tail object pointer.");
281 r = -EBADMSG;
282 goto fail;
283 }
284
285 n_objects ++;
286
287 r = journal_file_object_verify(f, o);
288 if (r < 0) {
289 log_error("Invalid object contents at %llu", (unsigned long long) p);
290 goto fail;
291 }
292
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);
296 r = -EBADMSG;
297 goto fail;
298 }
299
300 r = journal_file_hmac_put_object(f, -1, p);
301 if (r < 0) {
302 log_error("Failed to calculate HMAC at %llu", (unsigned long long) p);
303 goto fail;
304 }
305
306 if (o->object.type == OBJECT_TAG) {
307
308 if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) {
309 log_error("Tag object without authentication at %llu", (unsigned long long) p);
310 r = -EBADMSG;
311 goto fail;
312 }
313
314 if (le64toh(o->tag.seqnum) != tag_seqnum) {
315 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
316 r = -EBADMSG;
317 goto fail;
318 }
319
320 } else if (o->object.type == OBJECT_ENTRY) {
321
322 r = write_uint64(entry_fd, p);
323 if (r < 0)
324 goto fail;
325
326 if (!entry_seqnum_set &&
327 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
328 log_error("Head entry sequence number incorrect");
329 r = -EBADMSG;
330 goto fail;
331 }
332
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);
336 r = -EBADMSG;
337 goto fail;
338 }
339
340 entry_seqnum = le64toh(o->entry.seqnum);
341 entry_seqnum_set = true;
342
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);
347 r = -EBADMSG;
348 goto fail;
349 }
350
351 entry_monotonic = le64toh(o->entry.monotonic);
352 entry_boot_id = o->entry.boot_id;
353 entry_monotonic_set = true;
354
355 if (!entry_realtime_set &&
356 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
357 log_error("Head entry realtime timestamp incorrect");
358 r = -EBADMSG;
359 goto fail;
360 }
361
362 entry_realtime = le64toh(o->entry.realtime);
363 entry_realtime_set = true;
364
365 n_entries ++;
366 } else if (o->object.type == OBJECT_ENTRY_ARRAY) {
367
368 r = write_uint64(entry_array_fd, p);
369 if (r < 0)
370 goto fail;
371
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);
375 r = -EBADMSG;
376 goto fail;
377 }
378
379 found_main_entry_array = true;
380 }
381
382 n_entry_arrays++;
383
384 } else if (o->object.type == OBJECT_DATA) {
385
386 r = write_uint64(data_fd, p);
387 if (r < 0)
388 goto fail;
389
390 n_data++;
391
392 } else if (o->object.type == OBJECT_FIELD)
393 n_fields++;
394 else if (o->object.type == OBJECT_DATA_HASH_TABLE) {
395 n_data_hash_tables++;
396
397 if (n_data_hash_tables > 1) {
398 log_error("More than one data hash table at %llu", (unsigned long long) p);
399 r = -EBADMSG;
400 goto fail;
401 }
402
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.");
406 r = -EBADMSG;
407 goto fail;
408 }
409 } else if (o->object.type == OBJECT_FIELD_HASH_TABLE) {
410 n_field_hash_tables++;
411
412 if (n_field_hash_tables > 1) {
413 log_error("More than one field hash table at %llu", (unsigned long long) p);
414 r = -EBADMSG;
415 goto fail;
416 }
417
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.");
421 r = -EBADMSG;
422 goto fail;
423 }
424 } else if (o->object.type >= _OBJECT_TYPE_MAX)
425 n_weird ++;
426
427 if (p == le64toh(f->header->tail_object_offset))
428 p = 0;
429 else
430 p = p + ALIGN64(le64toh(o->object.size));
431 }
432
433 if (n_objects != le64toh(f->header->n_objects)) {
434 log_error("Object number mismatch");
435 r = -EBADMSG;
436 goto fail;
437 }
438
439 if (n_entries != le64toh(f->header->n_entries)) {
440 log_error("Entry number mismatch");
441 r = -EBADMSG;
442 goto fail;
443 }
444
445 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
446 n_data != le64toh(f->header->n_data)) {
447 log_error("Data number mismatch");
448 r = -EBADMSG;
449 goto fail;
450 }
451
452 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
453 n_fields != le64toh(f->header->n_fields)) {
454 log_error("Field number mismatch");
455 r = -EBADMSG;
456 goto fail;
457 }
458
459 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
460 tag_seqnum != le64toh(f->header->n_tags)) {
461 log_error("Tag number mismatch");
462 r = -EBADMSG;
463 goto fail;
464 }
465
466 if (n_data_hash_tables != 1) {
467 log_error("Missing data hash table");
468 r = -EBADMSG;
469 goto fail;
470 }
471
472 if (n_field_hash_tables != 1) {
473 log_error("Missing field hash table");
474 r = -EBADMSG;
475 goto fail;
476 }
477
478 if (!found_main_entry_array) {
479 log_error("Missing entry array");
480 r = -EBADMSG;
481 goto fail;
482 }
483
484 if (entry_seqnum_set &&
485 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
486 log_error("Invalid tail seqnum");
487 r = -EBADMSG;
488 goto fail;
489 }
490
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");
495 r = -EBADMSG;
496 goto fail;
497 }
498
499 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
500 log_error("Invalid tail realtime timestamp");
501 r = -EBADMSG;
502 goto fail;
503 }
504
505 /* Second iteration: we go through all objects again, this
506 * time verify all pointers. */
507
508 p = le64toh(f->header->header_size);
509 while (p != 0) {
510 draw_progress(0x8000 + (0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec);
511
512 r = journal_file_move_to_object(f, -1, p, &o);
513 if (r < 0) {
514 log_error("Invalid object at %llu", (unsigned long long) p);
515 goto fail;
516 }
517
518 if (o->object.type == OBJECT_ENTRY_ARRAY) {
519 uint64_t i = 0, n;
520
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);
524 r = -EBADMSG;
525 goto fail;
526 }
527
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]))) {
532
533 log_error("Entry array points to invalid next array at %llu", (unsigned long long) p);
534 r = -EBADMSG;
535 goto fail;
536 }
537 }
538
539 }
540
541 r = journal_file_move_to_object(f, -1, p, &o);
542 if (r < 0) {
543 log_error("Invalid object at %llu", (unsigned long long) p);
544 goto fail;
545 }
546
547 if (p == le64toh(f->header->tail_object_offset))
548 p = 0;
549 else
550 p = p + ALIGN64(le64toh(o->object.size));
551 }
552
553 flush_progress();
554
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);
558
559 close_nointr_nofail(data_fd);
560 close_nointr_nofail(entry_fd);
561 close_nointr_nofail(entry_array_fd);
562
563 return 0;
564
565 fail:
566 flush_progress();
567
568 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
569 f->path,
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));
573
574 if (data_fd >= 0) {
575 mmap_cache_close_fd(f->mmap, data_fd);
576 close_nointr_nofail(data_fd);
577 }
578
579 if (entry_fd >= 0) {
580 mmap_cache_close_fd(f->mmap, entry_fd);
581 close_nointr_nofail(entry_fd);
582 }
583
584 if (entry_array_fd >= 0) {
585 mmap_cache_close_fd(f->mmap, entry_array_fd);
586 close_nointr_nofail(entry_array_fd);
587 }
588
589 return r;
590 }