]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journal-authenticate.c
journal: count number of entry arrays in header
[thirdparty/systemd.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 }