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