#include <gnutls/crypto.h>
#include <unistd.h>
#include "errors.h"
+#include "file.h"
+#include "inih/ini.h"
#include <fips.h>
#include <gnutls/self-test.h>
#include <stdio.h>
#define HOGWEED_LIBRARY_NAME HOGWEED_LIBRARY_SONAME
#define GMP_LIBRARY_NAME GMP_LIBRARY_SONAME
-#define HMAC_SUFFIX ".hmac"
#define HMAC_SIZE 32
#define HMAC_ALGO GNUTLS_MAC_SHA256
+#define HMAC_FILE_NAME ".gnutls.hmac"
+#define HMAC_FORMAT_VERSION 1
+
+typedef struct
+{
+ int version;
+ char gnutls_path[GNUTLS_PATH_MAX];
+ char nettle_path[GNUTLS_PATH_MAX];
+ char hogweed_path[GNUTLS_PATH_MAX];
+ char gmp_path[GNUTLS_PATH_MAX];
+ uint8_t gnutls_hmac[HMAC_SIZE];
+ uint8_t nettle_hmac[HMAC_SIZE];
+ uint8_t hogweed_hmac[HMAC_SIZE];
+ uint8_t gmp_hmac[HMAC_SIZE];
+} hmac_file;
static int get_library_path(const char* lib, const char* symbol, char* path, size_t path_size)
{
-Dl_info info;
-int ret;
-void *dl, *sym;
+ int ret;
+ void *dl, *sym;
+ Dl_info info;
dl = dlopen(lib, RTLD_LAZY);
if (dl == NULL)
goto cleanup;
}
- snprintf(path, path_size, "%s", info.dli_fname);
+ 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:
return ret;
}
-static void get_hmac_file(char *mac_file, size_t mac_file_size, const char* orig)
+/* Parses hmac data and copies hex value into dest.
+ * dest must point to at least HMAC_SIZE amount of memory
+ */
+static int get_hmac(uint8_t *dest, const char *value)
{
-char* p;
+ int ret;
+ size_t hmac_size;
+ gnutls_datum_t data;
- p = strrchr(orig, '/');
- if (p==NULL) {
- snprintf(mac_file, mac_file_size, ".%s"HMAC_SUFFIX, orig);
- return;
+ data.size = strlen(value);
+ if (hex_data_size(data.size) != HMAC_SIZE)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ data.data = (uint8_t *)gnutls_strdup(value);
+ if (data.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ hmac_size = HMAC_SIZE;
+ ret = gnutls_hex_decode(&data, dest, &hmac_size);
+ gnutls_free(data.data);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ if (hmac_size != HMAC_SIZE)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ return 0;
+}
+
+static int handler(void *user, const char *section, const char *name, const char *value)
+{
+ hmac_file *p = (hmac_file *)user;
+
+ if (!strcmp(section, "global")) {
+ if (!strcmp(name, "format-version")) {
+ p->version = strtol(value, NULL, 10);
+ } else {
+ return 0;
+ }
+ } else if (!strcmp(section, GNUTLS_LIBRARY_NAME)) {
+ if (!strcmp(name, "path")) {
+ snprintf(p->gnutls_path, GNUTLS_PATH_MAX, "%s", value);
+ } else if (!strcmp(name, "hmac")) {
+ if (get_hmac(p->gnutls_hmac, value) < 0)
+ return 0;
+ } else {
+ return 0;
+ }
+ } else if (!strcmp(section, NETTLE_LIBRARY_NAME)) {
+ if (!strcmp(name, "path")) {
+ snprintf(p->nettle_path, GNUTLS_PATH_MAX, "%s", value);
+ } else if (!strcmp(name, "hmac")) {
+ if (get_hmac(p->nettle_hmac, value) < 0)
+ return 0;
+ } else {
+ return 0;
+ }
+ } else if (!strcmp(section, HOGWEED_LIBRARY_NAME)) {
+ if (!strcmp(name, "path")) {
+ snprintf(p->hogweed_path, GNUTLS_PATH_MAX, "%s", value);
+ } else if (!strcmp(name, "hmac")) {
+ if (get_hmac(p->hogweed_hmac, value) < 0)
+ return 0;
+ } else {
+ return 0;
+ }
+ } else if (!strcmp(section, GMP_LIBRARY_NAME)) {
+ if (!strcmp(name, "path")) {
+ snprintf(p->gmp_path, GNUTLS_PATH_MAX, "%s", value);
+ } else if (!strcmp(name, "hmac")) {
+ if (get_hmac(p->gmp_hmac, value) < 0)
+ return 0;
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
}
- snprintf(mac_file, mac_file_size, "%.*s/.%s"HMAC_SUFFIX, (int)(p-orig), orig, p+1);
+ return 1;
}
-static void get_hmac_file2(char *mac_file, size_t mac_file_size, const char* orig)
+static int get_hmac_path(char *mac_file, size_t mac_file_size)
{
-char* p;
+ int ret;
+ char *p;
+ char file[GNUTLS_PATH_MAX];
- p = strrchr(orig, '/');
- if (p==NULL) {
- snprintf(mac_file, mac_file_size, "fipscheck/%s"HMAC_SUFFIX, orig);
- return;
+ ret = get_library_path(GNUTLS_LIBRARY_NAME, "gnutls_global_init",
+ file, sizeof(file));
+ if (ret < 0)
+ return ret;
+
+ p = strrchr(file, '/');
+
+ if (p == NULL)
+ ret = snprintf(mac_file, mac_file_size, HMAC_FILE_NAME);
+ else
+ ret = snprintf(mac_file, mac_file_size,
+ "%.*s/"HMAC_FILE_NAME, (int)(p - file), file);
+ if ((size_t)ret >= mac_file_size)
+ return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+
+ ret = _gnutls_file_exists(mac_file);
+ if (ret == 0)
+ return GNUTLS_E_SUCCESS;
+
+ if (p == NULL)
+ ret = snprintf(mac_file, mac_file_size, "fipscheck/"HMAC_FILE_NAME);
+ else
+ ret = snprintf(mac_file, mac_file_size,
+ "%.*s/fipscheck/"HMAC_FILE_NAME, (int)(p - file), file);
+ if ((size_t)ret >= mac_file_size)
+ return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+
+ ret = _gnutls_file_exists(mac_file);
+ if (ret == 0)
+ return GNUTLS_E_SUCCESS;
+
+ return GNUTLS_E_FILE_ERROR;
+}
+
+static int load_hmac_file(hmac_file *p)
+{
+ 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);
+ fclose(stream);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ if (p->version != HMAC_FORMAT_VERSION)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ return 0;
+}
+
+static int check_lib_path(const char path[GNUTLS_PATH_MAX],
+ const char *lib, const char *sym)
+{
+ int ret;
+ char new_path[GNUTLS_PATH_MAX];
+
+ ret = get_library_path(lib, sym, new_path, sizeof(new_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);
}
- snprintf(mac_file, mac_file_size, "%.*s/fipscheck/%s"HMAC_SUFFIX, (int)(p-orig), orig, p+1);
+
+ if (strncmp(path, new_path, GNUTLS_PATH_MAX)) {
+ _gnutls_debug_log("Library path for %s does not match with HMAC file\n", lib);
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+ }
+
+ return 0;
}
/* Run an HMAC using the key above on the library binary data.
- * Returns true on success and false on error.
+ * Returns 0 on success and negative value on error.
*/
-static unsigned check_binary_integrity(const char* libname, const char* symbol)
+static int check_lib_hmac(const char path[GNUTLS_PATH_MAX],
+ const uint8_t hmac[HMAC_SIZE])
{
int ret;
unsigned prev;
- char mac_file[GNUTLS_PATH_MAX];
- char file[GNUTLS_PATH_MAX];
- uint8_t hmac[HMAC_SIZE];
uint8_t new_hmac[HMAC_SIZE];
- size_t hmac_size;
gnutls_datum_t data;
-
- ret = get_library_path(libname, symbol, file, sizeof(file));
- if (ret < 0) {
- _gnutls_debug_log("Could not get path for library %s\n", libname);
- return 0;
- }
-
- _gnutls_debug_log("Loading: %s\n", file);
- ret = gnutls_load_file(file, &data);
+
+ _gnutls_debug_log("Loading: %s\n", path);
+ ret = gnutls_load_file(path, &data);
if (ret < 0) {
- _gnutls_debug_log("Could not load: %s\n", file);
- return gnutls_assert_val(0);
+ _gnutls_debug_log("Could not load %s: %s\n",
+ path, gnutls_strerror(ret));
+ return gnutls_assert_val(ret);
}
prev = _gnutls_get_lib_state();
ret = gnutls_hmac_fast(HMAC_ALGO, FIPS_KEY, sizeof(FIPS_KEY)-1,
data.data, data.size, new_hmac);
_gnutls_switch_lib_state(prev);
-
- gnutls_free(data.data);
-
- if (ret < 0)
- return gnutls_assert_val(0);
-
- /* now open the .hmac file and compare */
- get_hmac_file(mac_file, sizeof(mac_file), file);
- ret = gnutls_load_file(mac_file, &data);
+ gnutls_free(data.data);
if (ret < 0) {
- get_hmac_file2(mac_file, sizeof(mac_file), file);
- ret = gnutls_load_file(mac_file, &data);
- if (ret < 0) {
- _gnutls_debug_log("Could not open %s for MAC testing: %s\n", mac_file, gnutls_strerror(ret));
- return gnutls_assert_val(0);
- }
+ _gnutls_debug_log("Could not calculate HMAC for %s: %s\n",
+ path, gnutls_strerror(ret));
+ return gnutls_assert_val(ret);
}
- hmac_size = hex_data_size(data.size);
-
- /* trim eventual newlines from the end of the data read from file */
- while ((data.size > 0) && (data.data[data.size - 1] == '\n')) {
- data.data[data.size - 1] = 0;
- data.size--;
+ if (gnutls_memcmp(hmac, new_hmac, HMAC_SIZE)) {
+ _gnutls_debug_log("Calculated MAC for %s does not match\n", path);
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
}
+ _gnutls_debug_log("Successfully verified MAC for %s\n", path);
+
+ return 0;
+}
- ret = gnutls_hex_decode(&data, hmac, &hmac_size);
- gnutls_free(data.data);
+static int check_binary_integrity(void)
+{
+ int ret;
+ hmac_file file;
+ ret = load_hmac_file(&file);
if (ret < 0) {
- _gnutls_debug_log("Could not convert hex data to binary for MAC testing for %s.\n", libname);
- return gnutls_assert_val(0);
+ _gnutls_debug_log("Could not load hmac file: %s\n",
+ gnutls_strerror(ret));
+ return ret;
}
- if (hmac_size != sizeof(hmac) ||
- memcmp(hmac, new_hmac, sizeof(hmac)) != 0) {
- _gnutls_debug_log("Calculated MAC for %s does not match\n", libname);
- return gnutls_assert_val(0);
- }
- _gnutls_debug_log("Successfully verified MAC for %s (%s)\n", mac_file, libname);
-
- return 1;
+ ret = check_lib_path(file.gnutls_path, GNUTLS_LIBRARY_NAME, "gnutls_global_init");
+ if (ret < 0)
+ return ret;
+ ret = check_lib_hmac(file.gnutls_path, file.gnutls_hmac);
+ if (ret < 0)
+ return ret;
+ ret = check_lib_path(file.nettle_path, NETTLE_LIBRARY_NAME, "nettle_aes_set_encrypt_key");
+ if (ret < 0)
+ return ret;
+ ret = check_lib_hmac(file.nettle_path, file.nettle_hmac);
+ if (ret < 0)
+ return ret;
+ ret = check_lib_path(file.hogweed_path, HOGWEED_LIBRARY_NAME, "nettle_mpz_sizeinbase_256_u");
+ if (ret < 0)
+ return ret;
+ ret = check_lib_hmac(file.hogweed_path, file.hogweed_hmac);
+ if (ret < 0)
+ return ret;
+ ret = check_lib_path(file.gmp_path, GMP_LIBRARY_NAME, "__gmpz_init");
+ if (ret < 0)
+ return ret;
+ ret = check_lib_hmac(file.gmp_path, file.gmp_hmac);
+ if (ret < 0)
+ return ret;
+
+ return 0;
}
int _gnutls_fips_perform_self_checks1(void)
}
if (_skip_integrity_checks == 0) {
- ret = check_binary_integrity(GNUTLS_LIBRARY_NAME, "gnutls_global_init");
- if (ret == 0) {
- gnutls_assert();
- goto error;
- }
-
- ret = check_binary_integrity(NETTLE_LIBRARY_NAME, "nettle_aes_set_encrypt_key");
- if (ret == 0) {
- gnutls_assert();
- goto error;
- }
-
- ret = check_binary_integrity(HOGWEED_LIBRARY_NAME, "nettle_mpz_sizeinbase_256_u");
- if (ret == 0) {
- gnutls_assert();
- goto error;
- }
-
- ret = check_binary_integrity(GMP_LIBRARY_NAME, "__gmpz_init");
- if (ret == 0) {
+ ret = check_binary_integrity();
+ if (ret < 0) {
gnutls_assert();
goto error;
}
}
-
+
return 0;
error:
/*
- * Copyright (C) 2020 Red Hat
+ * Copyright (C) 2020-2022 Red Hat, Inc.
*
- * Author: Ondrej Moris
+ * Authors: Ondrej Moris, Zoltan Fridrich
*
* This file is part of GnuTLS.
*
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
+#include <dlfcn.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include "errors.h"
+#define FORMAT_VERSION 1
#define HMAC_SIZE 32
#define HMAC_ALGO GNUTLS_MAC_SHA256
+#define HMAC_STR_SIZE (2 * HMAC_SIZE + 1)
-int main(int argc, char *argv[]) {
- gnutls_datum_t data = { NULL, 0 };
- gnutls_datum_t hex = { NULL, 0 };
- uint8_t buffer[HMAC_SIZE];
- gnutls_datum_t hmac = { buffer, sizeof(buffer) };
- int status = EXIT_FAILURE;
+static int get_path(const char *lib, const char *symbol, char *path, size_t path_size)
+{
int ret;
+ void *dl, *sym;
+ Dl_info info;
- if (argc != 2) {
- fprintf(stderr, "Usage: %s <file>\n", argv[0]);
- goto error;
- }
+ dl = dlopen(lib, RTLD_LAZY);
+ if (dl == NULL)
+ return gnutls_assert_val(GNUTLS_E_FILE_ERROR);
- ret = gnutls_load_file(argv[1], &data);
- if (ret < 0) {
- fprintf(stderr, "Could not load %s: %s\n", argv[1],
- gnutls_strerror(ret));
- goto 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;
+ size_t size;
+ uint8_t buffer[HMAC_SIZE];
+ gnutls_datum_t hex = { buffer, sizeof(buffer) };
+ gnutls_datum_t data = { NULL, 0 };
+
+ ret = gnutls_load_file(path, &data);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
GNUTLS_FIPS140_SET_LAX_MODE();
+ ret = gnutls_hmac_fast(HMAC_ALGO, FIPS_KEY, sizeof(FIPS_KEY) - 1,
+ data.data, data.size, buffer);
+ GNUTLS_FIPS140_SET_STRICT_MODE();
+
+ gnutls_free(data.data);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ size = hmac_size;
+ ret = gnutls_hex_encode(&hex, hmac, &size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
- ret = gnutls_hmac_fast(HMAC_ALGO, FIPS_KEY, sizeof(FIPS_KEY)-1,
- data.data, data.size, buffer);
+ return 0;
+}
+
+static int print_lib(const char *lib, const char *sym)
+{
+ int ret;
+ char path[GNUTLS_PATH_MAX];
+ char hmac[HMAC_STR_SIZE];
+
+ ret = get_path(lib, sym, path, sizeof(path));
if (ret < 0) {
- fprintf(stderr, "Could not calculate MAC on %s: %s\n", argv[1],
- gnutls_strerror(ret));
- goto error;
+ fprintf(stderr, "Could not get lib path for %s: %s\n",
+ lib, gnutls_strerror(ret));
+ return ret;
}
- GNUTLS_FIPS140_SET_STRICT_MODE();
-
- ret = gnutls_hex_encode2(&hmac, &hex);
+ ret = get_hmac(path, hmac, sizeof(hmac));
if (ret < 0) {
- fprintf(stderr, "Could not encode MAC value: %s\n",
- gnutls_strerror(ret));
- goto error;
+ fprintf(stderr, "Could not calculate HMAC for %s: %s\n",
+ lib, gnutls_strerror(ret));
+ return ret;
}
- printf("%s\n", hex.data);
+ printf("[%s]\n", lib);
+ printf("path = %s\n", path);
+ printf("hmac = %s\n", hmac);
- status = EXIT_SUCCESS;
+ return 0;
+}
- error:
- gnutls_free(data.data);
- gnutls_free(hex.data);
+int main(void)
+{
+ printf("[global]\n");
+ printf("format-version = %d\n", FORMAT_VERSION);
+
+ if (print_lib(GNUTLS_LIBRARY_SONAME, "gnutls_global_init") < 0)
+ return EXIT_FAILURE;
+ if (print_lib(NETTLE_LIBRARY_SONAME, "nettle_aes_set_encrypt_key") < 0)
+ return EXIT_FAILURE;
+ if (print_lib(HOGWEED_LIBRARY_SONAME, "nettle_mpz_sizeinbase_256_u") < 0)
+ return EXIT_FAILURE;
+ if (print_lib(GMP_LIBRARY_SONAME, "__gmpz_init") < 0)
+ return EXIT_FAILURE;
- return status;
+ return EXIT_SUCCESS;
}