chiark / gitweb /
journal: parse fsprg seed
[elogind.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->authenticate)
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
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
77 static 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);
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->authenticate);
104
105         if (f->fsprg_start_usec == 0 ||
106             f->fsprg_interval_usec == 0)
107                 return -ENOTSUP;
108
109         if (realtime < f->fsprg_start_usec)
110                 return -ESTALE;
111
112         t = realtime - f->fsprg_start_usec;
113         t = t / f->fsprg_interval_usec;
114
115         *epoch = t;
116         return 0;
117 }
118
119 static 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
131         epoch = FSPRG_GetEpoch(f->fsprg_state);
132         if (epoch > goal)
133                 return -ESTALE;
134
135         return epoch != goal;
136 }
137
138 static 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
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_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
193 int 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
242 int 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
268 int 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
332         f->fsprg_file_size = le64toh(m->header_size) + le64toh(m->state_size);
333         if ((uint64_t) st.st_size < f->fsprg_file_size) {
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
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;
352                 r = -errno;
353                 goto finish;
354         }
355
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
362         r = 0;
363
364 finish:
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
375 int 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
388 int 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 }
425
426 bool journal_file_fsprg_enabled(JournalFile *f) {
427         assert(f);
428
429         return !!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED);
430 }