slowly transitioning to a more efficient way of interacting with sealed packages

master
Chris Punches 2025-04-06 19:42:17 -04:00
parent 7ebc3ebad3
commit 9b3c86aa93
13 changed files with 1090 additions and 6 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
.idea
cmake-build-debug
design

View File

@ -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

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}