]> git.ipfire.org Git - pakfire.git/commitdiff
libpakfire: Read signatures from archives and verify them
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 24 Nov 2017 17:45:24 +0000 (18:45 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 24 Nov 2017 17:45:24 +0000 (18:45 +0100)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/_pakfire/_pakfiremodule.c
src/_pakfire/archive.c
src/_pakfire/archive.h
src/_pakfire/errors.h
src/libpakfire/archive.c
src/libpakfire/include/pakfire/archive.h
src/libpakfire/include/pakfire/key.h
src/libpakfire/key.c
src/libpakfire/libpakfire.sym

index 617cd0b16b136824ad438d01f8d0b09f5afbbb35..0af859b88045f619bf85f959b506cc37f43c3f19 100644 (file)
@@ -120,6 +120,10 @@ PyMODINIT_FUNC PyInit__pakfire(void) {
        if (!module)
                return NULL;
 
+       PyExc_BadSignatureError = PyErr_NewException("_pakfire.BadSignatureError", NULL, NULL);
+       Py_INCREF(PyExc_BadSignatureError);
+       PyModule_AddObject(module, "BadSignatureError", PyExc_BadSignatureError);
+
        PyExc_DependencyError = PyErr_NewException("_pakfire.DependencyError", NULL, NULL);
        Py_INCREF(PyExc_DependencyError);
        PyModule_AddObject(module, "DependencyError", PyExc_DependencyError);
index 5a9a64855bdfc580bc9197c1d43545c07bbaab41..439c97f6f0642da683fc4645f1c78969abb1846b 100644 (file)
 #include <pakfire/util.h>
 
 #include "archive.h"
+#include "errors.h"
 
 static PyObject* Archive_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
        ArchiveObject* self = (ArchiveObject *)type->tp_alloc(type, 0);
        if (self) {
+               self->pakfire = NULL;
                self->archive = NULL;
        }
 
@@ -38,16 +40,22 @@ static void Archive_dealloc(ArchiveObject* self) {
        if (self->archive)
                pakfire_archive_free(self->archive);
 
+       Py_DECREF(self->pakfire);
+
        Py_TYPE(self)->tp_free((PyObject *)self);
 }
 
 static int Archive_init(ArchiveObject* self, PyObject* args, PyObject* kwds) {
+       PakfireObject* pakfire = NULL;
        const char* filename = NULL;
 
-       if (!PyArg_ParseTuple(args, "s", &filename))
+       if (!PyArg_ParseTuple(args, "O!s", &PakfireType, &pakfire, &filename))
                return -1;
 
-       self->archive = pakfire_archive_open(filename);
+       self->pakfire = pakfire;
+       Py_INCREF(self->pakfire);
+
+       self->archive = pakfire_archive_open(self->pakfire->pakfire, filename);
 
        return 0;
 }
@@ -92,13 +100,71 @@ static PyObject* Archive_read(ArchiveObject* self, PyObject* args, PyObject* kwd
        return bytes;
 }
 
+static PyObject* Archive_verify(ArchiveObject* self) {
+       pakfire_archive_verify_status_t status = pakfire_archive_verify(self->archive);
+
+       // Return True if everything is fine
+       if (status == PAKFIRE_ARCHIVE_VERIFY_OK || status == PAKFIRE_ARCHIVE_VERIFY_KEY_EXPIRED)
+               Py_RETURN_TRUE;
+
+       // Raise an exception if not okay
+       PyErr_SetString(PyExc_BadSignatureError, pakfire_archive_verify_strerror(status));
+
+       return NULL;
+}
+
+static PyObject* Archive_get_signatures(ArchiveObject* self) {
+       PyObject* list = PyList_New(0);
+
+       char** head = pakfire_archive_get_signatures(self->archive);
+
+       char** signatures = head;
+       while (*signatures) {
+               char* signature = *signatures++;
+
+               PyObject* object = PyUnicode_FromString(signature);
+               PyList_Append(list, object);
+
+               Py_DECREF(object);
+               pakfire_free(signature);
+       }
+
+       pakfire_free(head);
+
+       return list;
+}
+
 static struct PyMethodDef Archive_methods[] = {
-       {"read", (PyCFunction)Archive_read, METH_VARARGS|METH_KEYWORDS, NULL},
+       {
+               "read",
+               (PyCFunction)Archive_read,
+               METH_VARARGS|METH_KEYWORDS,
+               NULL
+       },
+       {
+               "verify",
+               (PyCFunction)Archive_verify,
+               METH_NOARGS,
+               NULL
+       },
        { NULL },
 };
 
 static struct PyGetSetDef Archive_getsetters[] = {
-       {"format", (getter)Archive_get_format, NULL, NULL, NULL},
+       {
+               "format",
+               (getter)Archive_get_format,
+               NULL,
+               NULL,
+               NULL
+       },
+       {
+               "signatures",
+               (getter)Archive_get_signatures,
+               NULL,
+               NULL,
+               NULL
+       },
        { NULL },
 };
 
index a750842d235d5086ed04eef46e3c431025fa612a..0630627c2c03a1a33eb4c704818ea105bb8a43cb 100644 (file)
 
 #include <pakfire/archive.h>
 
+#include "pakfire.h"
+
 typedef struct {
        PyObject_HEAD
+       PakfireObject* pakfire;
        PakfireArchive archive;
 } ArchiveObject;
 
index 4b26f7efcfdac74c92cbf35481c8292241293629..35033c0c0627b6a5d0c86c3348500437886ca961 100644 (file)
@@ -24,6 +24,7 @@
 #include <Python.h>
 
 // Exceptions
+PyObject* PyExc_BadSignatureError;
 PyObject* PyExc_DependencyError;
 
 #endif /* PYTHON_PAKFIRE_ERRORS_H */
index 7a60035c5c6bc069ffa0fb47c7da11ef5d17e2f2..a82da107633c9e61f8665ea87b13bc1af9fb830d 100644 (file)
@@ -21,6 +21,7 @@
 #include <assert.h>
 #include <ctype.h>
 #include <fcntl.h>
+#include <gpgme.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
@@ -33,6 +34,9 @@
 #include <pakfire/archive.h>
 #include <pakfire/errno.h>
 #include <pakfire/file.h>
+#include <pakfire/i18n.h>
+#include <pakfire/key.h>
+#include <pakfire/pakfire.h>
 #include <pakfire/util.h>
 
 static void configure_archive(struct archive* a) {
@@ -146,10 +150,38 @@ static void pakfire_archive_checksum_free(archive_checksum_t* c) {
        pakfire_free(c);
 }
 
-PakfireArchive pakfire_archive_create() {
+static archive_signature_t* pakfire_archive_signature_create(const char* sigdata) {
+       archive_signature_t* s = pakfire_calloc(1, sizeof(*s));
+       if (s) {
+               s->sigdata = pakfire_strdup(sigdata);
+       }
+
+       return s;
+}
+
+static void pakfire_archive_signature_free(archive_signature_t* s) {
+       pakfire_free(s->sigdata);
+       pakfire_free(s);
+}
+
+size_t pakfire_archive_count_signatures(PakfireArchive archive) {
+       size_t i = 0;
+
+       archive_signature_t** signatures = archive->signatures;
+       while (*signatures++) {
+               i++;
+       }
+
+       return i;
+}
+
+PakfireArchive pakfire_archive_create(Pakfire pakfire) {
        PakfireArchive archive = pakfire_calloc(1, sizeof(*archive));
        if (archive) {
+               archive->pakfire = pakfire_ref(pakfire);
                archive->format = -1;
+
+               archive->signatures = NULL;
        }
 
        return archive;
@@ -164,6 +196,12 @@ void pakfire_archive_free(PakfireArchive archive) {
        while (*checksums)
                pakfire_archive_checksum_free(*checksums++);
 
+       // Free signatures
+       archive_signature_t** signatures = archive->signatures;
+       while (*signatures)
+               pakfire_archive_signature_free(*signatures++);
+
+       pakfire_unref(archive->pakfire);
        pakfire_free(archive);
 }
 
@@ -284,6 +322,47 @@ static int pakfire_archive_parse_entry_checksums(PakfireArchive archive,
        return 0;
 }
 
+static int pakfire_archive_parse_entry_signature(PakfireArchive archive,
+               struct archive* a, struct archive_entry* e) {
+       char* data;
+       size_t data_size;
+
+       int r = archive_read(a, (void**)&data, &data_size);
+       if (r)
+               return 1;
+
+       // Terminate string.
+       data[data_size] = '\0';
+
+       archive_signature_t* signature = pakfire_archive_signature_create(data);
+       if (!signature)
+               return 1;
+
+       if (archive->signatures) {
+               // Count signatures
+               size_t num_signatures = pakfire_archive_count_signatures(archive) + 1;
+
+               // Resize the array
+               archive->signatures = pakfire_realloc(archive->signatures, sizeof(*archive->signatures) * num_signatures);
+       } else {
+               archive->signatures = pakfire_calloc(2, sizeof(*archive->signatures));
+       }
+
+       // Look for last element
+       archive_signature_t** signatures = archive->signatures;
+       while (*signatures) {
+               *signatures++;
+       }
+
+       // Append signature
+       *signatures++ = signature;
+
+       // Terminate list
+       *signatures = NULL;
+
+       return 0;
+}
+
 static int pakfire_archive_read_metadata(PakfireArchive archive, struct archive* a) {
        int ret;
 
@@ -324,6 +403,12 @@ static int pakfire_archive_read_metadata(PakfireArchive archive, struct archive*
                                ret = pakfire_archive_parse_entry_checksums(archive, a, entry);
                                if (ret)
                                        return PAKFIRE_E_PKG_INVALID;
+
+                       // Parse signatures
+                       } else if (strncmp(PAKFIRE_ARCHIVE_FN_SIGNATURES, entry_name, strlen(PAKFIRE_ARCHIVE_FN_SIGNATURES)) == 0) {
+                               ret = pakfire_archive_parse_entry_signature(archive, a, entry);
+                               if (ret)
+                                       return PAKFIRE_E_PKG_INVALID;
                        }
                }
        }
@@ -430,8 +515,8 @@ out:
 }
 
 
-PakfireArchive pakfire_archive_open(const char* path) {
-       PakfireArchive archive = pakfire_archive_create();
+PakfireArchive pakfire_archive_open(Pakfire pakfire, const char* path) {
+       PakfireArchive archive = pakfire_archive_create(pakfire);
        archive->path = pakfire_strdup(path);
 
        // Open the archive file for reading.
@@ -555,3 +640,151 @@ unsigned int pakfire_archive_get_format(PakfireArchive archive) {
 PakfireFile pakfire_archive_get_filelist(PakfireArchive archive) {
        return archive->filelist;
 }
+
+char** pakfire_archive_get_signatures(PakfireArchive archive) {
+       size_t size = pakfire_archive_count_signatures(archive);
+
+       char** head = pakfire_calloc(size, sizeof(*head));
+       if (!head)
+               return NULL;
+
+       char** list = head;
+       archive_signature_t** signatures = archive->signatures;
+       while (*signatures) {
+               archive_signature_t* signature = *signatures++;
+
+               *list++ = pakfire_strdup(signature->sigdata);
+       }
+
+       // Terminate list
+       *list = NULL;
+
+       return head;
+}
+
+static pakfire_archive_verify_status_t pakfire_archive_verify_checksums(PakfireArchive archive) {
+       pakfire_archive_verify_status_t status = PAKFIRE_ARCHIVE_VERIFY_INVALID;
+
+       char* data = NULL;
+       size_t size = 0;
+       gpgme_error_t error;
+
+       // Load the checksums file
+       int r = pakfire_archive_read(archive, PAKFIRE_ARCHIVE_FN_CHECKSUMS,
+               (void *)&data, &size, 0);
+       if (r)
+               return status;
+
+       // Convert into gpgme data object
+       gpgme_data_t signed_text;
+       error = gpgme_data_new_from_mem(&signed_text, data, size, 0);
+       if (error != GPG_ERR_NO_ERROR)
+               return -1;
+
+       // Get GPG context
+       gpgme_ctx_t gpgctx = pakfire_get_gpgctx(archive->pakfire);
+
+       // Try for each signature
+       archive_signature_t** signatures = archive->signatures;
+       while (*signatures) {
+               archive_signature_t* signature = *signatures++;
+
+               gpgme_data_t sigdata;
+               error = gpgme_data_new_from_mem(&sigdata, signature->sigdata, strlen(signature->sigdata), 0);
+               if (error != GPG_ERR_NO_ERROR)
+                       continue;
+
+               // Perform verification
+               error = gpgme_op_verify(gpgctx, sigdata, signed_text, NULL);
+               if (error != GPG_ERR_NO_ERROR)
+                       goto CLEANUP;
+
+               // Run the operation
+               gpgme_verify_result_t result = gpgme_op_verify_result(gpgctx);
+
+               // Check if any signatures have been returned
+               if (!result || !result->signatures)
+                       goto CLEANUP;
+
+               // Walk through all signatures
+               for (gpgme_signature_t sig = result->signatures; sig; sig = sig->next) {
+                       switch (gpg_err_code(sig->status)) {
+                               // All good
+                               case GPG_ERR_NO_ERROR:
+                                       status = PAKFIRE_ARCHIVE_VERIFY_OK;
+                                       break;
+
+                               // Key has expired (still good)
+                               case GPG_ERR_KEY_EXPIRED:
+                                       status = PAKFIRE_ARCHIVE_VERIFY_KEY_EXPIRED;
+                                       break;
+
+                               // Signature has expired (bad)
+                               case GPG_ERR_SIG_EXPIRED:
+                                       status = PAKFIRE_ARCHIVE_VERIFY_SIG_EXPIRED;
+                                       break;
+
+                               // We don't have the key
+                               case GPG_ERR_NO_PUBKEY:
+                                       status = PAKFIRE_ARCHIVE_VERIFY_KEY_UNKNOWN;
+                                       break;
+
+                               // Bad signature (or any other errors)
+                               case GPG_ERR_BAD_SIGNATURE:
+                               default:
+                                       status = PAKFIRE_ARCHIVE_VERIFY_INVALID;
+                                       break;
+                       }
+               }
+
+CLEANUP:
+               gpgme_data_release(sigdata);
+       }
+
+       gpgme_data_release(signed_text);
+       gpgme_release(gpgctx);
+
+       return status;
+}
+
+static pakfire_archive_verify_status_t pakfire_archive_verify_file(PakfireArchive archive, const archive_checksum_t* checksum) {
+       return PAKFIRE_ARCHIVE_VERIFY_OK; // TODO
+}
+
+pakfire_archive_verify_status_t pakfire_archive_verify(PakfireArchive archive) {
+       // TODO Verify that checksums file is signed with a valid key
+       pakfire_archive_verify_status_t r = pakfire_archive_verify_checksums(archive);
+       if (r)
+               return r;
+
+       // TODO Verify checksum of each file
+       archive_checksum_t** checksum = archive->checksums;
+       while (*checksum) {
+               r = pakfire_archive_verify_file(archive, *checksum);
+               if (r)
+                       return r;
+       }
+
+       return 0;
+}
+
+const char* pakfire_archive_verify_strerror(pakfire_archive_verify_status_t status) {
+       switch (status) {
+               case PAKFIRE_ARCHIVE_VERIFY_OK:
+                       return _("Verify OK");
+
+               case PAKFIRE_ARCHIVE_VERIFY_INVALID:
+                       return _("Invalid signature");
+
+               case PAKFIRE_ARCHIVE_VERIFY_SIG_EXPIRED:
+                       return _("Signature expired");
+
+               case PAKFIRE_ARCHIVE_VERIFY_KEY_EXPIRED:
+                       return _("Key expired");
+
+               case PAKFIRE_ARCHIVE_VERIFY_KEY_UNKNOWN:
+                       return _("Key unknown");
+       }
+
+       return NULL;
+}
index 2bbea8f6549cc73713818a1205763021421c4aea..d102dbd9d50635469a8204ce45174bb4b2ca8bf3 100644 (file)
 
 #include <pakfire/types.h>
 
-PakfireArchive pakfire_archive_create();
+typedef enum pakfire_archive_verify_status {
+       PAKFIRE_ARCHIVE_VERIFY_OK = 0,
+       PAKFIRE_ARCHIVE_VERIFY_INVALID,
+       PAKFIRE_ARCHIVE_VERIFY_SIG_EXPIRED,
+       PAKFIRE_ARCHIVE_VERIFY_KEY_EXPIRED,
+       PAKFIRE_ARCHIVE_VERIFY_KEY_UNKNOWN,
+} pakfire_archive_verify_status_t;
+
+PakfireArchive pakfire_archive_create(Pakfire pakfire);
 void pakfire_archive_free(PakfireArchive archive);
 
-PakfireArchive pakfire_archive_open(const char* path);
+PakfireArchive pakfire_archive_open(Pakfire pakfire, const char* path);
 
 int pakfire_archive_read(PakfireArchive archive, const char* filename,
        void** data, size_t* data_size, int flags);
@@ -40,6 +48,12 @@ unsigned int pakfire_archive_get_format(PakfireArchive archive);
 
 PakfireFile pakfire_archive_get_filelist(PakfireArchive archive);
 
+size_t pakfire_archive_count_signatures(PakfireArchive archive);
+char** pakfire_archive_get_signatures(PakfireArchive archive);
+
+pakfire_archive_verify_status_t pakfire_archive_verify(PakfireArchive archive);
+const char* pakfire_archive_verify_strerror(pakfire_archive_verify_status_t status);
+
 enum pakfire_archive_flags {
        PAKFIRE_ARCHIVE_USE_PAYLOAD = 1 << 0,
 };
@@ -49,6 +63,7 @@ enum pakfire_archive_flags {
 #define PAKFIRE_ARCHIVE_FN_FORMAT                      "pakfire-format"
 #define PAKFIRE_ARCHIVE_FN_METADATA                    "info"
 #define PAKFIRE_ARCHIVE_FN_PAYLOAD                     "data.img"
+#define PAKFIRE_ARCHIVE_FN_SIGNATURES          "signatures"
 
 #ifdef PAKFIRE_PRIVATE
 
@@ -66,7 +81,12 @@ typedef struct archive_checksum {
        archive_checksum_algo_t algo;
 } archive_checksum_t;
 
+typedef struct archive_signature {
+       char* sigdata;
+} archive_signature_t;
+
 struct _PakfireArchive {
+       Pakfire pakfire;
        char* path;
 
        // metadata
@@ -74,6 +94,7 @@ struct _PakfireArchive {
 
        PakfireFile filelist;
        archive_checksum_t** checksums;
+       archive_signature_t** signatures;
 };
 
 struct payload_archive_data {
index 8bb8de1701e1617ed3c49fd28c905498a975fa74..36111c3196268e91705040900a90e42f0e7908e8 100644 (file)
@@ -59,6 +59,8 @@ char* pakfire_key_dump(PakfireKey key);
 
 #ifdef PAKFIRE_PRIVATE
 
+gpgme_ctx_t pakfire_get_gpgctx(Pakfire pakfire);
+
 struct _PakfireKey {
        Pakfire pakfire;
        gpgme_key_t gpgkey;
index 5fdbd4c71b641af9dff6d5be8e324c674a8ef203..938a6438f6216005e820e01ec6e4d527b2e8bccd 100644 (file)
@@ -31,7 +31,7 @@
 
 #define DEFAULT_KEY_SIZE "rsa4096"
 
-static gpgme_ctx_t pakfire_get_gpgctx(Pakfire pakfire) {
+gpgme_ctx_t pakfire_get_gpgctx(Pakfire pakfire) {
        static int gpg_initialized = 0;
        gpgme_error_t error;
        const char* error_string;
index ddcfa19d1d7bc4532b7114072ef4190b11954b23..460b5f6565924e7d779af4756b18a46c22695b6c 100644 (file)
@@ -29,14 +29,18 @@ global:
        pakfire_unref;
 
        # archive
+       pakfire_archive_count_signatures;
        pakfire_archive_create;
        pakfire_archive_extract;
        pakfire_archive_free;
        pakfire_archive_get_filelist;
        pakfire_archive_get_format;
        pakfire_archive_get_path;
+       pakfire_archive_get_signatures;
        pakfire_archive_open;
        pakfire_archive_read;
+       pakfire_archive_verify;
+       pakfire_archive_verify_strerror;
 
        # file
        pakfire_file_append;