1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
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.
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.
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/>.
25 #include "journal-def.h"
26 #include "journal-file.h"
27 #include "journal-authenticate.h"
30 static uint64_t journal_file_tag_seqnum(JournalFile *f) {
35 r = le64toh(f->header->n_tags) + 1;
36 f->header->n_tags = htole64(r);
41 int journal_file_append_tag(JournalFile *f) {
56 r = journal_file_append_object(f, OBJECT_TAG, sizeof(struct TagObject), &o, &p);
60 o->tag.seqnum = htole64(journal_file_tag_seqnum(f));
61 o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state));
63 log_debug("Writing tag %"PRIu64" for epoch %"PRIu64"\n",
64 le64toh(o->tag.seqnum),
65 FSPRG_GetEpoch(f->fsprg_state));
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);
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;
80 int journal_file_hmac_start(JournalFile *f) {
81 uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
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));
95 f->hmac_running = true;
100 static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) {
107 if (f->fss_start_usec == 0 ||
108 f->fss_interval_usec == 0)
111 if (realtime < f->fss_start_usec)
114 t = realtime - f->fss_start_usec;
115 t = t / f->fss_interval_usec;
121 static int journal_file_fsprg_need_evolve(JournalFile *f, uint64_t realtime) {
122 uint64_t goal, epoch;
129 r = journal_file_get_epoch(f, realtime, &goal);
133 epoch = FSPRG_GetEpoch(f->fsprg_state);
137 return epoch != goal;
140 int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) {
141 uint64_t goal, epoch;
149 r = journal_file_get_epoch(f, realtime, &goal);
153 epoch = FSPRG_GetEpoch(f->fsprg_state);
155 log_debug("Evolving FSPRG key from epoch %"PRIu64" to %"PRIu64".", epoch, goal);
163 FSPRG_Evolve(f->fsprg_state);
164 epoch = FSPRG_GetEpoch(f->fsprg_state);
168 int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
177 assert(f->fsprg_seed);
179 if (f->fsprg_state) {
182 epoch = FSPRG_GetEpoch(f->fsprg_state);
186 if (goal == epoch+1) {
187 FSPRG_Evolve(f->fsprg_state);
191 f->fsprg_state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
192 f->fsprg_state = malloc(f->fsprg_state_size);
198 log_debug("Seeking FSPRG key to %"PRIu64".", goal);
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);
206 int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
215 realtime = now(CLOCK_REALTIME);
217 r = journal_file_fsprg_need_evolve(f, realtime);
221 r = journal_file_append_tag(f);
225 r = journal_file_fsprg_evolve(f, realtime);
232 int journal_file_hmac_put_object(JournalFile *f, int type, Object *o, uint64_t p) {
240 r = journal_file_hmac_start(f);
245 r = journal_file_move_to_object(f, type, p, &o);
249 if (type >= 0 && o->object.type != type)
253 gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
255 switch (o->object.type) {
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));
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));
271 gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(EntryObject, seqnum));
274 case OBJECT_FIELD_HASH_TABLE:
275 case OBJECT_DATA_HASH_TABLE:
276 case OBJECT_ENTRY_ARRAY:
277 /* Nothing: everything is mutable */
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));
292 int journal_file_hmac_put_header(JournalFile *f) {
300 r = journal_file_hmac_start(f);
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,
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));
319 int journal_file_fss_load(JournalFile *f) {
331 r = sd_id128_get_machine(&machine);
335 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
336 SD_ID128_FORMAT_VAL(machine)) < 0)
339 fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
342 log_error("Failed to open %s: %m", p);
348 if (fstat(fd, &st) < 0) {
353 if (st.st_size < (off_t) sizeof(FSSHeader)) {
358 m = mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0);
359 if (m == MAP_FAILED) {
365 if (memcmp(m->signature, FSS_HEADER_SIGNATURE, 8) != 0) {
370 if (m->incompatible_flags != 0) {
371 r = -EPROTONOSUPPORT;
375 if (le64toh(m->header_size) < sizeof(FSSHeader)) {
380 if (le64toh(m->fsprg_state_size) != FSPRG_stateinbytes(le16toh(m->fsprg_secpar))) {
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) {
391 if (!sd_id128_equal(machine, m->machine_id)) {
396 if (le64toh(m->start_usec) <= 0 ||
397 le64toh(m->interval_usec) <= 0) {
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) {
409 f->fss_start_usec = le64toh(f->fss_file->start_usec);
410 f->fss_interval_usec = le64toh(f->fss_file->interval_usec);
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);
419 munmap(m, PAGE_ALIGN(sizeof(FSSHeader)));
422 close_nointr_nofail(fd);
428 static void initialize_libgcrypt(void) {
431 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
434 p = gcry_check_version("1.4.5");
437 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
440 int journal_file_hmac_setup(JournalFile *f) {
446 initialize_libgcrypt();
448 e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
455 int journal_file_append_first_tag(JournalFile *f) {
462 log_debug("Calculating first tag...");
464 r = journal_file_hmac_put_header(f);
468 p = le64toh(f->header->field_hash_table_offset);
469 if (p < offsetof(Object, hash_table.items))
471 p -= offsetof(Object, hash_table.items);
473 r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, NULL, p);
477 p = le64toh(f->header->data_hash_table_offset);
478 if (p < offsetof(Object, hash_table.items))
480 p -= offsetof(Object, hash_table.items);
482 r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, NULL, p);
486 r = journal_file_append_tag(f);
493 int journal_file_parse_verification_key(JournalFile *f, const char *key) {
498 unsigned long long start, interval;
500 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
501 seed = malloc(seed_size);
506 for (c = 0; c < seed_size; c++) {
525 seed[c] = (uint8_t) (x * 16 + y);
534 r = sscanf(k, "%llx-%llx", &start, &interval);
540 f->fsprg_seed = seed;
541 f->fsprg_seed_size = seed_size;
543 f->fss_start_usec = start * interval;
544 f->fss_interval_usec = interval;
549 bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u) {
558 epoch = FSPRG_GetEpoch(f->fsprg_state);
560 *u = (usec_t) (f->fss_start_usec + f->fss_interval_usec * epoch + f->fss_interval_usec);