]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journal-authenticate.c
Revert "Revert "sysctl: Enable ping(8) inside rootless Podman containers""
[thirdparty/systemd.git] / src / journal / journal-authenticate.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
0284adc6
LP
2
3#include <fcntl.h>
4#include <sys/mman.h>
5
3ffd4af2
LP
6#include "fd-util.h"
7#include "fsprg.h"
91e023d8 8#include "gcrypt-util.h"
cf0fbc49 9#include "hexdecoct.h"
3ffd4af2 10#include "journal-authenticate.h"
0284adc6
LP
11#include "journal-def.h"
12#include "journal-file.h"
0a970718 13#include "memory-util.h"
ca78ad1d 14#include "time-util.h"
0284adc6 15
0284adc6
LP
16static uint64_t journal_file_tag_seqnum(JournalFile *f) {
17 uint64_t r;
18
19 assert(f);
20
21 r = le64toh(f->header->n_tags) + 1;
22 f->header->n_tags = htole64(r);
23
24 return r;
25}
26
27int journal_file_append_tag(JournalFile *f) {
28 Object *o;
29 uint64_t p;
30 int r;
31
32 assert(f);
33
baed47c3 34 if (!f->seal)
0284adc6
LP
35 return 0;
36
37 if (!f->hmac_running)
38 return 0;
39
0284adc6
LP
40 assert(f->hmac);
41
42 r = journal_file_append_object(f, OBJECT_TAG, sizeof(struct TagObject), &o, &p);
43 if (r < 0)
44 return r;
45
46 o->tag.seqnum = htole64(journal_file_tag_seqnum(f));
14d10188 47 o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state));
0284adc6 48
9f6445e3 49 log_debug("Writing tag %"PRIu64" for epoch %"PRIu64"",
507f22bd
ZJS
50 le64toh(o->tag.seqnum),
51 FSPRG_GetEpoch(f->fsprg_state));
e627440b 52
0284adc6
LP
53 /* Add the tag object itself, so that we can protect its
54 * header. This will exclude the actual hash value in it */
5996c7c2 55 r = journal_file_hmac_put_object(f, OBJECT_TAG, o, p);
0284adc6
LP
56 if (r < 0)
57 return r;
58
59 /* Get the HMAC tag and store it in the object */
60 memcpy(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH);
61 f->hmac_running = false;
62
63 return 0;
64}
65
14d10188 66int journal_file_hmac_start(JournalFile *f) {
0284adc6 67 uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
0284adc6
LP
68 assert(f);
69
baed47c3 70 if (!f->seal)
0284adc6
LP
71 return 0;
72
73 if (f->hmac_running)
74 return 0;
75
76 /* Prepare HMAC for next cycle */
77 gcry_md_reset(f->hmac);
b7c9ae91 78 FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0);
0284adc6
LP
79 gcry_md_setkey(f->hmac, key, sizeof(key));
80
81 f->hmac_running = true;
82
83 return 0;
84}
85
86static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) {
87 uint64_t t;
88
89 assert(f);
90 assert(epoch);
baed47c3 91 assert(f->seal);
0284adc6 92
baed47c3
LP
93 if (f->fss_start_usec == 0 ||
94 f->fss_interval_usec == 0)
15411c0c 95 return -EOPNOTSUPP;
0284adc6 96
baed47c3 97 if (realtime < f->fss_start_usec)
0284adc6
LP
98 return -ESTALE;
99
baed47c3
LP
100 t = realtime - f->fss_start_usec;
101 t = t / f->fss_interval_usec;
0284adc6
LP
102
103 *epoch = t;
104 return 0;
105}
106
baed47c3 107static int journal_file_fsprg_need_evolve(JournalFile *f, uint64_t realtime) {
0284adc6
LP
108 uint64_t goal, epoch;
109 int r;
110 assert(f);
111
baed47c3 112 if (!f->seal)
0284adc6
LP
113 return 0;
114
115 r = journal_file_get_epoch(f, realtime, &goal);
116 if (r < 0)
117 return r;
118
b7c9ae91 119 epoch = FSPRG_GetEpoch(f->fsprg_state);
0284adc6
LP
120 if (epoch > goal)
121 return -ESTALE;
122
123 return epoch != goal;
124}
125
baed47c3 126int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) {
0284adc6
LP
127 uint64_t goal, epoch;
128 int r;
129
130 assert(f);
131
baed47c3 132 if (!f->seal)
0284adc6
LP
133 return 0;
134
135 r = journal_file_get_epoch(f, realtime, &goal);
136 if (r < 0)
137 return r;
138
b7c9ae91 139 epoch = FSPRG_GetEpoch(f->fsprg_state);
0284adc6 140 if (epoch < goal)
507f22bd 141 log_debug("Evolving FSPRG key from epoch %"PRIu64" to %"PRIu64".", epoch, goal);
0284adc6
LP
142
143 for (;;) {
144 if (epoch > goal)
145 return -ESTALE;
146 if (epoch == goal)
147 return 0;
148
b7c9ae91
LP
149 FSPRG_Evolve(f->fsprg_state);
150 epoch = FSPRG_GetEpoch(f->fsprg_state);
0284adc6
LP
151 }
152}
153
14d10188
LP
154int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
155 void *msk;
156 uint64_t epoch;
157
158 assert(f);
159
baed47c3 160 if (!f->seal)
14d10188
LP
161 return 0;
162
163 assert(f->fsprg_seed);
164
165 if (f->fsprg_state) {
166 /* Cheaper... */
167
168 epoch = FSPRG_GetEpoch(f->fsprg_state);
169 if (goal == epoch)
170 return 0;
171
172 if (goal == epoch+1) {
173 FSPRG_Evolve(f->fsprg_state);
174 return 0;
175 }
176 } else {
177 f->fsprg_state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
178 f->fsprg_state = malloc(f->fsprg_state_size);
179
180 if (!f->fsprg_state)
181 return -ENOMEM;
182 }
183
507f22bd 184 log_debug("Seeking FSPRG key to %"PRIu64".", goal);
14d10188
LP
185
186 msk = alloca(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR));
187 FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR);
188 FSPRG_Seek(f->fsprg_state, goal, msk, f->fsprg_seed, f->fsprg_seed_size);
189 return 0;
190}
191
0284adc6
LP
192int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
193 int r;
194
195 assert(f);
196
baed47c3 197 if (!f->seal)
0284adc6
LP
198 return 0;
199
89fef990 200 if (realtime <= 0)
671e021c 201 realtime = now(CLOCK_REALTIME);
89fef990 202
baed47c3 203 r = journal_file_fsprg_need_evolve(f, realtime);
0284adc6
LP
204 if (r <= 0)
205 return 0;
206
207 r = journal_file_append_tag(f);
208 if (r < 0)
209 return r;
210
baed47c3 211 r = journal_file_fsprg_evolve(f, realtime);
0284adc6
LP
212 if (r < 0)
213 return r;
214
0284adc6
LP
215 return 0;
216}
217
78519831 218int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) {
0284adc6 219 int r;
0284adc6
LP
220
221 assert(f);
222
baed47c3 223 if (!f->seal)
0284adc6
LP
224 return 0;
225
226 r = journal_file_hmac_start(f);
227 if (r < 0)
228 return r;
229
5996c7c2
LP
230 if (!o) {
231 r = journal_file_move_to_object(f, type, p, &o);
232 if (r < 0)
233 return r;
234 } else {
d05089d8 235 if (type > OBJECT_UNUSED && o->object.type != type)
5996c7c2
LP
236 return -EBADMSG;
237 }
0284adc6
LP
238
239 gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
240
241 switch (o->object.type) {
242
243 case OBJECT_DATA:
14d10188 244 /* All but hash and payload are mutable */
0284adc6
LP
245 gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
246 gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload));
247 break;
248
3c1668da
LP
249 case OBJECT_FIELD:
250 /* Same here */
251 gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash));
252 gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(FieldObject, payload));
253 break;
254
0284adc6
LP
255 case OBJECT_ENTRY:
256 /* All */
257 gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(EntryObject, seqnum));
258 break;
259
260 case OBJECT_FIELD_HASH_TABLE:
261 case OBJECT_DATA_HASH_TABLE:
262 case OBJECT_ENTRY_ARRAY:
263 /* Nothing: everything is mutable */
264 break;
265
266 case OBJECT_TAG:
267 /* All but the tag itself */
268 gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum));
14d10188 269 gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
0284adc6
LP
270 break;
271 default:
272 return -EINVAL;
273 }
274
275 return 0;
276}
277
278int journal_file_hmac_put_header(JournalFile *f) {
279 int r;
280
281 assert(f);
282
baed47c3 283 if (!f->seal)
0284adc6
LP
284 return 0;
285
286 r = journal_file_hmac_start(f);
287 if (r < 0)
288 return r;
289
290 /* All but state+reserved, boot_id, arena_size,
14d10188
LP
291 * tail_object_offset, n_objects, n_entries,
292 * tail_entry_seqnum, head_entry_seqnum, entry_array_offset,
0284adc6 293 * head_entry_realtime, tail_entry_realtime,
14d10188
LP
294 * tail_entry_monotonic, n_data, n_fields, n_tags,
295 * n_entry_arrays. */
0284adc6
LP
296
297 gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature));
298 gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, boot_id) - offsetof(Header, file_id));
299 gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id));
300 gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
0284adc6
LP
301
302 return 0;
303}
304
baed47c3 305int journal_file_fss_load(JournalFile *f) {
0284adc6
LP
306 int r, fd = -1;
307 char *p = NULL;
308 struct stat st;
baed47c3 309 FSSHeader *m = NULL;
0284adc6
LP
310 sd_id128_t machine;
311
312 assert(f);
313
baed47c3 314 if (!f->seal)
0284adc6
LP
315 return 0;
316
317 r = sd_id128_get_machine(&machine);
318 if (r < 0)
319 return r;
320
baed47c3 321 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
0284adc6
LP
322 SD_ID128_FORMAT_VAL(machine)) < 0)
323 return -ENOMEM;
324
325 fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
326 if (fd < 0) {
272410e1 327 if (errno != ENOENT)
56f64d95 328 log_error_errno(errno, "Failed to open %s: %m", p);
272410e1 329
0284adc6
LP
330 r = -errno;
331 goto finish;
332 }
333
334 if (fstat(fd, &st) < 0) {
335 r = -errno;
336 goto finish;
337 }
338
baed47c3 339 if (st.st_size < (off_t) sizeof(FSSHeader)) {
0284adc6
LP
340 r = -ENODATA;
341 goto finish;
342 }
343
baed47c3 344 m = mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0);
0284adc6
LP
345 if (m == MAP_FAILED) {
346 m = NULL;
347 r = -errno;
348 goto finish;
349 }
350
baed47c3 351 if (memcmp(m->signature, FSS_HEADER_SIGNATURE, 8) != 0) {
0284adc6
LP
352 r = -EBADMSG;
353 goto finish;
354 }
355
356 if (m->incompatible_flags != 0) {
357 r = -EPROTONOSUPPORT;
358 goto finish;
359 }
360
baed47c3 361 if (le64toh(m->header_size) < sizeof(FSSHeader)) {
0284adc6
LP
362 r = -EBADMSG;
363 goto finish;
364 }
365
3e4b9b50 366 if (le64toh(m->fsprg_state_size) != FSPRG_stateinbytes(le16toh(m->fsprg_secpar))) {
0284adc6
LP
367 r = -EBADMSG;
368 goto finish;
369 }
370
baed47c3
LP
371 f->fss_file_size = le64toh(m->header_size) + le64toh(m->fsprg_state_size);
372 if ((uint64_t) st.st_size < f->fss_file_size) {
0284adc6
LP
373 r = -ENODATA;
374 goto finish;
375 }
376
377 if (!sd_id128_equal(machine, m->machine_id)) {
378 r = -EHOSTDOWN;
379 goto finish;
380 }
381
baed47c3
LP
382 if (le64toh(m->start_usec) <= 0 ||
383 le64toh(m->interval_usec) <= 0) {
0284adc6
LP
384 r = -EBADMSG;
385 goto finish;
386 }
387
baed47c3
LP
388 f->fss_file = mmap(NULL, PAGE_ALIGN(f->fss_file_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
389 if (f->fss_file == MAP_FAILED) {
390 f->fss_file = NULL;
0284adc6
LP
391 r = -errno;
392 goto finish;
393 }
394
baed47c3
LP
395 f->fss_start_usec = le64toh(f->fss_file->start_usec);
396 f->fss_interval_usec = le64toh(f->fss_file->interval_usec);
b7c9ae91 397
baed47c3
LP
398 f->fsprg_state = (uint8_t*) f->fss_file + le64toh(f->fss_file->header_size);
399 f->fsprg_state_size = le64toh(f->fss_file->fsprg_state_size);
b7c9ae91 400
0284adc6
LP
401 r = 0;
402
403finish:
404 if (m)
baed47c3 405 munmap(m, PAGE_ALIGN(sizeof(FSSHeader)));
0284adc6 406
03e334a1 407 safe_close(fd);
0284adc6 408 free(p);
03e334a1 409
0284adc6
LP
410 return r;
411}
412
baed47c3 413int journal_file_hmac_setup(JournalFile *f) {
0284adc6
LP
414 gcry_error_t e;
415
baed47c3 416 if (!f->seal)
0284adc6
LP
417 return 0;
418
91e023d8 419 initialize_libgcrypt(true);
72fbdd33 420
0284adc6
LP
421 e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
422 if (e != 0)
15411c0c 423 return -EOPNOTSUPP;
0284adc6
LP
424
425 return 0;
426}
427
428int journal_file_append_first_tag(JournalFile *f) {
429 int r;
430 uint64_t p;
431
baed47c3 432 if (!f->seal)
0284adc6
LP
433 return 0;
434
435 log_debug("Calculating first tag...");
436
437 r = journal_file_hmac_put_header(f);
438 if (r < 0)
439 return r;
440
441 p = le64toh(f->header->field_hash_table_offset);
442 if (p < offsetof(Object, hash_table.items))
443 return -EINVAL;
444 p -= offsetof(Object, hash_table.items);
445
5996c7c2 446 r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, NULL, p);
0284adc6
LP
447 if (r < 0)
448 return r;
449
450 p = le64toh(f->header->data_hash_table_offset);
451 if (p < offsetof(Object, hash_table.items))
452 return -EINVAL;
453 p -= offsetof(Object, hash_table.items);
454
5996c7c2 455 r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, NULL, p);
0284adc6
LP
456 if (r < 0)
457 return r;
458
459 r = journal_file_append_tag(f);
460 if (r < 0)
461 return r;
462
463 return 0;
464}
4da416aa 465
feb12d3e
LP
466int journal_file_parse_verification_key(JournalFile *f, const char *key) {
467 uint8_t *seed;
468 size_t seed_size, c;
469 const char *k;
470 int r;
471 unsigned long long start, interval;
472
473 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
474 seed = malloc(seed_size);
475 if (!seed)
476 return -ENOMEM;
477
478 k = key;
479 for (c = 0; c < seed_size; c++) {
480 int x, y;
481
482 while (*k == '-')
483 k++;
484
485 x = unhexchar(*k);
486 if (x < 0) {
487 free(seed);
488 return -EINVAL;
489 }
490 k++;
491 y = unhexchar(*k);
492 if (y < 0) {
493 free(seed);
494 return -EINVAL;
495 }
496 k++;
497
498 seed[c] = (uint8_t) (x * 16 + y);
499 }
500
501 if (*k != '/') {
502 free(seed);
503 return -EINVAL;
504 }
505 k++;
506
507 r = sscanf(k, "%llx-%llx", &start, &interval);
508 if (r != 2) {
509 free(seed);
510 return -EINVAL;
511 }
512
513 f->fsprg_seed = seed;
514 f->fsprg_seed_size = seed_size;
515
516 f->fss_start_usec = start * interval;
517 f->fss_interval_usec = interval;
518
519 return 0;
4da416aa 520}
89fef990
LP
521
522bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u) {
523 uint64_t epoch;
524
525 assert(f);
526 assert(u);
527
528 if (!f->seal)
529 return false;
530
531 epoch = FSPRG_GetEpoch(f->fsprg_state);
532
533 *u = (usec_t) (f->fss_start_usec + f->fss_interval_usec * epoch + f->fss_interval_usec);
534
535 return true;
536}