]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journal-authenticate.c
headers: remove unneeded includes from util.h
[thirdparty/systemd.git] / src / journal / journal-authenticate.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <fcntl.h>
4 #include <sys/mman.h>
5
6 #include "fd-util.h"
7 #include "fsprg.h"
8 #include "gcrypt-util.h"
9 #include "hexdecoct.h"
10 #include "journal-authenticate.h"
11 #include "journal-def.h"
12 #include "journal-file.h"
13 #include "memory-util.h"
14 #include "time-util.h"
15
16 static 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
27 int journal_file_append_tag(JournalFile *f) {
28 Object *o;
29 uint64_t p;
30 int r;
31
32 assert(f);
33
34 if (!f->seal)
35 return 0;
36
37 if (!f->hmac_running)
38 return 0;
39
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));
47 o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state));
48
49 log_debug("Writing tag %"PRIu64" for epoch %"PRIu64"",
50 le64toh(o->tag.seqnum),
51 FSPRG_GetEpoch(f->fsprg_state));
52
53 /* Add the tag object itself, so that we can protect its
54 * header. This will exclude the actual hash value in it */
55 r = journal_file_hmac_put_object(f, OBJECT_TAG, o, p);
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
66 int journal_file_hmac_start(JournalFile *f) {
67 uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
68 assert(f);
69
70 if (!f->seal)
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);
78 FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0);
79 gcry_md_setkey(f->hmac, key, sizeof(key));
80
81 f->hmac_running = true;
82
83 return 0;
84 }
85
86 static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) {
87 uint64_t t;
88
89 assert(f);
90 assert(epoch);
91 assert(f->seal);
92
93 if (f->fss_start_usec == 0 ||
94 f->fss_interval_usec == 0)
95 return -EOPNOTSUPP;
96
97 if (realtime < f->fss_start_usec)
98 return -ESTALE;
99
100 t = realtime - f->fss_start_usec;
101 t = t / f->fss_interval_usec;
102
103 *epoch = t;
104 return 0;
105 }
106
107 static int journal_file_fsprg_need_evolve(JournalFile *f, uint64_t realtime) {
108 uint64_t goal, epoch;
109 int r;
110 assert(f);
111
112 if (!f->seal)
113 return 0;
114
115 r = journal_file_get_epoch(f, realtime, &goal);
116 if (r < 0)
117 return r;
118
119 epoch = FSPRG_GetEpoch(f->fsprg_state);
120 if (epoch > goal)
121 return -ESTALE;
122
123 return epoch != goal;
124 }
125
126 int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) {
127 uint64_t goal, epoch;
128 int r;
129
130 assert(f);
131
132 if (!f->seal)
133 return 0;
134
135 r = journal_file_get_epoch(f, realtime, &goal);
136 if (r < 0)
137 return r;
138
139 epoch = FSPRG_GetEpoch(f->fsprg_state);
140 if (epoch < goal)
141 log_debug("Evolving FSPRG key from epoch %"PRIu64" to %"PRIu64".", epoch, goal);
142
143 for (;;) {
144 if (epoch > goal)
145 return -ESTALE;
146 if (epoch == goal)
147 return 0;
148
149 FSPRG_Evolve(f->fsprg_state);
150 epoch = FSPRG_GetEpoch(f->fsprg_state);
151 }
152 }
153
154 int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
155 void *msk;
156 uint64_t epoch;
157
158 assert(f);
159
160 if (!f->seal)
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
184 log_debug("Seeking FSPRG key to %"PRIu64".", goal);
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
192 int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
193 int r;
194
195 assert(f);
196
197 if (!f->seal)
198 return 0;
199
200 if (realtime <= 0)
201 realtime = now(CLOCK_REALTIME);
202
203 r = journal_file_fsprg_need_evolve(f, realtime);
204 if (r <= 0)
205 return 0;
206
207 r = journal_file_append_tag(f);
208 if (r < 0)
209 return r;
210
211 r = journal_file_fsprg_evolve(f, realtime);
212 if (r < 0)
213 return r;
214
215 return 0;
216 }
217
218 int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) {
219 int r;
220
221 assert(f);
222
223 if (!f->seal)
224 return 0;
225
226 r = journal_file_hmac_start(f);
227 if (r < 0)
228 return r;
229
230 if (!o) {
231 r = journal_file_move_to_object(f, type, p, &o);
232 if (r < 0)
233 return r;
234 } else {
235 if (type > OBJECT_UNUSED && o->object.type != type)
236 return -EBADMSG;
237 }
238
239 gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
240
241 switch (o->object.type) {
242
243 case OBJECT_DATA:
244 /* All but hash and payload are mutable */
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
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
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));
269 gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
270 break;
271 default:
272 return -EINVAL;
273 }
274
275 return 0;
276 }
277
278 int journal_file_hmac_put_header(JournalFile *f) {
279 int r;
280
281 assert(f);
282
283 if (!f->seal)
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,
291 * tail_object_offset, n_objects, n_entries,
292 * tail_entry_seqnum, head_entry_seqnum, entry_array_offset,
293 * head_entry_realtime, tail_entry_realtime,
294 * tail_entry_monotonic, n_data, n_fields, n_tags,
295 * n_entry_arrays. */
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));
301
302 return 0;
303 }
304
305 int journal_file_fss_load(JournalFile *f) {
306 int r, fd = -1;
307 char *p = NULL;
308 struct stat st;
309 FSSHeader *m = NULL;
310 sd_id128_t machine;
311
312 assert(f);
313
314 if (!f->seal)
315 return 0;
316
317 r = sd_id128_get_machine(&machine);
318 if (r < 0)
319 return r;
320
321 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
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) {
327 if (errno != ENOENT)
328 log_error_errno(errno, "Failed to open %s: %m", p);
329
330 r = -errno;
331 goto finish;
332 }
333
334 if (fstat(fd, &st) < 0) {
335 r = -errno;
336 goto finish;
337 }
338
339 if (st.st_size < (off_t) sizeof(FSSHeader)) {
340 r = -ENODATA;
341 goto finish;
342 }
343
344 m = mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0);
345 if (m == MAP_FAILED) {
346 m = NULL;
347 r = -errno;
348 goto finish;
349 }
350
351 if (memcmp(m->signature, FSS_HEADER_SIGNATURE, 8) != 0) {
352 r = -EBADMSG;
353 goto finish;
354 }
355
356 if (m->incompatible_flags != 0) {
357 r = -EPROTONOSUPPORT;
358 goto finish;
359 }
360
361 if (le64toh(m->header_size) < sizeof(FSSHeader)) {
362 r = -EBADMSG;
363 goto finish;
364 }
365
366 if (le64toh(m->fsprg_state_size) != FSPRG_stateinbytes(le16toh(m->fsprg_secpar))) {
367 r = -EBADMSG;
368 goto finish;
369 }
370
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) {
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
382 if (le64toh(m->start_usec) <= 0 ||
383 le64toh(m->interval_usec) <= 0) {
384 r = -EBADMSG;
385 goto finish;
386 }
387
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;
391 r = -errno;
392 goto finish;
393 }
394
395 f->fss_start_usec = le64toh(f->fss_file->start_usec);
396 f->fss_interval_usec = le64toh(f->fss_file->interval_usec);
397
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);
400
401 r = 0;
402
403 finish:
404 if (m)
405 munmap(m, PAGE_ALIGN(sizeof(FSSHeader)));
406
407 safe_close(fd);
408 free(p);
409
410 return r;
411 }
412
413 int journal_file_hmac_setup(JournalFile *f) {
414 gcry_error_t e;
415
416 if (!f->seal)
417 return 0;
418
419 initialize_libgcrypt(true);
420
421 e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
422 if (e != 0)
423 return -EOPNOTSUPP;
424
425 return 0;
426 }
427
428 int journal_file_append_first_tag(JournalFile *f) {
429 int r;
430 uint64_t p;
431
432 if (!f->seal)
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
446 r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, NULL, p);
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
455 r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, NULL, p);
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 }
465
466 int 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;
520 }
521
522 bool 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 }