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 void* fssheader_free(FSSHeader
*p
) {
17 /* mmap() returns MAP_FAILED on error and sets the errno */
18 if (!p
|| p
== MAP_FAILED
)
21 assert_se(munmap(p
, PAGE_ALIGN(sizeof(FSSHeader
))) >= 0);
25 DEFINE_TRIVIAL_CLEANUP_FUNC(FSSHeader
*, fssheader_free
);
27 static uint64_t journal_file_tag_seqnum(JournalFile
*f
) {
32 r
= le64toh(f
->header
->n_tags
) + 1;
33 f
->header
->n_tags
= htole64(r
);
38 int journal_file_append_tag(JournalFile
*f
) {
45 if (!JOURNAL_HEADER_SEALED(f
->header
))
48 if (!f
->hmac_running
) {
49 r
= journal_file_hmac_start(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
"",
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 */
86 if (!JOURNAL_HEADER_SEALED(f
->header
))
92 /* Prepare HMAC for next cycle */
93 gcry_md_reset(f
->hmac
);
94 FSPRG_GetKey(f
->fsprg_state
, key
, sizeof(key
), 0);
95 err
= gcry_md_setkey(f
->hmac
, key
, sizeof(key
));
96 if (gcry_err_code(err
) != GPG_ERR_NO_ERROR
)
97 return log_debug_errno(SYNTHETIC_ERRNO(EIO
),
98 "gcry_md_setkey() failed with error code: %s",
101 f
->hmac_running
= true;
106 static int journal_file_get_epoch(JournalFile
*f
, uint64_t realtime
, uint64_t *epoch
) {
111 assert(JOURNAL_HEADER_SEALED(f
->header
));
113 if (f
->fss_start_usec
== 0 || f
->fss_interval_usec
== 0)
116 if (realtime
< f
->fss_start_usec
)
119 t
= realtime
- f
->fss_start_usec
;
120 t
= t
/ f
->fss_interval_usec
;
127 static int journal_file_fsprg_need_evolve(JournalFile
*f
, uint64_t realtime
) {
128 uint64_t goal
, epoch
;
133 if (!JOURNAL_HEADER_SEALED(f
->header
))
136 r
= journal_file_get_epoch(f
, realtime
, &goal
);
140 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
144 return epoch
!= goal
;
147 int journal_file_fsprg_evolve(JournalFile
*f
, uint64_t realtime
) {
148 uint64_t goal
, epoch
;
153 if (!JOURNAL_HEADER_SEALED(f
->header
))
156 r
= journal_file_get_epoch(f
, realtime
, &goal
);
160 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
162 log_debug("Evolving FSPRG key from epoch %"PRIu64
" to %"PRIu64
".", epoch
, goal
);
170 FSPRG_Evolve(f
->fsprg_state
);
171 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
173 r
= journal_file_append_tag(f
);
180 int journal_file_fsprg_seek(JournalFile
*f
, uint64_t goal
) {
186 if (!JOURNAL_HEADER_SEALED(f
->header
))
189 assert(f
->fsprg_seed
);
191 if (f
->fsprg_state
) {
194 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
198 if (goal
== epoch
+ 1) {
199 FSPRG_Evolve(f
->fsprg_state
);
203 f
->fsprg_state_size
= FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR
);
204 f
->fsprg_state
= malloc(f
->fsprg_state_size
);
209 log_debug("Seeking FSPRG key to %"PRIu64
".", goal
);
211 msk
= alloca_safe(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR
));
212 FSPRG_GenMK(msk
, NULL
, f
->fsprg_seed
, f
->fsprg_seed_size
, FSPRG_RECOMMENDED_SECPAR
);
213 FSPRG_Seek(f
->fsprg_state
, goal
, msk
, f
->fsprg_seed
, f
->fsprg_seed_size
);
218 int journal_file_maybe_append_tag(JournalFile
*f
, uint64_t realtime
) {
223 if (!JOURNAL_HEADER_SEALED(f
->header
))
227 realtime
= now(CLOCK_REALTIME
);
229 r
= journal_file_fsprg_need_evolve(f
, realtime
);
233 r
= journal_file_append_tag(f
);
237 r
= journal_file_fsprg_evolve(f
, realtime
);
244 int journal_file_hmac_put_object(JournalFile
*f
, ObjectType type
, Object
*o
, uint64_t p
) {
249 if (!JOURNAL_HEADER_SEALED(f
->header
))
252 r
= journal_file_hmac_start(f
);
257 r
= journal_file_move_to_object(f
, type
, p
, &o
);
260 } else if (type
> OBJECT_UNUSED
&& o
->object
.type
!= type
)
263 gcry_md_write(f
->hmac
, o
, offsetof(ObjectHeader
, payload
));
265 switch (o
->object
.type
) {
268 /* All but hash and payload are mutable */
269 gcry_md_write(f
->hmac
, &o
->data
.hash
, sizeof(o
->data
.hash
));
270 gcry_md_write(f
->hmac
, journal_file_data_payload_field(f
, o
), le64toh(o
->object
.size
) - journal_file_data_payload_offset(f
));
275 gcry_md_write(f
->hmac
, &o
->field
.hash
, sizeof(o
->field
.hash
));
276 gcry_md_write(f
->hmac
, o
->field
.payload
, le64toh(o
->object
.size
) - offsetof(Object
, field
.payload
));
281 gcry_md_write(f
->hmac
, &o
->entry
.seqnum
, le64toh(o
->object
.size
) - offsetof(Object
, entry
.seqnum
));
284 case OBJECT_FIELD_HASH_TABLE
:
285 case OBJECT_DATA_HASH_TABLE
:
286 case OBJECT_ENTRY_ARRAY
:
287 /* Nothing: everything is mutable */
291 /* All but the tag itself */
292 gcry_md_write(f
->hmac
, &o
->tag
.seqnum
, sizeof(o
->tag
.seqnum
));
293 gcry_md_write(f
->hmac
, &o
->tag
.epoch
, sizeof(o
->tag
.epoch
));
302 int journal_file_hmac_put_header(JournalFile
*f
) {
307 if (!JOURNAL_HEADER_SEALED(f
->header
))
310 r
= journal_file_hmac_start(f
);
314 /* All but state+reserved, boot_id, arena_size,
315 * tail_object_offset, n_objects, n_entries,
316 * tail_entry_seqnum, head_entry_seqnum, entry_array_offset,
317 * head_entry_realtime, tail_entry_realtime,
318 * tail_entry_monotonic, n_data, n_fields, n_tags,
321 gcry_md_write(f
->hmac
, f
->header
->signature
, offsetof(Header
, state
) - offsetof(Header
, signature
));
322 gcry_md_write(f
->hmac
, &f
->header
->file_id
, offsetof(Header
, tail_entry_boot_id
) - offsetof(Header
, file_id
));
323 gcry_md_write(f
->hmac
, &f
->header
->seqnum_id
, offsetof(Header
, arena_size
) - offsetof(Header
, seqnum_id
));
324 gcry_md_write(f
->hmac
, &f
->header
->data_hash_table_offset
, offsetof(Header
, tail_object_offset
) - offsetof(Header
, data_hash_table_offset
));
329 int journal_file_fss_load(JournalFile
*f
) {
330 _cleanup_close_
int fd
= -EBADF
;
331 _cleanup_free_
char *path
= NULL
;
332 _cleanup_(fssheader_freep
) FSSHeader
*header
= NULL
;
339 /* This function is used to determine whether sealing should be enabled in the journal header so we
340 * can't check the header to check if sealing is enabled here. */
342 r
= sd_id128_get_machine(&machine
);
346 if (asprintf(&path
, "/var/log/journal/" SD_ID128_FORMAT_STR
"/fss",
347 SD_ID128_FORMAT_VAL(machine
)) < 0)
350 fd
= open(path
, O_RDWR
|O_CLOEXEC
|O_NOCTTY
, 0600);
353 log_error_errno(errno
, "Failed to open %s: %m", path
);
358 if (fstat(fd
, &st
) < 0)
361 if (st
.st_size
< (off_t
) sizeof(FSSHeader
))
364 header
= mmap(NULL
, PAGE_ALIGN(sizeof(FSSHeader
)), PROT_READ
, MAP_SHARED
, fd
, 0);
365 if (header
== MAP_FAILED
)
368 if (memcmp(header
->signature
, FSS_HEADER_SIGNATURE
, 8) != 0)
371 if (header
->incompatible_flags
!= 0)
372 return -EPROTONOSUPPORT
;
374 if (le64toh(header
->header_size
) < sizeof(FSSHeader
))
377 if (le64toh(header
->fsprg_state_size
) != FSPRG_stateinbytes(le16toh(header
->fsprg_secpar
)))
380 f
->fss_file_size
= le64toh(header
->header_size
) + le64toh(header
->fsprg_state_size
);
381 if ((uint64_t) st
.st_size
< f
->fss_file_size
)
384 if (!sd_id128_equal(machine
, header
->machine_id
))
387 if (le64toh(header
->start_usec
) <= 0 || le64toh(header
->interval_usec
) <= 0)
390 size_t sz
= PAGE_ALIGN(f
->fss_file_size
);
391 assert(sz
< SIZE_MAX
);
392 f
->fss_file
= mmap(NULL
, sz
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
393 if (f
->fss_file
== MAP_FAILED
) {
398 f
->fss_start_usec
= le64toh(f
->fss_file
->start_usec
);
399 f
->fss_interval_usec
= le64toh(f
->fss_file
->interval_usec
);
401 f
->fsprg_state
= (uint8_t*) f
->fss_file
+ le64toh(f
->fss_file
->header_size
);
402 f
->fsprg_state_size
= le64toh(f
->fss_file
->fsprg_state_size
);
407 int journal_file_hmac_setup(JournalFile
*f
) {
410 if (!JOURNAL_HEADER_SEALED(f
->header
))
413 initialize_libgcrypt(true);
415 e
= gcry_md_open(&f
->hmac
, GCRY_MD_SHA256
, GCRY_MD_FLAG_HMAC
);
422 int journal_file_append_first_tag(JournalFile
*f
) {
426 if (!JOURNAL_HEADER_SEALED(f
->header
))
429 log_debug("Calculating first tag...");
431 r
= journal_file_hmac_put_header(f
);
435 p
= le64toh(f
->header
->field_hash_table_offset
);
436 if (p
< offsetof(Object
, hash_table
.items
))
438 p
-= offsetof(Object
, hash_table
.items
);
440 r
= journal_file_hmac_put_object(f
, OBJECT_FIELD_HASH_TABLE
, NULL
, p
);
444 p
= le64toh(f
->header
->data_hash_table_offset
);
445 if (p
< offsetof(Object
, hash_table
.items
))
447 p
-= offsetof(Object
, hash_table
.items
);
449 r
= journal_file_hmac_put_object(f
, OBJECT_DATA_HASH_TABLE
, NULL
, p
);
453 r
= journal_file_append_tag(f
);
460 int journal_file_parse_verification_key(JournalFile
*f
, const char *key
) {
461 _cleanup_free_
uint8_t *seed
= NULL
;
464 unsigned long long start
, interval
;
470 seed_size
= FSPRG_RECOMMENDED_SEEDLEN
;
471 seed
= malloc(seed_size
);
476 for (size_t c
= 0; c
< seed_size
; c
++) {
479 k
= skip_leading_chars(k
, "-");
491 seed
[c
] = (uint8_t) (x
* 16 + y
);
498 r
= sscanf(k
, "%llx-%llx", &start
, &interval
);
502 f
->fsprg_seed
= TAKE_PTR(seed
);
503 f
->fsprg_seed_size
= seed_size
;
505 f
->fss_start_usec
= start
* interval
;
506 f
->fss_interval_usec
= interval
;
511 bool journal_file_next_evolve_usec(JournalFile
*f
, usec_t
*u
) {
517 if (!JOURNAL_HEADER_SEALED(f
->header
))
520 epoch
= FSPRG_GetEpoch(f
->fsprg_state
);
522 *u
= (usec_t
) (f
->fss_start_usec
+ f
->fss_interval_usec
* epoch
+ f
->fss_interval_usec
);