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/>.
27 #include "journal-authenticate.h"
28 #include "journal-def.h"
29 #include "journal-file.h"
30 #include "hexdecoct.h"
32 static uint64_t journal_file_tag_seqnum(JournalFile
*f
) {
37 r
= le64toh(f
->header
->n_tags
) + 1;
38 f
->header
->n_tags
= htole64(r
);
43 int journal_file_append_tag(JournalFile
*f
) {
58 r
= journal_file_append_object(f
, OBJECT_TAG
, sizeof(struct TagObject
), &o
, &p
);
62 o
->tag
.seqnum
= htole64(journal_file_tag_seqnum(f
));
63 o
->tag
.epoch
= htole64(FSPRG_GetEpoch(f
->fsprg_state
));
65 log_debug("Writing tag %"PRIu64
" for epoch %"PRIu64
"",
66 le64toh(o
->tag
.seqnum
),
67 FSPRG_GetEpoch(f
->fsprg_state
));
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
);
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;
82 int journal_file_hmac_start(JournalFile
*f
) {
83 uint8_t key
[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
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
));
97 f
->hmac_running
= true;
102 static int journal_file_get_epoch(JournalFile
*f
, uint64_t realtime
, uint64_t *epoch
) {
109 if (f
->fss_start_usec
== 0 ||
110 f
->fss_interval_usec
== 0)
113 if (realtime
< f
->fss_start_usec
)
116 t
= realtime
- f
->fss_start_usec
;
117 t
= t
/ f
->fss_interval_usec
;
123 static int journal_file_fsprg_need_evolve(JournalFile
*f
, uint64_t realtime
) {
124 uint64_t goal
, epoch
;
131 r
= journal_file_get_epoch(f
, realtime
, &goal
);
135 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
139 return epoch
!= goal
;
142 int journal_file_fsprg_evolve(JournalFile
*f
, uint64_t realtime
) {
143 uint64_t goal
, epoch
;
151 r
= journal_file_get_epoch(f
, realtime
, &goal
);
155 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
157 log_debug("Evolving FSPRG key from epoch %"PRIu64
" to %"PRIu64
".", epoch
, goal
);
165 FSPRG_Evolve(f
->fsprg_state
);
166 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
170 int journal_file_fsprg_seek(JournalFile
*f
, uint64_t goal
) {
179 assert(f
->fsprg_seed
);
181 if (f
->fsprg_state
) {
184 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
188 if (goal
== epoch
+1) {
189 FSPRG_Evolve(f
->fsprg_state
);
193 f
->fsprg_state_size
= FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR
);
194 f
->fsprg_state
= malloc(f
->fsprg_state_size
);
200 log_debug("Seeking FSPRG key to %"PRIu64
".", goal
);
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
);
208 int journal_file_maybe_append_tag(JournalFile
*f
, uint64_t realtime
) {
217 realtime
= now(CLOCK_REALTIME
);
219 r
= journal_file_fsprg_need_evolve(f
, realtime
);
223 r
= journal_file_append_tag(f
);
227 r
= journal_file_fsprg_evolve(f
, realtime
);
234 int journal_file_hmac_put_object(JournalFile
*f
, ObjectType type
, Object
*o
, uint64_t p
) {
242 r
= journal_file_hmac_start(f
);
247 r
= journal_file_move_to_object(f
, type
, p
, &o
);
251 if (type
> OBJECT_UNUSED
&& o
->object
.type
!= type
)
255 gcry_md_write(f
->hmac
, o
, offsetof(ObjectHeader
, payload
));
257 switch (o
->object
.type
) {
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
));
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
));
273 gcry_md_write(f
->hmac
, &o
->entry
.seqnum
, le64toh(o
->object
.size
) - offsetof(EntryObject
, seqnum
));
276 case OBJECT_FIELD_HASH_TABLE
:
277 case OBJECT_DATA_HASH_TABLE
:
278 case OBJECT_ENTRY_ARRAY
:
279 /* Nothing: everything is mutable */
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
));
294 int journal_file_hmac_put_header(JournalFile
*f
) {
302 r
= journal_file_hmac_start(f
);
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,
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
));
321 int journal_file_fss_load(JournalFile
*f
) {
333 r
= sd_id128_get_machine(&machine
);
337 if (asprintf(&p
, "/var/log/journal/" SD_ID128_FORMAT_STR
"/fss",
338 SD_ID128_FORMAT_VAL(machine
)) < 0)
341 fd
= open(p
, O_RDWR
|O_CLOEXEC
|O_NOCTTY
, 0600);
344 log_error_errno(errno
, "Failed to open %s: %m", p
);
350 if (fstat(fd
, &st
) < 0) {
355 if (st
.st_size
< (off_t
) sizeof(FSSHeader
)) {
360 m
= mmap(NULL
, PAGE_ALIGN(sizeof(FSSHeader
)), PROT_READ
, MAP_SHARED
, fd
, 0);
361 if (m
== MAP_FAILED
) {
367 if (memcmp(m
->signature
, FSS_HEADER_SIGNATURE
, 8) != 0) {
372 if (m
->incompatible_flags
!= 0) {
373 r
= -EPROTONOSUPPORT
;
377 if (le64toh(m
->header_size
) < sizeof(FSSHeader
)) {
382 if (le64toh(m
->fsprg_state_size
) != FSPRG_stateinbytes(le16toh(m
->fsprg_secpar
))) {
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
) {
393 if (!sd_id128_equal(machine
, m
->machine_id
)) {
398 if (le64toh(m
->start_usec
) <= 0 ||
399 le64toh(m
->interval_usec
) <= 0) {
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
) {
411 f
->fss_start_usec
= le64toh(f
->fss_file
->start_usec
);
412 f
->fss_interval_usec
= le64toh(f
->fss_file
->interval_usec
);
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
);
421 munmap(m
, PAGE_ALIGN(sizeof(FSSHeader
)));
429 static void initialize_libgcrypt(void) {
432 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P
))
435 p
= gcry_check_version("1.4.5");
438 gcry_control(GCRYCTL_INITIALIZATION_FINISHED
, 0);
441 int journal_file_hmac_setup(JournalFile
*f
) {
447 initialize_libgcrypt();
449 e
= gcry_md_open(&f
->hmac
, GCRY_MD_SHA256
, GCRY_MD_FLAG_HMAC
);
456 int journal_file_append_first_tag(JournalFile
*f
) {
463 log_debug("Calculating first tag...");
465 r
= journal_file_hmac_put_header(f
);
469 p
= le64toh(f
->header
->field_hash_table_offset
);
470 if (p
< offsetof(Object
, hash_table
.items
))
472 p
-= offsetof(Object
, hash_table
.items
);
474 r
= journal_file_hmac_put_object(f
, OBJECT_FIELD_HASH_TABLE
, NULL
, p
);
478 p
= le64toh(f
->header
->data_hash_table_offset
);
479 if (p
< offsetof(Object
, hash_table
.items
))
481 p
-= offsetof(Object
, hash_table
.items
);
483 r
= journal_file_hmac_put_object(f
, OBJECT_DATA_HASH_TABLE
, NULL
, p
);
487 r
= journal_file_append_tag(f
);
494 int journal_file_parse_verification_key(JournalFile
*f
, const char *key
) {
499 unsigned long long start
, interval
;
501 seed_size
= FSPRG_RECOMMENDED_SEEDLEN
;
502 seed
= malloc(seed_size
);
507 for (c
= 0; c
< seed_size
; c
++) {
526 seed
[c
] = (uint8_t) (x
* 16 + y
);
535 r
= sscanf(k
, "%llx-%llx", &start
, &interval
);
541 f
->fsprg_seed
= seed
;
542 f
->fsprg_seed_size
= seed_size
;
544 f
->fss_start_usec
= start
* interval
;
545 f
->fss_interval_usec
= interval
;
550 bool journal_file_next_evolve_usec(JournalFile
*f
, usec_t
*u
) {
559 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
561 *u
= (usec_t
) (f
->fss_start_usec
+ f
->fss_interval_usec
* epoch
+ f
->fss_interval_usec
);