]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journal-authenticate.c
util-lib: split out hex/dec/oct encoding/decoding into its own file
[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"
e4e73a63 30#include "hexdecoct.h"
0284adc6 31
0284adc6
LP
32static uint64_t journal_file_tag_seqnum(JournalFile *f) {
33 uint64_t r;
34
35 assert(f);
36
37 r = le64toh(f->header->n_tags) + 1;
38 f->header->n_tags = htole64(r);
39
40 return r;
41}
42
43int journal_file_append_tag(JournalFile *f) {
44 Object *o;
45 uint64_t p;
46 int r;
47
48 assert(f);
49
baed47c3 50 if (!f->seal)
0284adc6
LP
51 return 0;
52
53 if (!f->hmac_running)
54 return 0;
55
0284adc6
LP
56 assert(f->hmac);
57
58 r = journal_file_append_object(f, OBJECT_TAG, sizeof(struct TagObject), &o, &p);
59 if (r < 0)
60 return r;
61
62 o->tag.seqnum = htole64(journal_file_tag_seqnum(f));
14d10188 63 o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state));
0284adc6 64
9f6445e3 65 log_debug("Writing tag %"PRIu64" for epoch %"PRIu64"",
507f22bd
ZJS
66 le64toh(o->tag.seqnum),
67 FSPRG_GetEpoch(f->fsprg_state));
e627440b 68
0284adc6
LP
69 /* Add the tag object itself, so that we can protect its
70 * header. This will exclude the actual hash value in it */
5996c7c2 71 r = journal_file_hmac_put_object(f, OBJECT_TAG, o, p);
0284adc6
LP
72 if (r < 0)
73 return r;
74
75 /* Get the HMAC tag and store it in the object */
76 memcpy(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH);
77 f->hmac_running = false;
78
79 return 0;
80}
81
14d10188 82int journal_file_hmac_start(JournalFile *f) {
0284adc6 83 uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
0284adc6
LP
84 assert(f);
85
baed47c3 86 if (!f->seal)
0284adc6
LP
87 return 0;
88
89 if (f->hmac_running)
90 return 0;
91
92 /* Prepare HMAC for next cycle */
93 gcry_md_reset(f->hmac);
b7c9ae91 94 FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0);
0284adc6
LP
95 gcry_md_setkey(f->hmac, key, sizeof(key));
96
97 f->hmac_running = true;
98
99 return 0;
100}
101
102static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) {
103 uint64_t t;
104
105 assert(f);
106 assert(epoch);
baed47c3 107 assert(f->seal);
0284adc6 108
baed47c3
LP
109 if (f->fss_start_usec == 0 ||
110 f->fss_interval_usec == 0)
15411c0c 111 return -EOPNOTSUPP;
0284adc6 112
baed47c3 113 if (realtime < f->fss_start_usec)
0284adc6
LP
114 return -ESTALE;
115
baed47c3
LP
116 t = realtime - f->fss_start_usec;
117 t = t / f->fss_interval_usec;
0284adc6
LP
118
119 *epoch = t;
120 return 0;
121}
122
baed47c3 123static int journal_file_fsprg_need_evolve(JournalFile *f, uint64_t realtime) {
0284adc6
LP
124 uint64_t goal, epoch;
125 int r;
126 assert(f);
127
baed47c3 128 if (!f->seal)
0284adc6
LP
129 return 0;
130
131 r = journal_file_get_epoch(f, realtime, &goal);
132 if (r < 0)
133 return r;
134
b7c9ae91 135 epoch = FSPRG_GetEpoch(f->fsprg_state);
0284adc6
LP
136 if (epoch > goal)
137 return -ESTALE;
138
139 return epoch != goal;
140}
141
baed47c3 142int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) {
0284adc6
LP
143 uint64_t goal, epoch;
144 int r;
145
146 assert(f);
147
baed47c3 148 if (!f->seal)
0284adc6
LP
149 return 0;
150
151 r = journal_file_get_epoch(f, realtime, &goal);
152 if (r < 0)
153 return r;
154
b7c9ae91 155 epoch = FSPRG_GetEpoch(f->fsprg_state);
0284adc6 156 if (epoch < goal)
507f22bd 157 log_debug("Evolving FSPRG key from epoch %"PRIu64" to %"PRIu64".", epoch, goal);
0284adc6
LP
158
159 for (;;) {
160 if (epoch > goal)
161 return -ESTALE;
162 if (epoch == goal)
163 return 0;
164
b7c9ae91
LP
165 FSPRG_Evolve(f->fsprg_state);
166 epoch = FSPRG_GetEpoch(f->fsprg_state);
0284adc6
LP
167 }
168}
169
14d10188
LP
170int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
171 void *msk;
172 uint64_t epoch;
173
174 assert(f);
175
baed47c3 176 if (!f->seal)
14d10188
LP
177 return 0;
178
179 assert(f->fsprg_seed);
180
181 if (f->fsprg_state) {
182 /* Cheaper... */
183
184 epoch = FSPRG_GetEpoch(f->fsprg_state);
185 if (goal == epoch)
186 return 0;
187
188 if (goal == epoch+1) {
189 FSPRG_Evolve(f->fsprg_state);
190 return 0;
191 }
192 } else {
193 f->fsprg_state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
194 f->fsprg_state = malloc(f->fsprg_state_size);
195
196 if (!f->fsprg_state)
197 return -ENOMEM;
198 }
199
507f22bd 200 log_debug("Seeking FSPRG key to %"PRIu64".", goal);
14d10188
LP
201
202 msk = alloca(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR));
203 FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR);
204 FSPRG_Seek(f->fsprg_state, goal, msk, f->fsprg_seed, f->fsprg_seed_size);
205 return 0;
206}
207
0284adc6
LP
208int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
209 int r;
210
211 assert(f);
212
baed47c3 213 if (!f->seal)
0284adc6
LP
214 return 0;
215
89fef990 216 if (realtime <= 0)
671e021c 217 realtime = now(CLOCK_REALTIME);
89fef990 218
baed47c3 219 r = journal_file_fsprg_need_evolve(f, realtime);
0284adc6
LP
220 if (r <= 0)
221 return 0;
222
223 r = journal_file_append_tag(f);
224 if (r < 0)
225 return r;
226
baed47c3 227 r = journal_file_fsprg_evolve(f, realtime);
0284adc6
LP
228 if (r < 0)
229 return r;
230
0284adc6
LP
231 return 0;
232}
233
78519831 234int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) {
0284adc6 235 int r;
0284adc6
LP
236
237 assert(f);
238
baed47c3 239 if (!f->seal)
0284adc6
LP
240 return 0;
241
242 r = journal_file_hmac_start(f);
243 if (r < 0)
244 return r;
245
5996c7c2
LP
246 if (!o) {
247 r = journal_file_move_to_object(f, type, p, &o);
248 if (r < 0)
249 return r;
250 } else {
d05089d8 251 if (type > OBJECT_UNUSED && o->object.type != type)
5996c7c2
LP
252 return -EBADMSG;
253 }
0284adc6
LP
254
255 gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
256
257 switch (o->object.type) {
258
259 case OBJECT_DATA:
14d10188 260 /* All but hash and payload are mutable */
0284adc6
LP
261 gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
262 gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload));
263 break;
264
3c1668da
LP
265 case OBJECT_FIELD:
266 /* Same here */
267 gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash));
268 gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(FieldObject, payload));
269 break;
270
0284adc6
LP
271 case OBJECT_ENTRY:
272 /* All */
273 gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(EntryObject, seqnum));
274 break;
275
276 case OBJECT_FIELD_HASH_TABLE:
277 case OBJECT_DATA_HASH_TABLE:
278 case OBJECT_ENTRY_ARRAY:
279 /* Nothing: everything is mutable */
280 break;
281
282 case OBJECT_TAG:
283 /* All but the tag itself */
284 gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum));
14d10188 285 gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
0284adc6
LP
286 break;
287 default:
288 return -EINVAL;
289 }
290
291 return 0;
292}
293
294int journal_file_hmac_put_header(JournalFile *f) {
295 int r;
296
297 assert(f);
298
baed47c3 299 if (!f->seal)
0284adc6
LP
300 return 0;
301
302 r = journal_file_hmac_start(f);
303 if (r < 0)
304 return r;
305
306 /* All but state+reserved, boot_id, arena_size,
14d10188
LP
307 * tail_object_offset, n_objects, n_entries,
308 * tail_entry_seqnum, head_entry_seqnum, entry_array_offset,
0284adc6 309 * head_entry_realtime, tail_entry_realtime,
14d10188
LP
310 * tail_entry_monotonic, n_data, n_fields, n_tags,
311 * n_entry_arrays. */
0284adc6
LP
312
313 gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature));
314 gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, boot_id) - offsetof(Header, file_id));
315 gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id));
316 gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
0284adc6
LP
317
318 return 0;
319}
320
baed47c3 321int journal_file_fss_load(JournalFile *f) {
0284adc6
LP
322 int r, fd = -1;
323 char *p = NULL;
324 struct stat st;
baed47c3 325 FSSHeader *m = NULL;
0284adc6
LP
326 sd_id128_t machine;
327
328 assert(f);
329
baed47c3 330 if (!f->seal)
0284adc6
LP
331 return 0;
332
333 r = sd_id128_get_machine(&machine);
334 if (r < 0)
335 return r;
336
baed47c3 337 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
0284adc6
LP
338 SD_ID128_FORMAT_VAL(machine)) < 0)
339 return -ENOMEM;
340
341 fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
342 if (fd < 0) {
272410e1 343 if (errno != ENOENT)
56f64d95 344 log_error_errno(errno, "Failed to open %s: %m", p);
272410e1 345
0284adc6
LP
346 r = -errno;
347 goto finish;
348 }
349
350 if (fstat(fd, &st) < 0) {
351 r = -errno;
352 goto finish;
353 }
354
baed47c3 355 if (st.st_size < (off_t) sizeof(FSSHeader)) {
0284adc6
LP
356 r = -ENODATA;
357 goto finish;
358 }
359
baed47c3 360 m = mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0);
0284adc6
LP
361 if (m == MAP_FAILED) {
362 m = NULL;
363 r = -errno;
364 goto finish;
365 }
366
baed47c3 367 if (memcmp(m->signature, FSS_HEADER_SIGNATURE, 8) != 0) {
0284adc6
LP
368 r = -EBADMSG;
369 goto finish;
370 }
371
372 if (m->incompatible_flags != 0) {
373 r = -EPROTONOSUPPORT;
374 goto finish;
375 }
376
baed47c3 377 if (le64toh(m->header_size) < sizeof(FSSHeader)) {
0284adc6
LP
378 r = -EBADMSG;
379 goto finish;
380 }
381
3e4b9b50 382 if (le64toh(m->fsprg_state_size) != FSPRG_stateinbytes(le16toh(m->fsprg_secpar))) {
0284adc6
LP
383 r = -EBADMSG;
384 goto finish;
385 }
386
baed47c3
LP
387 f->fss_file_size = le64toh(m->header_size) + le64toh(m->fsprg_state_size);
388 if ((uint64_t) st.st_size < f->fss_file_size) {
0284adc6
LP
389 r = -ENODATA;
390 goto finish;
391 }
392
393 if (!sd_id128_equal(machine, m->machine_id)) {
394 r = -EHOSTDOWN;
395 goto finish;
396 }
397
baed47c3
LP
398 if (le64toh(m->start_usec) <= 0 ||
399 le64toh(m->interval_usec) <= 0) {
0284adc6
LP
400 r = -EBADMSG;
401 goto finish;
402 }
403
baed47c3
LP
404 f->fss_file = mmap(NULL, PAGE_ALIGN(f->fss_file_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
405 if (f->fss_file == MAP_FAILED) {
406 f->fss_file = NULL;
0284adc6
LP
407 r = -errno;
408 goto finish;
409 }
410
baed47c3
LP
411 f->fss_start_usec = le64toh(f->fss_file->start_usec);
412 f->fss_interval_usec = le64toh(f->fss_file->interval_usec);
b7c9ae91 413
baed47c3
LP
414 f->fsprg_state = (uint8_t*) f->fss_file + le64toh(f->fss_file->header_size);
415 f->fsprg_state_size = le64toh(f->fss_file->fsprg_state_size);
b7c9ae91 416
0284adc6
LP
417 r = 0;
418
419finish:
420 if (m)
baed47c3 421 munmap(m, PAGE_ALIGN(sizeof(FSSHeader)));
0284adc6 422
03e334a1 423 safe_close(fd);
0284adc6 424 free(p);
03e334a1 425
0284adc6
LP
426 return r;
427}
428
72fbdd33
LP
429static void initialize_libgcrypt(void) {
430 const char *p;
431
432 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
433 return;
434
435 p = gcry_check_version("1.4.5");
436 assert(p);
437
438 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
439}
440
baed47c3 441int journal_file_hmac_setup(JournalFile *f) {
0284adc6
LP
442 gcry_error_t e;
443
baed47c3 444 if (!f->seal)
0284adc6
LP
445 return 0;
446
72fbdd33
LP
447 initialize_libgcrypt();
448
0284adc6
LP
449 e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
450 if (e != 0)
15411c0c 451 return -EOPNOTSUPP;
0284adc6
LP
452
453 return 0;
454}
455
456int journal_file_append_first_tag(JournalFile *f) {
457 int r;
458 uint64_t p;
459
baed47c3 460 if (!f->seal)
0284adc6
LP
461 return 0;
462
463 log_debug("Calculating first tag...");
464
465 r = journal_file_hmac_put_header(f);
466 if (r < 0)
467 return r;
468
469 p = le64toh(f->header->field_hash_table_offset);
470 if (p < offsetof(Object, hash_table.items))
471 return -EINVAL;
472 p -= offsetof(Object, hash_table.items);
473
5996c7c2 474 r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, NULL, p);
0284adc6
LP
475 if (r < 0)
476 return r;
477
478 p = le64toh(f->header->data_hash_table_offset);
479 if (p < offsetof(Object, hash_table.items))
480 return -EINVAL;
481 p -= offsetof(Object, hash_table.items);
482
5996c7c2 483 r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, NULL, p);
0284adc6
LP
484 if (r < 0)
485 return r;
486
487 r = journal_file_append_tag(f);
488 if (r < 0)
489 return r;
490
491 return 0;
492}
4da416aa 493
feb12d3e
LP
494int journal_file_parse_verification_key(JournalFile *f, const char *key) {
495 uint8_t *seed;
496 size_t seed_size, c;
497 const char *k;
498 int r;
499 unsigned long long start, interval;
500
501 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
502 seed = malloc(seed_size);
503 if (!seed)
504 return -ENOMEM;
505
506 k = key;
507 for (c = 0; c < seed_size; c++) {
508 int x, y;
509
510 while (*k == '-')
511 k++;
512
513 x = unhexchar(*k);
514 if (x < 0) {
515 free(seed);
516 return -EINVAL;
517 }
518 k++;
519 y = unhexchar(*k);
520 if (y < 0) {
521 free(seed);
522 return -EINVAL;
523 }
524 k++;
525
526 seed[c] = (uint8_t) (x * 16 + y);
527 }
528
529 if (*k != '/') {
530 free(seed);
531 return -EINVAL;
532 }
533 k++;
534
535 r = sscanf(k, "%llx-%llx", &start, &interval);
536 if (r != 2) {
537 free(seed);
538 return -EINVAL;
539 }
540
541 f->fsprg_seed = seed;
542 f->fsprg_seed_size = seed_size;
543
544 f->fss_start_usec = start * interval;
545 f->fss_interval_usec = interval;
546
547 return 0;
4da416aa 548}
89fef990
LP
549
550bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u) {
551 uint64_t epoch;
552
553 assert(f);
554 assert(u);
555
556 if (!f->seal)
557 return false;
558
559 epoch = FSPRG_GetEpoch(f->fsprg_state);
560
561 *u = (usec_t) (f->fss_start_usec + f->fss_interval_usec * epoch + f->fss_interval_usec);
562
563 return true;
564}