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