]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journal-authenticate.c
Merge pull request #1668 from ssahani/net1
[thirdparty/systemd.git] / src / journal / journal-authenticate.c
CommitLineData
0284adc6
LP
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 <fcntl.h>
23#include <sys/mman.h>
24
3ffd4af2
LP
25#include "fd-util.h"
26#include "fsprg.h"
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
72fbdd33
LP
428static void initialize_libgcrypt(void) {
429 const char *p;
430
431 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
432 return;
433
434 p = gcry_check_version("1.4.5");
435 assert(p);
436
437 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
438}
439
baed47c3 440int journal_file_hmac_setup(JournalFile *f) {
0284adc6
LP
441 gcry_error_t e;
442
baed47c3 443 if (!f->seal)
0284adc6
LP
444 return 0;
445
72fbdd33
LP
446 initialize_libgcrypt();
447
0284adc6
LP
448 e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
449 if (e != 0)
15411c0c 450 return -EOPNOTSUPP;
0284adc6
LP
451
452 return 0;
453}
454
455int journal_file_append_first_tag(JournalFile *f) {
456 int r;
457 uint64_t p;
458
baed47c3 459 if (!f->seal)
0284adc6
LP
460 return 0;
461
462 log_debug("Calculating first tag...");
463
464 r = journal_file_hmac_put_header(f);
465 if (r < 0)
466 return r;
467
468 p = le64toh(f->header->field_hash_table_offset);
469 if (p < offsetof(Object, hash_table.items))
470 return -EINVAL;
471 p -= offsetof(Object, hash_table.items);
472
5996c7c2 473 r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, NULL, p);
0284adc6
LP
474 if (r < 0)
475 return r;
476
477 p = le64toh(f->header->data_hash_table_offset);
478 if (p < offsetof(Object, hash_table.items))
479 return -EINVAL;
480 p -= offsetof(Object, hash_table.items);
481
5996c7c2 482 r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, NULL, p);
0284adc6
LP
483 if (r < 0)
484 return r;
485
486 r = journal_file_append_tag(f);
487 if (r < 0)
488 return r;
489
490 return 0;
491}
4da416aa 492
feb12d3e
LP
493int journal_file_parse_verification_key(JournalFile *f, const char *key) {
494 uint8_t *seed;
495 size_t seed_size, c;
496 const char *k;
497 int r;
498 unsigned long long start, interval;
499
500 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
501 seed = malloc(seed_size);
502 if (!seed)
503 return -ENOMEM;
504
505 k = key;
506 for (c = 0; c < seed_size; c++) {
507 int x, y;
508
509 while (*k == '-')
510 k++;
511
512 x = unhexchar(*k);
513 if (x < 0) {
514 free(seed);
515 return -EINVAL;
516 }
517 k++;
518 y = unhexchar(*k);
519 if (y < 0) {
520 free(seed);
521 return -EINVAL;
522 }
523 k++;
524
525 seed[c] = (uint8_t) (x * 16 + y);
526 }
527
528 if (*k != '/') {
529 free(seed);
530 return -EINVAL;
531 }
532 k++;
533
534 r = sscanf(k, "%llx-%llx", &start, &interval);
535 if (r != 2) {
536 free(seed);
537 return -EINVAL;
538 }
539
540 f->fsprg_seed = seed;
541 f->fsprg_seed_size = seed_size;
542
543 f->fss_start_usec = start * interval;
544 f->fss_interval_usec = interval;
545
546 return 0;
4da416aa 547}
89fef990
LP
548
549bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u) {
550 uint64_t epoch;
551
552 assert(f);
553 assert(u);
554
555 if (!f->seal)
556 return false;
557
558 epoch = FSPRG_GetEpoch(f->fsprg_state);
559
560 *u = (usec_t) (f->fss_start_usec + f->fss_interval_usec * epoch + f->fss_interval_usec);
561
562 return true;
563}