1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "gcrypt-util.h"
10 #include "journal-authenticate.h"
11 #include "journal-def.h"
12 #include "journal-file.h"
13 #include "memory-util.h"
14 #include "time-util.h"
16 static uint64_t journal_file_tag_seqnum(JournalFile
*f
) {
21 r
= le64toh(f
->header
->n_tags
) + 1;
22 f
->header
->n_tags
= htole64(r
);
27 int journal_file_append_tag(JournalFile
*f
) {
42 r
= journal_file_append_object(f
, OBJECT_TAG
, sizeof(struct TagObject
), &o
, &p
);
46 o
->tag
.seqnum
= htole64(journal_file_tag_seqnum(f
));
47 o
->tag
.epoch
= htole64(FSPRG_GetEpoch(f
->fsprg_state
));
49 log_debug("Writing tag %"PRIu64
" for epoch %"PRIu64
"",
50 le64toh(o
->tag
.seqnum
),
51 FSPRG_GetEpoch(f
->fsprg_state
));
53 /* Add the tag object itself, so that we can protect its
54 * header. This will exclude the actual hash value in it */
55 r
= journal_file_hmac_put_object(f
, OBJECT_TAG
, o
, p
);
59 /* Get the HMAC tag and store it in the object */
60 memcpy(o
->tag
.tag
, gcry_md_read(f
->hmac
, 0), TAG_LENGTH
);
61 f
->hmac_running
= false;
66 int journal_file_hmac_start(JournalFile
*f
) {
67 uint8_t key
[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
78 /* Prepare HMAC for next cycle */
79 gcry_md_reset(f
->hmac
);
80 FSPRG_GetKey(f
->fsprg_state
, key
, sizeof(key
), 0);
81 err
= gcry_md_setkey(f
->hmac
, key
, sizeof(key
));
82 if (gcry_err_code(err
) != GPG_ERR_NO_ERROR
)
83 return log_debug_errno(SYNTHETIC_ERRNO(EIO
),
84 "gcry_md_setkey() failed with error code: %d",
87 f
->hmac_running
= true;
92 static int journal_file_get_epoch(JournalFile
*f
, uint64_t realtime
, uint64_t *epoch
) {
99 if (f
->fss_start_usec
== 0 ||
100 f
->fss_interval_usec
== 0)
103 if (realtime
< f
->fss_start_usec
)
106 t
= realtime
- f
->fss_start_usec
;
107 t
= t
/ f
->fss_interval_usec
;
113 static int journal_file_fsprg_need_evolve(JournalFile
*f
, uint64_t realtime
) {
114 uint64_t goal
, epoch
;
121 r
= journal_file_get_epoch(f
, realtime
, &goal
);
125 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
129 return epoch
!= goal
;
132 int journal_file_fsprg_evolve(JournalFile
*f
, uint64_t realtime
) {
133 uint64_t goal
, epoch
;
141 r
= journal_file_get_epoch(f
, realtime
, &goal
);
145 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
147 log_debug("Evolving FSPRG key from epoch %"PRIu64
" to %"PRIu64
".", epoch
, goal
);
155 FSPRG_Evolve(f
->fsprg_state
);
156 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
160 int journal_file_fsprg_seek(JournalFile
*f
, uint64_t goal
) {
169 assert(f
->fsprg_seed
);
171 if (f
->fsprg_state
) {
174 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
178 if (goal
== epoch
+1) {
179 FSPRG_Evolve(f
->fsprg_state
);
183 f
->fsprg_state_size
= FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR
);
184 f
->fsprg_state
= malloc(f
->fsprg_state_size
);
189 log_debug("Seeking FSPRG key to %"PRIu64
".", goal
);
191 msk
= alloca_safe(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR
));
192 FSPRG_GenMK(msk
, NULL
, f
->fsprg_seed
, f
->fsprg_seed_size
, FSPRG_RECOMMENDED_SECPAR
);
193 FSPRG_Seek(f
->fsprg_state
, goal
, msk
, f
->fsprg_seed
, f
->fsprg_seed_size
);
197 int journal_file_maybe_append_tag(JournalFile
*f
, uint64_t realtime
) {
206 realtime
= now(CLOCK_REALTIME
);
208 r
= journal_file_fsprg_need_evolve(f
, realtime
);
212 r
= journal_file_append_tag(f
);
216 r
= journal_file_fsprg_evolve(f
, realtime
);
223 int journal_file_hmac_put_object(JournalFile
*f
, ObjectType type
, Object
*o
, uint64_t p
) {
231 r
= journal_file_hmac_start(f
);
236 r
= journal_file_move_to_object(f
, type
, p
, &o
);
240 if (type
> OBJECT_UNUSED
&& o
->object
.type
!= type
)
244 gcry_md_write(f
->hmac
, o
, offsetof(ObjectHeader
, payload
));
246 switch (o
->object
.type
) {
249 /* All but hash and payload are mutable */
250 gcry_md_write(f
->hmac
, &o
->data
.hash
, sizeof(o
->data
.hash
));
251 gcry_md_write(f
->hmac
, o
->data
.payload
, le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
));
256 gcry_md_write(f
->hmac
, &o
->field
.hash
, sizeof(o
->field
.hash
));
257 gcry_md_write(f
->hmac
, o
->field
.payload
, le64toh(o
->object
.size
) - offsetof(Object
, field
.payload
));
262 gcry_md_write(f
->hmac
, &o
->entry
.seqnum
, le64toh(o
->object
.size
) - offsetof(Object
, entry
.seqnum
));
265 case OBJECT_FIELD_HASH_TABLE
:
266 case OBJECT_DATA_HASH_TABLE
:
267 case OBJECT_ENTRY_ARRAY
:
268 /* Nothing: everything is mutable */
272 /* All but the tag itself */
273 gcry_md_write(f
->hmac
, &o
->tag
.seqnum
, sizeof(o
->tag
.seqnum
));
274 gcry_md_write(f
->hmac
, &o
->tag
.epoch
, sizeof(o
->tag
.epoch
));
283 int journal_file_hmac_put_header(JournalFile
*f
) {
291 r
= journal_file_hmac_start(f
);
295 /* All but state+reserved, boot_id, arena_size,
296 * tail_object_offset, n_objects, n_entries,
297 * tail_entry_seqnum, head_entry_seqnum, entry_array_offset,
298 * head_entry_realtime, tail_entry_realtime,
299 * tail_entry_monotonic, n_data, n_fields, n_tags,
302 gcry_md_write(f
->hmac
, f
->header
->signature
, offsetof(Header
, state
) - offsetof(Header
, signature
));
303 gcry_md_write(f
->hmac
, &f
->header
->file_id
, offsetof(Header
, boot_id
) - offsetof(Header
, file_id
));
304 gcry_md_write(f
->hmac
, &f
->header
->seqnum_id
, offsetof(Header
, arena_size
) - offsetof(Header
, seqnum_id
));
305 gcry_md_write(f
->hmac
, &f
->header
->data_hash_table_offset
, offsetof(Header
, tail_object_offset
) - offsetof(Header
, data_hash_table_offset
));
310 int journal_file_fss_load(JournalFile
*f
) {
322 r
= sd_id128_get_machine(&machine
);
326 if (asprintf(&p
, "/var/log/journal/" SD_ID128_FORMAT_STR
"/fss",
327 SD_ID128_FORMAT_VAL(machine
)) < 0)
330 fd
= open(p
, O_RDWR
|O_CLOEXEC
|O_NOCTTY
, 0600);
333 log_error_errno(errno
, "Failed to open %s: %m", p
);
339 if (fstat(fd
, &st
) < 0) {
344 if (st
.st_size
< (off_t
) sizeof(FSSHeader
)) {
349 m
= mmap(NULL
, PAGE_ALIGN(sizeof(FSSHeader
)), PROT_READ
, MAP_SHARED
, fd
, 0);
350 if (m
== MAP_FAILED
) {
356 if (memcmp(m
->signature
, FSS_HEADER_SIGNATURE
, 8) != 0) {
361 if (m
->incompatible_flags
!= 0) {
362 r
= -EPROTONOSUPPORT
;
366 if (le64toh(m
->header_size
) < sizeof(FSSHeader
)) {
371 if (le64toh(m
->fsprg_state_size
) != FSPRG_stateinbytes(le16toh(m
->fsprg_secpar
))) {
376 f
->fss_file_size
= le64toh(m
->header_size
) + le64toh(m
->fsprg_state_size
);
377 if ((uint64_t) st
.st_size
< f
->fss_file_size
) {
382 if (!sd_id128_equal(machine
, m
->machine_id
)) {
387 if (le64toh(m
->start_usec
) <= 0 ||
388 le64toh(m
->interval_usec
) <= 0) {
393 f
->fss_file
= mmap(NULL
, PAGE_ALIGN(f
->fss_file_size
), PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
394 if (f
->fss_file
== MAP_FAILED
) {
400 f
->fss_start_usec
= le64toh(f
->fss_file
->start_usec
);
401 f
->fss_interval_usec
= le64toh(f
->fss_file
->interval_usec
);
403 f
->fsprg_state
= (uint8_t*) f
->fss_file
+ le64toh(f
->fss_file
->header_size
);
404 f
->fsprg_state_size
= le64toh(f
->fss_file
->fsprg_state_size
);
410 munmap(m
, PAGE_ALIGN(sizeof(FSSHeader
)));
418 int journal_file_hmac_setup(JournalFile
*f
) {
424 initialize_libgcrypt(true);
426 e
= gcry_md_open(&f
->hmac
, GCRY_MD_SHA256
, GCRY_MD_FLAG_HMAC
);
433 int journal_file_append_first_tag(JournalFile
*f
) {
440 log_debug("Calculating first tag...");
442 r
= journal_file_hmac_put_header(f
);
446 p
= le64toh(f
->header
->field_hash_table_offset
);
447 if (p
< offsetof(Object
, hash_table
.items
))
449 p
-= offsetof(Object
, hash_table
.items
);
451 r
= journal_file_hmac_put_object(f
, OBJECT_FIELD_HASH_TABLE
, NULL
, p
);
455 p
= le64toh(f
->header
->data_hash_table_offset
);
456 if (p
< offsetof(Object
, hash_table
.items
))
458 p
-= offsetof(Object
, hash_table
.items
);
460 r
= journal_file_hmac_put_object(f
, OBJECT_DATA_HASH_TABLE
, NULL
, p
);
464 r
= journal_file_append_tag(f
);
471 int journal_file_parse_verification_key(JournalFile
*f
, const char *key
) {
476 unsigned long long start
, interval
;
478 seed_size
= FSPRG_RECOMMENDED_SEEDLEN
;
479 seed
= malloc(seed_size
);
484 for (c
= 0; c
< seed_size
; c
++) {
503 seed
[c
] = (uint8_t) (x
* 16 + y
);
512 r
= sscanf(k
, "%llx-%llx", &start
, &interval
);
518 f
->fsprg_seed
= seed
;
519 f
->fsprg_seed_size
= seed_size
;
521 f
->fss_start_usec
= start
* interval
;
522 f
->fss_interval_usec
= interval
;
527 bool journal_file_next_evolve_usec(JournalFile
*f
, usec_t
*u
) {
536 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
538 *u
= (usec_t
) (f
->fss_start_usec
+ f
->fss_interval_usec
* epoch
+ f
->fss_interval_usec
);