]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journal-authenticate.c
journal: parse fsprg seed
[thirdparty/systemd.git] / src / journal / journal-authenticate.c
CommitLineData
0284adc6
LP
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
0284adc6
LP
30static 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
41int journal_file_append_tag(JournalFile *f) {
42 Object *o;
43 uint64_t p;
44 int r;
45
46 assert(f);
47
48 if (!f->authenticate)
49 return 0;
50
51 if (!f->hmac_running)
52 return 0;
53
b7c9ae91 54 log_debug("Writing tag for epoch %llu\n", (unsigned long long) FSPRG_GetEpoch(f->fsprg_state));
0284adc6
LP
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
64 /* Add the tag object itself, so that we can protect its
65 * header. This will exclude the actual hash value in it */
66 r = journal_file_hmac_put_object(f, OBJECT_TAG, p);
67 if (r < 0)
68 return r;
69
70 /* Get the HMAC tag and store it in the object */
71 memcpy(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH);
72 f->hmac_running = false;
73
74 return 0;
75}
76
77static int journal_file_hmac_start(JournalFile *f) {
78 uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
79
80 assert(f);
81
82 if (!f->authenticate)
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);
b7c9ae91 90 FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0);
0284adc6
LP
91 gcry_md_setkey(f->hmac, key, sizeof(key));
92
93 f->hmac_running = true;
94
95 return 0;
96}
97
98static 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->authenticate);
104
b7c9ae91
LP
105 if (f->fsprg_start_usec == 0 ||
106 f->fsprg_interval_usec == 0)
0284adc6
LP
107 return -ENOTSUP;
108
b7c9ae91 109 if (realtime < f->fsprg_start_usec)
0284adc6
LP
110 return -ESTALE;
111
b7c9ae91
LP
112 t = realtime - f->fsprg_start_usec;
113 t = t / f->fsprg_interval_usec;
0284adc6
LP
114
115 *epoch = t;
116 return 0;
117}
118
119static int journal_file_need_evolve(JournalFile *f, uint64_t realtime) {
120 uint64_t goal, epoch;
121 int r;
122 assert(f);
123
124 if (!f->authenticate)
125 return 0;
126
127 r = journal_file_get_epoch(f, realtime, &goal);
128 if (r < 0)
129 return r;
130
b7c9ae91 131 epoch = FSPRG_GetEpoch(f->fsprg_state);
0284adc6
LP
132 if (epoch > goal)
133 return -ESTALE;
134
135 return epoch != goal;
136}
137
138static int journal_file_evolve(JournalFile *f, uint64_t realtime) {
139 uint64_t goal, epoch;
140 int r;
141
142 assert(f);
143
144 if (!f->authenticate)
145 return 0;
146
147 r = journal_file_get_epoch(f, realtime, &goal);
148 if (r < 0)
149 return r;
150
b7c9ae91 151 epoch = FSPRG_GetEpoch(f->fsprg_state);
0284adc6
LP
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
b7c9ae91
LP
161 FSPRG_Evolve(f->fsprg_state);
162 epoch = FSPRG_GetEpoch(f->fsprg_state);
0284adc6
LP
163 }
164}
165
166int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
167 int r;
168
169 assert(f);
170
171 if (!f->authenticate)
172 return 0;
173
174 r = journal_file_need_evolve(f, realtime);
175 if (r <= 0)
176 return 0;
177
178 r = journal_file_append_tag(f);
179 if (r < 0)
180 return r;
181
182 r = journal_file_evolve(f, realtime);
183 if (r < 0)
184 return r;
185
186 r = journal_file_hmac_start(f);
187 if (r < 0)
188 return r;
189
190 return 0;
191}
192
193int journal_file_hmac_put_object(JournalFile *f, int type, uint64_t p) {
194 int r;
195 Object *o;
196
197 assert(f);
198
199 if (!f->authenticate)
200 return 0;
201
202 r = journal_file_hmac_start(f);
203 if (r < 0)
204 return r;
205
206 r = journal_file_move_to_object(f, type, p, &o);
207 if (r < 0)
208 return r;
209
210 gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
211
212 switch (o->object.type) {
213
214 case OBJECT_DATA:
215 /* All but: hash and payload are mutable */
216 gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
217 gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload));
218 break;
219
220 case OBJECT_ENTRY:
221 /* All */
222 gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(EntryObject, seqnum));
223 break;
224
225 case OBJECT_FIELD_HASH_TABLE:
226 case OBJECT_DATA_HASH_TABLE:
227 case OBJECT_ENTRY_ARRAY:
228 /* Nothing: everything is mutable */
229 break;
230
231 case OBJECT_TAG:
232 /* All but the tag itself */
233 gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum));
234 break;
235 default:
236 return -EINVAL;
237 }
238
239 return 0;
240}
241
242int journal_file_hmac_put_header(JournalFile *f) {
243 int r;
244
245 assert(f);
246
247 if (!f->authenticate)
248 return 0;
249
250 r = journal_file_hmac_start(f);
251 if (r < 0)
252 return r;
253
254 /* All but state+reserved, boot_id, arena_size,
255 * tail_object_offset, n_objects, n_entries, tail_seqnum,
256 * head_entry_realtime, tail_entry_realtime,
257 * tail_entry_monotonic, n_data, n_fields, header_tag */
258
259 gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature));
260 gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, boot_id) - offsetof(Header, file_id));
261 gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id));
262 gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
263 gcry_md_write(f->hmac, &f->header->head_entry_seqnum, offsetof(Header, head_entry_realtime) - offsetof(Header, head_entry_seqnum));
264
265 return 0;
266}
267
268int journal_file_load_fsprg(JournalFile *f) {
269 int r, fd = -1;
270 char *p = NULL;
271 struct stat st;
272 FSPRGHeader *m = NULL;
273 sd_id128_t machine;
274
275 assert(f);
276
277 if (!f->authenticate)
278 return 0;
279
280 r = sd_id128_get_machine(&machine);
281 if (r < 0)
282 return r;
283
284 if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg",
285 SD_ID128_FORMAT_VAL(machine)) < 0)
286 return -ENOMEM;
287
288 fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
289 if (fd < 0) {
290 log_error("Failed to open %s: %m", p);
291 r = -errno;
292 goto finish;
293 }
294
295 if (fstat(fd, &st) < 0) {
296 r = -errno;
297 goto finish;
298 }
299
300 if (st.st_size < (off_t) sizeof(FSPRGHeader)) {
301 r = -ENODATA;
302 goto finish;
303 }
304
305 m = mmap(NULL, PAGE_ALIGN(sizeof(FSPRGHeader)), PROT_READ, MAP_SHARED, fd, 0);
306 if (m == MAP_FAILED) {
307 m = NULL;
308 r = -errno;
309 goto finish;
310 }
311
312 if (memcmp(m->signature, FSPRG_HEADER_SIGNATURE, 8) != 0) {
313 r = -EBADMSG;
314 goto finish;
315 }
316
317 if (m->incompatible_flags != 0) {
318 r = -EPROTONOSUPPORT;
319 goto finish;
320 }
321
322 if (le64toh(m->header_size) < sizeof(FSPRGHeader)) {
323 r = -EBADMSG;
324 goto finish;
325 }
326
327 if (le64toh(m->state_size) != FSPRG_stateinbytes(m->secpar)) {
328 r = -EBADMSG;
329 goto finish;
330 }
331
b7c9ae91
LP
332 f->fsprg_file_size = le64toh(m->header_size) + le64toh(m->state_size);
333 if ((uint64_t) st.st_size < f->fsprg_file_size) {
0284adc6
LP
334 r = -ENODATA;
335 goto finish;
336 }
337
338 if (!sd_id128_equal(machine, m->machine_id)) {
339 r = -EHOSTDOWN;
340 goto finish;
341 }
342
343 if (le64toh(m->fsprg_start_usec) <= 0 ||
344 le64toh(m->fsprg_interval_usec) <= 0) {
345 r = -EBADMSG;
346 goto finish;
347 }
348
b7c9ae91
LP
349 f->fsprg_file = mmap(NULL, PAGE_ALIGN(f->fsprg_file_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
350 if (f->fsprg_file == MAP_FAILED) {
351 f->fsprg_file = NULL;
0284adc6
LP
352 r = -errno;
353 goto finish;
354 }
355
b7c9ae91
LP
356 f->fsprg_start_usec = le64toh(f->fsprg_file->fsprg_start_usec);
357 f->fsprg_interval_usec = le64toh(f->fsprg_file->fsprg_interval_usec);
358
359 f->fsprg_state = (uint8_t*) f->fsprg_file + le64toh(f->fsprg_file->header_size);
360 f->fsprg_state_size = le64toh(f->fsprg_file->state_size);
361
0284adc6
LP
362 r = 0;
363
364finish:
365 if (m)
366 munmap(m, PAGE_ALIGN(sizeof(FSPRGHeader)));
367
368 if (fd >= 0)
369 close_nointr_nofail(fd);
370
371 free(p);
372 return r;
373}
374
375int journal_file_setup_hmac(JournalFile *f) {
376 gcry_error_t e;
377
378 if (!f->authenticate)
379 return 0;
380
381 e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
382 if (e != 0)
383 return -ENOTSUP;
384
385 return 0;
386}
387
388int journal_file_append_first_tag(JournalFile *f) {
389 int r;
390 uint64_t p;
391
392 if (!f->authenticate)
393 return 0;
394
395 log_debug("Calculating first tag...");
396
397 r = journal_file_hmac_put_header(f);
398 if (r < 0)
399 return r;
400
401 p = le64toh(f->header->field_hash_table_offset);
402 if (p < offsetof(Object, hash_table.items))
403 return -EINVAL;
404 p -= offsetof(Object, hash_table.items);
405
406 r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, p);
407 if (r < 0)
408 return r;
409
410 p = le64toh(f->header->data_hash_table_offset);
411 if (p < offsetof(Object, hash_table.items))
412 return -EINVAL;
413 p -= offsetof(Object, hash_table.items);
414
415 r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, p);
416 if (r < 0)
417 return r;
418
419 r = journal_file_append_tag(f);
420 if (r < 0)
421 return r;
422
423 return 0;
424}
4da416aa
LP
425
426bool journal_file_fsprg_enabled(JournalFile *f) {
427 assert(f);
428
429 return !!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED);
430}