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