Blame lib/fips.c

Packit 549fdc
/*
Packit 549fdc
 * Copyright (C) 2013 Red Hat
Packit 549fdc
 *
Packit 549fdc
 * Author: Nikos Mavrogiannopoulos
Packit 549fdc
 *
Packit 549fdc
 * This file is part of GnuTLS.
Packit 549fdc
 *
Packit 549fdc
 * The GnuTLS is free software; you can redistribute it and/or
Packit 549fdc
 * modify it under the terms of the GNU Lesser General Public License
Packit 549fdc
 * as published by the Free Software Foundation; either version 2.1 of
Packit 549fdc
 * the License, or (at your option) any later version.
Packit 549fdc
 *
Packit 549fdc
 * This library is distributed in the hope that it will be useful, but
Packit 549fdc
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 549fdc
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 549fdc
 * Lesser General Public License for more details.
Packit 549fdc
 *
Packit 549fdc
 * You should have received a copy of the GNU Lesser General Public License
Packit 549fdc
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
Packit 549fdc
 *
Packit 549fdc
 */
Packit 549fdc
#include "gnutls_int.h"
Packit 549fdc
#include <gnutls/gnutls.h>
Packit 549fdc
#include <gnutls/crypto.h>
Packit 549fdc
#include <unistd.h>
Packit 549fdc
#include "errors.h"
Packit 549fdc
#include <fips.h>
Packit 549fdc
#include <gnutls/self-test.h>
Packit 549fdc
#include <stdio.h>
Packit 549fdc
#include <extras/hex.h>
Packit 549fdc
#include <random.h>
Packit 549fdc
Packit 549fdc
unsigned int _gnutls_lib_mode = LIB_STATE_POWERON;
Packit 549fdc
#ifdef ENABLE_FIPS140
Packit 549fdc
Packit 549fdc
#include <dlfcn.h>
Packit 549fdc
Packit 549fdc
#define FIPS_KERNEL_FILE "/proc/sys/crypto/fips_enabled"
Packit 549fdc
#define FIPS_SYSTEM_FILE "/etc/system-fips"
Packit 549fdc
Packit 549fdc
static int _fips_mode = -1;
Packit 549fdc
static int _skip_integrity_checks = 0;
Packit 549fdc
Packit 549fdc
/* Returns:
Packit 549fdc
 * 0 - FIPS mode disabled
Packit 549fdc
 * 1 - FIPS mode enabled and enforced
Packit 549fdc
 * 2 - FIPS in testing mode
Packit 549fdc
 */
Packit 549fdc
unsigned _gnutls_fips_mode_enabled(void)
Packit 549fdc
{
Packit 549fdc
unsigned f1p = 0, f2p;
Packit 549fdc
FILE* fd;
Packit 549fdc
const char *p;
Packit 549fdc
Packit 549fdc
	if (_fips_mode != -1)
Packit 549fdc
		return _fips_mode;
Packit 549fdc
Packit 549fdc
	p = secure_getenv("GNUTLS_SKIP_FIPS_INTEGRITY_CHECKS");
Packit 549fdc
	if (p && p[0] == '1') {
Packit 549fdc
		_skip_integrity_checks = 1;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	p = secure_getenv("GNUTLS_FORCE_FIPS_MODE");
Packit 549fdc
	if (p) {
Packit 549fdc
		if (p[0] == '1')
Packit 549fdc
			_fips_mode = 1;
Packit 549fdc
		else if (p[0] == '2')
Packit 549fdc
			_fips_mode = 2;
Packit 549fdc
		else
Packit 549fdc
			_fips_mode = 0;
Packit 549fdc
		return _fips_mode;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	fd = fopen(FIPS_KERNEL_FILE, "r");
Packit 549fdc
	if (fd != NULL) {
Packit 549fdc
		f1p = fgetc(fd);
Packit 549fdc
		fclose(fd);
Packit 549fdc
		
Packit 549fdc
		if (f1p == '1') f1p = 1;
Packit 549fdc
		else f1p = 0;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	f2p = !access(FIPS_SYSTEM_FILE, F_OK);
Packit 549fdc
Packit 549fdc
	if (f1p != 0 && f2p != 0) {
Packit 549fdc
		_gnutls_debug_log("FIPS140-2 mode enabled\n");
Packit 549fdc
		_fips_mode = 1;
Packit 549fdc
		return _fips_mode;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	if (f2p != 0) {
Packit 549fdc
		/* a funny state where self tests are performed
Packit 549fdc
		 * and ignored */
Packit 549fdc
		_gnutls_debug_log("FIPS140-2 ZOMBIE mode enabled\n");
Packit 549fdc
		_fips_mode = 2;
Packit 549fdc
		return _fips_mode;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	_fips_mode = 0;
Packit 549fdc
	return _fips_mode;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
/* This _fips_mode == 2 is a strange mode where checks are being
Packit 549fdc
 * performed, but its output is ignored. */
Packit 549fdc
void _gnutls_fips_mode_reset_zombie(void)
Packit 549fdc
{
Packit 549fdc
	if (_fips_mode == 2) {
Packit 549fdc
		_fips_mode = 0;
Packit 549fdc
	}
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
#define GNUTLS_LIBRARY_NAME "libgnutls.so.28"
Packit 549fdc
#define NETTLE_LIBRARY_NAME "libnettle.so.4"
Packit 549fdc
#define HOGWEED_LIBRARY_NAME "libhogweed.so.2"
Packit 549fdc
#define GMP_LIBRARY_NAME "libgmp.so.10"
Packit 549fdc
Packit 549fdc
#define HMAC_SUFFIX ".hmac"
Packit 549fdc
#define HMAC_SIZE 32
Packit 549fdc
#define HMAC_ALGO GNUTLS_MAC_SHA256
Packit 549fdc
Packit 549fdc
static int get_library_path(const char* lib, const char* symbol, char* path, size_t path_size)
Packit 549fdc
{
Packit 549fdc
Dl_info info;
Packit 549fdc
int ret;
Packit 549fdc
void *dl, *sym;
Packit 549fdc
Packit 549fdc
	dl = dlopen(lib, RTLD_LAZY);
Packit 549fdc
	if (dl == NULL)
Packit 549fdc
		return gnutls_assert_val(GNUTLS_E_FILE_ERROR);
Packit 549fdc
Packit 549fdc
	sym = dlsym(dl, symbol);
Packit 549fdc
	if (sym == NULL) {
Packit 549fdc
		ret = gnutls_assert_val(GNUTLS_E_FILE_ERROR);
Packit 549fdc
		goto cleanup;
Packit 549fdc
	}
Packit 549fdc
	
Packit 549fdc
	ret = dladdr(sym, &info;;
Packit 549fdc
	if (ret == 0) {
Packit 549fdc
		ret = gnutls_assert_val(GNUTLS_E_FILE_ERROR);
Packit 549fdc
		goto cleanup;
Packit 549fdc
	}
Packit 549fdc
	
Packit 549fdc
	snprintf(path, path_size, "%s", info.dli_fname);
Packit 549fdc
Packit 549fdc
	ret = 0;
Packit 549fdc
cleanup:
Packit 549fdc
	dlclose(dl);
Packit 549fdc
	return ret;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
static void get_hmac_file(char *mac_file, size_t mac_file_size, const char* orig)
Packit 549fdc
{
Packit 549fdc
char* p;
Packit 549fdc
Packit 549fdc
	p = strrchr(orig, '/');
Packit 549fdc
	if (p==NULL) {
Packit 549fdc
		snprintf(mac_file, mac_file_size, ".%s"HMAC_SUFFIX, orig);
Packit 549fdc
		return;
Packit 549fdc
	}
Packit 549fdc
	snprintf(mac_file, mac_file_size, "%.*s/.%s"HMAC_SUFFIX, (int)(p-orig), orig, p+1);
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
static void get_hmac_file2(char *mac_file, size_t mac_file_size, const char* orig)
Packit 549fdc
{
Packit 549fdc
char* p;
Packit 549fdc
Packit 549fdc
	p = strrchr(orig, '/');
Packit 549fdc
	if (p==NULL) {
Packit 549fdc
		snprintf(mac_file, mac_file_size, "fipscheck/%s"HMAC_SUFFIX, orig);
Packit 549fdc
		return;
Packit 549fdc
	}
Packit 549fdc
	snprintf(mac_file, mac_file_size, "%.*s/fipscheck/%s"HMAC_SUFFIX, (int)(p-orig), orig, p+1);
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
/* Run an HMAC using the key above on the library binary data. 
Packit 549fdc
 * Returns true on success and false on error.
Packit 549fdc
 */
Packit 549fdc
static unsigned check_binary_integrity(const char* libname, const char* symbol)
Packit 549fdc
{
Packit 549fdc
	int ret;
Packit 549fdc
	unsigned prev;
Packit 549fdc
	char mac_file[GNUTLS_PATH_MAX];
Packit 549fdc
	char file[GNUTLS_PATH_MAX];
Packit 549fdc
	uint8_t hmac[HMAC_SIZE];
Packit 549fdc
	uint8_t new_hmac[HMAC_SIZE];
Packit 549fdc
	size_t hmac_size;
Packit 549fdc
	gnutls_datum_t data;
Packit 549fdc
Packit 549fdc
	ret = get_library_path(libname, symbol, file, sizeof(file));
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		_gnutls_debug_log("Could not get path for library %s\n", libname);
Packit 549fdc
		return 0;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	_gnutls_debug_log("Loading: %s\n", file);
Packit 549fdc
	ret = gnutls_load_file(file, &data);
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		_gnutls_debug_log("Could not load: %s\n", file);
Packit 549fdc
		return gnutls_assert_val(0);
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	prev = _gnutls_get_lib_state();
Packit 549fdc
	_gnutls_switch_lib_state(LIB_STATE_OPERATIONAL);
Packit 549fdc
	ret = gnutls_hmac_fast(HMAC_ALGO, FIPS_KEY, sizeof(FIPS_KEY)-1,
Packit 549fdc
		data.data, data.size, new_hmac);
Packit 549fdc
	_gnutls_switch_lib_state(prev);
Packit 549fdc
	
Packit 549fdc
	gnutls_free(data.data);
Packit 549fdc
Packit 549fdc
	if (ret < 0)
Packit 549fdc
		return gnutls_assert_val(0);
Packit 549fdc
Packit 549fdc
	/* now open the .hmac file and compare */
Packit 549fdc
	get_hmac_file(mac_file, sizeof(mac_file), file);
Packit 549fdc
Packit 549fdc
	ret = gnutls_load_file(mac_file, &data);
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		get_hmac_file2(mac_file, sizeof(mac_file), file);
Packit 549fdc
		ret = gnutls_load_file(mac_file, &data);
Packit 549fdc
		if (ret < 0) {
Packit 549fdc
			_gnutls_debug_log("Could not open %s for MAC testing: %s\n", mac_file, gnutls_strerror(ret));
Packit 549fdc
			return gnutls_assert_val(0);
Packit 549fdc
		}
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	hmac_size = hex_data_size(data.size);
Packit 549fdc
	ret = gnutls_hex_decode(&data, hmac, &hmac_size);
Packit 549fdc
	gnutls_free(data.data);
Packit 549fdc
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		_gnutls_debug_log("Could not convert hex data to binary for MAC testing for %s.\n", libname);
Packit 549fdc
		return gnutls_assert_val(0);
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	if (hmac_size != sizeof(hmac) ||
Packit 549fdc
			memcmp(hmac, new_hmac, sizeof(hmac)) != 0) {
Packit 549fdc
		_gnutls_debug_log("Calculated MAC for %s does not match\n", libname);
Packit 549fdc
		return gnutls_assert_val(0);
Packit 549fdc
	}
Packit 549fdc
	_gnutls_debug_log("Successfully verified MAC for %s (%s)\n", mac_file, libname);
Packit 549fdc
	
Packit 549fdc
	return 1;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
int _gnutls_fips_perform_self_checks1(void)
Packit 549fdc
{
Packit 549fdc
	int ret;
Packit 549fdc
Packit 549fdc
	_gnutls_switch_lib_state(LIB_STATE_SELFTEST);
Packit 549fdc
Packit 549fdc
	/* Tests the FIPS algorithms used by nettle internally.
Packit 549fdc
	 * In our case we test AES-CBC since nettle's AES is used by
Packit 549fdc
	 * the DRBG-AES.
Packit 549fdc
	 */
Packit 549fdc
Packit 549fdc
	/* ciphers - one test per cipher */
Packit 549fdc
	ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_AES_128_CBC);
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		goto error;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	return 0;
Packit 549fdc
Packit 549fdc
error:
Packit 549fdc
	_gnutls_switch_lib_state(LIB_STATE_ERROR);
Packit 549fdc
	_gnutls_audit_log(NULL, "FIPS140-2 self testing part1 failed\n");
Packit 549fdc
Packit 549fdc
	return GNUTLS_E_SELF_TEST_ERROR;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
int _gnutls_fips_perform_self_checks2(void)
Packit 549fdc
{
Packit 549fdc
	int ret;
Packit 549fdc
Packit 549fdc
	_gnutls_switch_lib_state(LIB_STATE_SELFTEST);
Packit 549fdc
Packit 549fdc
	/* Tests the FIPS algorithms */
Packit 549fdc
Packit 549fdc
	/* ciphers - one test per cipher */
Packit 549fdc
	ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_3DES_CBC);
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		goto error;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_AES_256_GCM);
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		goto error;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	/* MAC (includes message digest test) */
Packit 549fdc
	ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA1);
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		goto error;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA224);
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		goto error;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA256);
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		goto error;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA384);
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		goto error;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA512);
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		goto error;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	/* PK */
Packit 549fdc
	ret = gnutls_pk_self_test(0, GNUTLS_PK_RSA);
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		goto error;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	ret = gnutls_pk_self_test(0, GNUTLS_PK_DSA);
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		goto error;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	ret = gnutls_pk_self_test(0, GNUTLS_PK_EC);
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		goto error;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	ret = gnutls_pk_self_test(0, GNUTLS_PK_DH);
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		goto error;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	if (_gnutls_rnd_ops.self_test == NULL) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		goto error;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	/* this does not require rng initialization */
Packit 549fdc
	ret = _gnutls_rnd_ops.self_test();
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		goto error;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	if (_skip_integrity_checks == 0) {
Packit 549fdc
		ret = check_binary_integrity(GNUTLS_LIBRARY_NAME, "gnutls_global_init");
Packit 549fdc
		if (ret == 0) {
Packit 549fdc
			gnutls_assert();
Packit 549fdc
			goto error;
Packit 549fdc
		}
Packit 549fdc
Packit 549fdc
		ret = check_binary_integrity(NETTLE_LIBRARY_NAME, "nettle_aes_set_encrypt_key");
Packit 549fdc
		if (ret == 0) {
Packit 549fdc
			gnutls_assert();
Packit 549fdc
			goto error;
Packit 549fdc
		}
Packit 549fdc
Packit 549fdc
		ret = check_binary_integrity(HOGWEED_LIBRARY_NAME, "nettle_mpz_sizeinbase_256_u");
Packit 549fdc
		if (ret == 0) {
Packit 549fdc
			gnutls_assert();
Packit 549fdc
			goto error;
Packit 549fdc
		}
Packit 549fdc
Packit 549fdc
		ret = check_binary_integrity(GMP_LIBRARY_NAME, "__gmpz_init");
Packit 549fdc
		if (ret == 0) {
Packit 549fdc
			gnutls_assert();
Packit 549fdc
			goto error;
Packit 549fdc
		}
Packit 549fdc
	}
Packit 549fdc
	
Packit 549fdc
	return 0;
Packit 549fdc
Packit 549fdc
error:
Packit 549fdc
	_gnutls_switch_lib_state(LIB_STATE_ERROR);
Packit 549fdc
	_gnutls_audit_log(NULL, "FIPS140-2 self testing part 2 failed\n");
Packit 549fdc
Packit 549fdc
	return GNUTLS_E_SELF_TEST_ERROR;
Packit 549fdc
}
Packit 549fdc
#endif
Packit 549fdc
Packit 549fdc
/**
Packit 549fdc
 * gnutls_fips140_mode_enabled:
Packit 549fdc
 *
Packit 549fdc
 * Checks whether this library is in FIPS140 mode.
Packit 549fdc
 *
Packit 549fdc
 * Returns: return non-zero if true or zero if false.
Packit 549fdc
 *
Packit 549fdc
 * Since: 3.3.0
Packit 549fdc
 **/
Packit 549fdc
unsigned gnutls_fips140_mode_enabled(void)
Packit 549fdc
{
Packit 549fdc
#ifdef ENABLE_FIPS140
Packit 549fdc
int ret = _gnutls_fips_mode_enabled();
Packit 549fdc
Packit 549fdc
	if (ret == 1)
Packit 549fdc
		return ret;
Packit 549fdc
#endif
Packit 549fdc
	return 0;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
void _gnutls_lib_simulate_error(void)
Packit 549fdc
{
Packit 549fdc
	_gnutls_switch_lib_state(LIB_STATE_ERROR);
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
void _gnutls_lib_force_operational(void)
Packit 549fdc
{
Packit 549fdc
	_gnutls_switch_lib_state(LIB_STATE_OPERATIONAL);
Packit 549fdc
}