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