#include <pakfire/string.h>
#include <pakfire/util.h>
-static const char* pakfire_archive_files_without_chksums[] = {
- "pakfire-format",
- "chksums",
- NULL,
-};
-
-struct pakfire_archive_chksum {
- STAILQ_ENTRY(pakfire_archive_chksum) nodes;
-
- char path[PATH_MAX];
-
- unsigned char digest_sha512[64];
- unsigned char digest_sha256[32];
-};
-
struct pakfire_archive {
struct pakfire* pakfire;
int nrefs;
struct pakfire_filelist* filelist;
- // Checksums
- STAILQ_HEAD(chksums, pakfire_archive_chksum) chksums;
-
// Scriptlets
struct pakfire_scriptlet** scriptlets;
int scriptlets_loaded;
const char* file;
const char* legacy;
} filenames[] = {
- { "CHKSUMS", "chksums" },
{ "DATA", "data.img" },
{ "FILELIST", "filelist"},
{ "PKGINFO", "info" },
return NULL;
}
-// Checksum Stuff
-
-static int pakfire_archive_add_chksum(struct pakfire_archive* archive, const char* path,
- const unsigned char* digest_sha512, const unsigned char* digest_sha256) {
- int r = 1;
-
- // Path must be set
- if (!path) {
- errno = EINVAL;
- return 1;
- }
-
- // At least one of the digests must be set
- if (!digest_sha512 && !digest_sha256) {
- errno = EINVAL;
- return 1;
- }
-
- // Allocate a new chksum object
- struct pakfire_archive_chksum* chksum = calloc(1, sizeof(*chksum));
- if (!chksum)
- return ENOMEM;
-
- // Store path
- r = pakfire_string_set(chksum->path, path);
- if (r)
- goto ERROR;
-
- // SHA512
- if (digest_sha512)
- memcpy(chksum->digest_sha512, digest_sha512, sizeof(chksum->digest_sha512));
-
- // SHA256
- if (digest_sha256)
- memcpy(chksum->digest_sha256, digest_sha256, sizeof(chksum->digest_sha256));
-
- // Append it
- STAILQ_INSERT_TAIL(&archive->chksums, chksum, nodes);
-
- DEBUG(archive->pakfire, "Added checksum for %s\n", path);
-
- return 0;
-
-ERROR:
- free(chksum);
-
- return r;
-}
-
-static struct pakfire_archive_chksum* pakfire_archive_find_chksum(
- struct pakfire_archive* archive, const char* path) {
- struct pakfire_archive_chksum* chksum = NULL;
-
- STAILQ_FOREACH(chksum, &archive->chksums, nodes) {
- if (strcmp(chksum->path, path) == 0)
- return chksum;
- }
-
- // Nothing found
- 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;
-
- while ((chksum = STAILQ_FIRST(&archive->chksums))) {
- STAILQ_REMOVE_HEAD(&archive->chksums, nodes);
-
- free(chksum);
- }
-}
-
static void pakfire_archive_free(struct pakfire_archive* archive) {
// Close the file
if (archive->f)
fclose(archive->f);
- // Free all checksums
- pakfire_archive_free_chksums(archive);
-
// Free scriptlets
if (archive->scriptlets) {
for (struct pakfire_scriptlet** scriptlet = archive->scriptlets; *scriptlet; scriptlet++)
a->pakfire = pakfire_ref(pakfire);
a->nrefs = 1;
- STAILQ_INIT(&a->chksums);
-
*archive = a;
-
return 0;
}
return pakfire_filelist_ref(archive->filelist);
}
-static int pakfire_archive_load_checksums_mtree(struct pakfire_archive* archive) {
- struct archive* a = NULL;
- struct archive_entry* entry = NULL;
- int r;
-
- // Find chksums
- r = open_archive_and_find(archive, &a, &entry, "FILELIST");
- if (r)
- return r;
-
- // Allocate a new archive reader
- struct archive* mtree = archive_read_new();
- if (!mtree) {
- ERROR(archive->pakfire, "Could not allocate mtree reader\n");
- goto ERROR;
- }
-
- // Enable reading the mtree format
- r = archive_read_support_format_mtree(mtree);
- if (r) {
- ERROR(archive->pakfire, "Could not enable mtree format: %s\n",
- archive_error_string(mtree));
- goto ERROR;
- }
-
- // Filelists can be Zstandard-compressed
- r = archive_read_support_filter_zstd(mtree);
- if (r)
- goto ERROR;
-
- // Try opening the mtree
- r = archive_read_open2(mtree, a, NULL, pakfire_archive_read_callback, NULL, NULL);
- if (r) {
- ERROR(archive->pakfire, "Could not open checksum mtree in %s: %s\n",
- archive->path, archive_error_string(mtree));
- goto ERROR;
- }
-
- for (;;) {
- // Read next entry
- r = archive_read_next_header(mtree, &entry);
-
- // We have reached the end of the list
- if (r == ARCHIVE_EOF)
- break;
-
- // An unexpected error has occurred
- else if (r) {
- ERROR(archive->pakfire, "Could not read mtree entry: %s\n",
- archive_error_string(mtree));
- goto ERROR;
- }
-
- // Fetch pathname
- const char* path = archive_entry_pathname(entry);
-
- // Fetch all supported digests
- const unsigned char* digest_sha512 = archive_entry_digest(
- entry, ARCHIVE_ENTRY_DIGEST_SHA512);
- const unsigned char* digest_sha256 = archive_entry_digest(
- entry, ARCHIVE_ENTRY_DIGEST_SHA256);
-
- // Add the checksum to the internal list
- r = pakfire_archive_add_chksum(archive, path + 2, digest_sha512, digest_sha256);
- if (r)
- goto ERROR;
- }
-
- // Success
- r = 0;
-
-ERROR:
- if (mtree)
- archive_read_free(mtree);
- close_archive(archive, a);
-
- return r;
-}
-
-static int pakfire_archive_load_checksums_legacy_line(
- struct pakfire_archive* archive, char* line) {
- char path[PATH_MAX] = "";
- char hexdigest[LINE_MAX] = "";
- char* tok = NULL;
-
- unsigned int i = 0;
- int r = 1;
-
- char* word = strtok_r(line, " ", &tok);
- while (word) {
- switch (i) {
- // path
- case 0:
- r = pakfire_string_set(path, word);
- if (r)
- return r;
- break;
-
- // hexdigest
- case 1:
- r = pakfire_string_set(hexdigest, word);
- if (r)
- return r;
- break;
-
- default:
- ERROR(archive->pakfire, "Invalid line in checksums:\n%s\n", line);
- r = 1;
- return r;
- }
-
- word = strtok_r(NULL, " ", &tok);
- i++;
- }
-
- // Check if we have input for path and hexdigest
- if (!*path || !*hexdigest) {
- errno = EINVAL;
- return r;
- }
-
- unsigned char digest[EVP_MAX_MD_SIZE];
-
- // Convert hexdigest to digest
- r = pakfire_unhexlify(digest, hexdigest);
- if (r)
- return r;
-
- // Add a chksum object
- r = pakfire_archive_add_chksum(archive, path, digest, NULL);
- if (r)
- return r;
-
- // Success
- return 0;
-}
-
-static int pakfire_archive_load_checksums_legacy(struct pakfire_archive* archive) {
- char* buffer = NULL;
- size_t length = 0;
-
- // Find chksums
- int r = open_archive_and_read(archive, "CHKSUMS", &buffer, &length);
- if (r)
- return r;
-
- // Empty file
- if (!length) {
- ERROR(archive->pakfire, "Checksums file was unexpectedly empty\n");
- return 1;
- }
-
- char line[LINE_MAX];
- char* l = line;
-
- // Split the input into lines and call pakfire_archive_load_checksums_legacy_line
- for (char* p = buffer; (size_t)(p - buffer) < length; p++) {
- if (*p == '\n' || p == (buffer + length)) {
- // Terminate line
- *l = '\0';
-
- // Process the line
- r = pakfire_archive_load_checksums_legacy_line(archive, line);
- if (r)
- goto ERROR;
-
- // Reset line pointer
- l = line;
-
- continue;
- }
-
- // Copy character to line
- *l++ = *p;
-
- // Check if line has enough space
- if ((size_t)(l - line) >= sizeof(line)) {
- errno = ENOBUFS;
- goto ERROR;
- }
- }
-
-ERROR:
- if (buffer)
- free(buffer);
- return r;
-}
-
-static int pakfire_archive_load_checksums(struct pakfire_archive* archive) {
- // Do nothing if the list has already been loaded
- if (!STAILQ_EMPTY(&archive->chksums))
- return 0;
-
- DEBUG(archive->pakfire, "Loading checksums from archive %p\n", archive);
-
- if (archive->format >= 6)
- return pakfire_archive_load_checksums_mtree(archive);
- else
- return pakfire_archive_load_checksums_legacy(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) {
- // Allocate validator
- 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, void* data) {
- const char* path = archive_entry_pathname(entry);
-
- // Some files do not have checksums
- for (const char** file = pakfire_archive_files_without_chksums; *file; file++) {
- if (strcmp(*file, path) == 0)
- return 0;
- }
-
- // Fetch the checksum
- struct pakfire_archive_chksum* chksum = pakfire_archive_find_chksum(archive, path);
- if (!chksum) {
- ERROR(archive->pakfire, "No checksum found for %s\n", path);
- return 1;
- }
-
- 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 (;;) {
- 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);
-
- char* hexdigest_expected = pakfire_hexlify((*v)->digest);
- if (hexdigest_expected) {
- ERROR(archive->pakfire, " Expected: %s\n", hexdigest_expected);
- free(hexdigest_expected);
- }
-
- char* hexdigest_computed = pakfire_hexlify(digest);
- if (hexdigest_computed) {
- ERROR(archive->pakfire, " Computed: %s\n", hexdigest_computed);
- free(hexdigest_computed);
- }
-
- 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;
-}
-
PAKFIRE_EXPORT int pakfire_archive_verify(struct pakfire_archive* archive, int* status) {
- int r;
-
- // Read checksums from archive
- r = pakfire_archive_load_checksums(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, NULL);
- if (r) {
- ERROR(archive->pakfire, "File verification failed\n");
- return r;
- }
-
+ // XXX currently not implemented
return 0;
}