#include <pakfire/string.h>
#include <pakfire/util.h>
-enum pakfire_archive_verify_flags {
- PAKFIRE_ARCHIVE_VERIFY_ALL,
- PAKFIRE_ARCHIVE_VERIFY_BEST,
-};
-
static const char* pakfire_archive_files_without_chksums[] = {
"pakfire-format",
"chksums",
static int pakfire_archive_walk_entries(struct pakfire_archive* archive, struct archive* a,
int (*callback)(struct pakfire_archive* archive, struct archive* a,
- struct archive_entry* e, int flags, void* data),
- int flags, void* data, off_t* offset) {
+ struct archive_entry* e, void* data), void* data) {
struct archive_entry* e = NULL;
// Walk through the archive
int r = archive_read_next_header(a, &e);
// Return OK when we reached the end of the archive
- if (r == ARCHIVE_EOF) {
- if (offset)
- *offset = archive_read_header_position(a);
-
+ if (r == ARCHIVE_EOF)
return ARCHIVE_OK;
- }
// Raise any other errors
else if (r)
// Run callback
if (callback) {
- r = callback(archive, a, e, flags, data);
+ r = callback(archive, a, e, data);
if (r)
return r;
}
static int pakfire_archive_walk(struct pakfire_archive* archive,
int (*callback)(struct pakfire_archive* archive, struct archive* a,
- struct archive_entry* e, int flags, void* data),
- int flags, void* data, off_t* offset) {
+ struct archive_entry* e, void* data), void* data) {
struct archive* a;
// Open the archive file
return r;
// Walk through the archive
- r = pakfire_archive_walk_entries(archive, a, callback, flags, data, offset);
+ r = pakfire_archive_walk_entries(archive, a, callback, data);
// Close the archive
close_archive(archive, a);
return r;
}
-/*
- This function finds the end of the archive so that we can append more files
-*/
-static off_t pakfire_archive_find_end(struct pakfire_archive* archive, off_t* offset) {
- return pakfire_archive_walk(archive, NULL, 0, NULL, offset);
-}
-
static la_ssize_t pakfire_archive_read_callback(struct archive* a,
void* client_data, const void** buffer) {
struct archive* archive = (struct archive*)client_data;
};
static int pakfire_archive_verify_add_validator(struct pakfire_archive_validator*** list,
- struct pakfire* pakfire, const EVP_MD* md, const unsigned char* digest,
- enum pakfire_archive_verify_flags flags) {
-
- switch (flags) {
- // Fall through and add the validator
- case PAKFIRE_ARCHIVE_VERIFY_ALL:
- break;
-
- // We only accept one validator, so this function becomes a no-op when list
- // has any validators already
- case PAKFIRE_ARCHIVE_VERIFY_BEST:
- if (*list)
- return 0;
- break;
-
- default:
- errno = EINVAL;
- return 1;
- }
-
+ struct pakfire* pakfire, const EVP_MD* md, const unsigned char* digest) {
// Allocate validator
struct pakfire_archive_validator* v = calloc(1, sizeof(*v));
if (!v)
}
static int pakfire_archive_verify_file(struct pakfire_archive* archive,
- struct archive* a, struct archive_entry* entry, int flags, void* data) {
+ struct archive* a, struct archive_entry* entry, void* data) {
const char* path = archive_entry_pathname(entry);
- // Signatures do not have checksums
- if (pakfire_string_startswith(path, "signatures/"))
- return 0;
-
// Some files do not have checksums
for (const char** file = pakfire_archive_files_without_chksums; *file; file++) {
if (strcmp(*file, path) == 0)
// SHA512
if (pakfire_archive_chksum_has_digest(chksum->digest_sha512)) {
r = pakfire_archive_verify_add_validator(&validators, archive->pakfire,
- EVP_sha512(), chksum->digest_sha512, flags);
+ 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, flags);
+ EVP_sha256(), chksum->digest_sha256);
if (r)
return r;
}
return r;
}
-static int pakfire_archive_verify_checksums(struct pakfire_archive* archive, int flags) {
+PAKFIRE_EXPORT int pakfire_archive_verify(struct pakfire_archive* archive, int* status) {
int r;
// Read checksums from archive
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);
+ r = pakfire_archive_walk(archive, pakfire_archive_verify_file, NULL);
if (r) {
ERROR(archive->pakfire, "File verification failed\n");
return r;
return 0;
}
-struct pakfire_archive_signature_check {
- gpgme_data_t checksums;
-};
-
-/*
- This function is called to examine whether we have a signature and if so verify it
-*/
-static int pakfire_archive_verify_signature(struct pakfire_archive* archive, struct archive* a,
- struct archive_entry* e, int flags, void* data) {
- const char* entry_name = archive_entry_pathname(e);
-
- // This is not a signature
- if (!pakfire_string_startswith(entry_name, "signatures/"))
- return 0;
-
- // Fetch GPGME context
- gpgme_ctx_t gpgctx = pakfire_get_gpgctx(archive->pakfire);
- if (!gpgctx)
- return 1;
-
- struct pakfire_archive_signature_check* check = (struct pakfire_archive_signature_check*)data;
-
- char* buffer = NULL;
- size_t size = 0;
-
- // Load the signature into memory
- int r = pakfire_archive_copy_data_to_buffer(archive->pakfire, a, e, &buffer, &size);
- if (r)
- return 1;
-
- gpgme_data_t signature;
- gpgme_error_t error;
-
- // Make signature readable for GPGME
- error = gpgme_data_new_from_mem(&signature, buffer, size, 0);
- if (error != GPG_ERR_NO_ERROR) {
- r = 1;
- goto ERROR;
- }
-
- // Perform verification
- error = gpgme_op_verify(gpgctx, signature, check->checksums, NULL);
- if (error != GPG_ERR_NO_ERROR)
- goto ERROR;
-
- // Run the operation
- gpgme_verify_result_t result = gpgme_op_verify_result(gpgctx);
-
- // Check if any signatures have been returned
- if (!result || !result->signatures)
- goto ERROR;
-
- // Walk through all signatures
- for (gpgme_signature_t sig = result->signatures; sig; sig = sig->next) {
- // Log some information about this signature
- DEBUG(archive->pakfire, "Found signature %s\n", sig->fpr);
- DEBUG(archive->pakfire, " Status : %s\n", gpgme_strerror(sig->status));
- DEBUG(archive->pakfire, " Timestamp : %lu\n", sig->timestamp);
- if (sig->exp_timestamp)
- DEBUG(archive->pakfire, " Expires : %lu\n", sig->exp_timestamp);
- DEBUG(archive->pakfire, " Validity : %s\n", gpgme_strerror(sig->validity_reason));
-
- switch (gpg_err_code(sig->status)) {
- // All good
- case GPG_ERR_NO_ERROR:
- archive->verify = PAKFIRE_ARCHIVE_VERIFY_OK;
- break;
-
- // Key has expired (still good)
- case GPG_ERR_KEY_EXPIRED:
- archive->verify = PAKFIRE_ARCHIVE_VERIFY_KEY_EXPIRED;
- break;
-
- // Signature has expired (bad)
- case GPG_ERR_SIG_EXPIRED:
- archive->verify = PAKFIRE_ARCHIVE_VERIFY_SIG_EXPIRED;
- break;
-
- // We don't have the key
- case GPG_ERR_NO_PUBKEY:
- archive->verify = PAKFIRE_ARCHIVE_VERIFY_KEY_UNKNOWN;
- break;
-
- // Bad signature (or any other errors)
- case GPG_ERR_BAD_SIGNATURE:
- default:
- archive->verify = PAKFIRE_ARCHIVE_VERIFY_INVALID;
- break;
- }
- }
-
-ERROR:
- // Free signature
- gpgme_data_release(signature);
- if (buffer)
- free(buffer);
-
- return r;
-}
-
-/*
- This function walks through the archive looking for signatures and verifies them
-*/
-static int pakfire_archive_verify_signatures(struct pakfire_archive* archive,
- struct pakfire_key*** keys) {
- char* buffer = NULL;
- size_t size = 0;
-
- // Fetch GPGME context (to initialize GPGME)
- gpgme_ctx_t gpgctx = pakfire_get_gpgctx(archive->pakfire);
- if (!gpgctx)
- return 1;
-
- // Find checksums
- int r = open_archive_and_read(archive, "chksums", &buffer, &size);
- if (r) {
- ERROR(archive->pakfire, "Could not open chksums file: %m\n");
- return r;
- }
-
- struct pakfire_archive_signature_check check;
-
- // Convert checksums readable for GPGME
- gpgme_error_t error = gpgme_data_new_from_mem(&check.checksums, buffer, size, 0);
- if (error != GPG_ERR_NO_ERROR) {
- ERROR(archive->pakfire, "Could not initialize chksums: %s\n", gpgme_strerror(error));
- r = 1;
- goto ERROR;
- }
-
- // No signatures yet (will be reset later, maybe)
- archive->verify = PAKFIRE_ARCHIVE_VERIFY_NOT_SIGNED;
-
- // Verify all signatures
- r = pakfire_archive_walk(archive, pakfire_archive_verify_signature, 0, &check, NULL);
-
-ERROR:
- gpgme_data_release(check.checksums);
- if (buffer)
- free(buffer);
-
- return r;
-}
-
-PAKFIRE_EXPORT int pakfire_archive_verify(struct pakfire_archive* archive,
- pakfire_archive_verify_status_t* status, struct pakfire_key*** keys) {
- int r;
-
- DEBUG(archive->pakfire, "Verifying archive %p\n", archive);
-
- // Return previous result if this has already been called
- if (archive->verify == PAKFIRE_ARCHIVE_VERIFY_UNKNOWN) {
- // Verify all signatures
- r = pakfire_archive_verify_signatures(archive, keys);
- if (r)
- goto ERROR;
-
- // Verify checksums
- r = pakfire_archive_verify_checksums(archive, PAKFIRE_ARCHIVE_VERIFY_BEST);
- if (r)
- goto ERROR;
- }
-
- // Store result
- *status = archive->verify;
-
- // Log error
- ERROR(archive->pakfire, "Archive verification for %s has failed: %s\n",
- archive->path, pakfire_archive_verify_strerror(archive->verify));
-
- return 0;
-
-ERROR:
- if (keys && *keys) {
- for (struct pakfire_key** key = *keys; *key; key++)
- pakfire_key_unref(*key);
- free(*keys);
- }
-
- return r;
-}
-
-PAKFIRE_EXPORT const char* pakfire_archive_verify_strerror(pakfire_archive_verify_status_t status) {
- switch (status) {
- case PAKFIRE_ARCHIVE_VERIFY_UNKNOWN:
- return _("Unknown");
-
- case PAKFIRE_ARCHIVE_VERIFY_NOT_SIGNED:
- return _("Not signed");
-
- case PAKFIRE_ARCHIVE_VERIFY_OK:
- return _("Verify OK");
-
- case PAKFIRE_ARCHIVE_VERIFY_ERROR:
- return _("Error performing validation");
-
- case PAKFIRE_ARCHIVE_VERIFY_INVALID:
- return _("Invalid signature");
-
- case PAKFIRE_ARCHIVE_VERIFY_SIG_EXPIRED:
- return _("Signature expired");
-
- case PAKFIRE_ARCHIVE_VERIFY_KEY_EXPIRED:
- return _("Key expired");
-
- case PAKFIRE_ARCHIVE_VERIFY_KEY_UNKNOWN:
- return _("Key unknown");
- }
-
- return _("Unknown error");
-}
-
-static int pakfire_archive_create_signature(struct pakfire_archive* archive,
- struct pakfire_key* key, char** signature, size_t* signature_length, time_t* timestamp) {
- char* buffer = NULL;
- size_t length = 0;
-
- // Read chksums
- int r = open_archive_and_read(archive, "chksums", &buffer, &length);
- if (r) {
- ERROR(archive->pakfire, "Could not read chksums file: %m\n");
- goto ERROR;
- }
-
- // Use the key to sign the buffer
- r = pakfire_key_sign(key, buffer, length, signature, signature_length, timestamp);
- if (r)
- goto ERROR;
-
-ERROR:
- if (buffer)
- free(buffer);
-
- return r;
-}
-
-/*
- This function appends a single file to the archive
-
- Unfortunately libarchive cannot do this, so we have to manually find the end of the
- archive and write another entry...
-*/
-static int pakfire_archive_append_file(struct pakfire_archive* archive,
- struct archive_entry* entry, const char* buffer, const size_t length) {
- off_t offset = 0;
-
- // Find the end of the archive (exclusing the tar trailer)
- int r = pakfire_archive_find_end(archive, &offset);
- if (r)
- return r;
-
- // Create a new archive writer
- struct archive* a = archive_write_new();
- if (!a) {
- ERROR(archive->pakfire, "Could not create an archive writer: %m\n");
- return 1;
- }
-
- // Use the PAX format
- r = archive_write_set_format_pax(a);
- if (r) {
- ERROR(archive->pakfire, "Could not set format to PAX: %s\n", archive_error_string(a));
- goto ERROR;
- }
-
- // Seek to the end of the archive
- r = fseek(archive->f, offset, SEEK_SET);
- if (r) {
- ERROR(archive->pakfire, "Could not seek to position %jd in archive: %m\n", offset);
- return r;
- }
-
- // Write archive to f
- r = archive_write_open_FILE(a, archive->f);
- if (r) {
- ERROR(archive->pakfire, "archive_write_open_FILE() failed: %s\n",
- archive_error_string(a));
- goto ERROR;
- }
-
- // Write the header
- r = archive_write_header(a, entry);
- if (r) {
- ERROR(archive->pakfire, "Error writing file header: %s\n",
- archive_error_string(a));
- goto ERROR;
- }
-
- // Write the payload
- ssize_t bytes_written = archive_write_data(a, buffer, length);
- if (bytes_written < 0) {
- ERROR(archive->pakfire, "Error writing data: %s\n",
- archive_error_string(a));
- goto ERROR;
- }
-
- // Finish writing
- r = archive_write_finish_entry(a);
- if (r) {
- ERROR(archive->pakfire, "Could not finish entry: %s\n",
- archive_error_string(a));
- goto ERROR;
- }
-
- // Success
- r = 0;
-
-ERROR:
- archive_write_free(a);
-
- return r;
-}
-
-static int pakfire_archive_append_signature(struct pakfire_archive* archive,
- struct pakfire_key* key, const char* signature, size_t length, time_t timestamp) {
- char path[PATH_MAX];
- int r;
-
- // Make filename
- r = pakfire_string_format(path, "signatures/%s", pakfire_key_get_fingerprint(key));
- if (r)
- return r;
-
- // Create a new file entry
- struct archive_entry* entry = archive_entry_new();
- if (!entry)
- return 1;
-
- // Set filename
- archive_entry_set_pathname(entry, path);
-
- // This is a regular file
- archive_entry_set_filetype(entry, AE_IFREG);
- archive_entry_set_perm(entry, 0444);
-
- // Set size
- archive_entry_set_size(entry, length);
-
- // Set ownership
- archive_entry_set_uname(entry, "root");
- archive_entry_set_uid(entry, 0);
- archive_entry_set_gname(entry, "root");
- archive_entry_set_gid(entry, 0);
-
- // Set times
- archive_entry_set_birthtime(entry, timestamp, 0);
- archive_entry_set_ctime(entry, timestamp, 0);
- archive_entry_set_mtime(entry, timestamp, 0);
- archive_entry_set_atime(entry, timestamp, 0);
-
- // Write entry
- r = pakfire_archive_append_file(archive, entry, signature, length);
- archive_entry_free(entry);
-
- return r;
-}
-
-PAKFIRE_EXPORT int pakfire_archive_sign(struct pakfire_archive* archive, struct pakfire_key* key) {
- int r;
-
- // Verify checksums
- r = pakfire_archive_verify_checksums(archive, PAKFIRE_ARCHIVE_VERIFY_ALL);
- if (r) {
- ERROR(archive->pakfire, "The archive checksums don't match\n");
- return r;
- }
-
- char* signature = NULL;
- size_t signature_length = 0;
- time_t timestamp = 0;
-
- // Create the signature
- r = pakfire_archive_create_signature(archive, key,
- &signature, &signature_length, ×tamp);
- if (r)
- return r;
-
- // Append signature to archive
- r = pakfire_archive_append_signature(archive, key,
- signature, signature_length, timestamp);
- if (r)
- return r;
-
- return 0;
-}
-
PAKFIRE_EXPORT ssize_t pakfire_archive_get_size(struct pakfire_archive* archive) {
struct stat buf;
}
static int pakfire_archive_load_scriptlet(struct pakfire_archive* archive,
- struct archive* a, struct archive_entry* e, int flags, void* data) {
+ struct archive* a, struct archive_entry* e, void* data) {
const char* path = archive_entry_pathname(e);
// Skip if this isn't a scriptlet
int counter = 0;
- return pakfire_archive_walk(archive, pakfire_archive_load_scriptlet, 0, &counter, NULL);
+ return pakfire_archive_walk(archive, pakfire_archive_load_scriptlet, &counter);
}
struct pakfire_scriptlet* pakfire_archive_get_scriptlet(