From: Michael Tremer Date: Tue, 13 Jul 2021 11:40:10 +0000 (+0000) Subject: archive: Verify all checksums X-Git-Tag: 0.9.28~1036 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6c23f67dbee4d4b12757a390acd05517ca134606;p=pakfire.git archive: Verify all checksums Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/archive.c b/src/libpakfire/archive.c index 8b10e3d33..26ae04318 100644 --- a/src/libpakfire/archive.c +++ b/src/libpakfire/archive.c @@ -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; }