]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journal-authenticate.c
Merge pull request #2495 from heftig/master
[thirdparty/systemd.git] / src / journal / journal-authenticate.c
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
23 #include "fd-util.h"
24 #include "fsprg.h"
25 #include "hexdecoct.h"
26 #include "journal-authenticate.h"
27 #include "journal-def.h"
28 #include "journal-file.h"
29
30 static 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
41 int journal_file_append_tag(JournalFile *f) {
42 Object *o;
43 uint64_t p;
44 int r;
45
46 assert(f);
47
48 if (!f->seal)
49 return 0;
50
51 if (!f->hmac_running)
52 return 0;
53
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));
61 o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state));
62
63 log_debug("Writing tag %"PRIu64" for epoch %"PRIu64"",
64 le64toh(o->tag.seqnum),
65 FSPRG_GetEpoch(f->fsprg_state));
66
67 /* Add the tag object itself, so that we can protect its
68 * header. This will exclude the actual hash value in it */
69 r = journal_file_hmac_put_object(f, OBJECT_TAG, o, p);
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
80 int journal_file_hmac_start(JournalFile *f) {
81 uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
82 assert(f);
83
84 if (!f->seal)
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);
92 FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0);
93 gcry_md_setkey(f->hmac, key, sizeof(key));
94
95 f->hmac_running = true;
96
97 return 0;
98 }
99
100 static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) {
101 uint64_t t;
102
103 assert(f);
104 assert(epoch);
105 assert(f->seal);
106
107 if (f->fss_start_usec == 0 ||
108 f->fss_interval_usec == 0)
109 return -EOPNOTSUPP;
110
111 if (realtime < f->fss_start_usec)
112 return -ESTALE;
113
114 t = realtime - f->fss_start_usec;
115 t = t / f->fss_interval_usec;
116
117 *epoch = t;
118 return 0;
119 }
120
121 static int journal_file_fsprg_need_evolve(JournalFile *f, uint64_t realtime) {
122 uint64_t goal, epoch;
123 int r;
124 assert(f);
125
126 if (!f->seal)
127 return 0;
128
129 r = journal_file_get_epoch(f, realtime, &goal);
130 if (r < 0)
131 return r;
132
133 epoch = FSPRG_GetEpoch(f->fsprg_state);
134 if (epoch > goal)
135 return -ESTALE;
136
137 return epoch != goal;
138 }
139
140 int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) {
141 uint64_t goal, epoch;
142 int r;
143
144 assert(f);
145
146 if (!f->seal)
147 return 0;
148
149 r = journal_file_get_epoch(f, realtime, &goal);
150 if (r < 0)
151 return r;
152
153 epoch = FSPRG_GetEpoch(f->fsprg_state);
154 if (epoch < goal)
155 log_debug("Evolving FSPRG key from epoch %"PRIu64" to %"PRIu64".", epoch, goal);
156
157 for (;;) {
158 if (epoch > goal)
159 return -ESTALE;
160 if (epoch == goal)
161 return 0;
162
163 FSPRG_Evolve(f->fsprg_state);
164 epoch = FSPRG_GetEpoch(f->fsprg_state);
165 }
166 }
167
168 int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
169 void *msk;
170 uint64_t epoch;
171
172 assert(f);
173
174 if (!f->seal)
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
198 log_debug("Seeking FSPRG key to %"PRIu64".", goal);
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
206 int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
207 int r;
208
209 assert(f);
210
211 if (!f->seal)
212 return 0;
213
214 if (realtime <= 0)
215 realtime = now(CLOCK_REALTIME);
216
217 r = journal_file_fsprg_need_evolve(f, realtime);
218 if (r <= 0)
219 return 0;
220
221 r = journal_file_append_tag(f);
222 if (r < 0)
223 return r;
224
225 r = journal_file_fsprg_evolve(f, realtime);
226 if (r < 0)
227 return r;
228
229 return 0;
230 }
231
232 int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) {
233 int r;
234
235 assert(f);
236
237 if (!f->seal)
238 return 0;
239
240 r = journal_file_hmac_start(f);
241 if (r < 0)
242 return r;
243
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 > OBJECT_UNUSED && o->object.type != type)
250 return -EBADMSG;
251 }
252
253 gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
254
255 switch (o->object.type) {
256
257 case OBJECT_DATA:
258 /* All but hash and payload are mutable */
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
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
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));
283 gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
284 break;
285 default:
286 return -EINVAL;
287 }
288
289 return 0;
290 }
291
292 int journal_file_hmac_put_header(JournalFile *f) {
293 int r;
294
295 assert(f);
296
297 if (!f->seal)
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,
305 * tail_object_offset, n_objects, n_entries,
306 * tail_entry_seqnum, head_entry_seqnum, entry_array_offset,
307 * head_entry_realtime, tail_entry_realtime,
308 * tail_entry_monotonic, n_data, n_fields, n_tags,
309 * n_entry_arrays. */
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));
315
316 return 0;
317 }
318
319 int journal_file_fss_load(JournalFile *f) {
320 int r, fd = -1;
321 char *p = NULL;
322 struct stat st;
323 FSSHeader *m = NULL;
324 sd_id128_t machine;
325
326 assert(f);
327
328 if (!f->seal)
329 return 0;
330
331 r = sd_id128_get_machine(&machine);
332 if (r < 0)
333 return r;
334
335 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
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) {
341 if (errno != ENOENT)
342 log_error_errno(errno, "Failed to open %s: %m", p);
343
344 r = -errno;
345 goto finish;
346 }
347
348 if (fstat(fd, &st) < 0) {
349 r = -errno;
350 goto finish;
351 }
352
353 if (st.st_size < (off_t) sizeof(FSSHeader)) {
354 r = -ENODATA;
355 goto finish;
356 }
357
358 m = mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0);
359 if (m == MAP_FAILED) {
360 m = NULL;
361 r = -errno;
362 goto finish;
363 }
364
365 if (memcmp(m->signature, FSS_HEADER_SIGNATURE, 8) != 0) {
366 r = -EBADMSG;
367 goto finish;
368 }
369
370 if (m->incompatible_flags != 0) {
371 r = -EPROTONOSUPPORT;
372 goto finish;
373 }
374
375 if (le64toh(m->header_size) < sizeof(FSSHeader)) {
376 r = -EBADMSG;
377 goto finish;
378 }
379
380 if (le64toh(m->fsprg_state_size) != FSPRG_stateinbytes(le16toh(m->fsprg_secpar))) {
381 r = -EBADMSG;
382 goto finish;
383 }
384
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) {
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
396 if (le64toh(m->start_usec) <= 0 ||
397 le64toh(m->interval_usec) <= 0) {
398 r = -EBADMSG;
399 goto finish;
400 }
401
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;
405 r = -errno;
406 goto finish;
407 }
408
409 f->fss_start_usec = le64toh(f->fss_file->start_usec);
410 f->fss_interval_usec = le64toh(f->fss_file->interval_usec);
411
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);
414
415 r = 0;
416
417 finish:
418 if (m)
419 munmap(m, PAGE_ALIGN(sizeof(FSSHeader)));
420
421 safe_close(fd);
422 free(p);
423
424 return r;
425 }
426
427 static 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
439 int journal_file_hmac_setup(JournalFile *f) {
440 gcry_error_t e;
441
442 if (!f->seal)
443 return 0;
444
445 initialize_libgcrypt();
446
447 e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
448 if (e != 0)
449 return -EOPNOTSUPP;
450
451 return 0;
452 }
453
454 int journal_file_append_first_tag(JournalFile *f) {
455 int r;
456 uint64_t p;
457
458 if (!f->seal)
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
472 r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, NULL, p);
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
481 r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, NULL, p);
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 }
491
492 int 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;
546 }
547
548 bool 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 }