From 21d4cd5acdc6dc88b339724f484cec1521c8be46 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Tue, 12 Nov 2013 12:46:10 +0100 Subject: [PATCH] Updated FIPS140 initialization and added a self test for it. --- NEWS | 1 + build-aux/config.rpath | 106 +++++++++++++------------------ configure.ac | 16 ++++- lib/Makefile.am | 4 ++ lib/fips.c | 73 +++++++++++++++------- lib/fips.h | 5 +- lib/includes/Makefile.am | 4 ++ lib/includes/gnutls/fips140.h | 55 ++++++++++++++++ lib/libgnutls.map | 5 ++ lib/xssl.c | 4 -- tests/Makefile.am | 4 ++ tests/fips-test.c | 142 ++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 329 insertions(+), 90 deletions(-) create mode 100644 lib/includes/gnutls/fips140.h create mode 100644 tests/fips-test.c diff --git a/NEWS b/NEWS index 4699e5b..80fbed4 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ See the end for copying conditions. ** API and ABI modifications: gnutls_privkey_generate: Added +gnutls_fips140_mode_enabled: Added * Version 3.2.6 (released 2013-10-31) diff --git a/build-aux/config.rpath b/build-aux/config.rpath index c38b914..17298f2 100755 --- a/build-aux/config.rpath +++ b/build-aux/config.rpath @@ -2,7 +2,7 @@ # Output a system dependent set of variables, describing how to set the # run time search path of shared libraries in an executable. # -# Copyright 1996-2013 Free Software Foundation, Inc. +# Copyright 1996-2010 Free Software Foundation, Inc. # Taken from GNU libtool, 2001 # Originally by Gordon Matzigkeit , 1996 # @@ -25,7 +25,7 @@ # known workaround is to choose shorter directory names for the build # directory and/or the installation directory. -# All known linkers require a '.a' archive for static linking (except MSVC, +# All known linkers require a `.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a shrext=.so @@ -57,6 +57,13 @@ else aix*) wl='-Wl,' ;; + darwin*) + case $cc_basename in + xlc*) + wl='-Wl,' + ;; + esac + ;; mingw* | cygwin* | pw32* | os2* | cegcc*) ;; hpux9* | hpux10* | hpux11*) @@ -65,7 +72,9 @@ else irix5* | irix6* | nonstopux*) wl='-Wl,' ;; - linux* | k*bsd*-gnu | kopensolaris*-gnu) + newsos6) + ;; + linux* | k*bsd*-gnu) case $cc_basename in ecc*) wl='-Wl,' @@ -76,26 +85,17 @@ else lf95*) wl='-Wl,' ;; - nagfor*) - wl='-Wl,-Wl,,' - ;; - pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + pgcc | pgf77 | pgf90) wl='-Wl,' ;; ccc*) wl='-Wl,' ;; - xl* | bgxl* | bgf* | mpixl*) - wl='-Wl,' - ;; como) wl='-lopt=' ;; *) case `$CC -V 2>&1 | sed 5q` in - *Sun\ F* | *Sun*Fortran*) - wl= - ;; *Sun\ C*) wl='-Wl,' ;; @@ -103,24 +103,13 @@ else ;; esac ;; - newsos6) - ;; - *nto* | *qnx*) - ;; osf3* | osf4* | osf5*) wl='-Wl,' ;; rdos*) ;; solaris*) - case $cc_basename in - f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) - wl='-Qoption ld ' - ;; - *) - wl='-Wl,' - ;; - esac + wl='-Wl,' ;; sunos4*) wl='-Qoption ld ' @@ -182,14 +171,15 @@ if test "$with_gnu_ld" = yes; then fi ;; amigaos*) - case "$host_cpu" in - powerpc) - ;; - m68k) - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - ;; - esac + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + # Samuel A. Falvo II reports + # that the semantics of dynamic libraries on AmigaOS, at least up + # to version 4, is to share data among multiple programs linked + # with the same dynamic library. Since this doesn't match the + # behavior of shared libraries on other platforms, we cannot use + # them. + ld_shlibs=no ;; beos*) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then @@ -208,13 +198,11 @@ if test "$with_gnu_ld" = yes; then ld_shlibs=no fi ;; - haiku*) - ;; interix[3-9]*) hardcode_direct=no hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; - gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + gnu* | linux* | k*bsd*-gnu) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else @@ -337,14 +325,10 @@ else fi ;; amigaos*) - case "$host_cpu" in - powerpc) - ;; - m68k) - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - ;; - esac + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + # see comment about different semantics on the GNU ld section + ld_shlibs=no ;; bsdi[45]*) ;; @@ -358,15 +342,24 @@ else ;; darwin* | rhapsody*) hardcode_direct=no - if { case $cc_basename in ifort*) true;; *) test "$GCC" = yes;; esac; }; then + if test "$GCC" = yes ; then : else - ld_shlibs=no + case $cc_basename in + xlc*) + ;; + *) + ld_shlibs=no + ;; + esac fi ;; dgux*) hardcode_libdir_flag_spec='-L$libdir' ;; + freebsd1*) + ld_shlibs=no + ;; freebsd2.2*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes @@ -427,8 +420,6 @@ else hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; - *nto* | *qnx*) - ;; openbsd*) if test -f /usr/libexec/ld.so; then hardcode_direct=yes @@ -524,12 +515,7 @@ case "$host_os" in library_names_spec='$libname$shrext' ;; amigaos*) - case "$host_cpu" in - powerpc*) - library_names_spec='$libname$shrext' ;; - m68k) - library_names_spec='$libname.a' ;; - esac + library_names_spec='$libname.a' ;; beos*) library_names_spec='$libname$shrext' @@ -548,6 +534,8 @@ case "$host_os" in dgux*) library_names_spec='$libname$shrext' ;; + freebsd1*) + ;; freebsd* | dragonfly*) case "$host_os" in freebsd[123]*) @@ -559,9 +547,6 @@ case "$host_os" in gnu*) library_names_spec='$libname$shrext' ;; - haiku*) - library_names_spec='$libname$shrext' - ;; hpux9* | hpux10* | hpux11*) case $host_cpu in ia64*) @@ -597,7 +582,7 @@ case "$host_os" in ;; linux*oldld* | linux*aout* | linux*coff*) ;; - linux* | k*bsd*-gnu | kopensolaris*-gnu) + linux* | k*bsd*-gnu) library_names_spec='$libname$shrext' ;; knetbsd*-gnu) @@ -609,7 +594,7 @@ case "$host_os" in newsos6) library_names_spec='$libname$shrext' ;; - *nto* | *qnx*) + nto-qnx*) library_names_spec='$libname$shrext' ;; openbsd*) @@ -640,9 +625,6 @@ case "$host_os" in sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) library_names_spec='$libname$shrext' ;; - tpf*) - library_names_spec='$libname$shrext' - ;; uts4*) library_names_spec='$libname$shrext' ;; diff --git a/configure.ac b/configure.ac index 7681b56..18b0311 100644 --- a/configure.ac +++ b/configure.ac @@ -131,8 +131,20 @@ AC_ARG_ENABLE(fips140-mode, enable_fips=$enableval, enable_fips=no) AM_CONDITIONAL(ENABLE_FIPS140, test "$enable_fips" = "yes") if [ test "$enable_fips" = "yes" ];then - enable_self_checks=yes - AC_DEFINE([ENABLE_FIPS140], 1, [Enable FIPS140-2 mode]) + AC_LIB_HAVE_LINKFLAGS(dl,, [#include ], [dladdr (0, 0);]) + if test "x$HAVE_LIBDL" = "xyes";then + enable_self_checks=yes + AC_DEFINE([ENABLE_FIPS140], 1, [Enable FIPS140-2 mode]) + + AC_SUBST([FIPS140_LIBS], $LIBDL) + else + enable_fips=no + AC_MSG_WARN([[ +*** +*** This system is not supported in FIPS140 mode. +*** libdl and dladdr() are required. +*** ]]) + fi fi AM_CONDITIONAL(ENABLE_SELF_CHECKS, test "$enable_self_checks" = "yes") diff --git a/lib/Makefile.am b/lib/Makefile.am index 62deb9b..f851f4a 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -132,6 +132,10 @@ if NEEDS_LIBRT thirdparty_libadd += $(LTLIBRT) endif +if ENABLE_FIPS140 +thirdparty_libadd += $(FIPS140_LIBS) +endif + if ENABLE_OPENPGP libgnutls_la_LIBADD += openpgp/libgnutls_openpgp.la libgnutls_la_LIBADD += opencdk/libminiopencdk.la diff --git a/lib/fips.c b/lib/fips.c index c88ca41..fa6543d 100644 --- a/lib/fips.c +++ b/lib/fips.c @@ -26,6 +26,10 @@ #include #include #include +#include +#include + +#define FIPS140_TEST #ifdef ENABLE_FIPS140 @@ -38,14 +42,18 @@ unsigned _gnutls_fips_mode_enabled(void) access("/etc/system-fips", R_OK) == 0) return 1; +#ifndef FIPS140_TEST return 0; +#else + return 1; +#endif } -#ifdef HAVE_DLADDR static const char fips_key[] = "I'd rather be skiing."; #define HMAC_SUFFIX ".hmac" - +#define HMAC_SIZE 32 +#define HMAC_ALGO GNUTLS_MAC_SHA256 /* Run an HMAC using the key above on the library binary data. * Returns true success and false on error. @@ -56,23 +64,24 @@ static unsigned check_binary_integrity(void) Dl_info info; unsigned prev; char mac_file[GNUTLS_PATH_MAX]; - uint8_t sha256_hmac[32]; - uint8_t new_sha256_hmac[32]; - size_t sha256_hmac_size; + uint8_t hmac[HMAC_SIZE]; + uint8_t new_hmac[HMAC_SIZE]; + size_t hmac_size; gnutls_datum_t data; ret = dladdr("gnutls_global_init", &info); if (ret == 0) return gnutls_assert_val(0); + _gnutls_debug_log("Loading: %s\n", info.dli_fname); ret = gnutls_load_file(info.dli_fname, &data); if (ret < 0) return gnutls_assert_val(0); prev = _gnutls_get_fips_state(); _gnutls_switch_fips_state(FIPS_STATE_OPERATIONAL); - ret = gnutls_hmac_fast(GNUTLS_MAC_SHA256, fips_key, sizeof(fips_key)-1, - data.data, data.size, new_sha256_hmac); + ret = gnutls_hmac_fast(HMAC_ALGO, fips_key, sizeof(fips_key)-1, + data.data, data.size, new_hmac); _gnutls_switch_fips_state(prev); gnutls_free(data.data); @@ -83,31 +92,26 @@ static unsigned check_binary_integrity(void) /* now open the .hmac file and compare */ snprintf(mac_file, sizeof(mac_file), "%s"HMAC_SUFFIX, info.dli_fname); - ret = gnutls_load_file(info.dli_fname, &data); + ret = gnutls_load_file(mac_file, &data); if (ret < 0) return gnutls_assert_val(0); - sha256_hmac_size = sizeof(sha256_hmac); - ret = _gnutls_hex2bin(data.data, data.size, sha256_hmac, &sha256_hmac_size); + hmac_size = sizeof(hmac); + ret = _gnutls_hex2bin((void*)data.data, data.size, hmac, &hmac_size); gnutls_free(data.data); if (ret < 0) return gnutls_assert_val(0); - if (sha256_hmac_size != sizeof(sha256_hmac) || - memcmp(sha256_hmac, new_sha256_hmac, sizeof(sha256_hmac)) != 0) + if (hmac_size != sizeof(hmac) || + memcmp(hmac, new_hmac, sizeof(hmac)) != 0) { + _gnutls_debug_log("Calculated MAC does not match\n"); return gnutls_assert_val(0); + } return 1; } -#else -static int check_binary_integrity(void) -{ - return 1; -} -#endif - int _gnutls_fips_perform_self_checks(void) { int ret; @@ -210,16 +214,43 @@ int _gnutls_fips_perform_self_checks(void) } ret = check_binary_integrity(); - if (ret < 0) { + if (ret == 0) { gnutls_assert(); +#ifndef FIPS140_TEST goto error; +#endif } return 0; error: _gnutls_switch_fips_state(FIPS_STATE_ERROR); - return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR); + return GNUTLS_E_SELF_TEST_ERROR; } +#endif +/** + * gnutls_fips140_mode_enabled: + * + * Checks whether this library is in FIPS140 mode. + * + * Returns: return non-zero if true or zero if false. + * + * Since: 3.3.0 + **/ +int gnutls_fips140_mode_enabled(void) +{ +#ifdef ENABLE_FIPS140 + + return _gnutls_fips_mode_enabled(); +#else + return 0; #endif +} + +void _gnutls_fips140_simulate_error(void) +{ +#ifdef ENABLE_FIPS140 + _gnutls_switch_fips_state(FIPS_STATE_ERROR); +#endif +} diff --git a/lib/fips.h b/lib/fips.h index 355ab9e..25b3e3c 100644 --- a/lib/fips.h +++ b/lib/fips.h @@ -55,10 +55,13 @@ int _gnutls_fips_perform_self_checks(void); unsigned _gnutls_fips_mode_enabled(void); # define FAIL_IF_FIPS_ERROR \ - if (_gnutls_get_fips_state() != FIPS_STATE_OPERATIONAL) return GNUTLS_E_LIB_IN_ERROR_STATE + if (_gnutls_get_fips_state() != FIPS_STATE_OPERATIONAL && \ + _gnutls_get_fips_state() != FIPS_STATE_SELFTEST) return GNUTLS_E_LIB_IN_ERROR_STATE void _gnutls_switch_fips_state(gnutls_fips_state_t state); +void _gnutls_fips140_simulate_error(void); + #else # define _gnutls_switch_fips_state(x) 0 diff --git a/lib/includes/Makefile.am b/lib/includes/Makefile.am index 2fb8609..2fe1ce2 100644 --- a/lib/includes/Makefile.am +++ b/lib/includes/Makefile.am @@ -23,6 +23,10 @@ nobase_include_HEADERS = gnutls/x509.h gnutls/pkcs12.h gnutls/compat.h \ gnutls/abstract.h gnutls/dtls.h gnutls/ocsp.h gnutls/tpm.h \ gnutls/xssl.h +if ENABLE_FIPS140 +nobase_include_HEADERS += gnutls/fips140.h +endif + if ENABLE_CXX nobase_include_HEADERS += gnutls/gnutlsxx.h endif diff --git a/lib/includes/gnutls/fips140.h b/lib/includes/gnutls/fips140.h new file mode 100644 index 0000000..f60c814 --- /dev/null +++ b/lib/includes/gnutls/fips140.h @@ -0,0 +1,55 @@ +/* -*- c -*- + * Copyright (C) 2000-2012 Free Software Foundation, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + * + */ + +/* This file contains the types and prototypes for all the + * high level functionality of the gnutls main library. + * + * If the optional C++ binding was built, it is available in + * gnutls/gnutlsxx.h. + * + * The openssl compatibility layer (which is under the GNU GPL + * license) is in gnutls/openssl.h. + * + * The low level cipher functionality is in gnutls/crypto.h. + */ + + +#ifndef GNUTLS_FIPS140_H +#define GNUTLS_FIPS140_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus +extern "C" { +#endif +/* *INDENT-ON* */ + + +int gnutls_fips140_mode_enabled(void); + + +/* *INDENT-OFF* */ +#ifdef __cplusplus +} +#endif +/* *INDENT-ON* */ + +#endif diff --git a/lib/libgnutls.map b/lib/libgnutls.map index b3fea6e..57899b7 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -932,6 +932,8 @@ GNUTLS_3_1_0 { gnutls_mac_self_test; gnutls_digest_self_test; gnutls_privkey_generate; + + gnutls_fips140_mode_enabled; } GNUTLS_3_0_0; GNUTLS_PRIVATE { @@ -963,6 +965,9 @@ GNUTLS_PRIVATE { _gnutls_buffer_init; _gnutls_buffer_append_str; _gnutls_buffer_to_datum; + + # Internal symbols used by fips-test + _gnutls_fips140_simulate_error; # Internal symbols needed by psktool: # Internal symbols needed by gnutls-cli-debug: diff --git a/lib/xssl.c b/lib/xssl.c index 11780ee..237c686 100644 --- a/lib/xssl.c +++ b/lib/xssl.c @@ -303,8 +303,6 @@ int xssl_sinit(xssl_t * isb, gnutls_session_t session, unsigned int flags) { struct xssl_st *sb; - FAIL_IF_FIPS_ERROR; - sb = gnutls_calloc(1, sizeof(*sb)); if (sb == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); @@ -351,8 +349,6 @@ int xssl_client_init(xssl_t * isb, const char *hostname, int ret; unsigned len; - FAIL_IF_FIPS_ERROR; - ret = gnutls_init(&session, GNUTLS_CLIENT); if (ret < 0) return gnutls_assert_val(ret); diff --git a/tests/Makefile.am b/tests/Makefile.am index ae0a45b..4929de2 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -75,6 +75,10 @@ ctests = simple gc set_pkcs12_cred certder certuniqueid \ mini-record mini-dtls-record mini-handshake-timeout mini-record-range \ mini-cert-status mini-rsa-psk mini-record-2 +if ENABLE_FIPS140 +ctests += fips-test +endif + if ENABLE_OCSP ctests += ocsp endif diff --git a/tests/fips-test.c b/tests/fips-test.c new file mode 100644 index 0000000..cbe3dbb --- /dev/null +++ b/tests/fips-test.c @@ -0,0 +1,142 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void _gnutls_fips140_simulate_error(void); + +/* This does check the FIPS140 support. + */ + +static void tls_log_func(int level, const char *str) +{ + fprintf(stderr, "<%d>| %s", level, str); +} + +static char key16[16]; +static char iv16[16]; + +void doit(void) +{ +int ret; +#ifdef ENABLE_FIPS140 + gnutls_cipher_hd_t ch; + gnutls_hmac_hd_t mh; + gnutls_session_t session; + gnutls_pubkey_t pubkey; + gnutls_x509_privkey_t xprivkey; + gnutls_privkey_t privkey; + gnutls_datum_t key = { key16, sizeof(key16) }; + gnutls_datum_t iv = { iv16, sizeof(iv16) }; + + fprintf(stderr, "Please note that you need to assure the library's integrity prior to running this test\n"); + + gnutls_global_set_log_function(tls_log_func); + if (debug) + gnutls_global_set_log_level(4711); + + ret = gnutls_fips140_mode_enabled(); + if (ret == 0) { + fail("We are not in FIPS140 mode\n"); + } + + ret = global_init(); + if (ret < 0) { + fail("Cannot initialize library\n"); + } + + /* Try crypto.h functionality */ + ret = gnutls_cipher_init(&ch, GNUTLS_CIPHER_AES_128_CBC, &key, &iv); + if (ret < 0) { + fail("gnutls_cipher_init failed\n"); + } + gnutls_cipher_deinit(ch); + + ret = gnutls_hmac_init(&mh, GNUTLS_MAC_SHA1, key.data, key.size); + if (ret < 0) { + fail("gnutls_hmac_init failed\n"); + } + gnutls_hmac_deinit(mh, NULL); + + ret = gnutls_rnd(GNUTLS_RND_NONCE, key16, sizeof(key16)); + if (ret < 0) { + fail("gnutls_rnd failed\n"); + } + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) { + fail("gnutls_pubkey_init failed\n"); + } + gnutls_pubkey_deinit(pubkey); + + ret = gnutls_privkey_init(&privkey); + if (ret < 0) { + fail("gnutls_privkey_init failed\n"); + } + gnutls_privkey_deinit(privkey); + + ret = gnutls_x509_privkey_init(&xprivkey); + if (ret < 0) { + fail("gnutls_privkey_init failed\n"); + } + gnutls_x509_privkey_deinit(xprivkey); + + ret = gnutls_init(&session, 0); + if (ret < 0) { + fail("gnutls_init failed\n"); + } + gnutls_deinit(session); + + /* Test when FIPS140 is set to error state */ + _gnutls_fips140_simulate_error(); + + + /* Try crypto.h functionality */ + ret = gnutls_cipher_init(&ch, GNUTLS_CIPHER_AES_128_CBC, &key, &iv); + if (ret >= 0) { + fail("gnutls_cipher_init succeeded when in FIPS140 error state\n"); + } + + ret = gnutls_hmac_init(&mh, GNUTLS_MAC_SHA1, key.data, key.size); + if (ret >= 0) { + fail("gnutls_hmac_init succeeded when in FIPS140 error state\n"); + } + + ret = gnutls_rnd(GNUTLS_RND_NONCE, key16, sizeof(key16)); + if (ret >= 0) { + fail("gnutls_rnd succeeded when in FIPS140 error state\n"); + } + + ret = gnutls_pubkey_init(&pubkey); + if (ret >= 0) { + fail("gnutls_pubkey_init succeeded when in FIPS140 error state\n"); + } + + ret = gnutls_privkey_init(&privkey); + if (ret >= 0) { + fail("gnutls_privkey_init succeeded when in FIPS140 error state\n"); + } + + ret = gnutls_x509_privkey_init(&xprivkey); + if (ret >= 0) { + fail("gnutls_x509_privkey_init succeeded when in FIPS140 error state\n"); + } + + ret = gnutls_init(&session, 0); + if (ret >= 0) { + fail("gnutls_init succeeded when in FIPS140 error state\n"); + } + + gnutls_global_deinit(); + return 0; +#else + return 1; /* fail. This script shouldn't be called on this case */ +#endif +} -- 2.1.4