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;
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;
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
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;
}
return r;
// Verify checksums
- r = pakfire_archive_verify_checksums(archive);
+ r = pakfire_archive_verify_checksums(archive, 0);
if (r)
return r;
}