LCOV - code coverage report
Current view: top level - journal - journal-authenticate.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 39 302 12.9 %
Date: 2019-08-22 15:41:25 Functions: 6 15 40.0 %

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

Generated by: LCOV version 1.14