]> git.ipfire.org Git - people/stevee/pakfire.git/commitdiff
archive: Verify all checksums
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 13 Jul 2021 11:40:10 +0000 (11:40 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 13 Jul 2021 11:40:10 +0000 (11:40 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/archive.c

index 8b10e3d33e6ff6ce76591ac655b50ef6b91dbcb1..26ae04318c9ba7e9991a7d794caac1251553e1ec 100644 (file)
@@ -393,6 +393,18 @@ static struct pakfire_archive_chksum* pakfire_archive_find_chksum(
        return NULL;
 }
 
+static int __pakfire_archive_chksum_has_digest(const unsigned char* digest, const size_t length) {
+       for (unsigned int i = 0; i < length; i++) {
+               if (digest[i])
+                       return 1;
+       }
+
+       return 0;
+}
+
+#define pakfire_archive_chksum_has_digest(digest) \
+       __pakfire_archive_chksum_has_digest(digest, sizeof(digest))
+
 static void pakfire_archive_free_chksums(struct pakfire_archive* archive) {
        struct pakfire_archive_chksum* chksum;
 
@@ -1126,7 +1138,7 @@ PAKFIRE_EXPORT struct pakfire_filelist* pakfire_archive_get_filelist(struct pakf
        return pakfire_filelist_ref(archive->filelist);
 }
 
-static pakfire_archive_verify_status_t pakfire_archive_verify_file(struct pakfire* pakfire,
+static pakfire_archive_verify_status_t __pakfire_archive_verify_file(struct pakfire* pakfire,
                struct archive* a, const struct pakfire_archive_chksum* chksum) {
        pakfire_archive_verify_status_t status = PAKFIRE_ARCHIVE_VERIFY_ERROR;
 
@@ -1291,7 +1303,178 @@ static int pakfire_archive_load_checksums(struct pakfire_archive* archive) {
                return pakfire_archive_load_checksums_legacy(archive);
 }
 
-static int pakfire_archive_verify_checksums(struct pakfire_archive* archive) {
+struct pakfire_archive_validator {
+       EVP_MD_CTX* ctx;
+       const unsigned char* digest;
+};
+
+static int pakfire_archive_verify_add_validator(struct pakfire_archive_validator*** list,
+               struct pakfire* pakfire, const EVP_MD* md, const unsigned char* digest) {
+       struct pakfire_archive_validator* v = calloc(1, sizeof(*v));
+       if (!v)
+               return 1;
+
+       int r = 1;
+
+       // Allocate an EVP context
+       v->ctx = EVP_MD_CTX_new();
+       if (!v->ctx) {
+               ERROR(pakfire, "Could not allocate an EVP context\n");
+               goto ERROR;
+       }
+
+       // Initialise the hash algorithm
+       r = EVP_DigestInit_ex(v->ctx, md, NULL);
+       if (r != 1) {
+               ERROR(pakfire, "Could not initialize hash algorithm: %s\n",
+                       ERR_error_string(ERR_get_error(), NULL));
+               r = 1;
+               goto ERROR;
+       }
+
+       // Store digest
+       v->digest = digest;
+
+       unsigned int counter = 0;
+
+       // Calculate list size
+       if (*list) {
+               for (struct pakfire_archive_validator** l = *list; *l; l++)
+                       counter++;
+       }
+
+       // Grow list
+       *list = reallocarray(*list, counter + 2, sizeof(**list));
+       if (!*list) {
+               r = 1;
+               goto ERROR;
+       }
+
+       // Append validator
+       (*list)[counter] = v;
+
+       // Terminate list
+       (*list)[counter + 1] = NULL;
+
+       // Success
+       return 0;
+
+ERROR:
+       if (v->ctx)
+               EVP_MD_CTX_free(v->ctx);
+       free(v);
+
+       return r;
+}
+
+static int pakfire_archive_verify_file(struct pakfire_archive* archive,
+               struct archive* a, struct archive_entry* entry, int flags, void* data) {
+       const char* path = archive_entry_pathname(entry);
+
+       // Fetch the checksum
+       struct pakfire_archive_chksum* chksum = pakfire_archive_find_chksum(archive, path);
+       if (!chksum) {
+               DEBUG(archive->pakfire, "No checksum found for %s\n", path);
+               return 0;
+       }
+
+       struct pakfire_archive_validator** validators = NULL;
+       int r;
+
+       // SHA512
+       if (pakfire_archive_chksum_has_digest(chksum->digest_sha512)) {
+               r = pakfire_archive_verify_add_validator(&validators, archive->pakfire,
+                       EVP_sha512(), chksum->digest_sha512);
+               if (r)
+                       return r;
+       }
+
+       // SHA256
+       if (pakfire_archive_chksum_has_digest(chksum->digest_sha256)) {
+               r = pakfire_archive_verify_add_validator(&validators, archive->pakfire,
+                       EVP_sha256(), chksum->digest_sha256);
+               if (r)
+                       return r;
+       }
+
+       // Check if anything was selected (this should not happen)
+       if (!validators) {
+               ERROR(archive->pakfire, "No validators selected\n");
+               return 1;
+       }
+
+       DEBUG(archive->pakfire, "Verifying %s\n", path);
+
+       const void* buffer = NULL;
+       size_t size = 0;
+       off_t offset = 0;
+
+       // Read the payload of the file and feed it into the validators
+       for (;;) {
+               int r = archive_read_data_block(a, &buffer, &size, &offset);
+               if (r == ARCHIVE_EOF)
+                       break;
+
+               // End on any errors
+               if (r) {
+                       r = 1;
+                       goto ERROR;
+               }
+
+               // Update all hash digests
+               for (struct pakfire_archive_validator** v = validators; *v; v++) {
+                       r = EVP_DigestUpdate((*v)->ctx, buffer, size);
+                       if (r != 1) {
+                               ERROR(archive->pakfire, "%s\n", ERR_error_string(ERR_get_error(), NULL));
+                               r = 1;
+                               goto ERROR;
+                       }
+               }
+       }
+
+       unsigned char digest[EVP_MAX_MD_SIZE];
+
+       // Finalize all digests
+       for (struct pakfire_archive_validator** v = validators; *v; v++) {
+               // Reset digest array size
+               unsigned int length = sizeof(digest);
+
+               // Finish computation
+               r = EVP_DigestFinal_ex((*v)->ctx, digest, &length);
+               if (r != 1) {
+                       ERROR(archive->pakfire, "%s\n", ERR_error_string(ERR_get_error(), NULL));
+                       r = 1;
+                       goto ERROR;
+               }
+
+               // Compare digests
+               r = CRYPTO_memcmp(digest, (*v)->digest, length);
+
+               // Handle mismatches
+               if (r) {
+                       ERROR(archive->pakfire, "Checksum of %s did not match\n", chksum->path);
+                       goto ERROR;
+               }
+       }
+
+       DEBUG(archive->pakfire, "All (checked) checksums of %s matched\n", chksum->path);
+
+       // Success
+       r = 0;
+
+ERROR:
+       if (validators) {
+               for (struct pakfire_archive_validator** v = validators; *v; v++) {
+                       EVP_MD_CTX_free((*v)->ctx);
+                       free(*v);
+               }
+               free(validators);
+       }
+
+       return r;
+}
+
+static int pakfire_archive_verify_checksums(struct pakfire_archive* archive, int flags) {
        int r;
 
        // Read checksums from archive
@@ -1299,6 +1482,13 @@ static int pakfire_archive_verify_checksums(struct pakfire_archive* archive) {
        if (r)
                return r;
 
+       // Iterate over all files and verify them if a checksum is present
+       r = pakfire_archive_walk(archive, pakfire_archive_verify_file, flags, NULL, NULL);
+       if (r) {
+               ERROR(archive->pakfire, "File verification failed\n");
+               return r;
+       }
+
        return 0;
 }
 
@@ -1453,7 +1643,7 @@ PAKFIRE_EXPORT int pakfire_archive_verify(struct pakfire_archive* archive,
                        return r;
 
                // Verify checksums
-               r = pakfire_archive_verify_checksums(archive);
+               r = pakfire_archive_verify_checksums(archive, 0);
                if (r)
                        return r;
        }