]> git.ipfire.org Git - pakfire.git/commitdiff
file: Verify payload
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 19 Aug 2022 15:55:08 +0000 (15:55 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 19 Aug 2022 15:55:08 +0000 (15:55 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/file.c
src/libpakfire/include/pakfire/private.h

index 3fbe13234d58cf8473e581c1688c1893f158f8c0..d331027115280b729ff8f4e1c160c2a0987f1f5c 100644 (file)
@@ -27,6 +27,7 @@
 #include <time.h>
 
 #include <archive_entry.h>
+#include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/sha.h>
 
@@ -48,6 +49,7 @@ enum pakfire_file_verification_status {
        PAKFIRE_FILE_GROUP_CHANGED        = (1 << 6),
        PAKFIRE_FILE_CTIME_CHANGED        = (1 << 7),
        PAKFIRE_FILE_MTIME_CHANGED        = (1 << 8),
+       PAKFIRE_FILE_PAYLOAD_CHANGED      = (1 << 9),
 };
 
 struct pakfire_file {
@@ -382,7 +384,9 @@ PAKFIRE_EXPORT void pakfire_file_set_mtime(struct pakfire_file* file, time_t tim
 /*
        Returns one if the digest is not all zeros.
 */
-static int pakfire_file_has_digest(const unsigned char* digest, const size_t length) {
+#define pakfire_file_has_digest(digest) __pakfire_file_has_digest(digest, sizeof(digest))
+
+static int __pakfire_file_has_digest(const unsigned char* digest, const size_t length) {
        for (unsigned int i = 0; i < length; i++) {
                if (digest[i])
                        return 1;
@@ -396,7 +400,7 @@ PAKFIRE_EXPORT const unsigned char* pakfire_file_get_digest(
 
        switch (type) {
                case PAKFIRE_DIGEST_SHA512:
-                       if (!pakfire_file_has_digest(file->digests.sha512, sizeof(file->digests.sha512)))
+                       if (!pakfire_file_has_digest(file->digests.sha512))
                                return NULL;
 
                        if (length)
@@ -405,7 +409,7 @@ PAKFIRE_EXPORT const unsigned char* pakfire_file_get_digest(
                        return file->digests.sha512;
 
                case PAKFIRE_DIGEST_SHA256:
-                       if (!pakfire_file_has_digest(file->digests.sha256, sizeof(file->digests.sha256)))
+                       if (!pakfire_file_has_digest(file->digests.sha256))
                                return NULL;
 
                        if (length)
@@ -633,6 +637,155 @@ static int pakfire_file_verify_timestamps(struct pakfire_file* file, const struc
        return 0;
 }
 
+static int pakfire_file_verify_payload(struct pakfire_file* file, const struct stat* st) {
+       char buffer[PAKFIRE_BUFFER_SIZE];
+       FILE* f = NULL;
+       int r;
+
+       EVP_MD_CTX* sha512_ctx = NULL;
+       EVP_MD_CTX* sha256_ctx = NULL;
+
+       struct pakfire_file_digests computed_digests;
+
+       // Nothing to do for anything that isn't a regular file
+       if (!S_ISREG(st->st_mode))
+               return 0;
+
+       // Fast-path if size changed. The payload will have changed, too
+       if (file->verify_status & PAKFIRE_FILE_SIZE_CHANGED) {
+               file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED;
+               return 0;
+       }
+
+       // Check if this file has any digests at all
+       if (!pakfire_file_has_digest(file->digests.sha512) &&
+                       !pakfire_file_has_digest(file->digests.sha256)) {
+               ERROR(file->pakfire, "%s: No digests available\n", file->path);
+               return 0;
+       }
+
+       // Initialize context for SHA-512
+       sha512_ctx = EVP_MD_CTX_new();
+       if (!sha512_ctx) {
+               ERROR(file->pakfire, "Could not initialize OpenSSL context: %s\n",
+                       ERR_error_string(ERR_get_error(), NULL));
+               r = 1;
+               goto ERROR;
+       }
+
+       // Setup SHA-512 digest
+       r = EVP_DigestInit_ex(sha512_ctx, EVP_sha512(), NULL);
+       if (r != 1) {
+               ERROR(file->pakfire, "Could not setup SHA-512 digest: %s\n",
+                       ERR_error_string(ERR_get_error(), NULL));
+               r = 1;
+               goto ERROR;
+       }
+
+       // Initialize context for SHA-256
+       sha256_ctx = EVP_MD_CTX_new();
+       if (!sha256_ctx) {
+               ERROR(file->pakfire, "Could not initialize OpenSSL context: %s\n",
+                       ERR_error_string(ERR_get_error(), NULL));
+               r = 1;
+               goto ERROR;
+       }
+
+       // Setup SHA-256 digest
+       r = EVP_DigestInit_ex(sha256_ctx, EVP_sha256(), NULL);
+       if (r != 1) {
+               ERROR(file->pakfire, "Could not setup SHA-256 digest: %s\n",
+                       ERR_error_string(ERR_get_error(), NULL));
+               r = 1;
+               goto ERROR;
+       }
+
+       // Open the file
+       f = pakfire_file_open(file);
+       if (!f) {
+               ERROR(file->pakfire, "Could not open %s: %m\n", file->path);
+               goto ERROR;
+       }
+
+       // Read the file into the hash functions
+       while (!feof(f)) {
+               size_t bytes_read = fread(buffer, 1, sizeof(buffer), f);
+
+               // Raise any reading errors
+               if (ferror(f)) {
+                       r = 1;
+                       goto ERROR;
+               }
+
+               // SHA-512
+               r = EVP_DigestUpdate(sha512_ctx, buffer, bytes_read);
+               if (r != 1) {
+                       ERROR(file->pakfire, "EVP_Digest_Update() failed: %s\n",
+                               ERR_error_string(ERR_get_error(), NULL));
+                       r = 1;
+                       goto ERROR;
+               }
+
+               // SHA-256
+               r = EVP_DigestUpdate(sha256_ctx, buffer, bytes_read);
+               if (r != 1) {
+                       ERROR(file->pakfire, "EVP_Digest_Update() failed: %s\n",
+                               ERR_error_string(ERR_get_error(), NULL));
+                       r = 1;
+                       goto ERROR;
+               }
+       }
+
+       // Finalize SHA-512
+       r = EVP_DigestFinal_ex(sha512_ctx, computed_digests.sha512, NULL);
+       if (r != 1) {
+               ERROR(file->pakfire, "EVP_DigestFinal_ex() failed: %s\n",
+                       ERR_error_string(ERR_get_error(), NULL));
+               r = 1;
+               goto ERROR;
+       }
+
+       // Finalize SHA-256
+       r = EVP_DigestFinal_ex(sha256_ctx, computed_digests.sha256, NULL);
+       if (r != 1) {
+               ERROR(file->pakfire, "EVP_DigestFinal_ex() failed: %s\n",
+                       ERR_error_string(ERR_get_error(), NULL));
+               r = 1;
+               goto ERROR;
+       }
+
+       // Check SHA-512
+       r = CRYPTO_memcmp(computed_digests.sha512,
+               file->digests.sha512, sizeof(file->digests.sha512));
+       if (r) {
+               file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED;
+
+               DEBUG(file->pakfire, "%s: SHA-512 digest does not match\n", file->path);
+       }
+
+       // Check SHA-256
+       r = CRYPTO_memcmp(computed_digests.sha256,
+               file->digests.sha256, sizeof(file->digests.sha256));
+       if (r) {
+               file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED;
+
+               DEBUG(file->pakfire, "%s: SHA-256 digest does not match\n", file->path);
+       }
+
+       // Success
+       r = 0;
+
+ERROR:
+       if (sha512_ctx)
+               EVP_MD_CTX_free(sha512_ctx);
+       if (sha256_ctx)
+               EVP_MD_CTX_free(sha256_ctx);
+       if (f)
+               fclose(f);
+
+       return r;
+}
+
 /*
        Verify the file - i.e. does the metadata match what is on disk?
 */
@@ -675,5 +828,10 @@ int pakfire_file_verify(struct pakfire_file* file, int* status) {
        if (r)
                return r;
 
+       // Verify payload
+       r = pakfire_file_verify_payload(file, &st);
+       if (r)
+               return r;
+
        return 0;
 }
index d0de1e905530ab3d6c867a34387f94462627e7e0..76dc04ec101ea5fd4caafea96e353601535cb2bf 100644 (file)
 #define PAKFIRE_EXPORT __attribute__ ((visibility("default")))
 #endif
 
+/*
+       Buffer size that is used whenever we read any files into
+       something like a hash function.
+*/
+#define PAKFIRE_BUFFER_SIZE 65536
+
 #endif /* PAKFIRE_PRIVATE_H */