chiark / gitweb /
ddcf856aec1131bcb3cff4fc26d3f6e04236d860
[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->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 JOURNAL_HEADER_SEALED(f->header);
468 }