]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journal-authenticate.c
journal: rework terminology
[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 log_debug("Writing tag for epoch %llu\n", (unsigned long long) FSPRG_GetEpoch(f->fsprg_state));
55
56 assert(f->hmac);
57
58 r = journal_file_append_object(f, OBJECT_TAG, sizeof(struct TagObject), &o, &p);
59 if (r < 0)
60 return r;
61
62 o->tag.seqnum = htole64(journal_file_tag_seqnum(f));
63 o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state));
64
65 /* Add the tag object itself, so that we can protect its
66 * header. This will exclude the actual hash value in it */
67 r = journal_file_hmac_put_object(f, OBJECT_TAG, p);
68 if (r < 0)
69 return r;
70
71 /* Get the HMAC tag and store it in the object */
72 memcpy(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH);
73 f->hmac_running = false;
74
75 return 0;
76 }
77
78 int journal_file_hmac_start(JournalFile *f) {
79 uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
80 assert(f);
81
82 if (!f->seal)
83 return 0;
84
85 if (f->hmac_running)
86 return 0;
87
88 /* Prepare HMAC for next cycle */
89 gcry_md_reset(f->hmac);
90 FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0);
91 gcry_md_setkey(f->hmac, key, sizeof(key));
92
93 f->hmac_running = true;
94
95 return 0;
96 }
97
98 static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) {
99 uint64_t t;
100
101 assert(f);
102 assert(epoch);
103 assert(f->seal);
104
105 if (f->fss_start_usec == 0 ||
106 f->fss_interval_usec == 0)
107 return -ENOTSUP;
108
109 if (realtime < f->fss_start_usec)
110 return -ESTALE;
111
112 t = realtime - f->fss_start_usec;
113 t = t / f->fss_interval_usec;
114
115 *epoch = t;
116 return 0;
117 }
118
119 static int journal_file_fsprg_need_evolve(JournalFile *f, uint64_t realtime) {
120 uint64_t goal, epoch;
121 int r;
122 assert(f);
123
124 if (!f->seal)
125 return 0;
126
127 r = journal_file_get_epoch(f, realtime, &goal);
128 if (r < 0)
129 return r;
130
131 epoch = FSPRG_GetEpoch(f->fsprg_state);
132 if (epoch > goal)
133 return -ESTALE;
134
135 return epoch != goal;
136 }
137
138 int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) {
139 uint64_t goal, epoch;
140 int r;
141
142 assert(f);
143
144 if (!f->seal)
145 return 0;
146
147 r = journal_file_get_epoch(f, realtime, &goal);
148 if (r < 0)
149 return r;
150
151 epoch = FSPRG_GetEpoch(f->fsprg_state);
152 if (epoch < goal)
153 log_debug("Evolving FSPRG key from epoch %llu to %llu.", (unsigned long long) epoch, (unsigned long long) goal);
154
155 for (;;) {
156 if (epoch > goal)
157 return -ESTALE;
158 if (epoch == goal)
159 return 0;
160
161 FSPRG_Evolve(f->fsprg_state);
162 epoch = FSPRG_GetEpoch(f->fsprg_state);
163 }
164 }
165
166 int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
167 void *msk;
168 uint64_t epoch;
169
170 assert(f);
171
172 if (!f->seal)
173 return 0;
174
175 assert(f->fsprg_seed);
176
177 if (f->fsprg_state) {
178 /* Cheaper... */
179
180 epoch = FSPRG_GetEpoch(f->fsprg_state);
181 if (goal == epoch)
182 return 0;
183
184 if (goal == epoch+1) {
185 FSPRG_Evolve(f->fsprg_state);
186 return 0;
187 }
188 } else {
189 f->fsprg_state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
190 f->fsprg_state = malloc(f->fsprg_state_size);
191
192 if (!f->fsprg_state)
193 return -ENOMEM;
194 }
195
196 log_debug("Seeking FSPRG key to %llu.", (unsigned long long) goal);
197
198 msk = alloca(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR));
199 FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR);
200 FSPRG_Seek(f->fsprg_state, goal, msk, f->fsprg_seed, f->fsprg_seed_size);
201 return 0;
202 }
203
204 int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
205 int r;
206
207 assert(f);
208
209 if (!f->seal)
210 return 0;
211
212 r = journal_file_fsprg_need_evolve(f, realtime);
213 if (r <= 0)
214 return 0;
215
216 r = journal_file_append_tag(f);
217 if (r < 0)
218 return r;
219
220 r = journal_file_fsprg_evolve(f, realtime);
221 if (r < 0)
222 return r;
223
224 r = journal_file_hmac_start(f);
225 if (r < 0)
226 return r;
227
228 return 0;
229 }
230
231 int journal_file_hmac_put_object(JournalFile *f, int type, uint64_t p) {
232 int r;
233 Object *o;
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 r = journal_file_move_to_object(f, type, p, &o);
245 if (r < 0)
246 return r;
247
248 gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
249
250 switch (o->object.type) {
251
252 case OBJECT_DATA:
253 /* All but hash and payload are mutable */
254 gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
255 gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload));
256 break;
257
258 case OBJECT_ENTRY:
259 /* All */
260 gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(EntryObject, seqnum));
261 break;
262
263 case OBJECT_FIELD_HASH_TABLE:
264 case OBJECT_DATA_HASH_TABLE:
265 case OBJECT_ENTRY_ARRAY:
266 /* Nothing: everything is mutable */
267 break;
268
269 case OBJECT_TAG:
270 /* All but the tag itself */
271 gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum));
272 gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
273 break;
274 default:
275 return -EINVAL;
276 }
277
278 return 0;
279 }
280
281 int journal_file_hmac_put_header(JournalFile *f) {
282 int r;
283
284 assert(f);
285
286 if (!f->seal)
287 return 0;
288
289 r = journal_file_hmac_start(f);
290 if (r < 0)
291 return r;
292
293 /* All but state+reserved, boot_id, arena_size,
294 * tail_object_offset, n_objects, n_entries,
295 * tail_entry_seqnum, head_entry_seqnum, entry_array_offset,
296 * head_entry_realtime, tail_entry_realtime,
297 * tail_entry_monotonic, n_data, n_fields, n_tags,
298 * n_entry_arrays. */
299
300 gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature));
301 gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, boot_id) - offsetof(Header, file_id));
302 gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id));
303 gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
304
305 return 0;
306 }
307
308 int journal_file_fss_load(JournalFile *f) {
309 int r, fd = -1;
310 char *p = NULL;
311 struct stat st;
312 FSSHeader *m = NULL;
313 sd_id128_t machine;
314
315 assert(f);
316
317 if (!f->seal)
318 return 0;
319
320 r = sd_id128_get_machine(&machine);
321 if (r < 0)
322 return r;
323
324 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
325 SD_ID128_FORMAT_VAL(machine)) < 0)
326 return -ENOMEM;
327
328 fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
329 if (fd < 0) {
330 log_error("Failed to open %s: %m", p);
331 r = -errno;
332 goto finish;
333 }
334
335 if (fstat(fd, &st) < 0) {
336 r = -errno;
337 goto finish;
338 }
339
340 if (st.st_size < (off_t) sizeof(FSSHeader)) {
341 r = -ENODATA;
342 goto finish;
343 }
344
345 m = mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0);
346 if (m == MAP_FAILED) {
347 m = NULL;
348 r = -errno;
349 goto finish;
350 }
351
352 if (memcmp(m->signature, FSS_HEADER_SIGNATURE, 8) != 0) {
353 r = -EBADMSG;
354 goto finish;
355 }
356
357 if (m->incompatible_flags != 0) {
358 r = -EPROTONOSUPPORT;
359 goto finish;
360 }
361
362 if (le64toh(m->header_size) < sizeof(FSSHeader)) {
363 r = -EBADMSG;
364 goto finish;
365 }
366
367 if (le64toh(m->fsprg_state_size) != FSPRG_stateinbytes(m->fsprg_secpar)) {
368 r = -EBADMSG;
369 goto finish;
370 }
371
372 f->fss_file_size = le64toh(m->header_size) + le64toh(m->fsprg_state_size);
373 if ((uint64_t) st.st_size < f->fss_file_size) {
374 r = -ENODATA;
375 goto finish;
376 }
377
378 if (!sd_id128_equal(machine, m->machine_id)) {
379 r = -EHOSTDOWN;
380 goto finish;
381 }
382
383 if (le64toh(m->start_usec) <= 0 ||
384 le64toh(m->interval_usec) <= 0) {
385 r = -EBADMSG;
386 goto finish;
387 }
388
389 f->fss_file = mmap(NULL, PAGE_ALIGN(f->fss_file_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
390 if (f->fss_file == MAP_FAILED) {
391 f->fss_file = NULL;
392 r = -errno;
393 goto finish;
394 }
395
396 f->fss_start_usec = le64toh(f->fss_file->start_usec);
397 f->fss_interval_usec = le64toh(f->fss_file->interval_usec);
398
399 f->fsprg_state = (uint8_t*) f->fss_file + le64toh(f->fss_file->header_size);
400 f->fsprg_state_size = le64toh(f->fss_file->fsprg_state_size);
401
402 r = 0;
403
404 finish:
405 if (m)
406 munmap(m, PAGE_ALIGN(sizeof(FSSHeader)));
407
408 if (fd >= 0)
409 close_nointr_nofail(fd);
410
411 free(p);
412 return r;
413 }
414
415 int journal_file_hmac_setup(JournalFile *f) {
416 gcry_error_t e;
417
418 if (!f->seal)
419 return 0;
420
421 e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
422 if (e != 0)
423 return -ENOTSUP;
424
425 return 0;
426 }
427
428 int journal_file_append_first_tag(JournalFile *f) {
429 int r;
430 uint64_t p;
431
432 if (!f->seal)
433 return 0;
434
435 log_debug("Calculating first tag...");
436
437 r = journal_file_hmac_put_header(f);
438 if (r < 0)
439 return r;
440
441 p = le64toh(f->header->field_hash_table_offset);
442 if (p < offsetof(Object, hash_table.items))
443 return -EINVAL;
444 p -= offsetof(Object, hash_table.items);
445
446 r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, p);
447 if (r < 0)
448 return r;
449
450 p = le64toh(f->header->data_hash_table_offset);
451 if (p < offsetof(Object, hash_table.items))
452 return -EINVAL;
453 p -= offsetof(Object, hash_table.items);
454
455 r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, p);
456 if (r < 0)
457 return r;
458
459 r = journal_file_append_tag(f);
460 if (r < 0)
461 return r;
462
463 return 0;
464 }
465
466 bool journal_file_fss_enabled(JournalFile *f) {
467 assert(f);
468
469 return !!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED);
470 }