mirror of
https://github.com/pi-hole/FTL.git
synced 2024-10-26 16:52:18 +02:00
Add new binary integrity verification function
Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user