summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2018-01-21 15:07:00 +0100
committerNikos Mavrogiannopoulos <nmav@redhat.com>2018-02-19 08:39:36 +0100
commitdc3aa56779581b661311f1c4603f3383f252b3b3 (patch)
tree6f7c19ca00afb58b1f988a5b8b7e6333c4d29575
parente89e981a2c6fab9ea5419207710b0f716df1c148 (diff)
downloadgnutls-dc3aa56779581b661311f1c4603f3383f252b3b3.tar.gz
fips140: added function for applications to switch the FIPS140-2 mode
That would allow FIPS140-2 compliant applications to use forbidden algorithms by switching to a lax FIPS140-2 mode. Resolves #352 Resolves #353 Signed-off-by: Nikos Mavrogiannopoulos <nmav@gnutls.org>
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/fips.c139
-rw-r--r--lib/fips.h43
-rw-r--r--lib/gthreads.h31
-rw-r--r--lib/includes/gnutls/gnutls.h.in28
-rw-r--r--lib/libgnutls.map8
-rw-r--r--lib/nettle/cipher.c3
-rw-r--r--lib/nettle/int/dsa-keygen-fips186.c8
-rw-r--r--lib/nettle/int/rsa-keygen-fips186.c26
-rw-r--r--lib/random.c8
10 files changed, 225 insertions, 71 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 3ea1c6cebb..1b4f4fd6aa 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -65,7 +65,7 @@ SRP_COBJECTS = srp.c
PSK_COBJECTS = psk.c
-COBJECTS = range.c record.c compress.c debug.c cipher.c \
+COBJECTS = range.c record.c compress.c debug.c cipher.c gthreads.h \
mbuffers.c buffers.c handshake.c num.c errors.c dh.c kx.c \
priority.c hash_int.c cipher_int.c session.c db.c x509_b64.c \
extensions.c auth.c sslv2_compat.c datum.c session_pack.c mpi.c \
diff --git a/lib/fips.c b/lib/fips.c
index 9d0d9977ca..2715af599f 100644
--- a/lib/fips.c
+++ b/lib/fips.c
@@ -30,7 +30,9 @@
#include <extras/hex.h>
#include <random.h>
-unsigned int _gnutls_lib_mode = LIB_STATE_POWERON;
+#include "gthreads.h"
+
+unsigned int _gnutls_lib_state = LIB_STATE_POWERON;
#ifdef ENABLE_FIPS140
#include <dlfcn.h>
@@ -38,22 +40,37 @@ unsigned int _gnutls_lib_mode = LIB_STATE_POWERON;
#define FIPS_KERNEL_FILE "/proc/sys/crypto/fips_enabled"
#define FIPS_SYSTEM_FILE "/etc/system-fips"
-static int _fips_mode = -1;
+/* We provide a per-thread FIPS-mode so that an application
+ * can use gnutls_fips140_set_mode() to override a specific
+ * operation on a thread */
+static gnutls_fips_mode_t _global_fips_mode = -1;
+static _Thread_local gnutls_fips_mode_t _tfips_mode = -1;
+
static int _skip_integrity_checks = 0;
/* Returns:
- * 0 - FIPS mode disabled
- * 1 - FIPS mode enabled and enforced
- * 2 - FIPS in testing mode
+ * a gnutls_fips_mode_t value
*/
unsigned _gnutls_fips_mode_enabled(void)
{
-unsigned f1p = 0, f2p;
-FILE* fd;
-const char *p;
+ unsigned f1p = 0, f2p;
+ FILE* fd;
+ const char *p;
+ unsigned ret;
+
+ /* We initialize this threads' mode, and
+ * the global mode if not already initialized.
+ * When the global mode is initialized, then
+ * the thread mode is copied from it. As this
+ * is called on library initialization, the
+ * _global_fips_mode is always set during app run.
+ */
+ if (_tfips_mode != (gnutls_fips_mode_t)-1)
+ return _tfips_mode;
- if (_fips_mode != -1)
- return _fips_mode;
+ if (_global_fips_mode != (gnutls_fips_mode_t)-1) {
+ return _global_fips_mode;
+ }
p = secure_getenv("GNUTLS_SKIP_FIPS_INTEGRITY_CHECKS");
if (p && p[0] == '1') {
@@ -63,12 +80,17 @@ const char *p;
p = secure_getenv("GNUTLS_FORCE_FIPS_MODE");
if (p) {
if (p[0] == '1')
- _fips_mode = 1;
+ ret = 1;
else if (p[0] == '2')
- _fips_mode = 2;
+ ret = GNUTLS_FIPS140_SELFTESTS;
+ else if (p[0] == '3')
+ ret = GNUTLS_FIPS140_LAX;
+ else if (p[0] == '4')
+ ret = GNUTLS_FIPS140_LOG;
else
- _fips_mode = 0;
- return _fips_mode;
+ ret = GNUTLS_FIPS140_DISABLED;
+
+ goto exit;
}
fd = fopen(FIPS_KERNEL_FILE, "r");
@@ -84,28 +106,32 @@ const char *p;
if (f1p != 0 && f2p != 0) {
_gnutls_debug_log("FIPS140-2 mode enabled\n");
- _fips_mode = 1;
- return _fips_mode;
+ ret = GNUTLS_FIPS140_STRICT;
+ goto exit;
}
if (f2p != 0) {
/* a funny state where self tests are performed
* and ignored */
_gnutls_debug_log("FIPS140-2 ZOMBIE mode enabled\n");
- _fips_mode = 2;
- return _fips_mode;
+ ret = GNUTLS_FIPS140_SELFTESTS;
+ goto exit;
}
- _fips_mode = 0;
- return _fips_mode;
+ ret = GNUTLS_FIPS140_DISABLED;
+ goto exit;
+
+ exit:
+ _global_fips_mode = ret;
+ return ret;
}
/* This _fips_mode == 2 is a strange mode where checks are being
* performed, but its output is ignored. */
void _gnutls_fips_mode_reset_zombie(void)
{
- if (_fips_mode == 2) {
- _fips_mode = 0;
+ if (_global_fips_mode == GNUTLS_FIPS140_SELFTESTS) {
+ _global_fips_mode = GNUTLS_FIPS140_DISABLED;
}
}
@@ -397,7 +423,13 @@ error:
/**
* gnutls_fips140_mode_enabled:
*
- * Checks whether this library is in FIPS140 mode.
+ * Checks whether this library is in FIPS140 mode. The returned
+ * value corresponds to the library mode as set with
+ * gnutls_fips140_set_mode().
+ *
+ * If gnutls_fips140_set_mode() was called with %GNUTLS_FIPS140_SET_MODE_THREAD
+ * then this function will return the current thread's FIPS140 mode, otherwise
+ * the global value is returned.
*
* Returns: return non-zero if true or zero if false.
*
@@ -406,14 +438,71 @@ error:
unsigned gnutls_fips140_mode_enabled(void)
{
#ifdef ENABLE_FIPS140
-int ret = _gnutls_fips_mode_enabled();
+ unsigned ret = _gnutls_fips_mode_enabled();
- if (ret == 1)
+ if (ret > GNUTLS_FIPS140_DISABLED)
return ret;
#endif
return 0;
}
+/**
+ * gnutls_fips140_set_mode:
+ * @mode: the FIPS140-2 mode to switch to
+ * @flags: should be zero or %GNUTLS_FIPS140_SET_MODE_THREAD
+ *
+ * That function is not thread-safe when changing the mode with no flags
+ * (globally), and should be called prior to creating any threads. Its
+ * behavior with no flags after threads are created is undefined.
+ *
+ * When the flag %GNUTLS_FIPS140_SET_MODE_THREAD is specified
+ * then this call will change the FIPS140-2 mode for this particular
+ * thread and not for the whole process. That way an application
+ * can utilize this function to set and reset mode for specific
+ * operations.
+ *
+ * This function never fails but will be a no-op if used when
+ * the library is not in FIPS140-2 mode. When asked to switch to unknown
+ * values for @mode or to %GNUTLS_FIPS140_SELFTESTS mode, the library
+ * switches to %GNUTLS_FIPS140_STRICT mode.
+ *
+ * Since: 3.6.2
+ **/
+void gnutls_fips140_set_mode(gnutls_fips_mode_t mode, unsigned flags)
+{
+#ifdef ENABLE_FIPS140
+ gnutls_fips_mode_t prev = _gnutls_fips_mode_enabled();
+ if (prev == GNUTLS_FIPS140_DISABLED || prev == GNUTLS_FIPS140_SELFTESTS) {
+ /* we need to run self-tests first to be in FIPS140-2 mode */
+ _gnutls_audit_log(NULL, "The library should be initialized in FIPS140-2 mode to do that operation\n");
+ return;
+ }
+
+ switch (mode) {
+ case GNUTLS_FIPS140_STRICT:
+ case GNUTLS_FIPS140_LAX:
+ case GNUTLS_FIPS140_LOG:
+ case GNUTLS_FIPS140_DISABLED:
+ break;
+ case GNUTLS_FIPS140_SELFTESTS:
+ _gnutls_audit_log(NULL, "Cannot switch library to FIPS140-2 self-tests mode; defaulting to strict\n");
+ mode = GNUTLS_FIPS140_STRICT;
+ break;
+ default:
+ _gnutls_audit_log(NULL, "Cannot switch library to mode %u; defaulting to strict\n", (unsigned)mode);
+ mode = GNUTLS_FIPS140_STRICT;
+ break;
+ }
+
+ if (flags & GNUTLS_FIPS140_SET_MODE_THREAD)
+ _tfips_mode = mode;
+ else {
+ _global_fips_mode = mode;
+ _tfips_mode = -1;
+ }
+#endif
+}
+
void _gnutls_lib_simulate_error(void)
{
_gnutls_switch_lib_state(LIB_STATE_ERROR);
diff --git a/lib/fips.h b/lib/fips.h
index ea9b5b3399..7d3f3cfd39 100644
--- a/lib/fips.h
+++ b/lib/fips.h
@@ -38,19 +38,19 @@ typedef enum {
} gnutls_lib_state_t;
/* do not access directly */
-extern unsigned int _gnutls_lib_mode;
+extern unsigned int _gnutls_lib_state;
extern gnutls_crypto_rnd_st _gnutls_fips_rnd_ops;
inline static
void _gnutls_switch_lib_state(gnutls_lib_state_t state)
{
/* Once into zombie state no errors can change us */
- _gnutls_lib_mode = state;
+ _gnutls_lib_state = state;
}
inline static gnutls_lib_state_t _gnutls_get_lib_state(void)
{
- return _gnutls_lib_mode;
+ return _gnutls_lib_state;
}
int _gnutls_fips_perform_self_checks1(void);
@@ -74,12 +74,27 @@ void _gnutls_lib_simulate_error(void);
void _gnutls_lib_force_operational(void);
#ifdef ENABLE_FIPS140
+/* This will test the condition when in FIPS140-2 mode
+ * and return an error if necessary or ignore */
+# define FIPS_RULE(condition, ret_error, ...) { \
+ gnutls_fips_mode_t _mode = _gnutls_fips_mode_enabled(); \
+ if (_mode != GNUTLS_FIPS140_DISABLED) { \
+ if (condition) { \
+ if (_mode == GNUTLS_FIPS140_LOG) { \
+ _gnutls_audit_log(NULL, "fips140-2: allowing "__VA_ARGS__); \
+ } else if (_mode != GNUTLS_FIPS140_LAX) { \
+ _gnutls_debug_log("fips140-2: disallowing "__VA_ARGS__); \
+ return ret_error; \
+ } \
+ } \
+ }}
+
inline
static unsigned is_mac_algo_forbidden(gnutls_mac_algorithm_t algo)
{
- if (_gnutls_fips_mode_enabled() != 0 &&
+ gnutls_fips_mode_t mode = _gnutls_fips_mode_enabled();
+ if (mode != GNUTLS_FIPS140_DISABLED &&
_gnutls_get_lib_state() != LIB_STATE_SELFTEST) {
-
switch(algo) {
case GNUTLS_MAC_SHA1:
case GNUTLS_MAC_SHA256:
@@ -92,6 +107,13 @@ static unsigned is_mac_algo_forbidden(gnutls_mac_algorithm_t algo)
case GNUTLS_MAC_SHA3_512:
return 0;
default:
+ if (mode == GNUTLS_FIPS140_LAX)
+ return 0;
+ else if (mode == GNUTLS_FIPS140_LOG) {
+ _gnutls_audit_log(NULL, "fips140-2: allowing access to %s\n",
+ gnutls_mac_get_name(algo));
+ return 0;
+ }
return 1;
}
}
@@ -102,7 +124,8 @@ static unsigned is_mac_algo_forbidden(gnutls_mac_algorithm_t algo)
inline
static unsigned is_cipher_algo_forbidden(gnutls_cipher_algorithm_t algo)
{
- if (_gnutls_fips_mode_enabled() != 0 &&
+ gnutls_fips_mode_t mode = _gnutls_fips_mode_enabled();
+ if (mode != GNUTLS_FIPS140_DISABLED &&
_gnutls_get_lib_state() != LIB_STATE_SELFTEST) {
switch(algo) {
@@ -118,6 +141,13 @@ static unsigned is_cipher_algo_forbidden(gnutls_cipher_algorithm_t algo)
case GNUTLS_CIPHER_AES_256_CCM_8:
return 0;
default:
+ if (mode == GNUTLS_FIPS140_LAX)
+ return 0;
+ else if (mode == GNUTLS_FIPS140_LOG) {
+ _gnutls_audit_log(NULL, "fips140-2: allowing access to %s\n",
+ gnutls_cipher_get_name(algo));
+ return 0;
+ }
return 1;
}
}
@@ -127,6 +157,7 @@ static unsigned is_cipher_algo_forbidden(gnutls_cipher_algorithm_t algo)
#else
# define is_mac_algo_forbidden(x) 0
# define is_cipher_algo_forbidden(x) 0
+# define FIPS_RULE(condition, ret_error, ...)
#endif
#endif /* FIPS_H */
diff --git a/lib/gthreads.h b/lib/gthreads.h
new file mode 100644
index 0000000000..5887d46ba5
--- /dev/null
+++ b/lib/gthreads.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 Red Hat, 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <config.h>
+
+#ifdef HAVE_THREADS_H
+# include <threads.h>
+#elif defined(__GNUC__)
+# define _Thread_local __thread
+#else
+# error Unsupported platform
+#endif
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index 93cddb98c0..a199e420bc 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -2701,6 +2701,34 @@ void gnutls_supplemental_send(gnutls_session_t session, unsigned do_send_supplem
/* FIPS140-2 related functions */
unsigned gnutls_fips140_mode_enabled(void);
+/**
+ * gnutls_fips_mode_t:
+ * @GNUTLS_FIPS140_DISABLED: The FIPS140-2 mode is disabled.
+ * @GNUTLS_FIPS140_STRICT: The default mode; all forbidden operations will cause an
+ * operation failure via error code.
+ * @GNUTLS_FIPS140_LAX: The library still uses the FIPS140-2 relevant algorithms but all
+ * forbidden by FIPS140-2 operations are allowed; this is useful when the
+ * application is aware of the followed security policy, and needs
+ * to utilize disallowed operations for other reasons (e.g., compatibility).
+ * @GNUTLS_FIPS140_LOG: Similarly to %GNUTLS_FIPS140_LAX, it allows forbidden operations; any use of them results
+ * to a message to the audit callback functions.
+ * @GNUTLS_FIPS140_SELFTESTS: A transient state during library initialization. That state
+ * cannot be set or seen by applications.
+ *
+ * Enumeration of different operational modes under FIPS140-2.
+ */
+typedef enum gnutls_fips_mode_t {
+ GNUTLS_FIPS140_DISABLED = 0,
+ GNUTLS_FIPS140_STRICT = 1,
+ GNUTLS_FIPS140_SELFTESTS = 2,
+ GNUTLS_FIPS140_LAX = 3,
+ GNUTLS_FIPS140_LOG = 4
+} gnutls_fips_mode_t;
+
+#define GNUTLS_FIPS140_SET_MODE_THREAD 1
+
+void gnutls_fips140_set_mode(gnutls_fips_mode_t mode, unsigned flags);
+
/* Gnutls error codes. The mapping to a TLS alert is also shown in
* comments.
*/
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index 72764ebab1..fd99091767 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1196,10 +1196,14 @@ GNUTLS_3_6_2
global:
gnutls_srp_8192_group_generator;
gnutls_srp_8192_group_prime;
- local:
- *;
} GNUTLS_3_6_0;
+GNUTLS_3_6_3
+{
+ global:
+ gnutls_fips140_set_mode;
+} GNUTLS_3_6_2;
+
GNUTLS_FIPS140_3_4 {
gnutls_cipher_self_test;
gnutls_pk_self_test;
diff --git a/lib/nettle/cipher.c b/lib/nettle/cipher.c
index 68215c50cf..4bcbe42269 100644
--- a/lib/nettle/cipher.c
+++ b/lib/nettle/cipher.c
@@ -562,8 +562,7 @@ wrap_nettle_cipher_setiv(void *_ctx, const void *iv, size_t iv_size)
switch (ctx->cipher->algo) {
case GNUTLS_CIPHER_AES_128_GCM:
case GNUTLS_CIPHER_AES_256_GCM:
- if (_gnutls_fips_mode_enabled() != 0 && iv_size < GCM_IV_SIZE)
- return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ FIPS_RULE(iv_size < GCM_IV_SIZE, GNUTLS_E_INVALID_REQUEST, "access to short GCM nonce size\n");
break;
case GNUTLS_CIPHER_SALSA20_256:
case GNUTLS_CIPHER_ESTREAM_SALSA20_256:
diff --git a/lib/nettle/int/dsa-keygen-fips186.c b/lib/nettle/int/dsa-keygen-fips186.c
index 7328f7761e..30ba004e0f 100644
--- a/lib/nettle/int/dsa-keygen-fips186.c
+++ b/lib/nettle/int/dsa-keygen-fips186.c
@@ -39,8 +39,7 @@ unsigned _dsa_check_qp_sizes(unsigned q_bits, unsigned p_bits, unsigned generate
{
switch (q_bits) {
case 160:
- if (_gnutls_fips_mode_enabled() != 0 && generate != 0)
- return 0;
+ FIPS_RULE(generate != 0, 0, "DSA 160-bit generation\n");
if (p_bits != 1024)
return 0;
@@ -422,10 +421,7 @@ _dsa_generate_dss_pqg(struct dsa_params *params,
if (_gnutls_fips_mode_enabled() != 0) {
cert->seed_length = 2 * (q_bits / 8) + 1;
- if (cert->seed_length != seed_size) {
- _gnutls_debug_log("Seed length must be %d bytes (it is %d)\n", cert->seed_length, seed_size);
- return 0;
- }
+ FIPS_RULE(cert->seed_length != seed_size, 0, "unsupported DSA seed length (is %d, shoudl be %d)\n", seed_size, cert->seed_length);
} else {
cert->seed_length = seed_size;
}
diff --git a/lib/nettle/int/rsa-keygen-fips186.c b/lib/nettle/int/rsa-keygen-fips186.c
index 506f758dd1..a76e5eaa00 100644
--- a/lib/nettle/int/rsa-keygen-fips186.c
+++ b/lib/nettle/int/rsa-keygen-fips186.c
@@ -267,22 +267,9 @@ _rsa_generate_fips186_4_keypair(struct rsa_public_key *pub,
struct dss_params_validation_seeds cert;
unsigned l = n_size / 2;
- if (_gnutls_fips_mode_enabled() != 0) {
- if (n_size == 2048) {
- if (seed_length != 14 * 2) {
- _gnutls_debug_log("Seed length must be 28 bytes (it is %d)\n", seed_length);
- return 0;
- }
- } else if (n_size == 3072) {
- if (seed_length != 16 * 2) {
- _gnutls_debug_log("Seed length must be 32 bytes (it is %d)\n", seed_length);
- return 0;
- }
- } else {
- _gnutls_debug_log("Unsupported size for modulus\n");
- return 0;
- }
- }
+ FIPS_RULE(n_size == 2048 && seed_length != 14 * 2, 0, "seed length other than 28 bytes\n");
+ FIPS_RULE(n_size == 3072 && seed_length != 16 * 2, 0, "seed length other than 32 bytes\n");
+ FIPS_RULE(n_size != 2048 && n_size != 3072, 0, "unsupported size for modulus\n");
if (!mpz_tstbit(pub->e, 0)) {
_gnutls_debug_log("Unacceptable e (it is even)\n");
@@ -420,12 +407,7 @@ rsa_generate_fips186_4_keypair(struct rsa_public_key *pub,
unsigned seed_length;
int ret;
- if (_gnutls_fips_mode_enabled() != 0) {
- if (n_size != 2048 && n_size != 3072) {
- _gnutls_debug_log("The size of a prime can only be 2048 or 3072\n");
- return 0;
- }
- }
+ FIPS_RULE(n_size != 2048 && n_size != 3072, 0, "size of prime of other than 2048 or 3072\n");
seed_length = SEED_LENGTH(n_size);
if (seed_length > sizeof(seed))
diff --git a/lib/random.c b/lib/random.c
index 0c05dbe187..469fc4b9a6 100644
--- a/lib/random.c
+++ b/lib/random.c
@@ -29,13 +29,7 @@
#include "locks.h"
#include <fips.h>
-#ifdef HAVE_THREADS_H
-# include <threads.h>
-#elif defined(__GNUC__)
-# define _Thread_local __thread
-#else
-# error Unsupported platform
-#endif
+#include "gthreads.h"
#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
extern gnutls_crypto_rnd_st _gnutls_fuzz_rnd_ops;