diff --git a/src/args.c b/src/args.c index 57550e52c..381b7f3bc 100644 --- a/src/args.c +++ b/src/args.c @@ -62,6 +62,8 @@ #include "config/password.h" // idn2_to_ascii_lz() #include +// sha256sum() +#include "files.h" // defined in dnsmasq.c extern void print_dnsmasq_version(const char *yellow, const char *green, const char *bold, const char *normal); @@ -476,6 +478,24 @@ void parse_args(int argc, char* argv[]) } } + // sha256sum mode + if(argc == 3 && strcmp(argv[1], "sha256sum") == 0) + { + // Enable stdout printing + cli_mode = true; + uint8_t checksum[SHA256_DIGEST_SIZE]; + if(!sha256sum(argv[2], checksum)) + exit(EXIT_FAILURE); + + // Convert checksum to hex string + char hex[SHA256_DIGEST_SIZE*2+1]; + sha256_raw_to_hex(checksum, hex); + + // Print result + printf("%s %s\n", hex, argv[2]); + exit(EXIT_SUCCESS); + } + // start from 1, as argv[0] is the executable name for(int i = 1; i < argc; i++) { @@ -934,6 +954,7 @@ void parse_args(int argc, char* argv[]) printf(" Decoding: %spihole-FTL idn2 -d %spunycode%s\n\n", green, cyan, normal); printf("%sOther:%s\n", yellow, 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); diff --git a/src/config/config.c b/src/config/config.c index 58f2c40b6..4e756198c 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -29,9 +29,12 @@ #include "api/api.h" // exit_code #include "signals.h" +// sha256sum() +#include "files.h" struct config config = { 0 }; static bool config_initialized = false; +uint8_t last_checksum[SHA256_DIGEST_SIZE] = { 0 }; // Private prototypes static bool port_in_use(const in_port_t port); @@ -1571,6 +1574,23 @@ void replace_config(struct config *newconf) void reread_config(void) { + + // Create checksum of config file + uint8_t checksum[SHA256_DIGEST_SIZE]; + if(!sha256sum(GLOBALTOMLPATH, checksum)) + { + log_err("Unable to create checksum of %s, not re-reading config file", GLOBALTOMLPATH); + return; + } + + // Compare checksums + if(memcmp(checksum, last_checksum, SHA256_DIGEST_SIZE) == 0) + { + log_debug(DEBUG_CONFIG, "Checksum of %s has not changed, not re-reading config file", GLOBALTOMLPATH); + return; + } + + log_info("Reloading config due to pihole.toml change"); struct config conf_copy; duplicate_config(&conf_copy, &config); diff --git a/src/config/toml_writer.c b/src/config/toml_writer.c index add2a1e1c..5f892daf4 100644 --- a/src/config/toml_writer.c +++ b/src/config/toml_writer.c @@ -22,6 +22,9 @@ // files_different() #include "files.h" +// defined in config/config.c +extern uint8_t last_checksum[SHA256_DIGEST_SIZE]; + static void migrate_config(void) { // Migrating dhcp.domain -> dns.domain @@ -176,5 +179,8 @@ bool writeFTLtoml(const bool verbose) log_debug(DEBUG_CONFIG, "pihole.toml unchanged"); } + if(!sha256sum(GLOBALTOMLPATH, last_checksum)) + log_err("Unable to create checksum of %s", GLOBALTOMLPATH); + return true; } diff --git a/src/files.c b/src/files.c index 3c15891b3..0f36def4f 100644 --- a/src/files.c +++ b/src/files.c @@ -641,3 +641,40 @@ bool files_different(const char *pathA, const char* pathB, unsigned int from) return different; } + +// Create SHA256 checksum of a file +bool sha256sum(const char *path, uint8_t checksum[SHA256_DIGEST_SIZE]) +{ + // Open file + FILE *fp = fopen(path, "rb"); + if(fp == NULL) + { + log_warn("sha256_file(): Failed to open \"%s\" for reading: %s", path, strerror(errno)); + return false; + } + + // Initialize SHA2-256 context + struct sha256_ctx ctx; + sha256_init(&ctx); + + // Read file in chunks of bytes + const size_t pagesize = getpagesize(); + unsigned char *buf = calloc(pagesize, sizeof(char)); + size_t len; + while((len = fread(buf, sizeof(char), pagesize, fp)) > 0) + { + // Update SHA256 context + sha256_update(&ctx, len, buf); + } + + // Finalize SHA256 context + sha256_digest(&ctx, SHA256_DIGEST_SIZE, checksum); + + // Close file + fclose(fp); + + // Free memory + free(buf); + + return true; +} diff --git a/src/files.h b/src/files.h index 555e5ac04..742e8d9a5 100644 --- a/src/files.h +++ b/src/files.h @@ -14,6 +14,8 @@ #include // setmntent() #include +// SHA256_DIGEST_SIZE +#include #define MAX_ROTATIONS 15 #define BACKUP_DIR "/etc/pihole/config_backups" @@ -31,6 +33,7 @@ struct mntent *get_filesystem_details(const char *path); bool directory_exists(const char *path); 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]); int parse_line(char *line, char **key, char **value); diff --git a/src/gc.c b/src/gc.c index 322a92ab0..ad793413d 100644 --- a/src/gc.c +++ b/src/gc.c @@ -374,7 +374,6 @@ void *GC_thread(void *val) if(check_inotify_event()) { // Reload config - log_info("Reloading config due to pihole.toml change"); reread_config(); } diff --git a/test/test_suite.bats b/test/test_suite.bats index 650f9a48c..dfc1bff1b 100644 --- a/test/test_suite.bats +++ b/test/test_suite.bats @@ -1520,6 +1520,12 @@ [[ $status == 0 ]] } +@test "SHA256 checksum working" { + run bash -c './pihole-FTL sha256sum test/test.pem' + printf "%s\n" "${lines[@]}" + [[ ${lines[0]} == "eae293f0c30369935a7457a789658bedebf92d544e7526bc43aa07883a597fa9 test/test.pem" ]] +} + @test "API validation" { run python3 test/api/checkAPI.py printf "%s\n" "${lines[@]}" @@ -1571,7 +1577,7 @@ [[ ${lines[0]} == "3" ]] run bash -c 'grep -c "DEBUG_CONFIG: pihole.toml unchanged" /var/log/pihole/FTL.log' printf "%s\n" "${lines[@]}" - [[ ${lines[0]} == "4" ]] + [[ ${lines[0]} == "3" ]] run bash -c 'grep -c "DEBUG_CONFIG: Config file written to /etc/pihole/dnsmasq.conf" /var/log/pihole/FTL.log' printf "%s\n" "${lines[@]}" [[ ${lines[0]} == "1" ]] @@ -1583,5 +1589,5 @@ [[ ${lines[0]} == "1" ]] run bash -c 'grep -c "DEBUG_CONFIG: custom.list unchanged" /var/log/pihole/FTL.log' printf "%s\n" "${lines[@]}" - [[ ${lines[0]} == "4" ]] + [[ ${lines[0]} == "3" ]] }