slowly transitioning to a more efficient way of interacting with sealed packages
parent
7ebc3ebad3
commit
9b3c86aa93
|
@ -1,2 +1,3 @@
|
|||
.idea
|
||||
cmake-build-debug
|
||||
design
|
||||
|
|
|
@ -35,6 +35,7 @@ src/signing.cpp
|
|||
src/checksums.cpp
|
||||
src/metadata.cpp
|
||||
src/sealing.cpp
|
||||
src/archive_reader.cpp
|
||||
)
|
||||
|
||||
# Set output properties
|
||||
|
@ -67,6 +68,7 @@ src/signing.cpp
|
|||
src/checksums.cpp
|
||||
src/metadata.cpp
|
||||
src/sealing.cpp
|
||||
src/archive_reader.cpp
|
||||
)
|
||||
|
||||
# Define the BUILD_STANDALONE macro for the standalone build
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* @file archive_reader.hpp
|
||||
* @brief Functions for in-memory archive reading and verification
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <fcntl.h>
|
||||
#include "checksums.hpp"
|
||||
|
||||
extern "C" {
|
||||
/**
|
||||
* Extracts a specific file from a package file (gzipped tarball)
|
||||
*
|
||||
* @param package_file_path Path to the package file (.dpm)
|
||||
* @param file_path_in_archive Path of the file to extract within the archive
|
||||
* @param data Pointer to buffer pointer - will be allocated by function
|
||||
* @param data_size Pointer to size variable that will receive file size
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
bool get_file_from_package_file(const char* package_file_path, const char* file_path_in_archive, unsigned char** data, size_t* data_size);
|
||||
|
||||
/**
|
||||
* Extracts a specific file from an in-memory archive (gzipped tarball)
|
||||
*
|
||||
* @param archive_data Pointer to the archive data in memory
|
||||
* @param archive_data_size Size of the archive data in memory
|
||||
* @param file_path_in_archive Path of the file to extract within the archive
|
||||
* @param result_data Pointer to buffer pointer - will be allocated by function
|
||||
* @param result_data_size Pointer to size variable that will receive file size
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
bool get_file_from_memory_loaded_archive(const unsigned char* archive_data, const size_t archive_data_size,
|
||||
const char* file_path_in_archive,
|
||||
unsigned char** result_data, size_t* result_data_size);
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
/**
|
||||
* @file archive_reader.cpp
|
||||
* @brief Implementation of in-memory archive reading and verification
|
||||
*/
|
||||
|
||||
#include "archive_reader.hpp"
|
||||
#include "checksums.hpp"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
/**
|
||||
* Extracts a specific file from a package file (gzipped tarball)
|
||||
*
|
||||
* @param package_file_path Path to the package file (.dpm)
|
||||
* @param file_path_in_archive Path of the file to extract within the archive
|
||||
* @param data Pointer to buffer pointer - will be allocated by function
|
||||
* @param data_size Pointer to size variable that will receive file size
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
extern "C" bool get_file_from_package_file(const char* package_file_path, const char* file_path_in_archive, unsigned char** data, size_t* data_size)
|
||||
{
|
||||
if (!package_file_path || !file_path_in_archive || !data || !data_size) {
|
||||
dpm_log(LOG_ERROR, "Invalid parameters passed to get_file_from_package_file");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize output parameters
|
||||
*data = NULL;
|
||||
*data_size = 0;
|
||||
|
||||
// Create a new archive for reading
|
||||
struct archive* a = archive_read_new();
|
||||
if (!a) {
|
||||
dpm_log(LOG_ERROR, "Failed to create archive object");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable support for gzipped tarballs
|
||||
archive_read_support_filter_gzip(a);
|
||||
archive_read_support_format_tar(a);
|
||||
|
||||
// Open the package file - using 0 for block size lets libarchive choose the optimal size
|
||||
int r = archive_read_open_filename(a, package_file_path, 0);
|
||||
if (r != ARCHIVE_OK) {
|
||||
dpm_log(LOG_ERROR, ("Failed to open package file: " + std::string(package_file_path) +
|
||||
" - " + std::string(archive_error_string(a))).c_str());
|
||||
archive_read_free(a);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Iterate through archive entries
|
||||
bool found = false;
|
||||
struct archive_entry* entry;
|
||||
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
|
||||
const char* current_path = archive_entry_pathname(entry);
|
||||
|
||||
// Check if this is the file we're looking for
|
||||
if (strcmp(current_path, file_path_in_archive) == 0) {
|
||||
// Get the file size
|
||||
size_t file_size = archive_entry_size(entry);
|
||||
*data_size = file_size;
|
||||
|
||||
// Allocate buffer of appropriate size
|
||||
*data = (unsigned char*)malloc(file_size);
|
||||
if (!*data) {
|
||||
dpm_log(LOG_ERROR, "Failed to allocate memory for file contents");
|
||||
archive_read_free(a);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the file content into the buffer
|
||||
ssize_t bytes_read = archive_read_data(a, *data, file_size);
|
||||
if (bytes_read < 0 || (size_t)bytes_read != file_size) {
|
||||
dpm_log(LOG_ERROR, ("Failed to read file data: " +
|
||||
std::string(archive_error_string(a))).c_str());
|
||||
free(*data);
|
||||
*data = NULL;
|
||||
*data_size = 0;
|
||||
archive_read_free(a);
|
||||
return false;
|
||||
}
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip to next entry
|
||||
archive_read_data_skip(a);
|
||||
}
|
||||
|
||||
// Clean up
|
||||
archive_read_free(a);
|
||||
|
||||
if (!found) {
|
||||
dpm_log(LOG_ERROR, ("File not found in package: " +
|
||||
std::string(file_path_in_archive)).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a specific file from an in-memory archive (gzipped tarball)
|
||||
*
|
||||
* @param archive_data Pointer to the archive data in memory
|
||||
* @param archive_data_size Size of the archive data in memory
|
||||
* @param file_path_in_archive Path of the file to extract within the archive
|
||||
* @param result_data Pointer to buffer pointer - will be allocated by function
|
||||
* @param result_data_size Pointer to size variable that will receive file size
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
extern "C" bool get_file_from_memory_loaded_archive(const unsigned char* archive_data, const size_t archive_data_size,
|
||||
const char* file_path_in_archive,
|
||||
unsigned char** result_data, size_t* result_data_size)
|
||||
{
|
||||
if (!archive_data || archive_data_size == 0 || !file_path_in_archive ||
|
||||
!result_data || !result_data_size) {
|
||||
dpm_log(LOG_ERROR, "Invalid parameters passed to get_file_from_memory_loaded_archive");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize output parameters
|
||||
*result_data = NULL;
|
||||
*result_data_size = 0;
|
||||
|
||||
// Create a new archive for reading
|
||||
struct archive* a = archive_read_new();
|
||||
if (!a) {
|
||||
dpm_log(LOG_ERROR, "Failed to create archive object");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable support for gzipped tarballs
|
||||
archive_read_support_filter_gzip(a);
|
||||
archive_read_support_format_tar(a);
|
||||
|
||||
// Open the archive from memory
|
||||
int r = archive_read_open_memory(a, (void*)archive_data, archive_data_size);
|
||||
if (r != ARCHIVE_OK) {
|
||||
dpm_log(LOG_ERROR, ("Failed to open archive from memory: " +
|
||||
std::string(archive_error_string(a))).c_str());
|
||||
archive_read_free(a);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Iterate through archive entries
|
||||
bool found = false;
|
||||
struct archive_entry* entry;
|
||||
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
|
||||
const char* current_path = archive_entry_pathname(entry);
|
||||
|
||||
// Check if this is the file we're looking for
|
||||
if (strcmp(current_path, file_path_in_archive) == 0) {
|
||||
// Get the file size
|
||||
size_t file_size = archive_entry_size(entry);
|
||||
*result_data_size = file_size;
|
||||
|
||||
// Allocate buffer of appropriate size
|
||||
*result_data = (unsigned char*)malloc(file_size);
|
||||
if (!*result_data) {
|
||||
dpm_log(LOG_ERROR, "Failed to allocate memory for file contents");
|
||||
archive_read_free(a);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the file content into the buffer
|
||||
ssize_t bytes_read = archive_read_data(a, *result_data, file_size);
|
||||
if (bytes_read < 0 || (size_t)bytes_read != file_size) {
|
||||
dpm_log(LOG_ERROR, ("Failed to read file data from memory archive: " +
|
||||
std::string(archive_error_string(a))).c_str());
|
||||
free(*result_data);
|
||||
*result_data = NULL;
|
||||
*result_data_size = 0;
|
||||
archive_read_free(a);
|
||||
return false;
|
||||
}
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip to next entry
|
||||
archive_read_data_skip(a);
|
||||
}
|
||||
|
||||
// Clean up
|
||||
archive_read_free(a);
|
||||
|
||||
if (!found) {
|
||||
dpm_log(LOG_ERROR, ("File not found in memory archive: " +
|
||||
std::string(file_path_in_archive)).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -19,6 +19,8 @@ add_library(verify MODULE
|
|||
src/verification.cpp
|
||||
src/checksum.cpp
|
||||
../../dpmdk/src/ModuleOperations.cpp
|
||||
src/package_operations.cpp
|
||||
src/checksum_memory.cpp
|
||||
)
|
||||
|
||||
# Set output properties
|
||||
|
@ -47,6 +49,8 @@ add_executable(verify_standalone
|
|||
src/verification.cpp
|
||||
src/checksum.cpp
|
||||
../../dpmdk/src/ModuleOperations.cpp
|
||||
src/package_operations.cpp
|
||||
src/checksum_memory.cpp
|
||||
)
|
||||
|
||||
# Define the BUILD_STANDALONE macro for the standalone build
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* @file checksum_memory.hpp
|
||||
* @brief In-memory package checksum verification functions
|
||||
*
|
||||
* Defines functions for verifying checksums of DPM package components in memory
|
||||
* without requiring them to be extracted to disk first.
|
||||
*
|
||||
* @copyright Copyright (c) 2025 SILO GROUP LLC
|
||||
* @author Chris Punches <chris.punches@silogroup.org>
|
||||
*
|
||||
* Part of the Dark Horse Linux Package Manager (DPM)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <dpmdk/include/CommonModuleAPI.hpp>
|
||||
#include "package_operations.hpp"
|
||||
#include <filesystem>
|
||||
#include <dlfcn.h>
|
||||
|
||||
/**
|
||||
* @brief Verifies the package digest from in-memory metadata
|
||||
*
|
||||
* Calculates the package digest from in-memory CONTENTS_MANIFEST_DIGEST and
|
||||
* HOOKS_DIGEST files and compares it to the value in PACKAGE_DIGEST.
|
||||
*
|
||||
* @param data Pointer to the metadata file data
|
||||
* @param data_size Size of the metadata file data
|
||||
* @param build_module Handle to the loaded build module
|
||||
* @return 0 on successful verification, non-zero on failure
|
||||
*/
|
||||
int checksum_verify_package_digest_memory(
|
||||
const unsigned char* data,
|
||||
size_t data_size,
|
||||
void* build_module);
|
||||
|
||||
/**
|
||||
* @brief Verifies the contents manifest digest from in-memory data
|
||||
*
|
||||
* Compares checksums in the contents manifest with actual file checksums
|
||||
* using in-memory data rather than extracting files to disk.
|
||||
*
|
||||
* @param contents_data Pointer to the contents component data
|
||||
* @param contents_data_size Size of the contents component data
|
||||
* @param metadata_data Pointer to the metadata component data
|
||||
* @param metadata_data_size Size of the metadata component data
|
||||
* @param build_module Handle to the loaded build module
|
||||
* @return 0 on successful verification, non-zero on failure
|
||||
*/
|
||||
int checksum_verify_contents_digest_memory(
|
||||
const unsigned char* contents_data,
|
||||
size_t contents_data_size,
|
||||
const unsigned char* metadata_data,
|
||||
size_t metadata_data_size,
|
||||
void* build_module);
|
||||
|
||||
/**
|
||||
* @brief Verifies the hooks digest from in-memory data
|
||||
*
|
||||
* Calculates the digest of the hooks archive and compares it with the
|
||||
* value stored in HOOKS_DIGEST metadata file.
|
||||
*
|
||||
* @param hooks_data Pointer to the hooks component data
|
||||
* @param hooks_data_size Size of the hooks component data
|
||||
* @param metadata_data Pointer to the metadata component data
|
||||
* @param metadata_data_size Size of the metadata component data
|
||||
* @param build_module Handle to the loaded build module
|
||||
* @return 0 on successful verification, non-zero on failure
|
||||
*/
|
||||
int checksum_verify_hooks_digest_memory(
|
||||
const unsigned char* hooks_data,
|
||||
size_t hooks_data_size,
|
||||
const unsigned char* metadata_data,
|
||||
size_t metadata_data_size,
|
||||
void* build_module);
|
|
@ -18,6 +18,8 @@
|
|||
#include <dlfcn.h>
|
||||
#include <sys/stat.h>
|
||||
#include <filesystem>
|
||||
#include "checksum_memory.hpp"
|
||||
#include "package_operations.hpp"
|
||||
|
||||
/**
|
||||
* @brief Handler for the checksum command
|
||||
|
@ -157,4 +159,27 @@ int verify_signature_package(const std::string& package_path);
|
|||
* @param stage_dir Path to the stage directory
|
||||
* @return 0 on success, non-zero on failure
|
||||
*/
|
||||
int verify_signature_stage(const std::string& stage_dir);
|
||||
int verify_signature_stage(const std::string& stage_dir);
|
||||
|
||||
/**
|
||||
* @brief Verifies checksums of a package file in memory
|
||||
*
|
||||
* Loads the components of a package file into memory and verifies their checksums
|
||||
* without extracting them to disk.
|
||||
*
|
||||
* @param package_path Path to the package file
|
||||
* @return 0 on success, non-zero on failure
|
||||
*/
|
||||
int verify_checksums_package_memory(const std::string& package_path);
|
||||
|
||||
/**
|
||||
* @brief Converts binary data to a C++ string
|
||||
*
|
||||
* Takes a buffer of binary data and its size, creates a properly
|
||||
* null-terminated string, and returns it as an std::string.
|
||||
*
|
||||
* @param data Pointer to the binary data
|
||||
* @param data_size Size of the binary data
|
||||
* @return std::string containing the data, or empty string on error
|
||||
*/
|
||||
std::string binary_to_string(const unsigned char* data, size_t data_size);
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* @file package_operations.hpp
|
||||
* @brief Functions for operating on DPM packages
|
||||
*
|
||||
* Defines functions for extracting and verifying components from DPM packages.
|
||||
*
|
||||
* @copyright Copyright (c) 2025 SILO GROUP LLC
|
||||
* @author Chris Punches <chris.punches@silogroup.org>
|
||||
*
|
||||
* Part of the Dark Horse Linux Package Manager (DPM)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <dpmdk/include/CommonModuleAPI.hpp>
|
||||
#include "commands.hpp"
|
||||
#include <filesystem>
|
||||
|
||||
/**
|
||||
* @brief Extracts a component from a package file
|
||||
*
|
||||
* Loads a component (metadata, contents, hooks, signatures) from a package file
|
||||
* by calling into the build module's get_file_from_package_file function.
|
||||
*
|
||||
* @param package_path Path to the package file
|
||||
* @param component_name Name of the component to extract (metadata, contents, hooks, signatures)
|
||||
* @param data Pointer to a pointer that will be populated with the component data
|
||||
* @param data_size Pointer to a size_t that will be populated with the size of the component data
|
||||
* @return 0 on success, non-zero on failure
|
||||
*/
|
||||
int get_component_from_package(const std::string& package_path,
|
||||
const std::string& component_name,
|
||||
unsigned char** data,
|
||||
size_t* data_size);
|
||||
|
||||
/**
|
||||
* @brief Extracts a file from a component archive
|
||||
*
|
||||
* Extracts a specific file from a component archive that has already been loaded into memory.
|
||||
* Uses the build module's get_file_from_memory_loaded_archive function.
|
||||
*
|
||||
* @param component_data Pointer to the component archive data in memory
|
||||
* @param component_size Size of the component archive in memory
|
||||
* @param filename Name of the file to extract from the component
|
||||
* @param data Pointer to a pointer that will be populated with the file data
|
||||
* @param data_size Pointer to a size_t that will be populated with the size of the file data
|
||||
* @return 0 on success, non-zero on failure
|
||||
*/
|
||||
int get_file_from_component(const unsigned char* component_data,
|
||||
size_t component_size,
|
||||
const std::string& filename,
|
||||
unsigned char** data,
|
||||
size_t* data_size);
|
|
@ -297,4 +297,5 @@ int checksum_verify_package_digest(const std::string& stage_dir, void* build_mod
|
|||
|
||||
dpm_log(LOG_INFO, "Package digest verification successful");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,427 @@
|
|||
/**
|
||||
* @file checksum_memory.cpp
|
||||
* @brief Implementation of in-memory package checksum verification functions
|
||||
*
|
||||
* Implements functions for verifying checksums of DPM package components in memory
|
||||
* without requiring them to be extracted to disk first.
|
||||
*
|
||||
* @copyright Copyright (c) 2025 SILO GROUP LLC
|
||||
* @author Chris Punches <chris.punches@silogroup.org>
|
||||
*
|
||||
* Part of the Dark Horse Linux Package Manager (DPM)
|
||||
*/
|
||||
|
||||
#include "checksum_memory.hpp"
|
||||
|
||||
/**
|
||||
* @brief Converts binary data to a C++ string
|
||||
*
|
||||
* Takes a buffer of binary data and its size, creates a properly
|
||||
* null-terminated string, and returns it as an std::string.
|
||||
*
|
||||
* @param data Pointer to the binary data
|
||||
* @param data_size Size of the binary data
|
||||
* @return std::string containing the data, or empty string on error
|
||||
*/
|
||||
std::string binary_to_string(const unsigned char* data, size_t data_size) {
|
||||
if (!data || data_size == 0) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// Create a temporary C-string with null termination
|
||||
char* temp = (char*)malloc(data_size + 1);
|
||||
if (!temp) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
memcpy(temp, data, data_size);
|
||||
temp[data_size] = '\0';
|
||||
|
||||
// Create std::string from the C-string
|
||||
std::string result(temp);
|
||||
|
||||
// Free the temporary buffer
|
||||
free(temp);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Verifies the package digest from in-memory metadata
|
||||
*
|
||||
* Calculates the package digest from in-memory CONTENTS_MANIFEST_DIGEST and
|
||||
* HOOKS_DIGEST files and compares it to the value in PACKAGE_DIGEST.
|
||||
*
|
||||
* @param package_data Pointer to the metadata component data
|
||||
* @param package_data_size Size of the metadata component data
|
||||
* @param build_module Handle to the loaded build module
|
||||
* @return 0 on successful verification, non-zero on failure
|
||||
*/
|
||||
int checksum_verify_package_digest_memory(
|
||||
const unsigned char* package_data,
|
||||
size_t package_data_size,
|
||||
void* build_module)
|
||||
{
|
||||
// Validate input parameters
|
||||
if (!package_data || package_data_size == 0 || !build_module) {
|
||||
dpm_log(LOG_ERROR, "Invalid parameters passed to checksum_verify_package_digest_memory");
|
||||
return 1;
|
||||
}
|
||||
|
||||
dpm_log(LOG_INFO, "Verifying package digest from in-memory data...");
|
||||
|
||||
// First, extract PACKAGE_DIGEST, CONTENTS_MANIFEST_DIGEST, and HOOKS_DIGEST from the metadata component
|
||||
unsigned char* package_digest_data = nullptr;
|
||||
size_t package_digest_size = 0;
|
||||
unsigned char* contents_manifest_data = nullptr;
|
||||
size_t contents_manifest_size = 0;
|
||||
unsigned char* hooks_digest_data = nullptr;
|
||||
size_t hooks_digest_size = 0;
|
||||
|
||||
// Get PACKAGE_DIGEST from the metadata component
|
||||
int result = get_file_from_component(
|
||||
package_data,
|
||||
package_data_size,
|
||||
"PACKAGE_DIGEST",
|
||||
&package_digest_data,
|
||||
&package_digest_size
|
||||
);
|
||||
|
||||
if (result != 0 || !package_digest_data || package_digest_size == 0) {
|
||||
dpm_log(LOG_ERROR, "Failed to extract PACKAGE_DIGEST from metadata component");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get CONTENTS_MANIFEST_DIGEST from the metadata component
|
||||
result = get_file_from_component(
|
||||
package_data,
|
||||
package_data_size,
|
||||
"CONTENTS_MANIFEST_DIGEST",
|
||||
&contents_manifest_data,
|
||||
&contents_manifest_size
|
||||
);
|
||||
|
||||
if (result != 0 || !contents_manifest_data || contents_manifest_size == 0) {
|
||||
dpm_log(LOG_ERROR, "Failed to extract CONTENTS_MANIFEST_DIGEST from metadata component");
|
||||
free(package_digest_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get HOOKS_DIGEST from the metadata component
|
||||
result = get_file_from_component(
|
||||
package_data,
|
||||
package_data_size,
|
||||
"HOOKS_DIGEST",
|
||||
&hooks_digest_data,
|
||||
&hooks_digest_size
|
||||
);
|
||||
|
||||
if (result != 0 || !hooks_digest_data || hooks_digest_size == 0) {
|
||||
dpm_log(LOG_ERROR, "Failed to extract HOOKS_DIGEST from metadata component");
|
||||
free(package_digest_data);
|
||||
free(contents_manifest_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Convert binary data to strings using our utility function
|
||||
std::string package_digest_str = binary_to_string(package_digest_data, package_digest_size);
|
||||
std::string contents_manifest_str = binary_to_string(contents_manifest_data, contents_manifest_size);
|
||||
std::string hooks_digest_str = binary_to_string(hooks_digest_data, hooks_digest_size);
|
||||
|
||||
// Check if any conversion failed
|
||||
if (package_digest_str.empty() || contents_manifest_str.empty() || hooks_digest_str.empty()) {
|
||||
dpm_log(LOG_ERROR, "Failed to convert binary data to strings");
|
||||
free(package_digest_data);
|
||||
free(contents_manifest_data);
|
||||
free(hooks_digest_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Calculate checksums using the build module's functions through dpm_execute_symbol
|
||||
std::string contents_manifest_checksum;
|
||||
result = dpm_execute_symbol(build_module, "generate_string_checksum",
|
||||
contents_manifest_str, &contents_manifest_checksum);
|
||||
|
||||
if (result != 0 || contents_manifest_checksum.empty()) {
|
||||
dpm_log(LOG_ERROR, "Failed to calculate checksum for contents manifest");
|
||||
free(package_digest_data);
|
||||
free(contents_manifest_data);
|
||||
free(hooks_digest_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string hooks_digest_checksum;
|
||||
result = dpm_execute_symbol(build_module, "generate_string_checksum",
|
||||
hooks_digest_str, &hooks_digest_checksum);
|
||||
|
||||
if (result != 0 || hooks_digest_checksum.empty()) {
|
||||
dpm_log(LOG_ERROR, "Failed to calculate checksum for hooks digest");
|
||||
free(package_digest_data);
|
||||
free(contents_manifest_data);
|
||||
free(hooks_digest_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Combine checksums and calculate package digest
|
||||
std::string combined_checksums = contents_manifest_checksum + hooks_digest_checksum;
|
||||
std::string calculated_package_digest;
|
||||
|
||||
result = dpm_execute_symbol(build_module, "generate_string_checksum",
|
||||
combined_checksums, &calculated_package_digest);
|
||||
|
||||
if (result != 0 || calculated_package_digest.empty()) {
|
||||
dpm_log(LOG_ERROR, "Failed to calculate package digest");
|
||||
free(package_digest_data);
|
||||
free(contents_manifest_data);
|
||||
free(hooks_digest_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Compare with the stored package digest
|
||||
bool match = (calculated_package_digest == package_digest_str);
|
||||
|
||||
// Clean up
|
||||
free(package_digest_data);
|
||||
free(contents_manifest_data);
|
||||
free(hooks_digest_data);
|
||||
|
||||
if (!match) {
|
||||
dpm_log(LOG_ERROR, ("Package digest mismatch\n Expected: " + package_digest_str +
|
||||
"\n Actual: " + calculated_package_digest).c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
dpm_log(LOG_INFO, "Package digest verification successful");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Verifies the contents manifest digest from in-memory data
|
||||
*
|
||||
* Compares checksums in the contents manifest with actual file checksums
|
||||
* using in-memory data rather than extracting files to disk.
|
||||
*
|
||||
* @param contents_data Pointer to the contents component data
|
||||
* @param contents_data_size Size of the contents component data
|
||||
* @param metadata_data Pointer to the metadata component data
|
||||
* @param metadata_data_size Size of the metadata component data
|
||||
* @param build_module Handle to the loaded build module
|
||||
* @return 0 on successful verification, non-zero on failure
|
||||
*/
|
||||
int checksum_verify_contents_digest_memory(
|
||||
const unsigned char* contents_data,
|
||||
size_t contents_data_size,
|
||||
const unsigned char* metadata_data,
|
||||
size_t metadata_data_size,
|
||||
void* build_module)
|
||||
{
|
||||
// Validate input parameters
|
||||
if (!contents_data || contents_data_size == 0 ||
|
||||
!metadata_data || metadata_data_size == 0 || !build_module) {
|
||||
dpm_log(LOG_ERROR, "Invalid parameters passed to checksum_verify_contents_digest_memory");
|
||||
return 1;
|
||||
}
|
||||
|
||||
dpm_log(LOG_INFO, "Verifying contents manifest digest from in-memory data...");
|
||||
|
||||
// Extract CONTENTS_MANIFEST_DIGEST from the metadata component
|
||||
unsigned char* manifest_data = nullptr;
|
||||
size_t manifest_size = 0;
|
||||
|
||||
int result = get_file_from_component(
|
||||
metadata_data,
|
||||
metadata_data_size,
|
||||
"CONTENTS_MANIFEST_DIGEST",
|
||||
&manifest_data,
|
||||
&manifest_size
|
||||
);
|
||||
|
||||
if (result != 0 || !manifest_data || manifest_size == 0) {
|
||||
dpm_log(LOG_ERROR, "Failed to extract CONTENTS_MANIFEST_DIGEST from metadata component");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Convert binary data to string
|
||||
std::string manifest_str = binary_to_string(manifest_data, manifest_size);
|
||||
if (manifest_str.empty()) {
|
||||
dpm_log(LOG_ERROR, "Failed to convert manifest data to string");
|
||||
free(manifest_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Parse the manifest lines
|
||||
std::istringstream manifest_stream(manifest_str);
|
||||
std::string line;
|
||||
int errors = 0;
|
||||
int line_number = 0;
|
||||
|
||||
// Process each line in the manifest
|
||||
while (std::getline(manifest_stream, line)) {
|
||||
line_number++;
|
||||
|
||||
// Skip empty lines
|
||||
if (line.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse the line into its components
|
||||
std::istringstream iss(line);
|
||||
char control_designation;
|
||||
std::string expected_checksum, permissions, ownership, file_path;
|
||||
|
||||
// Extract components (C checksum permissions owner:group /path/to/file)
|
||||
if (!(iss >> control_designation >> expected_checksum >> permissions >> ownership)) {
|
||||
dpm_log(LOG_WARN, ("Malformed manifest line " + std::to_string(line_number) +
|
||||
": " + line).c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the rest of the line as the file path
|
||||
std::getline(iss >> std::ws, file_path);
|
||||
|
||||
if (file_path.empty()) {
|
||||
dpm_log(LOG_WARN, ("Missing file path in manifest line " +
|
||||
std::to_string(line_number)).c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove leading slash if present
|
||||
if (file_path[0] == '/') {
|
||||
file_path = file_path.substr(1);
|
||||
}
|
||||
|
||||
// Extract the file from the contents component
|
||||
unsigned char* file_data = nullptr;
|
||||
size_t file_size = 0;
|
||||
|
||||
result = get_file_from_component(
|
||||
contents_data,
|
||||
contents_data_size,
|
||||
file_path,
|
||||
&file_data,
|
||||
&file_size
|
||||
);
|
||||
|
||||
if (result != 0 || !file_data) {
|
||||
dpm_log(LOG_ERROR, ("Failed to extract file from contents: " + file_path).c_str());
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate the checksum of the file data
|
||||
std::string calculated_checksum;
|
||||
result = dpm_execute_symbol(build_module, "generate_string_checksum",
|
||||
std::string(reinterpret_cast<char*>(file_data), file_size),
|
||||
&calculated_checksum);
|
||||
|
||||
// Free the file data now that we're done with it
|
||||
free(file_data);
|
||||
|
||||
if (result != 0 || calculated_checksum.empty()) {
|
||||
dpm_log(LOG_ERROR, ("Failed to calculate checksum for file: " + file_path).c_str());
|
||||
errors++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compare with the expected checksum
|
||||
if (calculated_checksum != expected_checksum) {
|
||||
dpm_log(LOG_ERROR, ("Checksum mismatch for " + file_path +
|
||||
"\n Expected: " + expected_checksum +
|
||||
"\n Actual: " + calculated_checksum).c_str());
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up
|
||||
free(manifest_data);
|
||||
|
||||
if (errors > 0) {
|
||||
dpm_log(LOG_ERROR, (std::to_string(errors) + " checksum errors found in contents manifest").c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
dpm_log(LOG_INFO, "Contents manifest checksum verification successful");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Verifies the hooks digest from in-memory data
|
||||
*
|
||||
* Calculates the digest of the hooks archive and compares it with the
|
||||
* value stored in HOOKS_DIGEST metadata file.
|
||||
*
|
||||
* @param hooks_data Pointer to the hooks component data
|
||||
* @param hooks_data_size Size of the hooks component data
|
||||
* @param metadata_data Pointer to the metadata component data
|
||||
* @param metadata_data_size Size of the metadata component data
|
||||
* @param build_module Handle to the loaded build module
|
||||
* @return 0 on successful verification, non-zero on failure
|
||||
*/
|
||||
int checksum_verify_hooks_digest_memory(
|
||||
const unsigned char* hooks_data,
|
||||
size_t hooks_data_size,
|
||||
const unsigned char* metadata_data,
|
||||
size_t metadata_data_size,
|
||||
void* build_module)
|
||||
{
|
||||
// Validate input parameters
|
||||
if (!hooks_data || hooks_data_size == 0 ||
|
||||
!metadata_data || metadata_data_size == 0 || !build_module) {
|
||||
dpm_log(LOG_ERROR, "Invalid parameters passed to checksum_verify_hooks_digest_memory");
|
||||
return 1;
|
||||
}
|
||||
|
||||
dpm_log(LOG_INFO, "Verifying hooks digest from in-memory data...");
|
||||
|
||||
// Extract HOOKS_DIGEST from the metadata component
|
||||
unsigned char* hooks_digest_data = nullptr;
|
||||
size_t hooks_digest_size = 0;
|
||||
|
||||
int result = get_file_from_component(
|
||||
metadata_data,
|
||||
metadata_data_size,
|
||||
"HOOKS_DIGEST",
|
||||
&hooks_digest_data,
|
||||
&hooks_digest_size
|
||||
);
|
||||
|
||||
if (result != 0 || !hooks_digest_data || hooks_digest_size == 0) {
|
||||
dpm_log(LOG_ERROR, "Failed to extract HOOKS_DIGEST from metadata component");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Convert binary data to string
|
||||
std::string stored_hooks_digest = binary_to_string(hooks_digest_data, hooks_digest_size);
|
||||
if (stored_hooks_digest.empty()) {
|
||||
dpm_log(LOG_ERROR, "Failed to convert hooks digest data to string");
|
||||
free(hooks_digest_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Trim whitespace and newlines
|
||||
stored_hooks_digest = stored_hooks_digest.substr(0, stored_hooks_digest.find_first_of("\r\n"));
|
||||
|
||||
// Calculate the checksum for the hooks archive data
|
||||
std::string calculated_hooks_digest;
|
||||
result = dpm_execute_symbol(build_module, "generate_string_checksum",
|
||||
std::string(reinterpret_cast<const char*>(hooks_data), hooks_data_size),
|
||||
&calculated_hooks_digest);
|
||||
|
||||
// Clean up
|
||||
free(hooks_digest_data);
|
||||
|
||||
if (result != 0 || calculated_hooks_digest.empty()) {
|
||||
dpm_log(LOG_ERROR, "Failed to calculate hooks digest");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Compare with the stored digest
|
||||
if (calculated_hooks_digest != stored_hooks_digest) {
|
||||
dpm_log(LOG_ERROR, ("Hooks digest mismatch\n Expected: " + stored_hooks_digest +
|
||||
"\n Actual: " + calculated_hooks_digest).c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
dpm_log(LOG_INFO, "Hooks digest verification successful");
|
||||
return 0;
|
||||
}
|
|
@ -269,5 +269,131 @@ int cmd_check(int argc, char** argv) {
|
|||
dpm_unload_module(module_handle);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Verifies checksums of a package file in memory
|
||||
*
|
||||
* Loads the components of a package file into memory and verifies their checksums
|
||||
* without extracting them to disk.
|
||||
*
|
||||
* @param package_path Path to the package file
|
||||
* @return 0 on success, non-zero on failure
|
||||
*/
|
||||
int verify_checksums_package_memory(const std::string& package_path) {
|
||||
// Check if the package file exists
|
||||
if (!std::filesystem::exists(package_path)) {
|
||||
dpm_log(LOG_ERROR, ("Package file not found: " + package_path).c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
dpm_log(LOG_INFO, ("Verifying checksums for package in memory: " + package_path).c_str());
|
||||
|
||||
// Load the build module
|
||||
void* build_module = nullptr;
|
||||
int result = check_and_load_build_module(build_module);
|
||||
if (result != 0 || build_module == nullptr) {
|
||||
dpm_log(LOG_ERROR, "Failed to load build module");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Extract package components into memory
|
||||
unsigned char* metadata_data = nullptr;
|
||||
size_t metadata_data_size = 0;
|
||||
unsigned char* contents_data = nullptr;
|
||||
size_t contents_data_size = 0;
|
||||
unsigned char* hooks_data = nullptr;
|
||||
size_t hooks_data_size = 0;
|
||||
|
||||
// Load metadata component
|
||||
dpm_log(LOG_INFO, "Loading metadata component...");
|
||||
result = get_component_from_package(package_path, "metadata", &metadata_data, &metadata_data_size);
|
||||
if (result != 0 || !metadata_data || metadata_data_size == 0) {
|
||||
dpm_log(LOG_ERROR, "Failed to load metadata component");
|
||||
dpm_unload_module(build_module);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load contents component
|
||||
dpm_log(LOG_INFO, "Loading contents component...");
|
||||
result = get_component_from_package(package_path, "contents", &contents_data, &contents_data_size);
|
||||
if (result != 0 || !contents_data || contents_data_size == 0) {
|
||||
dpm_log(LOG_ERROR, "Failed to load contents component");
|
||||
free(metadata_data);
|
||||
dpm_unload_module(build_module);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load hooks component
|
||||
dpm_log(LOG_INFO, "Loading hooks component...");
|
||||
result = get_component_from_package(package_path, "hooks", &hooks_data, &hooks_data_size);
|
||||
if (result != 0 || !hooks_data || hooks_data_size == 0) {
|
||||
dpm_log(LOG_ERROR, "Failed to load hooks component");
|
||||
free(metadata_data);
|
||||
free(contents_data);
|
||||
dpm_unload_module(build_module);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Verify package digest
|
||||
dpm_log(LOG_INFO, "Verifying package digest...");
|
||||
result = checksum_verify_package_digest_memory(
|
||||
metadata_data,
|
||||
metadata_data_size,
|
||||
build_module
|
||||
);
|
||||
if (result != 0) {
|
||||
dpm_log(LOG_ERROR, "Package digest verification failed");
|
||||
free(metadata_data);
|
||||
free(contents_data);
|
||||
free(hooks_data);
|
||||
dpm_unload_module(build_module);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Verify contents manifest digest
|
||||
dpm_log(LOG_INFO, "Verifying contents manifest digest...");
|
||||
result = checksum_verify_contents_digest_memory(
|
||||
contents_data,
|
||||
contents_data_size,
|
||||
metadata_data,
|
||||
metadata_data_size,
|
||||
build_module
|
||||
);
|
||||
if (result != 0) {
|
||||
dpm_log(LOG_ERROR, "Contents manifest verification failed");
|
||||
free(metadata_data);
|
||||
free(contents_data);
|
||||
free(hooks_data);
|
||||
dpm_unload_module(build_module);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Verify hooks digest
|
||||
dpm_log(LOG_INFO, "Verifying hooks digest...");
|
||||
result = checksum_verify_hooks_digest_memory(
|
||||
hooks_data,
|
||||
hooks_data_size,
|
||||
metadata_data,
|
||||
metadata_data_size,
|
||||
build_module
|
||||
);
|
||||
if (result != 0) {
|
||||
dpm_log(LOG_ERROR, "Hooks digest verification failed");
|
||||
free(metadata_data);
|
||||
free(contents_data);
|
||||
free(hooks_data);
|
||||
dpm_unload_module(build_module);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Clean up
|
||||
free(metadata_data);
|
||||
free(contents_data);
|
||||
free(hooks_data);
|
||||
dpm_unload_module(build_module);
|
||||
|
||||
dpm_log(LOG_INFO, "All in-memory checksums verified successfully");
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* @file package_operations.cpp
|
||||
* @brief Implementation of package operation functions
|
||||
*
|
||||
* Implements functions for extracting and verifying components from DPM packages.
|
||||
*
|
||||
* @copyright Copyright (c) 2025 SILO GROUP LLC
|
||||
* @author Chris Punches <chris.punches@silogroup.org>
|
||||
*
|
||||
* Part of the Dark Horse Linux Package Manager (DPM)
|
||||
*/
|
||||
|
||||
#include "package_operations.hpp"
|
||||
|
||||
|
||||
int get_component_from_package(const std::string& package_path,
|
||||
const std::string& component_name,
|
||||
unsigned char** data,
|
||||
size_t* data_size)
|
||||
{
|
||||
// Validate input parameters
|
||||
if (package_path.empty() || component_name.empty() || !data || !data_size) {
|
||||
dpm_log(LOG_ERROR, "Invalid parameters passed to get_component_from_package");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialize output parameters
|
||||
*data = nullptr;
|
||||
*data_size = 0;
|
||||
|
||||
// Check if the package file exists
|
||||
if (!std::filesystem::exists(package_path)) {
|
||||
dpm_log(LOG_ERROR, ("Package file not found: " + package_path).c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load the build module
|
||||
void* build_module = nullptr;
|
||||
int result = check_and_load_build_module(build_module);
|
||||
if (result != 0 || build_module == nullptr) {
|
||||
dpm_log(LOG_ERROR, "Failed to load build module");
|
||||
return 1;
|
||||
}
|
||||
|
||||
dpm_log(LOG_DEBUG, ("Extracting " + component_name + " from package: " + package_path).c_str());
|
||||
|
||||
// Call the function from the build module
|
||||
bool success = dpm_execute_symbol(build_module, "get_file_from_package_file",
|
||||
package_path.c_str(), component_name.c_str(),
|
||||
data, data_size);
|
||||
|
||||
// Unload the build module
|
||||
dpm_unload_module(build_module);
|
||||
|
||||
// Check if the function call was successful
|
||||
if (!success || *data == nullptr || *data_size == 0) {
|
||||
dpm_log(LOG_ERROR, ("Failed to extract " + component_name + " from package").c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
dpm_log(LOG_DEBUG, ("Successfully extracted " + component_name + " (" +
|
||||
std::to_string(*data_size) + " bytes)").c_str());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extracts a file from a component archive
|
||||
*
|
||||
* Extracts a specific file from a component archive that has already been loaded into memory.
|
||||
* Uses the build module's get_file_from_memory_loaded_archive function.
|
||||
*
|
||||
* @param component_data Pointer to the component archive data in memory
|
||||
* @param component_size Size of the component archive in memory
|
||||
* @param filename Name of the file to extract from the component
|
||||
* @param data Pointer to a pointer that will be populated with the file data
|
||||
* @param data_size Pointer to a size_t that will be populated with the size of the file data
|
||||
* @return 0 on success, non-zero on failure
|
||||
*/
|
||||
int get_file_from_component(const unsigned char* component_data,
|
||||
size_t component_size,
|
||||
const std::string& filename,
|
||||
unsigned char** data,
|
||||
size_t* data_size)
|
||||
{
|
||||
// Validate input parameters
|
||||
if (!component_data || component_size == 0 || filename.empty() || !data || !data_size) {
|
||||
dpm_log(LOG_ERROR, "Invalid parameters passed to get_file_from_component");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialize output parameters
|
||||
*data = nullptr;
|
||||
*data_size = 0;
|
||||
|
||||
// Load the build module
|
||||
void* build_module = nullptr;
|
||||
int result = check_and_load_build_module(build_module);
|
||||
if (result != 0 || build_module == nullptr) {
|
||||
dpm_log(LOG_ERROR, "Failed to load build module");
|
||||
return 1;
|
||||
}
|
||||
|
||||
dpm_log(LOG_DEBUG, ("Extracting file '" + filename + "' from component archive").c_str());
|
||||
|
||||
// Call the function from the build module
|
||||
bool success = dpm_execute_symbol(build_module, "get_file_from_memory_loaded_archive",
|
||||
component_data, component_size,
|
||||
filename.c_str(),
|
||||
data, data_size);
|
||||
|
||||
// Unload the build module
|
||||
dpm_unload_module(build_module);
|
||||
|
||||
// Check if the function call was successful
|
||||
if (!success || *data == nullptr || *data_size == 0) {
|
||||
dpm_log(LOG_ERROR, ("Failed to extract file '" + filename + "' from component archive").c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
dpm_log(LOG_DEBUG, ("Successfully extracted file '" + filename + "' (" +
|
||||
std::to_string(*data_size) + " bytes)").c_str());
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -10,10 +10,8 @@
|
|||
*
|
||||
* Part of the Dark Horse Linux Package Manager (DPM)
|
||||
*/
|
||||
|
||||
#include "verification.hpp"
|
||||
|
||||
|
||||
int verify_checksums_package(const std::string& package_path) {
|
||||
// Check if the package file exists
|
||||
if (!std::filesystem::exists(package_path)) {
|
||||
|
@ -193,7 +191,6 @@ int verify_signature_package(const std::string& package_path) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int verify_signature_stage(const std::string& stage_dir) {
|
||||
// Check if the stage directory exists
|
||||
if (!std::filesystem::exists(stage_dir)) {
|
||||
|
@ -212,4 +209,4 @@ int verify_signature_stage(const std::string& stage_dir) {
|
|||
dpm_log(LOG_INFO, "Stage directory signature verification not yet implemented");
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue