]> git.ipfire.org Git - location/libloc.git/commitdiff
database: Add scaffolding for checking signatures
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 26 Nov 2019 22:29:05 +0000 (22:29 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 26 Nov 2019 22:29:05 +0000 (22:29 +0000)
The added function computes a SHA512 hash of the database
file where the signature is cleared.

The actual cryptographic check of the signature is
not implemented, yet.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/database.c
src/libloc.sym
src/loc/database.h
src/loc/format.h
src/python/database.c
src/python/location-query.in
src/writer.c

index dd4ae32cdd92b24677f25ceb5fa365c23ccad0a9..3c1c34af45a1e24052f45478fa35c0afb00a92c8 100644 (file)
@@ -32,6 +32,8 @@
 #  include <endian.h>
 #endif
 
+#include <openssl/evp.h>
+
 #include <loc/libloc.h>
 #include <loc/as.h>
 #include <loc/compat.h>
@@ -46,6 +48,8 @@ struct loc_database {
        struct loc_ctx* ctx;
        int refcount;
 
+       FILE* f;
+
        unsigned int version;
        time_t created_at;
        off_t vendor;
@@ -101,11 +105,11 @@ struct loc_database_enumerator {
        unsigned int* networks_visited;
 };
 
-static int loc_database_read_magic(struct loc_database* db, FILE* f) {
+static int loc_database_read_magic(struct loc_database* db) {
        struct loc_database_magic magic;
 
        // Read from file
-       size_t bytes_read = fread(&magic, 1, sizeof(magic), f);
+       size_t bytes_read = fread(&magic, 1, sizeof(magic), db->f);
 
        // Check if we have been able to read enough data
        if (bytes_read < sizeof(magic)) {
@@ -132,7 +136,7 @@ static int loc_database_read_magic(struct loc_database* db, FILE* f) {
 }
 
 static int loc_database_read_as_section_v0(struct loc_database* db,
-               FILE* f, const struct loc_database_header_v0* header) {
+               const struct loc_database_header_v0* header) {
        off_t as_offset  = be32toh(header->as_offset);
        size_t as_length = be32toh(header->as_length);
 
@@ -140,7 +144,7 @@ static int loc_database_read_as_section_v0(struct loc_database* db,
 
        if (as_length > 0) {
                db->as_v0 = mmap(NULL, as_length, PROT_READ,
-                       MAP_SHARED, fileno(f), as_offset);
+                       MAP_SHARED, fileno(db->f), as_offset);
 
                if (db->as_v0 == MAP_FAILED)
                        return -errno;
@@ -154,7 +158,7 @@ static int loc_database_read_as_section_v0(struct loc_database* db,
 }
 
 static int loc_database_read_network_nodes_section_v0(struct loc_database* db,
-               FILE* f, const struct loc_database_header_v0* header) {
+               const struct loc_database_header_v0* header) {
        off_t network_nodes_offset  = be32toh(header->network_tree_offset);
        size_t network_nodes_length = be32toh(header->network_tree_length);
 
@@ -163,7 +167,7 @@ static int loc_database_read_network_nodes_section_v0(struct loc_database* db,
 
        if (network_nodes_length > 0) {
                db->network_nodes_v0 = mmap(NULL, network_nodes_length, PROT_READ,
-                       MAP_SHARED, fileno(f), network_nodes_offset);
+                       MAP_SHARED, fileno(db->f), network_nodes_offset);
 
                if (db->network_nodes_v0 == MAP_FAILED)
                        return -errno;
@@ -177,7 +181,7 @@ static int loc_database_read_network_nodes_section_v0(struct loc_database* db,
 }
 
 static int loc_database_read_networks_section_v0(struct loc_database* db,
-               FILE* f, const struct loc_database_header_v0* header) {
+               const struct loc_database_header_v0* header) {
        off_t networks_offset  = be32toh(header->network_data_offset);
        size_t networks_length = be32toh(header->network_data_length);
 
@@ -186,7 +190,7 @@ static int loc_database_read_networks_section_v0(struct loc_database* db,
 
        if (networks_length > 0) {
                db->networks_v0 = mmap(NULL, networks_length, PROT_READ,
-                       MAP_SHARED, fileno(f), networks_offset);
+                       MAP_SHARED, fileno(db->f), networks_offset);
 
                if (db->networks_v0 == MAP_FAILED)
                        return -errno;
@@ -200,7 +204,7 @@ static int loc_database_read_networks_section_v0(struct loc_database* db,
 }
 
 static int loc_database_read_countries_section_v0(struct loc_database* db,
-               FILE* f, const struct loc_database_header_v0* header) {
+               const struct loc_database_header_v0* header) {
        off_t countries_offset  = be32toh(header->countries_offset);
        size_t countries_length = be32toh(header->countries_length);
 
@@ -209,7 +213,7 @@ static int loc_database_read_countries_section_v0(struct loc_database* db,
 
        if (countries_length > 0) {
                db->countries_v0 = mmap(NULL, countries_length, PROT_READ,
-                       MAP_SHARED, fileno(f), countries_offset);
+                       MAP_SHARED, fileno(db->f), countries_offset);
 
                if (db->countries_v0 == MAP_FAILED)
                        return -errno;
@@ -223,11 +227,11 @@ static int loc_database_read_countries_section_v0(struct loc_database* db,
        return 0;
 }
 
-static int loc_database_read_header_v0(struct loc_database* db, FILE* f) {
+static int loc_database_read_header_v0(struct loc_database* db) {
        struct loc_database_header_v0 header;
 
        // Read from file
-       size_t size = fread(&header, 1, sizeof(header), f);
+       size_t size = fread(&header, 1, sizeof(header), db->f);
 
        if (size < sizeof(header)) {
                ERROR(db->ctx, "Could not read enough data for header\n");
@@ -245,37 +249,37 @@ static int loc_database_read_header_v0(struct loc_database* db, FILE* f) {
        size_t pool_length = be32toh(header.pool_length);
 
        int r = loc_stringpool_open(db->ctx, &db->pool,
-               f, pool_length, pool_offset);
+               db->f, pool_length, pool_offset);
        if (r)
                return r;
 
        // AS section
-       r = loc_database_read_as_section_v0(db, f, &header);
+       r = loc_database_read_as_section_v0(db, &header);
        if (r)
                return r;
 
        // Network Nodes
-       r = loc_database_read_network_nodes_section_v0(db, f, &header);
+       r = loc_database_read_network_nodes_section_v0(db, &header);
        if (r)
                return r;
 
        // Networks
-       r = loc_database_read_networks_section_v0(db, f, &header);
+       r = loc_database_read_networks_section_v0(db, &header);
        if (r)
                return r;
 
        // countries
-       r = loc_database_read_countries_section_v0(db, f, &header);
+       r = loc_database_read_countries_section_v0(db, &header);
        if (r)
                return r;
 
        return 0;
 }
 
-static int loc_database_read_header(struct loc_database* db, FILE* f) {
+static int loc_database_read_header(struct loc_database* db) {
        switch (db->version) {
                case 0:
-                       return loc_database_read_header_v0(db, f);
+                       return loc_database_read_header_v0(db);
 
                default:
                        ERROR(db->ctx, "Incompatible database version: %u\n", db->version);
@@ -286,13 +290,32 @@ static int loc_database_read_header(struct loc_database* db, FILE* f) {
 static int loc_database_read(struct loc_database* db, FILE* f) {
        clock_t start = clock();
 
+       int fd = fileno(f);
+
+       // Clone file descriptor
+       fd = dup(fd);
+       if (!fd) {
+               ERROR(db->ctx, "Could not duplicate file descriptor\n");
+               return -1;
+       }
+
+       // Reopen the file so that we can keep our own file handle
+       db->f = fdopen(fd, "r");
+       if (!db->f) {
+               ERROR(db->ctx, "Could not re-open database file\n");
+               return -1;
+       }
+
+       // Rewind to the start of the file
+       rewind(db->f);
+
        // Read magic bytes
-       int r = loc_database_read_magic(db, f);
+       int r = loc_database_read_magic(db);
        if (r)
                return r;
 
        // Read the header
-       r = loc_database_read_header(db, f);
+       r = loc_database_read_header(db);
        if (r)
                return r;
 
@@ -364,6 +387,10 @@ static void loc_database_free(struct loc_database* db) {
 
        loc_stringpool_unref(db->pool);
 
+       // Close database file
+       if (db->f)
+               fclose(db->f);
+
        loc_unref(db->ctx);
        free(db);
 }
@@ -376,6 +403,97 @@ LOC_EXPORT struct loc_database* loc_database_unref(struct loc_database* db) {
        return NULL;
 }
 
+static int loc_database_hash(struct loc_database* db,
+               unsigned char* hash, unsigned int* length) {
+       int r = 0;
+
+       EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
+
+       // Select SHA512
+       const EVP_MD* md = EVP_sha512();
+
+       // Initialise hash function
+       EVP_DigestInit_ex(mdctx, md, NULL);
+
+       // Reset file to start
+       rewind(db->f);
+
+       // Read magic
+       struct loc_database_magic magic;
+       fread(&magic, 1, sizeof(magic), db->f);
+
+       // Feed magic into the hash
+       EVP_DigestUpdate(mdctx, &magic, sizeof(magic));
+
+       // Read the header
+       struct loc_database_header_v0 header_v0;
+
+       switch (db->version) {
+               case 0:
+                       fread(&header_v0, 1, sizeof(header_v0), db->f);
+
+                       // Clear signature
+                       for (unsigned int i = 0; i < sizeof(header_v0.signature); i++) {
+                               header_v0.signature[i] = '\0';
+                       }
+
+                       // Feed header into the hash
+                       EVP_DigestUpdate(mdctx, &header_v0, sizeof(header_v0));
+                       break;
+
+               default:
+                       ERROR(db->ctx, "Cannot compute hash for database with format %d\n",
+                               db->version);
+                       r = -EINVAL;
+                       goto CLEANUP;
+       }
+
+       // Walk through the file in chunks of 100kB
+       char buffer[1024 * 100];
+
+       while (!feof(db->f)) {
+               size_t bytes_read = fread(buffer, 1, sizeof(buffer), db->f);
+
+               EVP_DigestUpdate(mdctx, buffer, bytes_read);
+       }
+
+       // Finish hash
+       EVP_DigestFinal_ex(mdctx, hash, length);
+
+#ifdef ENABLE_DEBUG
+       char hash_string[(EVP_MAX_MD_SIZE * 2) + 1];
+
+       for (unsigned int i = 0; i < *length; i++) {
+               sprintf(&hash_string[i*2], "%02x", hash[i]);
+       }
+
+       DEBUG(db->ctx, "Database hash: %s\n", hash_string);
+#endif
+
+CLEANUP:
+       // Cleanup
+       EVP_MD_CTX_free(mdctx);
+
+       return r;
+}
+
+LOC_EXPORT int loc_database_verify(struct loc_database* db) {
+       // Hash
+       unsigned char hash[EVP_MAX_MD_SIZE];
+       unsigned int hash_length;
+
+       // Compute hash of the database
+       int r = loc_database_hash(db, hash, &hash_length);
+       if (r) {
+               ERROR(db->ctx, "Could not compute hash of the database\n");
+               return r;
+       }
+
+       # warning TODO Check signature against hash
+
+       return 0;
+}
+
 LOC_EXPORT time_t loc_database_created_at(struct loc_database* db) {
        return db->created_at;
 }
index 12f82ba88a8a97b8f8c4a25b1d2ede7e4640f24d..7fe59f1c649dcc69c5111574a07b8f0c00b8e421 100644 (file)
@@ -62,6 +62,7 @@ global:
        loc_database_new;
        loc_database_ref;
        loc_database_unref;
+       loc_database_verify;
 
        # Database Enumerator
        loc_database_enumerator_new;
index b1f0856feb9ff34b4572dcfec5f5cae559c56ed4..47a516a8264942c7b406599771dd454738cee6c7 100644 (file)
@@ -31,6 +31,8 @@ int loc_database_new(struct loc_ctx* ctx, struct loc_database** database, FILE*
 struct loc_database* loc_database_ref(struct loc_database* db);
 struct loc_database* loc_database_unref(struct loc_database* db);
 
+int loc_database_verify(struct loc_database* db);
+
 time_t loc_database_created_at(struct loc_database* db);
 const char* loc_database_get_vendor(struct loc_database* db);
 const char* loc_database_get_description(struct loc_database* db);
index 3679828d1a9d4cbd04a31bf06c5f19c8b9f1af0b..333030163c2ddece2a470536850b1fdd3f28d103 100644 (file)
@@ -72,6 +72,9 @@ struct loc_database_header_v0 {
 
        // Add some padding for future extensions
        char padding[32];
+
+       // Signature
+       char signature[4096];
 };
 
 struct loc_database_network_node_v0 {
index c31f8cf417d3746c105659930a0bab28e05baf4b..f6247cfbc87cdbffd06f44b24ad414b72f9c8b49 100644 (file)
@@ -71,6 +71,15 @@ static PyObject* Database_repr(DatabaseObject* self) {
        return PyUnicode_FromFormat("<Database %s>", self->path);
 }
 
+static PyObject* Database_verify(DatabaseObject* self) {
+       int r = loc_database_verify(self->db);
+
+       if (r == 0)
+               Py_RETURN_TRUE;
+
+       Py_RETURN_FALSE;
+}
+
 static PyObject* Database_get_description(DatabaseObject* self) {
        const char* description = loc_database_get_description(self->db);
 
@@ -285,6 +294,12 @@ static struct PyMethodDef Database_methods[] = {
                METH_VARARGS|METH_KEYWORDS,
                NULL,
        },
+       {
+               "verify",
+               (PyCFunction)Database_verify,
+               METH_NOARGS,
+               NULL,
+       },
        { NULL },
 };
 
index e047e0378e29ce59d915a9b159ad83582a18fe83..ed35d29804bfcc90cbd3b80ad92b093092d4443b 100644 (file)
@@ -215,6 +215,11 @@ class CLI(object):
                                % (args.database, e))
                        sys.exit(1)
 
+               # Verify the database
+               if not db.verify():
+                       sys.stderr.write("location-query: Could not verify the database\n")
+                       sys.exit(1)
+
                # Call function
                ret = args.func(db, args)
 
index 15dbe084cef02116f2b112cd8590cc389f1b76cc..e303969f8d3cb44cbd61df78780e95feeba95809 100644 (file)
@@ -480,6 +480,10 @@ LOC_EXPORT int loc_writer_write(struct loc_writer* writer, FILE* f) {
        time_t now = time(NULL);
        header.created_at = htobe64(now);
 
+       // Clear the signature
+       for (unsigned int i = 0; i < sizeof(header.signature); i++)
+               header.signature[i] = '\0';
+
        int r;
        off_t offset = 0;