]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journal-authenticate.c
man: systemd.unit(5): add examples for common tasks
[thirdparty/systemd.git] / src / journal / journal-authenticate.c
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
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 -ENOTSUP;
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 -ENOTSUP;
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 }