#include <time.h>
#include <archive_entry.h>
+#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
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 {
/*
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;
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)
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)
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?
*/
if (r)
return r;
+ // Verify payload
+ r = pakfire_file_verify_payload(file, &st);
+ if (r)
+ return r;
+
return 0;
}