]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journal-authenticate.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / journal / journal-authenticate.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2012 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <fcntl.h>
22 #include <sys/mman.h>
23
24 #include "fd-util.h"
25 #include "fsprg.h"
26 #include "gcrypt-util.h"
27 #include "hexdecoct.h"
28 #include "journal-authenticate.h"
29 #include "journal-def.h"
30 #include "journal-file.h"
31
32 static 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
43 int journal_file_append_tag(JournalFile *f) {
44 Object *o;
45 uint64_t p;
46 int r;
47
48 assert(f);
49
50 if (!f->seal)
51 return 0;
52
53 if (!f->hmac_running)
54 return 0;
55
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));
63 o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state));
64
65 log_debug("Writing tag %"PRIu64" for epoch %"PRIu64"",
66 le64toh(o->tag.seqnum),
67 FSPRG_GetEpoch(f->fsprg_state));
68
69 /* Add the tag object itself, so that we can protect its
70 * header. This will exclude the actual hash value in it */
71 r = journal_file_hmac_put_object(f, OBJECT_TAG, o, p);
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
82 int journal_file_hmac_start(JournalFile *f) {
83 uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
84 assert(f);
85
86 if (!f->seal)
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);
94 FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0);
95 gcry_md_setkey(f->hmac, key, sizeof(key));
96
97 f->hmac_running = true;
98
99 return 0;
100 }
101
102 static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) {
103 uint64_t t;
104
105 assert(f);
106 assert(epoch);
107 assert(f->seal);
108
109 if (f->fss_start_usec == 0 ||
110 f->fss_interval_usec == 0)
111 return -EOPNOTSUPP;
112
113 if (realtime < f->fss_start_usec)
114 return -ESTALE;
115
116 t = realtime - f->fss_start_usec;
117 t = t / f->fss_interval_usec;
118
119 *epoch = t;
120 return 0;
121 }
122
123 static int journal_file_fsprg_need_evolve(JournalFile *f, uint64_t realtime) {
124 uint64_t goal, epoch;
125 int r;
126 assert(f);
127
128 if (!f->seal)
129 return 0;
130
131 r = journal_file_get_epoch(f, realtime, &goal);
132 if (r < 0)
133 return r;
134
135 epoch = FSPRG_GetEpoch(f->fsprg_state);
136 if (epoch > goal)
137 return -ESTALE;
138
139 return epoch != goal;
140 }
141
142 int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) {
143 uint64_t goal, epoch;
144 int r;
145
146 assert(f);
147
148 if (!f->seal)
149 return 0;
150
151 r = journal_file_get_epoch(f, realtime, &goal);
152 if (r < 0)
153 return r;
154
155 epoch = FSPRG_GetEpoch(f->fsprg_state);
156 if (epoch < goal)
157 log_debug("Evolving FSPRG key from epoch %"PRIu64" to %"PRIu64".", epoch, goal);
158
159 for (;;) {
160 if (epoch > goal)
161 return -ESTALE;
162 if (epoch == goal)
163 return 0;
164
165 FSPRG_Evolve(f->fsprg_state);
166 epoch = FSPRG_GetEpoch(f->fsprg_state);
167 }
168 }
169
170 int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
171 void *msk;
172 uint64_t epoch;
173
174 assert(f);
175
176 if (!f->seal)
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
200 log_debug("Seeking FSPRG key to %"PRIu64".", goal);
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
208 int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
209 int r;
210
211 assert(f);
212
213 if (!f->seal)
214 return 0;
215
216 if (realtime <= 0)
217 realtime = now(CLOCK_REALTIME);
218
219 r = journal_file_fsprg_need_evolve(f, realtime);
220 if (r <= 0)
221 return 0;
222
223 r = journal_file_append_tag(f);
224 if (r < 0)
225 return r;
226
227 r = journal_file_fsprg_evolve(f, realtime);
228 if (r < 0)
229 return r;
230
231 return 0;
232 }
233
234 int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) {
235 int r;
236
237 assert(f);
238
239 if (!f->seal)
240 return 0;
241
242 r = journal_file_hmac_start(f);
243 if (r < 0)
244 return r;
245
246 if (!o) {
247 r = journal_file_move_to_object(f, type, p, &o);
248 if (r < 0)
249 return r;
250 } else {
251 if (type > OBJECT_UNUSED && o->object.type != type)
252 return -EBADMSG;
253 }
254
255 gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
256
257 switch (o->object.type) {
258
259 case OBJECT_DATA:
260 /* All but hash and payload are mutable */
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
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
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));
285 gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
286 break;
287 default:
288 return -EINVAL;
289 }
290
291 return 0;
292 }
293
294 int journal_file_hmac_put_header(JournalFile *f) {
295 int r;
296
297 assert(f);
298
299 if (!f->seal)
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,
307 * tail_object_offset, n_objects, n_entries,
308 * tail_entry_seqnum, head_entry_seqnum, entry_array_offset,
309 * head_entry_realtime, tail_entry_realtime,
310 * tail_entry_monotonic, n_data, n_fields, n_tags,
311 * n_entry_arrays. */
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));
317
318 return 0;
319 }
320
321 int journal_file_fss_load(JournalFile *f) {
322 int r, fd = -1;
323 char *p = NULL;
324 struct stat st;
325 FSSHeader *m = NULL;
326 sd_id128_t machine;
327
328 assert(f);
329
330 if (!f->seal)
331 return 0;
332
333 r = sd_id128_get_machine(&machine);
334 if (r < 0)
335 return r;
336
337 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
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) {
343 if (errno != ENOENT)
344 log_error_errno(errno, "Failed to open %s: %m", p);
345
346 r = -errno;
347 goto finish;
348 }
349
350 if (fstat(fd, &st) < 0) {
351 r = -errno;
352 goto finish;
353 }
354
355 if (st.st_size < (off_t) sizeof(FSSHeader)) {
356 r = -ENODATA;
357 goto finish;
358 }
359
360 m = mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0);
361 if (m == MAP_FAILED) {
362 m = NULL;
363 r = -errno;
364 goto finish;
365 }
366
367 if (memcmp(m->signature, FSS_HEADER_SIGNATURE, 8) != 0) {
368 r = -EBADMSG;
369 goto finish;
370 }
371
372 if (m->incompatible_flags != 0) {
373 r = -EPROTONOSUPPORT;
374 goto finish;
375 }
376
377 if (le64toh(m->header_size) < sizeof(FSSHeader)) {
378 r = -EBADMSG;
379 goto finish;
380 }
381
382 if (le64toh(m->fsprg_state_size) != FSPRG_stateinbytes(le16toh(m->fsprg_secpar))) {
383 r = -EBADMSG;
384 goto finish;
385 }
386
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) {
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
398 if (le64toh(m->start_usec) <= 0 ||
399 le64toh(m->interval_usec) <= 0) {
400 r = -EBADMSG;
401 goto finish;
402 }
403
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;
407 r = -errno;
408 goto finish;
409 }
410
411 f->fss_start_usec = le64toh(f->fss_file->start_usec);
412 f->fss_interval_usec = le64toh(f->fss_file->interval_usec);
413
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);
416
417 r = 0;
418
419 finish:
420 if (m)
421 munmap(m, PAGE_ALIGN(sizeof(FSSHeader)));
422
423 safe_close(fd);
424 free(p);
425
426 return r;
427 }
428
429 int journal_file_hmac_setup(JournalFile *f) {
430 gcry_error_t e;
431
432 if (!f->seal)
433 return 0;
434
435 initialize_libgcrypt(true);
436
437 e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
438 if (e != 0)
439 return -EOPNOTSUPP;
440
441 return 0;
442 }
443
444 int journal_file_append_first_tag(JournalFile *f) {
445 int r;
446 uint64_t p;
447
448 if (!f->seal)
449 return 0;
450
451 log_debug("Calculating first tag...");
452
453 r = journal_file_hmac_put_header(f);
454 if (r < 0)
455 return r;
456
457 p = le64toh(f->header->field_hash_table_offset);
458 if (p < offsetof(Object, hash_table.items))
459 return -EINVAL;
460 p -= offsetof(Object, hash_table.items);
461
462 r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, NULL, p);
463 if (r < 0)
464 return r;
465
466 p = le64toh(f->header->data_hash_table_offset);
467 if (p < offsetof(Object, hash_table.items))
468 return -EINVAL;
469 p -= offsetof(Object, hash_table.items);
470
471 r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, NULL, p);
472 if (r < 0)
473 return r;
474
475 r = journal_file_append_tag(f);
476 if (r < 0)
477 return r;
478
479 return 0;
480 }
481
482 int journal_file_parse_verification_key(JournalFile *f, const char *key) {
483 uint8_t *seed;
484 size_t seed_size, c;
485 const char *k;
486 int r;
487 unsigned long long start, interval;
488
489 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
490 seed = malloc(seed_size);
491 if (!seed)
492 return -ENOMEM;
493
494 k = key;
495 for (c = 0; c < seed_size; c++) {
496 int x, y;
497
498 while (*k == '-')
499 k++;
500
501 x = unhexchar(*k);
502 if (x < 0) {
503 free(seed);
504 return -EINVAL;
505 }
506 k++;
507 y = unhexchar(*k);
508 if (y < 0) {
509 free(seed);
510 return -EINVAL;
511 }
512 k++;
513
514 seed[c] = (uint8_t) (x * 16 + y);
515 }
516
517 if (*k != '/') {
518 free(seed);
519 return -EINVAL;
520 }
521 k++;
522
523 r = sscanf(k, "%llx-%llx", &start, &interval);
524 if (r != 2) {
525 free(seed);
526 return -EINVAL;
527 }
528
529 f->fsprg_seed = seed;
530 f->fsprg_seed_size = seed_size;
531
532 f->fss_start_usec = start * interval;
533 f->fss_interval_usec = interval;
534
535 return 0;
536 }
537
538 bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u) {
539 uint64_t epoch;
540
541 assert(f);
542 assert(u);
543
544 if (!f->seal)
545 return false;
546
547 epoch = FSPRG_GetEpoch(f->fsprg_state);
548
549 *u = (usec_t) (f->fss_start_usec + f->fss_interval_usec * epoch + f->fss_interval_usec);
550
551 return true;
552 }