Skip to content

Commit

Permalink
Merge pull request #2102 from pi-hole/fix/verify_cmake_install
Browse files Browse the repository at this point in the history
Fix binary verification with modern cmake
  • Loading branch information
DL6ER authored Nov 1, 2024
2 parents 3dd756d + 37f2f61 commit 51dd2c5
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 25 deletions.
5 changes: 4 additions & 1 deletion build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,11 @@ fi
# Build the sources with the number of available cores
cmake --build . -- -j $(nproc)

# Checksum verification
./pihole-FTL verify

# If we are asked to install, we do this here (requires root privileges)
# Otherwise, we simply copy the binary one level up
# Otherwise, we simply copy the binary one level down
if [[ -n "${install}" ]]; then
echo "Installing pihole-FTL"
SUDO=$(command -v sudo)
Expand Down
17 changes: 14 additions & 3 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ if (NOT EXISTS ${CMAKE_BINARY_DIR}/CMakeCache.txt)
endif()
endif()

# Do not add run time path information
# This ensures CMake does not add rpath information to the binary and
# subsequently strip it from the binary during install causing the binary
# validation to fail. RPATH is not needed for the FTL binary
SET(CMAKE_SKIP_RPATH TRUE)

# Put runtime output, i.e. pihole-FTL, in the root of the build dir
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})

Expand Down Expand Up @@ -376,13 +382,18 @@ endif()
add_custom_command(TARGET pihole-FTL POST_BUILD COMMENT "Appending sha256sum to pihole-FTL"
COMMAND echo -n "SHA256 checksum of pihole-FTL: " && sha256sum $<TARGET_FILE:pihole-FTL> | cut -d ' ' -f 1
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:pihole-FTL> $<TARGET_FILE_DIR:pihole-FTL>/pihole-FTL.tmp
COMMAND echo -n "sha256sum" >> $<TARGET_FILE: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)
######### Installation target ############
# Install the binary
install(TARGETS pihole-FTL
RUNTIME DESTINATION bin
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(CODE "execute_process(COMMAND ${SETCAP} CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_NET_ADMIN,CAP_SYS_NICE,CAP_CHOWN,CAP_SYS_TIME+eip \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/bin/pihole-FTL)")

find_program(SETCAP setcap)
# After installing the binary, we set the capabilities on the binary ...
install(CODE "execute_process(COMMAND ${SETCAP} CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_NET_ADMIN,CAP_SYS_NICE,CAP_CHOWN,CAP_SYS_TIME+eip \${CMAKE_INSTALL_PREFIX}/bin/pihole-FTL)")
# ... and verify the binary integrity
install(CODE "execute_process(COMMAND \${CMAKE_INSTALL_PREFIX}/bin/pihole-FTL verify)")
11 changes: 7 additions & 4 deletions src/args.c
Original file line number Diff line number Diff line change
Expand Up @@ -562,11 +562,14 @@ void parse_args(int argc, char *argv[])
{
// Enable stdout printing
cli_mode = true;
const bool match = verify_FTL(true);
const enum verify_result match = verify_FTL(true);
printf("%s Binary integrity check: %s\n",
match ? cli_tick() : cli_cross() ,
match ? "OK" : "FAILED");
exit(match ? EXIT_SUCCESS : EXIT_FAILURE);
match == VERIFY_OK ? cli_tick() :
match == VERIFY_NO_CHECKSUM ? cli_qst() : cli_cross(),
match == VERIFY_OK ? "OK" :
match == VERIFY_NO_CHECKSUM ? "No checksum found" :
match == VERIFY_ERROR ? "Error" : "Failed");
exit(match);
}

// Local reverse name resolver
Expand Down
7 changes: 7 additions & 0 deletions src/enums.h
Original file line number Diff line number Diff line change
Expand Up @@ -342,4 +342,11 @@ enum api_flags {
API_BATCHDELETE = 1 << 2,
};

enum verify_result {
VERIFY_OK = 0, // EXIT_SUCCESS
VERIFY_FAILED,
VERIFY_ERROR,
VERIFY_NO_CHECKSUM
} __attribute__ ((packed));

#endif // ENUMS_H
48 changes: 32 additions & 16 deletions src/files.c
Original file line number Diff line number Diff line change
Expand Up @@ -735,25 +735,25 @@ bool sha256sum(const char *path, uint8_t checksum[SHA256_DIGEST_SIZE], const boo
size_t filesize = ftell(fp);
fseek(fp, 0, SEEK_SET);

if(skip_end)
{
// Skip the checksum at the end
filesize -= SHA256_DIGEST_SIZE;
}

// 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), chunksize, fp)) > 0)
while((len = fread(buf, sizeof(char), min(chunksize, filesize), 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
Expand All @@ -778,39 +778,55 @@ bool sha256sum(const char *path, uint8_t checksum[SHA256_DIGEST_SIZE], const boo
* @return Returns true if the checksum matches the expected value, false
* otherwise.
*/
bool verify_FTL(bool verbose)
enum verify_result verify_FTL(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;
return VERIFY_ERROR;
}

// Read the pre-computed hash - it is stored in the last 8 bytes of the
// binary itself
// Read the pre-computed hash as well as the checksum mark from the
// binary (last 9 + 32 bytes)
uint8_t self_hash[SHA256_DIGEST_SIZE];
uint8_t checksum_mark[9] = { 0 };
FILE *f = fopen(filename, "r");
if(f == NULL)
{
log_err("Failed to open self file \"%s\": %s", filename, strerror(errno));
return -1;
return VERIFY_ERROR;
}
if(fseek(f, -SHA256_DIGEST_SIZE, SEEK_END) != 0)
if(fseek(f, -(SHA256_DIGEST_SIZE + 9), SEEK_END) != 0)
{
log_err("Failed to seek to hash: %s", strerror(errno));
fclose(f);
return -1;
return VERIFY_ERROR;
}
if(fread(checksum_mark, 9, 1, f) != 1)
{
log_err("Failed to read checksum mark: %s", strerror(errno));
fclose(f);
return VERIFY_ERROR;
}
if(fread(self_hash, SHA256_DIGEST_SIZE, 1, f) != 1)
{
log_err("Failed to read hash: %s", strerror(errno));
fclose(f);
return -1;
return VERIFY_ERROR;
}
fclose(f);

// Verify checksum mark matches magic string "sha256sum"
if(memcmp(checksum_mark, "sha256sum", 9) != 0)
{
log_warn("Binary integrity check not possible: No checksum mark found");
// This is not an error, as the binary may not have a checksum mark
// if it was built with a different toolchain
return VERIFY_NO_CHECKSUM;
}

// Calculate the hash of the binary
// Skip the last 256 bit as it contains the hast itself
uint8_t checksum[SHA256_DIGEST_SIZE];
Expand Down Expand Up @@ -840,5 +856,5 @@ bool verify_FTL(bool verbose)
}
}

return success;
return success ? VERIFY_OK : VERIFY_FAILED;
}
2 changes: 1 addition & 1 deletion src/files.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ 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], const bool skip_end);
bool verify_FTL(bool verbose);
enum verify_result verify_FTL(bool verbose);

int parse_line(char *line, char **key, char **value);

Expand Down

0 comments on commit 51dd2c5

Please sign in to comment.