]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journal-authenticate.c
log: log_error() and friends add a newline after each line anyway, so avoid including...
[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
25#include "journal-def.h"
26#include "journal-file.h"
27#include "journal-authenticate.h"
28#include "fsprg.h"
29
0284adc6
LP
30static uint64_t journal_file_tag_seqnum(JournalFile *f) {
31 uint64_t r;
32
33 assert(f);
34
35 r = le64toh(f->header->n_tags) + 1;
36 f->header->n_tags = htole64(r);
37
38 return r;
39}
40
41int journal_file_append_tag(JournalFile *f) {
42 Object *o;
43 uint64_t p;
44 int r;
45
46 assert(f);
47
baed47c3 48 if (!f->seal)
0284adc6
LP
49 return 0;
50
51 if (!f->hmac_running)
52 return 0;
53
0284adc6
LP
54 assert(f->hmac);
55
56 r = journal_file_append_object(f, OBJECT_TAG, sizeof(struct TagObject), &o, &p);
57 if (r < 0)
58 return r;
59
60 o->tag.seqnum = htole64(journal_file_tag_seqnum(f));
14d10188 61 o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state));
0284adc6 62
9f6445e3 63 log_debug("Writing tag %"PRIu64" for epoch %"PRIu64"",
507f22bd
ZJS
64 le64toh(o->tag.seqnum),
65 FSPRG_GetEpoch(f->fsprg_state));
e627440b 66
0284adc6
LP
67 /* Add the tag object itself, so that we can protect its
68 * header. This will exclude the actual hash value in it */
5996c7c2 69 r = journal_file_hmac_put_object(f, OBJECT_TAG, o, p);
0284adc6
LP
70 if (r < 0)
71 return r;
72
73 /* Get the HMAC tag and store it in the object */
74 memcpy(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH);
75 f->hmac_running = false;
76
77 return 0;
78}
79
14d10188 80int journal_file_hmac_start(JournalFile *f) {
0284adc6 81 uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
0284adc6
LP
82 assert(f);
83
baed47c3 84 if (!f->seal)
0284adc6
LP
85 return 0;
86
87 if (f->hmac_running)
88 return 0;
89
90 /* Prepare HMAC for next cycle */
91 gcry_md_reset(f->hmac);
b7c9ae91 92 FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0);
0284adc6
LP
93 gcry_md_setkey(f->hmac, key, sizeof(key));
94
95 f->hmac_running = true;
96
97 return 0;
98}
99
100static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) {
101 uint64_t t;
102
103 assert(f);
104 assert(epoch);
baed47c3 105 assert(f->seal);
0284adc6 106
baed47c3
LP
107 if (f->fss_start_usec == 0 ||
108 f->fss_interval_usec == 0)
0284adc6
LP
109 return -ENOTSUP;
110
baed47c3 111 if (realtime < f->fss_start_usec)
0284adc6
LP
112 return -ESTALE;
113
baed47c3
LP
114 t = realtime - f->fss_start_usec;
115 t = t / f->fss_interval_usec;
0284adc6
LP
116
117 *epoch = t;
118 return 0;
119}
120
baed47c3 121static int journal_file_fsprg_need_evolve(JournalFile *f, uint64_t realtime) {
0284adc6
LP
122 uint64_t goal, epoch;
123 int r;
124 assert(f);
125
baed47c3 126 if (!f->seal)
0284adc6
LP
127 return 0;
128
129 r = journal_file_get_epoch(f, realtime, &goal);
130 if (r < 0)
131 return r;
132
b7c9ae91 133 epoch = FSPRG_GetEpoch(f->fsprg_state);
0284adc6
LP
134 if (epoch > goal)
135 return -ESTALE;
136
137 return epoch != goal;
138}
139
baed47c3 140int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) {
0284adc6
LP
141 uint64_t goal, epoch;
142 int r;
143
144 assert(f);
145
baed47c3 146 if (!f->seal)
0284adc6
LP
147 return 0;
148
149 r = journal_file_get_epoch(f, realtime, &goal);
150 if (r < 0)
151 return r;
152
b7c9ae91 153 epoch = FSPRG_GetEpoch(f->fsprg_state);
0284adc6 154 if (epoch < goal)
507f22bd 155 log_debug("Evolving FSPRG key from epoch %"PRIu64" to %"PRIu64".", epoch, goal);
0284adc6
LP
156
157 for (;;) {
158 if (epoch > goal)
159 return -ESTALE;
160 if (epoch == goal)
161 return 0;
162
b7c9ae91
LP
163 FSPRG_Evolve(f->fsprg_state);
164 epoch = FSPRG_GetEpoch(f->fsprg_state);
0284adc6
LP
165 }
166}
167
14d10188
LP
168int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
169 void *msk;
170 uint64_t epoch;
171
172 assert(f);
173
baed47c3 174 if (!f->seal)
14d10188
LP
175 return 0;
176
177 assert(f->fsprg_seed);
178
179 if (f->fsprg_state) {
180 /* Cheaper... */
181
182 epoch = FSPRG_GetEpoch(f->fsprg_state);
183 if (goal == epoch)
184 return 0;
185
186 if (goal == epoch+1) {
187 FSPRG_Evolve(f->fsprg_state);
188 return 0;
189 }
190 } else {
191 f->fsprg_state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
192 f->fsprg_state = malloc(f->fsprg_state_size);
193
194 if (!f->fsprg_state)
195 return -ENOMEM;
196 }
197
507f22bd 198 log_debug("Seeking FSPRG key to %"PRIu64".", goal);
14d10188
LP
199
200 msk = alloca(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR));
201 FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR);
202 FSPRG_Seek(f->fsprg_state, goal, msk, f->fsprg_seed, f->fsprg_seed_size);
203 return 0;
204}
205
0284adc6
LP
206int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
207 int r;
208
209 assert(f);
210
baed47c3 211 if (!f->seal)
0284adc6
LP
212 return 0;
213
89fef990 214 if (realtime <= 0)
671e021c 215 realtime = now(CLOCK_REALTIME);
89fef990 216
baed47c3 217 r = journal_file_fsprg_need_evolve(f, realtime);
0284adc6
LP
218 if (r <= 0)
219 return 0;
220
221 r = journal_file_append_tag(f);
222 if (r < 0)
223 return r;
224
baed47c3 225 r = journal_file_fsprg_evolve(f, realtime);
0284adc6
LP
226 if (r < 0)
227 return r;
228
0284adc6
LP
229 return 0;
230}
231
5996c7c2 232int journal_file_hmac_put_object(JournalFile *f, int type, Object *o, uint64_t p) {
0284adc6 233 int r;
0284adc6
LP
234
235 assert(f);
236
baed47c3 237 if (!f->seal)
0284adc6
LP
238 return 0;
239
240 r = journal_file_hmac_start(f);
241 if (r < 0)
242 return r;
243
5996c7c2
LP
244 if (!o) {
245 r = journal_file_move_to_object(f, type, p, &o);
246 if (r < 0)
247 return r;
248 } else {
249 if (type >= 0 && o->object.type != type)
250 return -EBADMSG;
251 }
0284adc6
LP
252
253 gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
254
255 switch (o->object.type) {
256
257 case OBJECT_DATA:
14d10188 258 /* All but hash and payload are mutable */
0284adc6
LP
259 gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
260 gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload));
261 break;
262
3c1668da
LP
263 case OBJECT_FIELD:
264 /* Same here */
265 gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash));
266 gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(FieldObject, payload));
267 break;
268
0284adc6
LP
269 case OBJECT_ENTRY:
270 /* All */
271 gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(EntryObject, seqnum));
272 break;
273
274 case OBJECT_FIELD_HASH_TABLE:
275 case OBJECT_DATA_HASH_TABLE:
276 case OBJECT_ENTRY_ARRAY:
277 /* Nothing: everything is mutable */
278 break;
279
280 case OBJECT_TAG:
281 /* All but the tag itself */
282 gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum));
14d10188 283 gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
0284adc6
LP
284 break;
285 default:
286 return -EINVAL;
287 }
288
289 return 0;
290}
291
292int journal_file_hmac_put_header(JournalFile *f) {
293 int r;
294
295 assert(f);
296
baed47c3 297 if (!f->seal)
0284adc6
LP
298 return 0;
299
300 r = journal_file_hmac_start(f);
301 if (r < 0)
302 return r;
303
304 /* All but state+reserved, boot_id, arena_size,
14d10188
LP
305 * tail_object_offset, n_objects, n_entries,
306 * tail_entry_seqnum, head_entry_seqnum, entry_array_offset,
0284adc6 307 * head_entry_realtime, tail_entry_realtime,
14d10188
LP
308 * tail_entry_monotonic, n_data, n_fields, n_tags,
309 * n_entry_arrays. */
0284adc6
LP
310
311 gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature));
312 gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, boot_id) - offsetof(Header, file_id));
313 gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id));
314 gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
0284adc6
LP
315
316 return 0;
317}
318
baed47c3 319int journal_file_fss_load(JournalFile *f) {
0284adc6
LP
320 int r, fd = -1;
321 char *p = NULL;
322 struct stat st;
baed47c3 323 FSSHeader *m = NULL;
0284adc6
LP
324 sd_id128_t machine;
325
326 assert(f);
327
baed47c3 328 if (!f->seal)
0284adc6
LP
329 return 0;
330
331 r = sd_id128_get_machine(&machine);
332 if (r < 0)
333 return r;
334
baed47c3 335 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
0284adc6
LP
336 SD_ID128_FORMAT_VAL(machine)) < 0)
337 return -ENOMEM;
338
339 fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
340 if (fd < 0) {
272410e1
LP
341 if (errno != ENOENT)
342 log_error("Failed to open %s: %m", p);
343
0284adc6
LP
344 r = -errno;
345 goto finish;
346 }
347
348 if (fstat(fd, &st) < 0) {
349 r = -errno;
350 goto finish;
351 }
352
baed47c3 353 if (st.st_size < (off_t) sizeof(FSSHeader)) {
0284adc6
LP
354 r = -ENODATA;
355 goto finish;
356 }
357
baed47c3 358 m = mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0);
0284adc6
LP
359 if (m == MAP_FAILED) {
360 m = NULL;
361 r = -errno;
362 goto finish;
363 }
364
baed47c3 365 if (memcmp(m->signature, FSS_HEADER_SIGNATURE, 8) != 0) {
0284adc6
LP
366 r = -EBADMSG;
367 goto finish;
368 }
369
370 if (m->incompatible_flags != 0) {
371 r = -EPROTONOSUPPORT;
372 goto finish;
373 }
374
baed47c3 375 if (le64toh(m->header_size) < sizeof(FSSHeader)) {
0284adc6
LP
376 r = -EBADMSG;
377 goto finish;
378 }
379
3e4b9b50 380 if (le64toh(m->fsprg_state_size) != FSPRG_stateinbytes(le16toh(m->fsprg_secpar))) {
0284adc6
LP
381 r = -EBADMSG;
382 goto finish;
383 }
384
baed47c3
LP
385 f->fss_file_size = le64toh(m->header_size) + le64toh(m->fsprg_state_size);
386 if ((uint64_t) st.st_size < f->fss_file_size) {
0284adc6
LP
387 r = -ENODATA;
388 goto finish;
389 }
390
391 if (!sd_id128_equal(machine, m->machine_id)) {
392 r = -EHOSTDOWN;
393 goto finish;
394 }
395
baed47c3
LP
396 if (le64toh(m->start_usec) <= 0 ||
397 le64toh(m->interval_usec) <= 0) {
0284adc6
LP
398 r = -EBADMSG;
399 goto finish;
400 }
401
baed47c3
LP
402 f->fss_file = mmap(NULL, PAGE_ALIGN(f->fss_file_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
403 if (f->fss_file == MAP_FAILED) {
404 f->fss_file = NULL;
0284adc6
LP
405 r = -errno;
406 goto finish;
407 }
408
baed47c3
LP
409 f->fss_start_usec = le64toh(f->fss_file->start_usec);
410 f->fss_interval_usec = le64toh(f->fss_file->interval_usec);
b7c9ae91 411
baed47c3
LP
412 f->fsprg_state = (uint8_t*) f->fss_file + le64toh(f->fss_file->header_size);
413 f->fsprg_state_size = le64toh(f->fss_file->fsprg_state_size);
b7c9ae91 414
0284adc6
LP
415 r = 0;
416
417finish:
418 if (m)
baed47c3 419 munmap(m, PAGE_ALIGN(sizeof(FSSHeader)));
0284adc6
LP
420
421 if (fd >= 0)
422 close_nointr_nofail(fd);
423
424 free(p);
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)
450 return -ENOTSUP;
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}