chiark / gitweb /
rules: simplify mmc RPMB handling
[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 %"PRIu64" for epoch %"PRIu64"",
64                   le64toh(o->tag.seqnum),
65                   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, o, 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 %"PRIu64" to %"PRIu64".", epoch, 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 %"PRIu64".", 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         if (realtime <= 0)
215                 realtime = now(CLOCK_REALTIME);
216
217         r = journal_file_fsprg_need_evolve(f, realtime);
218         if (r <= 0)
219                 return 0;
220
221         r = journal_file_append_tag(f);
222         if (r < 0)
223                 return r;
224
225         r = journal_file_fsprg_evolve(f, realtime);
226         if (r < 0)
227                 return r;
228
229         return 0;
230 }
231
232 int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) {
233         int r;
234
235         assert(f);
236
237         if (!f->seal)
238                 return 0;
239
240         r = journal_file_hmac_start(f);
241         if (r < 0)
242                 return r;
243
244         if (!o) {
245                 r = journal_file_move_to_object(f, type, p, &o);
246                 if (r < 0)
247                         return r;
248         } else {
249                 if (type > OBJECT_UNUSED && o->object.type != type)
250                         return -EBADMSG;
251         }
252
253         gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
254
255         switch (o->object.type) {
256
257         case OBJECT_DATA:
258                 /* All but hash and payload are mutable */
259                 gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
260                 gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload));
261                 break;
262
263         case OBJECT_FIELD:
264                 /* Same here */
265                 gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash));
266                 gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(FieldObject, payload));
267                 break;
268
269         case OBJECT_ENTRY:
270                 /* All */
271                 gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(EntryObject, seqnum));
272                 break;
273
274         case OBJECT_FIELD_HASH_TABLE:
275         case OBJECT_DATA_HASH_TABLE:
276         case OBJECT_ENTRY_ARRAY:
277                 /* Nothing: everything is mutable */
278                 break;
279
280         case OBJECT_TAG:
281                 /* All but the tag itself */
282                 gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum));
283                 gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
284                 break;
285         default:
286                 return -EINVAL;
287         }
288
289         return 0;
290 }
291
292 int journal_file_hmac_put_header(JournalFile *f) {
293         int r;
294
295         assert(f);
296
297         if (!f->seal)
298                 return 0;
299
300         r = journal_file_hmac_start(f);
301         if (r < 0)
302                 return r;
303
304         /* All but state+reserved, boot_id, arena_size,
305          * tail_object_offset, n_objects, n_entries,
306          * tail_entry_seqnum, head_entry_seqnum, entry_array_offset,
307          * head_entry_realtime, tail_entry_realtime,
308          * tail_entry_monotonic, n_data, n_fields, n_tags,
309          * n_entry_arrays. */
310
311         gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature));
312         gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, boot_id) - offsetof(Header, file_id));
313         gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id));
314         gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
315
316         return 0;
317 }
318
319 int journal_file_fss_load(JournalFile *f) {
320         int r, fd = -1;
321         char *p = NULL;
322         struct stat st;
323         FSSHeader *m = NULL;
324         sd_id128_t machine;
325
326         assert(f);
327
328         if (!f->seal)
329                 return 0;
330
331         r = sd_id128_get_machine(&machine);
332         if (r < 0)
333                 return r;
334
335         if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
336                      SD_ID128_FORMAT_VAL(machine)) < 0)
337                 return -ENOMEM;
338
339         fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
340         if (fd < 0) {
341                 if (errno != ENOENT)
342                         log_error_errno(errno, "Failed to open %s: %m", p);
343
344                 r = -errno;
345                 goto finish;
346         }
347
348         if (fstat(fd, &st) < 0) {
349                 r = -errno;
350                 goto finish;
351         }
352
353         if (st.st_size < (off_t) sizeof(FSSHeader)) {
354                 r = -ENODATA;
355                 goto finish;
356         }
357
358         m = mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0);
359         if (m == MAP_FAILED) {
360                 m = NULL;
361                 r = -errno;
362                 goto finish;
363         }
364
365         if (memcmp(m->signature, FSS_HEADER_SIGNATURE, 8) != 0) {
366                 r = -EBADMSG;
367                 goto finish;
368         }
369
370         if (m->incompatible_flags != 0) {
371                 r = -EPROTONOSUPPORT;
372                 goto finish;
373         }
374
375         if (le64toh(m->header_size) < sizeof(FSSHeader)) {
376                 r = -EBADMSG;
377                 goto finish;
378         }
379
380         if (le64toh(m->fsprg_state_size) != FSPRG_stateinbytes(le16toh(m->fsprg_secpar))) {
381                 r = -EBADMSG;
382                 goto finish;
383         }
384
385         f->fss_file_size = le64toh(m->header_size) + le64toh(m->fsprg_state_size);
386         if ((uint64_t) st.st_size < f->fss_file_size) {
387                 r = -ENODATA;
388                 goto finish;
389         }
390
391         if (!sd_id128_equal(machine, m->machine_id)) {
392                 r = -EHOSTDOWN;
393                 goto finish;
394         }
395
396         if (le64toh(m->start_usec) <= 0 ||
397             le64toh(m->interval_usec) <= 0) {
398                 r = -EBADMSG;
399                 goto finish;
400         }
401
402         f->fss_file = mmap(NULL, PAGE_ALIGN(f->fss_file_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
403         if (f->fss_file == MAP_FAILED) {
404                 f->fss_file = NULL;
405                 r = -errno;
406                 goto finish;
407         }
408
409         f->fss_start_usec = le64toh(f->fss_file->start_usec);
410         f->fss_interval_usec = le64toh(f->fss_file->interval_usec);
411
412         f->fsprg_state = (uint8_t*) f->fss_file + le64toh(f->fss_file->header_size);
413         f->fsprg_state_size = le64toh(f->fss_file->fsprg_state_size);
414
415         r = 0;
416
417 finish:
418         if (m)
419                 munmap(m, PAGE_ALIGN(sizeof(FSSHeader)));
420
421         safe_close(fd);
422         free(p);
423
424         return r;
425 }
426
427 static void initialize_libgcrypt(void) {
428         const char *p;
429
430         if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
431                 return;
432
433         p = gcry_check_version("1.4.5");
434         assert(p);
435
436         gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
437 }
438
439 int journal_file_hmac_setup(JournalFile *f) {
440         gcry_error_t e;
441
442         if (!f->seal)
443                 return 0;
444
445         initialize_libgcrypt();
446
447         e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
448         if (e != 0)
449                 return -ENOTSUP;
450
451         return 0;
452 }
453
454 int journal_file_append_first_tag(JournalFile *f) {
455         int r;
456         uint64_t p;
457
458         if (!f->seal)
459                 return 0;
460
461         log_debug("Calculating first tag...");
462
463         r = journal_file_hmac_put_header(f);
464         if (r < 0)
465                 return r;
466
467         p = le64toh(f->header->field_hash_table_offset);
468         if (p < offsetof(Object, hash_table.items))
469                 return -EINVAL;
470         p -= offsetof(Object, hash_table.items);
471
472         r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, NULL, p);
473         if (r < 0)
474                 return r;
475
476         p = le64toh(f->header->data_hash_table_offset);
477         if (p < offsetof(Object, hash_table.items))
478                 return -EINVAL;
479         p -= offsetof(Object, hash_table.items);
480
481         r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, NULL, p);
482         if (r < 0)
483                 return r;
484
485         r = journal_file_append_tag(f);
486         if (r < 0)
487                 return r;
488
489         return 0;
490 }
491
492 int journal_file_parse_verification_key(JournalFile *f, const char *key) {
493         uint8_t *seed;
494         size_t seed_size, c;
495         const char *k;
496         int r;
497         unsigned long long start, interval;
498
499         seed_size = FSPRG_RECOMMENDED_SEEDLEN;
500         seed = malloc(seed_size);
501         if (!seed)
502                 return -ENOMEM;
503
504         k = key;
505         for (c = 0; c < seed_size; c++) {
506                 int x, y;
507
508                 while (*k == '-')
509                         k++;
510
511                 x = unhexchar(*k);
512                 if (x < 0) {
513                         free(seed);
514                         return -EINVAL;
515                 }
516                 k++;
517                 y = unhexchar(*k);
518                 if (y < 0) {
519                         free(seed);
520                         return -EINVAL;
521                 }
522                 k++;
523
524                 seed[c] = (uint8_t) (x * 16 + y);
525         }
526
527         if (*k != '/') {
528                 free(seed);
529                 return -EINVAL;
530         }
531         k++;
532
533         r = sscanf(k, "%llx-%llx", &start, &interval);
534         if (r != 2) {
535                 free(seed);
536                 return -EINVAL;
537         }
538
539         f->fsprg_seed = seed;
540         f->fsprg_seed_size = seed_size;
541
542         f->fss_start_usec = start * interval;
543         f->fss_interval_usec = interval;
544
545         return 0;
546 }
547
548 bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u) {
549         uint64_t epoch;
550
551         assert(f);
552         assert(u);
553
554         if (!f->seal)
555                 return false;
556
557         epoch = FSPRG_GetEpoch(f->fsprg_state);
558
559         *u = (usec_t) (f->fss_start_usec + f->fss_interval_usec * epoch + f->fss_interval_usec);
560
561         return true;
562 }