diff --color -ruNp a/configure.ac b/configure.ac --- a/configure.ac 2022-05-27 09:17:26.000000000 +0200 +++ b/configure.ac 2022-12-15 11:00:18.830698584 +0100 @@ -619,6 +619,8 @@ if [ test "$enable_fips" = "yes" ];then if test "x$fips_module_version" != xnone; then AC_DEFINE_UNQUOTED([FIPS_MODULE_VERSION], ["$fips_module_version"], [The FIPS140 module version]) fi + + AC_CHECK_FUNCS(dl_iterate_phdr) else enable_fips=no AC_MSG_WARN([[ diff --color -ruNp a/lib/fips.c b/lib/fips.c --- a/lib/fips.c 2022-12-15 10:59:57.460279029 +0100 +++ b/lib/fips.c 2022-12-15 11:00:18.831698604 +0100 @@ -23,9 +23,11 @@ #include #include #include +#include "dirname.h" #include "errors.h" #include "file.h" #include "inih/ini.h" +#include "str.h" #include #include #include @@ -34,6 +36,10 @@ #include "gthreads.h" +#ifdef HAVE_DL_ITERATE_PHDR +#include +#endif + unsigned int _gnutls_lib_state = LIB_STATE_POWERON; struct gnutls_fips140_context_st { @@ -153,7 +159,6 @@ void _gnutls_fips_mode_reset_zombie(void #define HMAC_SIZE 32 #define HMAC_ALGO GNUTLS_MAC_SHA256 -#define HMAC_FILE_NAME ".gnutls.hmac" #define HMAC_FORMAT_VERSION 1 struct hmac_entry @@ -162,51 +167,32 @@ struct hmac_entry uint8_t hmac[HMAC_SIZE]; }; -typedef struct +struct hmac_file { int version; struct hmac_entry gnutls; struct hmac_entry nettle; struct hmac_entry hogweed; struct hmac_entry gmp; -} hmac_file; +}; -static int get_library_path(const char* lib, const char* symbol, char* path, size_t path_size) +struct lib_paths { - int ret; - void *dl, *sym; - Dl_info info; - - dl = dlopen(lib, RTLD_LAZY); - if (dl == NULL) - return gnutls_assert_val(GNUTLS_E_FILE_ERROR); - - sym = dlsym(dl, symbol); - if (sym == NULL) { - ret = gnutls_assert_val(GNUTLS_E_FILE_ERROR); - goto cleanup; - } - - ret = dladdr(sym, &info); - if (ret == 0) { - ret = gnutls_assert_val(GNUTLS_E_FILE_ERROR); - goto cleanup; - } - - ret = snprintf(path, path_size, "%s", info.dli_fname); - if ((size_t)ret >= path_size) { - ret = gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); - goto cleanup; - } - - ret = 0; -cleanup: - dlclose(dl); - return ret; -} + char gnutls[GNUTLS_PATH_MAX]; + char nettle[GNUTLS_PATH_MAX]; + char hogweed[GNUTLS_PATH_MAX]; + char gmp[GNUTLS_PATH_MAX]; +}; -/* Parses hmac data and copies hex value into dest. +/* + * get_hmac: + * @dest: buffer for the hex value + * @value: hmac value + * + * Parses hmac data and copies hex value into dest. * dest must point to at least HMAC_SIZE amount of memory + * + * Returns: 0 on success, a negative error code otherwise */ static int get_hmac(uint8_t *dest, const char *value) { @@ -245,7 +231,7 @@ lib_handler(struct hmac_entry *entry, static int handler(void *user, const char *section, const char *name, const char *value) { - hmac_file *p = (hmac_file *)user; + struct hmac_file *p = (struct hmac_file *)user; if (!strcmp(section, "global")) { if (!strcmp(name, "format-version")) { @@ -267,24 +253,29 @@ static int handler(void *user, const cha return 1; } -static int get_hmac_path(char *mac_file, size_t mac_file_size) +/* + * get_hmac_path: + * @mac_file: buffer where the hmac file path will be written to + * @mac_file_size: size of the mac_file buffer + * @gnutls_path: path to the gnutls library, used to deduce hmac file path + * + * Deduces hmac file path from the gnutls library path. + * + * Returns: 0 on success, a negative error code otherwise + */ +static int get_hmac_path(char *mac_file, size_t mac_file_size, const char *gnutls_path) { int ret; char *p; - char file[GNUTLS_PATH_MAX]; - ret = get_library_path(GNUTLS_LIBRARY_NAME, "gnutls_global_init", - file, sizeof(file)); - if (ret < 0) - return ret; - - p = strrchr(file, '/'); + p = strrchr(gnutls_path, '/'); if (p == NULL) - ret = snprintf(mac_file, mac_file_size, HMAC_FILE_NAME); + ret = snprintf(mac_file, mac_file_size, ".%s.hmac", gnutls_path); else - ret = snprintf(mac_file, mac_file_size, - "%.*s/"HMAC_FILE_NAME, (int)(p - file), file); + ret = snprintf(mac_file, mac_file_size, "%.*s/.%s.hmac", + (int)(p - gnutls_path), gnutls_path, p + 1); + if ((size_t)ret >= mac_file_size) return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); @@ -293,10 +284,11 @@ static int get_hmac_path(char *mac_file, return GNUTLS_E_SUCCESS; if (p == NULL) - ret = snprintf(mac_file, mac_file_size, "fipscheck/"HMAC_FILE_NAME); + ret = snprintf(mac_file, mac_file_size, "fipscheck/.%s.hmac", gnutls_path); else - ret = snprintf(mac_file, mac_file_size, - "%.*s/fipscheck/"HMAC_FILE_NAME, (int)(p - file), file); + ret = snprintf(mac_file, mac_file_size, "%.*s/fipscheck/.%s.hmac", + (int)(p - gnutls_path), gnutls_path, p + 1); + if ((size_t)ret >= mac_file_size) return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); @@ -307,51 +299,52 @@ static int get_hmac_path(char *mac_file, return GNUTLS_E_FILE_ERROR; } -static int load_hmac_file(hmac_file *p) +/* + * load_hmac_file: + * @hmac_file: hmac file structure + * @hmac_path: path to the hmac file + * + * Loads the hmac file into the hmac file structure. + * + * Returns: 0 on success, a negative error code otherwise + */ +static int load_hmac_file(struct hmac_file *hmac_file, const char *hmac_path) { int ret; FILE *stream; - char hmac_path[GNUTLS_PATH_MAX]; - - ret = get_hmac_path(hmac_path, sizeof(hmac_path)); - if (ret < 0) - return gnutls_assert_val(ret); stream = fopen(hmac_path, "r"); if (stream == NULL) return gnutls_assert_val(GNUTLS_E_FILE_ERROR); - gnutls_memset(p, 0, sizeof(*p)); - ret = ini_parse_file(stream, handler, p); + gnutls_memset(hmac_file, 0, sizeof(*hmac_file)); + ret = ini_parse_file(stream, handler, hmac_file); fclose(stream); if (ret < 0) return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); - if (p->version != HMAC_FORMAT_VERSION) + if (hmac_file->version != HMAC_FORMAT_VERSION) return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); return 0; } -/* Run an HMAC using the key above on the library binary data. - * Returns 0 on success and negative value on error. +/* + * check_lib_hmac: + * @entry: hmac file entry + * @path: path to the library which hmac should be compared + * + * Verify that HMAC from hmac file entry matches HMAC of given library. + * + * Returns: 0 on successful HMAC verification, a negative error code otherwise */ -static int check_lib_hmac(struct hmac_entry *entry, - const char *lib, const char *sym) +static int check_lib_hmac(struct hmac_entry *entry, const char *path) { int ret; unsigned prev; - char path[GNUTLS_PATH_MAX]; uint8_t hmac[HMAC_SIZE]; gnutls_datum_t data; - ret = get_library_path(lib, sym, path, sizeof(path)); - if (ret < 0) { - _gnutls_debug_log("Could not get lib path for %s: %s\n", - lib, gnutls_strerror(ret)); - return gnutls_assert_val(ret); - } - _gnutls_debug_log("Loading: %s\n", path); ret = gnutls_load_file(path, &data); if (ret < 0) { @@ -382,28 +375,99 @@ static int check_lib_hmac(struct hmac_en return 0; } +#ifdef HAVE_DL_ITERATE_PHDR + +static int callback(struct dl_phdr_info *info, size_t size, void *data) +{ + const char *path = info->dlpi_name; + const char *soname = last_component(path); + struct lib_paths *paths = (struct lib_paths *)data; + + if (!strcmp(soname, GNUTLS_LIBRARY_SONAME)) + _gnutls_str_cpy(paths->gnutls, GNUTLS_PATH_MAX, path); + else if (!strcmp(soname, NETTLE_LIBRARY_SONAME)) + _gnutls_str_cpy(paths->nettle, GNUTLS_PATH_MAX, path); + else if (!strcmp(soname, HOGWEED_LIBRARY_SONAME)) + _gnutls_str_cpy(paths->hogweed, GNUTLS_PATH_MAX, path); + else if (!strcmp(soname, GMP_LIBRARY_SONAME)) + _gnutls_str_cpy(paths->gmp, GNUTLS_PATH_MAX, path); + return 0; +} + +static int load_lib_paths(struct lib_paths *paths) +{ + memset(paths, 0, sizeof(*paths)); + dl_iterate_phdr(callback, paths); + + if (paths->gnutls[0] == '\0') { + _gnutls_debug_log("Gnutls library path was not found\n"); + return gnutls_assert_val(GNUTLS_E_FILE_ERROR); + } + if (paths->nettle[0] == '\0') { + _gnutls_debug_log("Nettle library path was not found\n"); + return gnutls_assert_val(GNUTLS_E_FILE_ERROR); + } + if (paths->hogweed[0] == '\0') { + _gnutls_debug_log("Hogweed library path was not found\n"); + return gnutls_assert_val(GNUTLS_E_FILE_ERROR); + } + if (paths->gmp[0] == '\0') { + _gnutls_debug_log("Gmp library path was not found\n"); + return gnutls_assert_val(GNUTLS_E_FILE_ERROR); + } + + return GNUTLS_E_SUCCESS; +} + +#else + +static int load_lib_paths(struct lib_paths *paths) +{ + (void)paths; + _gnutls_debug_log("Function dl_iterate_phdr is missing\n"); + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); +} + +#endif /* HAVE_DL_ITERATE_PHDR */ + static int check_binary_integrity(void) { int ret; - hmac_file file; + struct lib_paths paths; + struct hmac_file hmac; + char hmac_path[GNUTLS_PATH_MAX]; + + ret = load_lib_paths(&paths); + if (ret < 0) { + _gnutls_debug_log("Could not load library paths: %s\n", + gnutls_strerror(ret)); + return ret; + } + + ret = get_hmac_path(hmac_path, sizeof(hmac_path), paths.gnutls); + if (ret < 0) { + _gnutls_debug_log("Could not get hmac file path: %s\n", + gnutls_strerror(ret)); + return ret; + } - ret = load_hmac_file(&file); + ret = load_hmac_file(&hmac, hmac_path); if (ret < 0) { _gnutls_debug_log("Could not load hmac file: %s\n", gnutls_strerror(ret)); return ret; } - ret = check_lib_hmac(&file.gnutls, GNUTLS_LIBRARY_NAME, "gnutls_global_init"); + ret = check_lib_hmac(&hmac.gnutls, paths.gnutls); if (ret < 0) return ret; - ret = check_lib_hmac(&file.nettle, NETTLE_LIBRARY_NAME, "nettle_aes_set_encrypt_key"); + ret = check_lib_hmac(&hmac.nettle, paths.nettle); if (ret < 0) return ret; - ret = check_lib_hmac(&file.hogweed, HOGWEED_LIBRARY_NAME, "nettle_mpz_sizeinbase_256_u"); + ret = check_lib_hmac(&hmac.hogweed, paths.hogweed); if (ret < 0) return ret; - ret = check_lib_hmac(&file.gmp, GMP_LIBRARY_NAME, "__gmpz_init"); + ret = check_lib_hmac(&hmac.gmp, paths.gmp); if (ret < 0) return ret; diff --color -ruNp a/lib/fipshmac.c b/lib/fipshmac.c --- a/lib/fipshmac.c 2022-12-15 10:59:57.461279049 +0100 +++ b/lib/fipshmac.c 2022-12-15 11:00:18.832698623 +0100 @@ -22,12 +22,14 @@ #include "config.h" -#include -#include -#include -#include #include #include + +#ifdef HAVE_DL_ITERATE_PHDR + +#include +#include +#include #include "dirname.h" #include "errors.h" @@ -36,40 +38,6 @@ #define HMAC_ALGO GNUTLS_MAC_SHA256 #define HMAC_STR_SIZE (2 * HMAC_SIZE + 1) -static int get_path(const char *lib, const char *symbol, char *path, size_t path_size) -{ - int ret; - void *dl, *sym; - Dl_info info; - - dl = dlopen(lib, RTLD_LAZY); - if (dl == NULL) - return gnutls_assert_val(GNUTLS_E_FILE_ERROR); - - sym = dlsym(dl, symbol); - if (sym == NULL) { - ret = gnutls_assert_val(GNUTLS_E_FILE_ERROR); - goto cleanup; - } - - ret = dladdr(sym, &info); - if (ret == 0) { - ret = gnutls_assert_val(GNUTLS_E_FILE_ERROR); - goto cleanup; - } - - ret = snprintf(path, path_size, "%s", info.dli_fname); - if ((size_t)ret >= path_size) { - ret = gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); - goto cleanup; - } - - ret = 0; -cleanup: - dlclose(dl); - return ret; -} - static int get_hmac(const char *path, char *hmac, size_t hmac_size) { int ret; @@ -99,7 +67,7 @@ static int get_hmac(const char *path, ch return 0; } -static int print_lib_path(const char *path) +static int print_lib(const char *path, const char *soname) { int ret; char *real_path = NULL; @@ -119,7 +87,7 @@ static int print_lib_path(const char *pa goto cleanup; } - printf("[%s]\n", last_component(path)); + printf("[%s]\n", soname); printf("path = %s\n", real_path); printf("hmac = %s\n", hmac); @@ -128,25 +96,24 @@ cleanup: return ret; } -static int print_lib_dl(const char *lib, const char *sym) +static int callback(struct dl_phdr_info *info, size_t size, void *data) { - int ret; - char path[GNUTLS_PATH_MAX]; - - ret = get_path(lib, sym, path, sizeof(path)); - if (ret < 0) { - fprintf(stderr, "Could not get lib path for %s: %s\n", - lib, gnutls_strerror(ret)); - return ret; - } + const char *path = info->dlpi_name; + const char *soname = last_component(path); - return print_lib_path(path); + if (!strcmp(soname, GNUTLS_LIBRARY_SONAME)) + return print_lib(data ? data : path, soname); + if (!strcmp(soname, NETTLE_LIBRARY_SONAME)) + return print_lib(path, soname); + if (!strcmp(soname, HOGWEED_LIBRARY_SONAME)) + return print_lib(path, soname); + if (!strcmp(soname, GMP_LIBRARY_SONAME)) + return print_lib(path, soname); + return 0; } int main(int argc, char **argv) { - int ret; - if (argc != 1 && argc != 2) { fprintf(stderr, "Usage: %s [gnutls_so_path]\n", last_component(argv[0])); return EXIT_FAILURE; @@ -155,24 +122,15 @@ int main(int argc, char **argv) printf("[global]\n"); printf("format-version = %d\n", FORMAT_VERSION); - if (argc == 2) - ret = print_lib_path(argv[1]); - else - ret = print_lib_dl(GNUTLS_LIBRARY_SONAME, "gnutls_global_init"); - if (ret < 0) - return EXIT_FAILURE; + return dl_iterate_phdr(callback, argc == 2 ? argv[1] : NULL); +} - ret = print_lib_dl(NETTLE_LIBRARY_SONAME, "nettle_aes_set_encrypt_key"); - if (ret < 0) - return EXIT_FAILURE; - - ret = print_lib_dl(HOGWEED_LIBRARY_SONAME, "nettle_mpz_sizeinbase_256_u"); - if (ret < 0) - return EXIT_FAILURE; - - ret = print_lib_dl(GMP_LIBRARY_SONAME, "__gmpz_init"); - if (ret < 0) - return EXIT_FAILURE; +#else - return EXIT_SUCCESS; +int main(void) +{ + fprintf(stderr, "Function dl_iterate_phdr is missing\n"); + return EXIT_FAILURE; } + +#endif /* HAVE_DL_ITERATE_PHDR */ diff --color -ruNp a/lib/Makefile.am b/lib/Makefile.am --- a/lib/Makefile.am 2022-05-18 16:46:00.000000000 +0200 +++ b/lib/Makefile.am 2022-12-15 11:00:18.789697779 +0100 @@ -202,14 +202,14 @@ noinst_PROGRAMS = fipshmac fipshmac_SOURCES = fipshmac.c fipshmac_LDADD = libgnutls.la ../gl/libgnu.la -hmac_files = .libs/.gnutls.hmac +hmac_file = .libs/.$(gnutls_so).hmac -all-local: $(hmac_files) +all-local: $(hmac_file) -.libs/.gnutls.hmac: libgnutls.la fipshmac +$(hmac_file): libgnutls.la fipshmac $(AM_V_GEN) $(builddir)/fipshmac > $@-t && mv $@-t $@ -CLEANFILES = $(hmac_files) +CLEANFILES = $(hmac_file) endif if NEED_LTLIBDL