Add new binary integrity verification function

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER
2024-09-26 11:02:39 +02:00
parent cb8e8b0647
commit bb93467442
6 changed files with 134 additions and 15 deletions
+7
View File
@@ -371,6 +371,13 @@ else()
target_compile_definitions(civetweb PRIVATE NO_SSL)
endif()
# After finishing building the FTL binary, we append the sha256sum of the binary in raw form to itself
add_custom_command(TARGET pihole-FTL POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:pihole-FTL> $<TARGET_FILE_DIR:pihole-FTL>/pihole-FTL.tmp
COMMAND sha256sum $<TARGET_FILE:pihole-FTL>.tmp | cut -d ' ' -f 1 | xxd -r -p >> $<TARGET_FILE:pihole-FTL>.tmp
COMMAND mv $<TARGET_FILE:pihole-FTL>.tmp $<TARGET_FILE:pihole-FTL>
)
find_program(SETCAP setcap)
install(TARGETS pihole-FTL
RUNTIME DESTINATION bin
+26 -4
View File
@@ -532,12 +532,13 @@ void parse_args(int argc, char* argv[])
}
// sha256sum mode
if(argc == 3 && strcmp(argv[1], "sha256sum") == 0)
if((argc == 3 || (argc == 4 && strcmp(argv[2], "--skip-end"))) && strcmp(argv[1], "sha256sum") == 0)
{
const bool skip_end = argc == 4;
// Enable stdout printing
cli_mode = true;
uint8_t checksum[SHA256_DIGEST_SIZE];
if(!sha256sum(argv[2], checksum))
if(!sha256sum(argv[skip_end ? 3 : 2], checksum, skip_end))
exit(EXIT_FAILURE);
// Convert checksum to hex string
@@ -545,10 +546,23 @@ void parse_args(int argc, char* argv[])
sha256_raw_to_hex(checksum, hex);
// Print result
printf("%s %s\n", hex, argv[2]);
printf("%s %s\n", hex, argv[skip_end ? 3 : 2]);
exit(EXIT_SUCCESS);
}
// Checksum verification mode
if(argc == 2 && strcmp(argv[1], "verify") == 0)
{
// Enable stdout printing
cli_mode = true;
const bool match = verify_self_hash(true);
if(match)
printf("%s SHA256 checksum matches\n", cli_tick());
else
printf("%s SHA256 checksum does not match\n", cli_cross());
exit(match ? EXIT_SUCCESS : EXIT_FAILURE);
}
// Local reverse name resolver
if((argc == 3 || argc == 4) && strcasecmp(argv[1], "ptr") == 0)
{
@@ -1082,10 +1096,18 @@ void parse_args(int argc, char* argv[])
printf(" %s--update%s flag is given.\n\n", purple, normal);
printf(" Usage: %spihole-FTL ntp %s[server]%s %s[--update]%s\n\n", green, cyan, normal, purple, normal);
printf("%sSHA256 checksum tools:%s\n", yellow, normal);
printf(" Calculates the SHA256 checksum of a file.\n\n");
printf(" Usage: %spihole-FTL sha256sum %sfile%s\n\n", green, cyan, normal);
printf(" The special flag %s--skip-end%s can be used to skip the last 32\n", purple, normal);
printf(" bytes of the file. This is useful for files which have their\n");
printf(" checksum appended at the end of the file, e.g., pihole-FTL:\n\n");
printf(" %spihole-FTL sha256sum %s--skip_end %sfile%s\n\n", green, purple, cyan, normal);
printf("%sOther:%s\n", yellow, normal);
printf("\t%sverify%s Verify the integrity of the FTL binary\n", green, normal);
printf("\t%sptr %sIP%s %s[tcp]%s Resolve IP address to hostname\n", green, cyan, normal, purple, normal);
printf("\t Append %stcp%s to use TCP instead of UDP\n", purple, normal);
printf("\t%ssha256sum %sfile%s Calculate SHA256 checksum of a file\n", green, cyan, normal);
printf("\t%sdhcp-discover%s Discover DHCP servers in the local\n", green, normal);
printf("\t network\n");
printf("\t%sarp-scan %s[-a/-x]%s Use ARP to scan local network for\n", green, cyan, normal);
+1 -1
View File
@@ -1831,7 +1831,7 @@ void reread_config(void)
// Create checksum of config file
uint8_t checksum[SHA256_DIGEST_SIZE];
if(!sha256sum(GLOBALTOMLPATH, checksum))
if(!sha256sum(GLOBALTOMLPATH, checksum, false))
{
log_err("Unable to create checksum of %s, not re-reading config file", GLOBALTOMLPATH);
return;
+2 -2
View File
@@ -35,7 +35,7 @@ bool writeFTLtoml(const bool verbose)
// We need to (re-)calculate the checksum here as it'd otherwise
// be outdated (in non-read-only mode, it's calculated at the
// end of this function)
if(!sha256sum(GLOBALTOMLPATH, last_checksum))
if(!sha256sum(GLOBALTOMLPATH, last_checksum, false))
log_err("Unable to create checksum of %s", GLOBALTOMLPATH);
return true;
}
@@ -209,7 +209,7 @@ bool writeFTLtoml(const bool verbose)
log_debug(DEBUG_CONFIG, "pihole.toml unchanged");
}
if(!sha256sum(GLOBALTOMLPATH, last_checksum))
if(!sha256sum(GLOBALTOMLPATH, last_checksum, false))
log_err("Unable to create checksum of %s", GLOBALTOMLPATH);
return true;
+96 -7
View File
@@ -13,6 +13,8 @@
#include "config/config.h"
#include "config/setupVars.h"
#include "log.h"
// sha256_raw_to_hex()
#include "config/password.h"
// opendir(), readdir()
#include <dirent.h>
@@ -27,10 +29,8 @@
// sendfile()
#include <fcntl.h>
#include <sys/sendfile.h>
// PRIu64
#include <inttypes.h>
//basename()
#include <libgen.h>
@@ -714,7 +714,7 @@ bool files_different(const char *pathA, const char* pathB, unsigned int from)
}
// Create SHA256 checksum of a file
bool sha256sum(const char *path, uint8_t checksum[SHA256_DIGEST_SIZE])
bool sha256sum(const char *path, uint8_t checksum[SHA256_DIGEST_SIZE], const bool skip_end)
{
// Open file
FILE *fp = fopen(path, "rb");
@@ -728,14 +728,30 @@ bool sha256sum(const char *path, uint8_t checksum[SHA256_DIGEST_SIZE])
struct sha256_ctx ctx;
sha256_init(&ctx);
// Read file in chunks of <pagesize> bytes
const size_t pagesize = getpagesize();
unsigned char *buf = calloc(pagesize, sizeof(char));
// Get size of file
fseek(fp, 0, SEEK_END);
size_t filesize = ftell(fp);
fseek(fp, 0, SEEK_SET);
// Determine chunk size
size_t chunksize = getpagesize();
// Read file in chunks
unsigned char *buf = calloc(chunksize, sizeof(char));
size_t len;
while((len = fread(buf, sizeof(char), pagesize, fp)) > 0)
while((len = fread(buf, sizeof(char), chunksize, fp)) > 0)
{
// Update SHA256 context
sha256_update(&ctx, len, buf);
// Reduce filesize by the number of bytes read
filesize -= len;
// If we want to skip the end of the file, we have to adjust the
// chunk size to the remaining bytes minus the size of the SHA256
// checksum itself
if(skip_end && filesize <= chunksize + SHA256_DIGEST_SIZE)
chunksize = filesize - SHA256_DIGEST_SIZE;
}
// Finalize SHA256 context
@@ -749,3 +765,76 @@ bool sha256sum(const char *path, uint8_t checksum[SHA256_DIGEST_SIZE])
return true;
}
/**
* @brief Verifies the integrity of the current executable file by comparing its
* SHA256 checksum with a pre-computed hash stored in the last 8 bytes of the
* binary.
*
* @param verbose A boolean value indicating whether verbose output should be
* enabled.
* @return Returns true if the checksum matches the expected value, false
* otherwise.
*/
bool verify_self_hash(bool verbose)
{
// Get the filename of the current executable
char filename[PATH_MAX] = { 0 };
if(readlink("/proc/self/exe", filename, sizeof(filename)) == -1)
{
log_err("Failed to read self filename: %s", strerror(errno));
return -1;
}
// Read the pre-computed hash - it is stored in the last 8 bytes of the
// binary itself
uint8_t self_hash[SHA256_DIGEST_SIZE];
FILE *f = fopen(filename, "r");
if(f == NULL)
{
log_err("Failed to open self file \"%s\": %s", filename, strerror(errno));
return -1;
}
if(fseek(f, -SHA256_DIGEST_SIZE, SEEK_END) != 0)
{
log_err("Failed to seek to hash: %s", strerror(errno));
fclose(f);
return -1;
}
if(fread(self_hash, SHA256_DIGEST_SIZE, 1, f) != 1)
{
log_err("Failed to read hash: %s", strerror(errno));
fclose(f);
return -1;
}
fclose(f);
// Calculate the hash of the binary
uint8_t checksum[SHA256_DIGEST_SIZE];
if(!sha256sum(filename, checksum, true))
{
log_err("Failed to calculate SHA256 checksum of %s", filename);
return false;
}
// Compare the checksums
bool success = memcmp(checksum, self_hash, SHA256_DIGEST_SIZE) == 0;
if(!success)
log_err("SHA256 checksum of %s does not match the expected value", filename);
// Log the checksums if the verification failed or if verbose output is
// requested
if(!success || verbose)
{
// Convert checksums to human-readable hex strings
char expected_hex[SHA256_DIGEST_SIZE*2+1];
sha256_raw_to_hex(self_hash, expected_hex);
char actual_hex[SHA256_DIGEST_SIZE*2+1];
sha256_raw_to_hex(checksum, actual_hex);
log_info("Expected: %s", expected_hex);
log_info("Actual: %s", actual_hex);
}
return success;
}
+2 -1
View File
@@ -36,7 +36,8 @@ bool directory_exists(const char *path);
bool chown_pihole(const char *path, struct passwd *pwd);
void rotate_files(const char *path, char **first_file);
bool files_different(const char *pathA, const char* pathB, unsigned int from);
bool sha256sum(const char *path, uint8_t checksum[SHA256_DIGEST_SIZE]);
bool sha256sum(const char *path, uint8_t checksum[SHA256_DIGEST_SIZE], const bool skip_end);
bool verify_self_hash(bool verbose);
int parse_line(char *line, char **key, char **value);