]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/test-journal-verify.c
journald: drop ManagedJournalFile
[thirdparty/systemd.git] / src / journal / test-journal-verify.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <fcntl.h>
4 #include <stdio.h>
5 #include <unistd.h>
6
7 #include "chattr-util.h"
8 #include "fd-util.h"
9 #include "io-util.h"
10 #include "journal-verify.h"
11 #include "log.h"
12 #include "managed-journal-file.h"
13 #include "mmap-cache.h"
14 #include "rm-rf.h"
15 #include "strv.h"
16 #include "terminal-util.h"
17 #include "tests.h"
18
19 #define N_ENTRIES 6000
20 #define RANDOM_RANGE 77
21
22 static void bit_toggle(const char *fn, uint64_t p) {
23 uint8_t b;
24 ssize_t r;
25 int fd;
26
27 fd = open(fn, O_RDWR|O_CLOEXEC);
28 assert_se(fd >= 0);
29
30 r = pread(fd, &b, 1, p/8);
31 assert_se(r == 1);
32
33 b ^= 1 << (p % 8);
34
35 r = pwrite(fd, &b, 1, p/8);
36 assert_se(r == 1);
37
38 safe_close(fd);
39 }
40
41 static int raw_verify(const char *fn, const char *verification_key) {
42 _cleanup_(mmap_cache_unrefp) MMapCache *m = NULL;
43 JournalFile *f;
44 int r;
45
46 m = mmap_cache_new();
47 assert_se(m != NULL);
48
49 r = journal_file_open(
50 /* fd= */ -1,
51 fn,
52 O_RDONLY,
53 JOURNAL_COMPRESS|(verification_key ? JOURNAL_SEAL : 0),
54 0666,
55 /* compress_threshold_bytes= */ UINT64_MAX,
56 /* metrics= */ NULL,
57 m,
58 /* template= */ NULL,
59 &f);
60 if (r < 0)
61 return r;
62
63 r = journal_file_verify(f, verification_key, NULL, NULL, NULL, false);
64 (void) journal_file_close(f);
65
66 return r;
67 }
68
69 static int run_test(const char *verification_key, ssize_t max_iterations) {
70 _cleanup_(mmap_cache_unrefp) MMapCache *m = NULL;
71 char t[] = "/var/tmp/journal-XXXXXX";
72 struct stat st;
73 JournalFile *f;
74 JournalFile *df;
75 usec_t from = 0, to = 0, total = 0;
76 uint64_t start, end;
77 int r;
78
79 m = mmap_cache_new();
80 assert_se(m != NULL);
81
82 /* journal_file_open() requires a valid machine id */
83 if (sd_id128_get_machine(NULL) < 0)
84 return log_tests_skipped("No valid machine ID found");
85
86 test_setup_logging(LOG_DEBUG);
87
88 assert_se(mkdtemp(t));
89 assert_se(chdir(t) >= 0);
90 (void) chattr_path(t, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
91
92 log_info("Generating a test journal");
93
94 assert_se(journal_file_open(
95 /* fd= */ -1,
96 "test.journal",
97 O_RDWR|O_CREAT,
98 JOURNAL_COMPRESS|(verification_key ? JOURNAL_SEAL : 0),
99 0666,
100 /* compress_threshold_bytes= */ UINT64_MAX,
101 /* metrics= */ NULL,
102 m,
103 /* template= */ NULL,
104 &df) == 0);
105
106 for (size_t n = 0; n < N_ENTRIES; n++) {
107 _cleanup_free_ char *test = NULL;
108 struct iovec iovec;
109 struct dual_timestamp ts;
110
111 dual_timestamp_get(&ts);
112 assert_se(asprintf(&test, "RANDOM=%li", random() % RANDOM_RANGE));
113 iovec = IOVEC_MAKE_STRING(test);
114 assert_se(journal_file_append_entry(
115 df,
116 &ts,
117 /* boot_id= */ NULL,
118 &iovec,
119 /* n_iovec= */ 1,
120 /* seqnum= */ NULL,
121 /* seqnum_id= */ NULL,
122 /* ret_object= */ NULL,
123 /* ret_offset= */ NULL) == 0);
124 }
125
126 (void) journal_file_offline_close(df);
127
128 log_info("Verifying with key: %s", strna(verification_key));
129
130 assert_se(journal_file_open(
131 /* fd= */ -1,
132 "test.journal",
133 O_RDONLY,
134 JOURNAL_COMPRESS|(verification_key ? JOURNAL_SEAL : 0),
135 0666,
136 /* compress_threshold_bytes= */ UINT64_MAX,
137 /* metrics= */ NULL,
138 m,
139 /* template= */ NULL,
140 &f) == 0);
141 journal_file_print_header(f);
142 journal_file_dump(f);
143
144 assert_se(journal_file_verify(f, verification_key, &from, &to, &total, true) >= 0);
145
146 if (verification_key && JOURNAL_HEADER_SEALED(f->header))
147 log_info("=> Validated from %s to %s, %s missing",
148 FORMAT_TIMESTAMP(from),
149 FORMAT_TIMESTAMP(to),
150 FORMAT_TIMESPAN(total > to ? total - to : 0, 0));
151
152 (void) journal_file_close(f);
153 assert_se(stat("test.journal", &st) >= 0);
154
155 start = 38448 * 8 + 0;
156 end = max_iterations < 0 ? (uint64_t)st.st_size * 8 : start + max_iterations;
157 log_info("Toggling bits %"PRIu64 " to %"PRIu64, start, end);
158
159 for (uint64_t p = start; p < end; p++) {
160 bit_toggle("test.journal", p);
161
162 if (max_iterations < 0)
163 log_info("[ %"PRIu64"+%"PRIu64"]", p / 8, p % 8);
164
165 r = raw_verify("test.journal", verification_key);
166 /* Suppress the notice when running in the limited (CI) mode */
167 if (verification_key && max_iterations < 0 && r >= 0)
168 log_notice(ANSI_HIGHLIGHT_RED ">>>> %"PRIu64" (bit %"PRIu64") can be toggled without detection." ANSI_NORMAL, p / 8, p % 8);
169
170 bit_toggle("test.journal", p);
171 }
172
173 assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
174
175 return 0;
176 }
177
178 int main(int argc, char *argv[]) {
179 const char *verification_key = NULL;
180 int max_iterations = 512;
181
182 if (argc > 1) {
183 /* Don't limit the number of iterations when the verification key
184 * is provided on the command line, we want to do that only in CIs */
185 verification_key = argv[1];
186 max_iterations = -1;
187 }
188
189 assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "0", 1) >= 0);
190 run_test(verification_key, max_iterations);
191
192 assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "1", 1) >= 0);
193 run_test(verification_key, max_iterations);
194
195 #if HAVE_GCRYPT
196 /* If we're running without any arguments and we're compiled with gcrypt
197 * check the journal verification stuff with a valid key as well */
198 if (argc <= 1) {
199 verification_key = "c262bd-85187f-0b1b04-877cc5/1c7af8-35a4e900";
200
201 assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "0", 1) >= 0);
202 run_test(verification_key, max_iterations);
203
204 assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "1", 1) >= 0);
205 run_test(verification_key, max_iterations);
206 }
207 #endif
208
209 return 0;
210 }