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