summaryrefslogtreecommitdiff
path: root/lib/fips.c
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 /lib/fips.c
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>
Diffstat (limited to 'lib/fips.c')
-rw-r--r--lib/fips.c139
1 files changed, 114 insertions, 25 deletions
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);