Line data Source code
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 0 : static uint64_t journal_file_tag_seqnum(JournalFile *f) {
31 : uint64_t r;
32 :
33 0 : assert(f);
34 :
35 0 : r = le64toh(f->header->n_tags) + 1;
36 0 : f->header->n_tags = htole64(r);
37 :
38 0 : return r;
39 : }
40 :
41 1 : int journal_file_append_tag(JournalFile *f) {
42 : Object *o;
43 : uint64_t p;
44 : int r;
45 :
46 1 : assert(f);
47 :
48 1 : if (!f->seal)
49 1 : return 0;
50 :
51 0 : if (!f->hmac_running)
52 0 : return 0;
53 :
54 0 : assert(f->hmac);
55 :
56 0 : r = journal_file_append_object(f, OBJECT_TAG, sizeof(struct TagObject), &o, &p);
57 0 : if (r < 0)
58 0 : return r;
59 :
60 0 : o->tag.seqnum = htole64(journal_file_tag_seqnum(f));
61 0 : o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state));
62 :
63 0 : 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 0 : r = journal_file_hmac_put_object(f, OBJECT_TAG, o, p);
70 0 : if (r < 0)
71 0 : return r;
72 :
73 : /* Get the HMAC tag and store it in the object */
74 0 : memcpy(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH);
75 0 : f->hmac_running = false;
76 :
77 0 : return 0;
78 : }
79 :
80 0 : int journal_file_hmac_start(JournalFile *f) {
81 : uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
82 0 : assert(f);
83 :
84 0 : if (!f->seal)
85 0 : return 0;
86 :
87 0 : if (f->hmac_running)
88 0 : return 0;
89 :
90 : /* Prepare HMAC for next cycle */
91 0 : gcry_md_reset(f->hmac);
92 0 : FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0);
93 0 : gcry_md_setkey(f->hmac, key, sizeof(key));
94 :
95 0 : f->hmac_running = true;
96 :
97 0 : return 0;
98 : }
99 :
100 0 : static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) {
101 : uint64_t t;
102 :
103 0 : assert(f);
104 0 : assert(epoch);
105 0 : assert(f->seal);
106 :
107 0 : if (f->fss_start_usec == 0 ||
108 0 : f->fss_interval_usec == 0)
109 0 : return -EOPNOTSUPP;
110 :
111 0 : if (realtime < f->fss_start_usec)
112 0 : return -ESTALE;
113 :
114 0 : t = realtime - f->fss_start_usec;
115 0 : t = t / f->fss_interval_usec;
116 :
117 0 : *epoch = t;
118 0 : return 0;
119 : }
120 :
121 0 : static int journal_file_fsprg_need_evolve(JournalFile *f, uint64_t realtime) {
122 : uint64_t goal, epoch;
123 : int r;
124 0 : assert(f);
125 :
126 0 : if (!f->seal)
127 0 : return 0;
128 :
129 0 : r = journal_file_get_epoch(f, realtime, &goal);
130 0 : if (r < 0)
131 0 : return r;
132 :
133 0 : epoch = FSPRG_GetEpoch(f->fsprg_state);
134 0 : if (epoch > goal)
135 0 : return -ESTALE;
136 :
137 0 : return epoch != goal;
138 : }
139 :
140 0 : int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) {
141 : uint64_t goal, epoch;
142 : int r;
143 :
144 0 : assert(f);
145 :
146 0 : if (!f->seal)
147 0 : return 0;
148 :
149 0 : r = journal_file_get_epoch(f, realtime, &goal);
150 0 : if (r < 0)
151 0 : return r;
152 :
153 0 : epoch = FSPRG_GetEpoch(f->fsprg_state);
154 0 : if (epoch < goal)
155 0 : log_debug("Evolving FSPRG key from epoch %"PRIu64" to %"PRIu64".", epoch, goal);
156 :
157 : for (;;) {
158 0 : if (epoch > goal)
159 0 : return -ESTALE;
160 0 : if (epoch == goal)
161 0 : return 0;
162 :
163 0 : FSPRG_Evolve(f->fsprg_state);
164 0 : epoch = FSPRG_GetEpoch(f->fsprg_state);
165 0 : }
166 : }
167 :
168 0 : int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
169 : void *msk;
170 : uint64_t epoch;
171 :
172 0 : assert(f);
173 :
174 0 : if (!f->seal)
175 0 : return 0;
176 :
177 0 : assert(f->fsprg_seed);
178 :
179 0 : if (f->fsprg_state) {
180 : /* Cheaper... */
181 :
182 0 : epoch = FSPRG_GetEpoch(f->fsprg_state);
183 0 : if (goal == epoch)
184 0 : return 0;
185 :
186 0 : if (goal == epoch+1) {
187 0 : FSPRG_Evolve(f->fsprg_state);
188 0 : return 0;
189 : }
190 : } else {
191 0 : f->fsprg_state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
192 0 : f->fsprg_state = malloc(f->fsprg_state_size);
193 :
194 0 : if (!f->fsprg_state)
195 0 : return -ENOMEM;
196 : }
197 :
198 0 : log_debug("Seeking FSPRG key to %"PRIu64".", goal);
199 :
200 0 : msk = alloca(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR));
201 0 : FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR);
202 0 : FSPRG_Seek(f->fsprg_state, goal, msk, f->fsprg_seed, f->fsprg_seed_size);
203 0 : return 0;
204 : }
205 :
206 6278 : int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
207 : int r;
208 :
209 6278 : assert(f);
210 :
211 6278 : if (!f->seal)
212 6278 : return 0;
213 :
214 0 : if (realtime <= 0)
215 0 : realtime = now(CLOCK_REALTIME);
216 :
217 0 : r = journal_file_fsprg_need_evolve(f, realtime);
218 0 : if (r <= 0)
219 0 : return 0;
220 :
221 0 : r = journal_file_append_tag(f);
222 0 : if (r < 0)
223 0 : return r;
224 :
225 0 : r = journal_file_fsprg_evolve(f, realtime);
226 0 : if (r < 0)
227 0 : return r;
228 :
229 0 : return 0;
230 : }
231 :
232 32611 : int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) {
233 : int r;
234 :
235 32611 : assert(f);
236 :
237 32611 : if (!f->seal)
238 32611 : return 0;
239 :
240 0 : r = journal_file_hmac_start(f);
241 0 : if (r < 0)
242 0 : return r;
243 :
244 0 : if (!o) {
245 0 : r = journal_file_move_to_object(f, type, p, &o);
246 0 : if (r < 0)
247 0 : return r;
248 : } else {
249 0 : if (type > OBJECT_UNUSED && o->object.type != type)
250 0 : return -EBADMSG;
251 : }
252 :
253 0 : gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
254 :
255 0 : switch (o->object.type) {
256 :
257 : case OBJECT_DATA:
258 : /* All but hash and payload are mutable */
259 0 : gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
260 0 : gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload));
261 0 : break;
262 :
263 : case OBJECT_FIELD:
264 : /* Same here */
265 0 : gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash));
266 0 : gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(FieldObject, payload));
267 0 : break;
268 :
269 : case OBJECT_ENTRY:
270 : /* All */
271 0 : gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(EntryObject, seqnum));
272 0 : 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 0 : break;
279 :
280 : case OBJECT_TAG:
281 : /* All but the tag itself */
282 0 : gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum));
283 0 : gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
284 0 : break;
285 : default:
286 0 : return -EINVAL;
287 : }
288 :
289 0 : return 0;
290 : }
291 :
292 0 : int journal_file_hmac_put_header(JournalFile *f) {
293 : int r;
294 :
295 0 : assert(f);
296 :
297 0 : if (!f->seal)
298 0 : return 0;
299 :
300 0 : r = journal_file_hmac_start(f);
301 0 : if (r < 0)
302 0 : 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 0 : gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature));
312 0 : gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, boot_id) - offsetof(Header, file_id));
313 0 : gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id));
314 0 : gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
315 :
316 0 : return 0;
317 : }
318 :
319 6 : int journal_file_fss_load(JournalFile *f) {
320 6 : int r, fd = -1;
321 6 : char *p = NULL;
322 : struct stat st;
323 6 : FSSHeader *m = NULL;
324 : sd_id128_t machine;
325 :
326 6 : assert(f);
327 :
328 6 : if (!f->seal)
329 1 : return 0;
330 :
331 5 : r = sd_id128_get_machine(&machine);
332 5 : if (r < 0)
333 0 : return r;
334 :
335 80 : if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
336 80 : SD_ID128_FORMAT_VAL(machine)) < 0)
337 0 : return -ENOMEM;
338 :
339 5 : fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
340 5 : if (fd < 0) {
341 5 : if (errno != ENOENT)
342 0 : log_error_errno(errno, "Failed to open %s: %m", p);
343 :
344 5 : r = -errno;
345 5 : goto finish;
346 : }
347 :
348 0 : if (fstat(fd, &st) < 0) {
349 0 : r = -errno;
350 0 : goto finish;
351 : }
352 :
353 0 : if (st.st_size < (off_t) sizeof(FSSHeader)) {
354 0 : r = -ENODATA;
355 0 : goto finish;
356 : }
357 :
358 0 : m = mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0);
359 0 : if (m == MAP_FAILED) {
360 0 : m = NULL;
361 0 : r = -errno;
362 0 : goto finish;
363 : }
364 :
365 0 : if (memcmp(m->signature, FSS_HEADER_SIGNATURE, 8) != 0) {
366 0 : r = -EBADMSG;
367 0 : goto finish;
368 : }
369 :
370 0 : if (m->incompatible_flags != 0) {
371 0 : r = -EPROTONOSUPPORT;
372 0 : goto finish;
373 : }
374 :
375 0 : if (le64toh(m->header_size) < sizeof(FSSHeader)) {
376 0 : r = -EBADMSG;
377 0 : goto finish;
378 : }
379 :
380 0 : if (le64toh(m->fsprg_state_size) != FSPRG_stateinbytes(le16toh(m->fsprg_secpar))) {
381 0 : r = -EBADMSG;
382 0 : goto finish;
383 : }
384 :
385 0 : f->fss_file_size = le64toh(m->header_size) + le64toh(m->fsprg_state_size);
386 0 : if ((uint64_t) st.st_size < f->fss_file_size) {
387 0 : r = -ENODATA;
388 0 : goto finish;
389 : }
390 :
391 0 : if (!sd_id128_equal(machine, m->machine_id)) {
392 0 : r = -EHOSTDOWN;
393 0 : goto finish;
394 : }
395 :
396 0 : if (le64toh(m->start_usec) <= 0 ||
397 0 : le64toh(m->interval_usec) <= 0) {
398 0 : r = -EBADMSG;
399 0 : goto finish;
400 : }
401 :
402 0 : f->fss_file = mmap(NULL, PAGE_ALIGN(f->fss_file_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
403 0 : if (f->fss_file == MAP_FAILED) {
404 0 : f->fss_file = NULL;
405 0 : r = -errno;
406 0 : goto finish;
407 : }
408 :
409 0 : f->fss_start_usec = le64toh(f->fss_file->start_usec);
410 0 : f->fss_interval_usec = le64toh(f->fss_file->interval_usec);
411 :
412 0 : f->fsprg_state = (uint8_t*) f->fss_file + le64toh(f->fss_file->header_size);
413 0 : f->fsprg_state_size = le64toh(f->fss_file->fsprg_state_size);
414 :
415 0 : r = 0;
416 :
417 : finish:
418 5 : if (m)
419 0 : munmap(m, PAGE_ALIGN(sizeof(FSSHeader)));
420 :
421 5 : safe_close(fd);
422 5 : free(p);
423 :
424 5 : return r;
425 : }
426 :
427 0 : static void initialize_libgcrypt(void) {
428 : const char *p;
429 :
430 0 : if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
431 0 : return;
432 :
433 0 : p = gcry_check_version("1.4.5");
434 0 : assert(p);
435 :
436 0 : gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
437 : }
438 :
439 549 : int journal_file_hmac_setup(JournalFile *f) {
440 : gcry_error_t e;
441 :
442 549 : if (!f->seal)
443 549 : return 0;
444 :
445 0 : initialize_libgcrypt();
446 :
447 0 : e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
448 0 : if (e != 0)
449 0 : return -EOPNOTSUPP;
450 :
451 0 : return 0;
452 : }
453 :
454 18 : int journal_file_append_first_tag(JournalFile *f) {
455 : int r;
456 : uint64_t p;
457 :
458 18 : if (!f->seal)
459 18 : return 0;
460 :
461 0 : log_debug("Calculating first tag...");
462 :
463 0 : r = journal_file_hmac_put_header(f);
464 0 : if (r < 0)
465 0 : return r;
466 :
467 0 : p = le64toh(f->header->field_hash_table_offset);
468 0 : if (p < offsetof(Object, hash_table.items))
469 0 : return -EINVAL;
470 0 : p -= offsetof(Object, hash_table.items);
471 :
472 0 : r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, NULL, p);
473 0 : if (r < 0)
474 0 : return r;
475 :
476 0 : p = le64toh(f->header->data_hash_table_offset);
477 0 : if (p < offsetof(Object, hash_table.items))
478 0 : return -EINVAL;
479 0 : p -= offsetof(Object, hash_table.items);
480 :
481 0 : r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, NULL, p);
482 0 : if (r < 0)
483 0 : return r;
484 :
485 0 : r = journal_file_append_tag(f);
486 0 : if (r < 0)
487 0 : return r;
488 :
489 0 : return 0;
490 : }
491 :
492 0 : 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 0 : seed_size = FSPRG_RECOMMENDED_SEEDLEN;
500 0 : seed = malloc(seed_size);
501 0 : if (!seed)
502 0 : return -ENOMEM;
503 :
504 0 : k = key;
505 0 : for (c = 0; c < seed_size; c++) {
506 : int x, y;
507 :
508 0 : while (*k == '-')
509 0 : k++;
510 :
511 0 : x = unhexchar(*k);
512 0 : if (x < 0) {
513 0 : free(seed);
514 0 : return -EINVAL;
515 : }
516 0 : k++;
517 0 : y = unhexchar(*k);
518 0 : if (y < 0) {
519 0 : free(seed);
520 0 : return -EINVAL;
521 : }
522 0 : k++;
523 :
524 0 : seed[c] = (uint8_t) (x * 16 + y);
525 : }
526 :
527 0 : if (*k != '/') {
528 0 : free(seed);
529 0 : return -EINVAL;
530 : }
531 0 : k++;
532 :
533 0 : r = sscanf(k, "%llx-%llx", &start, &interval);
534 0 : if (r != 2) {
535 0 : free(seed);
536 0 : return -EINVAL;
537 : }
538 :
539 0 : f->fsprg_seed = seed;
540 0 : f->fsprg_seed_size = seed_size;
541 :
542 0 : f->fss_start_usec = start * interval;
543 0 : f->fss_interval_usec = interval;
544 :
545 0 : return 0;
546 : }
547 :
548 0 : bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u) {
549 : uint64_t epoch;
550 :
551 0 : assert(f);
552 0 : assert(u);
553 :
554 0 : if (!f->seal)
555 0 : return false;
556 :
557 0 : epoch = FSPRG_GetEpoch(f->fsprg_state);
558 :
559 0 : *u = (usec_t) (f->fss_start_usec + f->fss_interval_usec * epoch + f->fss_interval_usec);
560 :
561 0 : return true;
562 : }
|