From: Michael Tremer Date: Fri, 19 Aug 2022 15:55:08 +0000 (+0000) Subject: file: Verify payload X-Git-Tag: 0.9.28~417 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=760112058a05176b889391e32d909dcb2a7086ab;p=pakfire.git file: Verify payload Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/file.c b/src/libpakfire/file.c index 3fbe13234..d33102711 100644 --- a/src/libpakfire/file.c +++ b/src/libpakfire/file.c @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -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; } diff --git a/src/libpakfire/include/pakfire/private.h b/src/libpakfire/include/pakfire/private.h index d0de1e905..76dc04ec1 100644 --- a/src/libpakfire/include/pakfire/private.h +++ b/src/libpakfire/include/pakfire/private.h @@ -25,4 +25,10 @@ #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 */