From: Nikos Mavrogiannopoulos Date: Mon, 11 Nov 2013 17:07:17 +0000 (+0100) Subject: Added support for fips states. X-Git-Tag: gnutls_3_3_0pre0~520^2~89 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0f8af4f83efde3c8d448ed4bd8ae0879e2112607;p=thirdparty%2Fgnutls.git Added support for fips states. This implies that when in FIPS mode and the library is not in operational state (i.e., all self checks succeeded), crypto functionality of the library will fail. This includes: * API functions of gnutls/crypto.h * API functions of gnutls/abstract.h * API functions of gnutls/x509.h * gnutls_init() * API functions of gnutls/xssl.h --- diff --git a/configure.ac b/configure.ac index 8387847ed6..7681b56b79 100644 --- a/configure.ac +++ b/configure.ac @@ -125,6 +125,16 @@ AM_CONDITIONAL(HAVE_GCC, test "$GCC" = "yes") AC_ARG_ENABLE(self-checks, AS_HELP_STRING([--enable-self-checks], [enable self checking functionality]), enable_self_checks=$enableval, enable_self_checks=no) + +AC_ARG_ENABLE(fips140-mode, + AS_HELP_STRING([--enable-fips140-mode], [enable FIPS140-2 mode (implies self checks)]), + 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]) +fi + AM_CONDITIONAL(ENABLE_SELF_CHECKS, test "$enable_self_checks" = "yes") if [ test "$enable_self_checks" = "yes" ];then AC_DEFINE([ENABLE_SELF_CHECKS], 1, [Self checks are included in the library]) @@ -729,6 +739,7 @@ if features are disabled) RSA-EXPORT compat: $ac_enable_rsa_export Unicode support: $ac_have_unicode Self checks: $enable_self_checks + FIPS140 mode: $enable_fips ]) AC_MSG_NOTICE([Optional applications: diff --git a/lib/Makefile.am b/lib/Makefile.am index 2f5d5e5fea..62deb9b6df 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -78,7 +78,7 @@ COBJECTS = gnutls_range.c gnutls_record.c \ gnutls_rsa_export.c gnutls_helper.c gnutls_supplemental.c \ random.c crypto-api.c gnutls_privkey.c gnutls_pcert.c \ gnutls_pubkey.c locks.c gnutls_dtls.c system_override.c \ - crypto-backend.c verify-tofu.c pin.c tpm.c + crypto-backend.c verify-tofu.c pin.c tpm.c fips.c if ENABLE_SELF_CHECKS COBJECTS += crypto-selftests.c crypto-selftests-pk.c @@ -105,7 +105,7 @@ HFILES = abstract_int.h debug.h gnutls_compress.h gnutls_cipher.h \ gnutls_state.h gnutls_x509.h crypto-backend.h \ gnutls_srp.h auth/srp.h auth/srp_passwd.h \ gnutls_helper.h gnutls_supplemental.h crypto.h random.h system.h\ - locks.h gnutls_mbuffers.h gnutls_ecc.h pin.h + locks.h gnutls_mbuffers.h gnutls_ecc.h pin.h fips.h if ENABLE_PKCS11 HFILES += pkcs11_int.h diff --git a/lib/fips.c b/lib/fips.c new file mode 100644 index 0000000000..726dae632c --- /dev/null +++ b/lib/fips.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2013 Red Hat + * + * 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 + * + */ + +#ifdef ENABLE_FIPS140 + +# include +# include +# include + +unsigned int _gnutls_fips_mode = STATE_POWERON; + +unsigned _gnutls_fips_mode_enabled(void) +{ + /* FIXME: There are some subtle differences here. Check it out later */ + if (access("/proc/sys/crypto/fips_enabled", R_OK) == 0 && + access("/etc/system-fips", R_OK) == 0) + return 1; + + return 0; +} + +int _gnutls_fips_perform_self_checks(void) +{ + _gnutls_switch_fips_state(FIPS_STATE_SELFTEST); + + /* Tests the FIPS algorithms */ + + /* ciphers */ + ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_AES_128_CBC); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_AES_192_CBC); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_AES_256_CBC); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_3DES_CBC); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_AES_128_GCM); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_AES_256_GCM); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + /* MAC (includes message digest test) */ + ret = gnutls_mac_self_test(0, GNUTLS_MAC_MD5); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA1); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA224); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA256); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA384); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA512); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + /* PK */ + ret = gnutls_pk_self_test(0, GNUTLS_PK_RSA); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = gnutls_pk_self_test(0, GNUTLS_PK_DSA); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = gnutls_pk_self_test(0, GNUTLS_PK_ECDSA); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + return 0; +error: + _gnutls_switch_fips_state(FIPS_STATE_ERROR); + return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERR); +} + +#endif diff --git a/lib/fips.h b/lib/fips.h new file mode 100644 index 0000000000..9cd17a40c0 --- /dev/null +++ b/lib/fips.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2013 Red Hat + * + * 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 + * + */ + +#ifndef FIPS_H +# define FIPS_H + +#include + +typedef enum { + FIPS_STATE_POWERON, + FIPS_STATE_INIT, + FIPS_STATE_SELFTEST, + FIPS_STATE_OPERATIONAL, + FIPS_STATE_ERROR, + FIPS_STATE_SHUTDOWN +} gnutls_fips_state_t; + +#ifdef ENABLE_FIPS140 + +/* do not access directly */ +extern unsigned int _gnutls_fips_mode; + +inline static +void _gnutls_switch_fips_state(gnutls_fips_state_t state) +{ + _gnutls_fips_mode = state; +} + +inline static gnutls_fips_state_t _gnutls_get_fips_state(void) +{ + return _gnutls_fips_mode; +} + +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 + +void _gnutls_switch_fips_state(gnutls_fips_state_t state); + +#else + +# define _gnutls_switch_fips_state(x) 0 +# define _gnutls_get_fips_state() STATE_OPERATIONAL +# define FAIL_IF_FIPS_ERROR 0 +# define _gnutls_fips_perform_self_checks() 0 +# define _gnutls_fips_mode_enabled() 0 +#endif + +#endif /* FIPS_H */ diff --git a/lib/gnutls_cipher_int.c b/lib/gnutls_cipher_int.c index d6483ab2d8..184156725e 100644 --- a/lib/gnutls_cipher_int.c +++ b/lib/gnutls_cipher_int.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #define SR(x, cleanup) if ( (x)<0 ) { \ @@ -61,6 +62,8 @@ _gnutls_cipher_init(cipher_hd_st * handle, const cipher_entry_st * e, if (unlikely(e == NULL || e->id == GNUTLS_CIPHER_NULL)) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + FAIL_IF_FIPS_ERROR; handle->e = e; @@ -144,6 +147,8 @@ int _gnutls_auth_cipher_init(auth_cipher_hd_st * handle, if (unlikely(e == NULL)) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + FAIL_IF_FIPS_ERROR; + memset(handle, 0, sizeof(*handle)); if (e->id != GNUTLS_CIPHER_NULL) { diff --git a/lib/gnutls_errors.c b/lib/gnutls_errors.c index 3c932859fa..2c46552799 100644 --- a/lib/gnutls_errors.c +++ b/lib/gnutls_errors.c @@ -396,6 +396,12 @@ static const gnutls_error_entry error_algorithms[] = { ERROR_ENTRY(N_ ("No common application protocol could be negotiated."), GNUTLS_E_NO_APPLICATION_PROTOCOL, 1), + ERROR_ENTRY(N_("Error while performing self checks."), + GNUTLS_E_SELF_TEST_ERROR, 1), + ERROR_ENTRY(N_("There is no self test for this algorithm."), + GNUTLS_E_NO_SELF_TEST, 1), + ERROR_ENTRY(N_("An error has been detected in the library and cannot continue operations."), + GNUTLS_E_LIB_IN_ERROR_STATE, 1), {NULL, NULL, 0, 0} }; diff --git a/lib/gnutls_global.c b/lib/gnutls_global.c index 6dbc534dc0..12912f73d9 100644 --- a/lib/gnutls_global.c +++ b/lib/gnutls_global.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "sockets.h" #include "gettext.h" @@ -201,6 +202,8 @@ int gnutls_global_init(void) if (_gnutls_init++) goto out; + + _gnutls_switch_fips_state(FIPS_STATE_INIT); e = getenv("GNUTLS_DEBUG_LEVEL"); if (e != NULL) { @@ -279,8 +282,17 @@ int gnutls_global_init(void) #endif _gnutls_cryptodev_init(); + + if (_gnutls_fips_mode_enabled()) { + result = _gnutls_fips_perform_self_checks(); + if (result < 0) { + gnutls_assert(); + goto out; + } + _gnutls_switch_fips_state(FIPS_STATE_OPERATIONAL); + } - out: +out: return result; } diff --git a/lib/gnutls_hash_int.c b/lib/gnutls_hash_int.c index 7f16a54d3e..ddeff10bdb 100644 --- a/lib/gnutls_hash_int.c +++ b/lib/gnutls_hash_int.c @@ -28,12 +28,15 @@ #include #include #include +#include int _gnutls_hash_init(digest_hd_st * dig, const mac_entry_st * e) { int result; const gnutls_crypto_digest_st *cc = NULL; + FAIL_IF_FIPS_ERROR; + dig->e = e; /* check if a digest has been registered @@ -84,6 +87,8 @@ _gnutls_hash_fast(gnutls_digest_algorithm_t algorithm, { int ret; const gnutls_crypto_digest_st *cc = NULL; + + FAIL_IF_FIPS_ERROR; /* check if a digest has been registered */ @@ -117,6 +122,8 @@ _gnutls_mac_fast(gnutls_mac_algorithm_t algorithm, const void *key, int ret; const gnutls_crypto_mac_st *cc = NULL; + FAIL_IF_FIPS_ERROR; + /* check if a digest has been registered */ cc = _gnutls_get_crypto_mac(algorithm); @@ -164,6 +171,8 @@ _gnutls_mac_init(mac_hd_st * mac, const mac_entry_st * e, int result; const gnutls_crypto_mac_st *cc = NULL; + FAIL_IF_FIPS_ERROR; + mac->e = e; mac->mac_len = _gnutls_mac_get_algo_len(e); @@ -245,6 +254,8 @@ _gnutls_mac_init_ssl3(digest_hd_st * ret, const mac_entry_st * e, uint8_t ipad[48]; int padsize, result; + FAIL_IF_FIPS_ERROR; + padsize = get_padsize((gnutls_digest_algorithm_t) e->id); if (padsize == 0) { gnutls_assert(); diff --git a/lib/gnutls_privkey.c b/lib/gnutls_privkey.c index 48afb06604..81507fa7f9 100644 --- a/lib/gnutls_privkey.c +++ b/lib/gnutls_privkey.c @@ -33,6 +33,7 @@ #include #include #include +#include #include /** @@ -237,6 +238,8 @@ _gnutls_privkey_get_public_mpis(gnutls_privkey_t key, **/ int gnutls_privkey_init(gnutls_privkey_t * key) { + FAIL_IF_FIPS_ERROR; + *key = gnutls_calloc(1, sizeof(struct gnutls_privkey_st)); if (*key == NULL) { gnutls_assert(); diff --git a/lib/gnutls_pubkey.c b/lib/gnutls_pubkey.c index d8979f77c0..21826b866d 100644 --- a/lib/gnutls_pubkey.c +++ b/lib/gnutls_pubkey.c @@ -34,6 +34,7 @@ #include #include #include +#include #include @@ -110,6 +111,8 @@ int gnutls_pubkey_get_key_usage(gnutls_pubkey_t key, unsigned int *usage) **/ int gnutls_pubkey_init(gnutls_pubkey_t * key) { + FAIL_IF_FIPS_ERROR; + *key = gnutls_calloc(1, sizeof(struct gnutls_pubkey_st)); if (*key == NULL) { gnutls_assert(); diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c index 231e6b0b64..21a8f99cee 100644 --- a/lib/gnutls_state.c +++ b/lib/gnutls_state.c @@ -45,6 +45,7 @@ #include #include #include +#include #include /* These should really be static, but src/tests.c calls them. Make @@ -307,6 +308,8 @@ int gnutls_init(gnutls_session_t * session, unsigned int flags) { int ret; record_parameters_st *epoch; + + FAIL_IF_FIPS_ERROR; *session = gnutls_calloc(1, sizeof(struct gnutls_session_int)); if (*session == NULL) diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 742f44f008..541fda6d53 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -2288,6 +2288,7 @@ int gnutls_pk_self_test(unsigned all, gnutls_pk_algorithm_t pk); #define GNUTLS_E_SELF_TEST_ERROR -400 #define GNUTLS_E_NO_SELF_TEST -401 +#define GNUTLS_E_LIB_IN_ERROR_STATE -402 #define GNUTLS_E_UNIMPLEMENTED_FEATURE -1250 diff --git a/lib/pkcs11_privkey.c b/lib/pkcs11_privkey.c index 7a0aa38912..d24f6a3dee 100644 --- a/lib/pkcs11_privkey.c +++ b/lib/pkcs11_privkey.c @@ -27,6 +27,7 @@ #include #include #include +#include #include struct gnutls_pkcs11_privkey_st { @@ -51,6 +52,8 @@ struct gnutls_pkcs11_privkey_st { **/ int gnutls_pkcs11_privkey_init(gnutls_pkcs11_privkey_t * key) { + FAIL_IF_FIPS_ERROR; + *key = gnutls_calloc(1, sizeof(struct gnutls_pkcs11_privkey_st)); if (*key == NULL) { gnutls_assert(); diff --git a/lib/random.c b/lib/random.c index 62fa785232..b5f6cd94f9 100644 --- a/lib/random.c +++ b/lib/random.c @@ -26,6 +26,7 @@ #include #include #include +#include void *gnutls_rnd_ctx; @@ -65,6 +66,7 @@ void _gnutls_rnd_deinit(void) **/ int gnutls_rnd(gnutls_rnd_level_t level, void *data, size_t len) { + FAIL_IF_FIPS_ERROR; return _gnutls_rnd(level, data, len); } diff --git a/lib/x509/common.h b/lib/x509/common.h index 7ad55c2da7..8bc51d1ee0 100644 --- a/lib/x509/common.h +++ b/lib/x509/common.h @@ -26,6 +26,7 @@ #include #include #include +#include #define MAX_STRING_LEN 512 diff --git a/lib/x509/crl.c b/lib/x509/crl.c index bd2560dc78..480074ec39 100644 --- a/lib/x509/crl.c +++ b/lib/x509/crl.c @@ -46,6 +46,8 @@ **/ int gnutls_x509_crl_init(gnutls_x509_crl_t * crl) { + FAIL_IF_FIPS_ERROR; + *crl = gnutls_calloc(1, sizeof(gnutls_x509_crl_int)); if (*crl) { diff --git a/lib/x509/crq.c b/lib/x509/crq.c index 4e28fedd7c..67f425a4a4 100644 --- a/lib/x509/crq.c +++ b/lib/x509/crq.c @@ -49,6 +49,8 @@ int gnutls_x509_crq_init(gnutls_x509_crq_t * crq) { int result; + + FAIL_IF_FIPS_ERROR; *crq = gnutls_calloc(1, sizeof(gnutls_x509_crq_int)); if (!*crq) diff --git a/lib/x509/privkey.c b/lib/x509/privkey.c index 1a779772bc..0c94ac52b4 100644 --- a/lib/x509/privkey.c +++ b/lib/x509/privkey.c @@ -44,6 +44,8 @@ **/ int gnutls_x509_privkey_init(gnutls_x509_privkey_t * key) { + FAIL_IF_FIPS_ERROR; + *key = gnutls_calloc(1, sizeof(gnutls_x509_privkey_int)); if (*key) { diff --git a/lib/x509/verify-high.c b/lib/x509/verify-high.c index 72e6656519..28f89edeeb 100644 --- a/lib/x509/verify-high.c +++ b/lib/x509/verify-high.c @@ -75,7 +75,11 @@ int gnutls_x509_trust_list_init(gnutls_x509_trust_list_t * list, unsigned int size) { - gnutls_x509_trust_list_t tmp = + gnutls_x509_trust_list_t tmp; + + FAIL_IF_FIPS_ERROR; + + tmp = gnutls_calloc(1, sizeof(struct gnutls_x509_trust_list_st)); if (!tmp) diff --git a/lib/x509/x509.c b/lib/x509/x509.c index 9bc677d74d..0bc8617581 100644 --- a/lib/x509/x509.c +++ b/lib/x509/x509.c @@ -44,7 +44,11 @@ **/ int gnutls_x509_crt_init(gnutls_x509_crt_t * cert) { - gnutls_x509_crt_t tmp = + gnutls_x509_crt_t tmp; + + FAIL_IF_FIPS_ERROR; + + tmp = gnutls_calloc(1, sizeof(gnutls_x509_crt_int)); int result; diff --git a/lib/xssl.c b/lib/xssl.c index 7b9e198d94..11780ee85f 100644 --- a/lib/xssl.c +++ b/lib/xssl.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "vasprintf.h" #include @@ -301,6 +302,8 @@ int xssl_cred_init(xssl_cred_t * c, unsigned vflags, 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) @@ -347,6 +350,8 @@ int xssl_client_init(xssl_t * isb, const char *hostname, gnutls_session_t session; int ret; unsigned len; + + FAIL_IF_FIPS_ERROR; ret = gnutls_init(&session, GNUTLS_CLIENT); if (ret < 0)