diff options
-rw-r--r-- | NEWS | 6 | ||||
-rw-r--r-- | devel/libgnutls.abignore | 18 | ||||
-rw-r--r-- | devel/symbols.last | 5 | ||||
-rw-r--r-- | doc/Makefile.am | 11 | ||||
-rw-r--r-- | doc/cha-internals.texi | 24 | ||||
-rw-r--r-- | doc/manpages/Makefile.am | 5 | ||||
-rw-r--r-- | lib/fips.c | 182 | ||||
-rw-r--r-- | lib/fips.h | 156 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 39 | ||||
-rw-r--r-- | lib/libgnutls.map | 5 |
10 files changed, 386 insertions, 65 deletions
@@ -32,6 +32,12 @@ gnutls_sign_set_secure: Added. gnutls_sign_set_secure_for_certs: Added. gnutls_digest_set_secure: Added. gnutls_protocol_set_enabled: Added. +gnutls_fips140_context_init: New function +gnutls_fips140_context_deinit: New function +gnutls_fips140_push_context: New function +gnutls_fips140_pop_context: New function +gnutls_fips140_get_operation_state: New function +gnutls_fips140_operation_state_t: New enum * Version 3.7.2 (released 2021-05-29) diff --git a/devel/libgnutls.abignore b/devel/libgnutls.abignore index 1589707eeb..afa9168b79 100644 --- a/devel/libgnutls.abignore +++ b/devel/libgnutls.abignore @@ -105,3 +105,21 @@ name = gnutls_x509_ct_sct_get [suppress_function] name = gnutls_transport_is_ktls_enabled + +[suppress_function] +name = gnutls_fips140_context_init + +[suppress_function] +name = gnutls_fips140_context_deinit + +[suppress_function] +name = gnutls_fips140_push_context + +[suppress_function] +name = gnutls_fips140_pop_context + +[suppress_function] +name = gnutls_fips140_get_operation_state + +[suppress_type] +name = gnutls_fips140_operation_state_t diff --git a/devel/symbols.last b/devel/symbols.last index 9142d5a3ed..d10b12d3ed 100644 --- a/devel/symbols.last +++ b/devel/symbols.last @@ -250,7 +250,12 @@ gnutls_ffdhe_8192_group_prime@GNUTLS_3_4 gnutls_ffdhe_8192_group_q@GNUTLS_3_6_8 gnutls_ffdhe_8192_key_bits@GNUTLS_3_4 gnutls_fingerprint@GNUTLS_3_4 +gnutls_fips140_context_deinit@GNUTLS_3_7_3 +gnutls_fips140_context_init@GNUTLS_3_7_3 +gnutls_fips140_get_operation_state@GNUTLS_3_7_3 gnutls_fips140_mode_enabled@GNUTLS_3_4 +gnutls_fips140_pop_context@GNUTLS_3_7_3 +gnutls_fips140_push_context@GNUTLS_3_7_3 gnutls_fips140_set_mode@GNUTLS_3_6_3 gnutls_free@GNUTLS_3_4 gnutls_get_system_config_file@GNUTLS_3_6_9 diff --git a/doc/Makefile.am b/doc/Makefile.am index 3c2d5e68d3..ab414add8b 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -550,6 +550,7 @@ ENUMS += enums/gnutls_digest_algorithm_t ENUMS += enums/gnutls_ecc_curve_t ENUMS += enums/gnutls_ext_flags_t ENUMS += enums/gnutls_ext_parse_type_t +ENUMS += enums/gnutls_fips140_operation_state_t ENUMS += enums/gnutls_fips_mode_t ENUMS += enums/gnutls_gost_paramset_t ENUMS += enums/gnutls_group_t @@ -1042,8 +1043,18 @@ FUNCS += functions/gnutls_ext_set_data FUNCS += functions/gnutls_ext_set_data.short FUNCS += functions/gnutls_fingerprint FUNCS += functions/gnutls_fingerprint.short +FUNCS += functions/gnutls_fips140_context_deinit +FUNCS += functions/gnutls_fips140_context_deinit.short +FUNCS += functions/gnutls_fips140_context_init +FUNCS += functions/gnutls_fips140_context_init.short +FUNCS += functions/gnutls_fips140_get_operation_state +FUNCS += functions/gnutls_fips140_get_operation_state.short FUNCS += functions/gnutls_fips140_mode_enabled FUNCS += functions/gnutls_fips140_mode_enabled.short +FUNCS += functions/gnutls_fips140_pop_context +FUNCS += functions/gnutls_fips140_pop_context.short +FUNCS += functions/gnutls_fips140_push_context +FUNCS += functions/gnutls_fips140_push_context.short FUNCS += functions/gnutls_fips140_set_mode FUNCS += functions/gnutls_fips140_set_mode.short FUNCS += functions/gnutls_get_system_config_file diff --git a/doc/cha-internals.texi b/doc/cha-internals.texi index f188caecc9..9dd1d8b6cc 100644 --- a/doc/cha-internals.texi +++ b/doc/cha-internals.texi @@ -754,3 +754,27 @@ Applications could also switch FIPS140-2 mode explicitly off, by calling @example gnutls_fips140_set_mode(GNUTLS_FIPS140_LAX, 0); @end example + +@subheading Service indicator + +The above restrictions may not cover all the requirements in every +usage context, and as the FIPS140 standard evolves (like FIPS140-3), +GnuTLS may not be able to add new restrictions without breaking +compatibility. + +Therefore an additional set of API functions is provided to +communicate with the user whether any approved mode of operations is +performed within a given context. + +@showfuncD{gnutls_fips140_context_init,gnutls_fips140_context_deinit,gnutls_fips140_push_context,gnutls_fips140_pop_context} + +The @code{gnutls_fips140_context_t} represents the FIPS140-2 mode of +operation. It can be attached to the current execution thread with +@funcref{gnutls_fips140_push_context} and its internal state will be +updated until it is detached with +@funcref{gnutls_fips140_pop_context}. Afterwards +@funcref{gnutls_fips140_get_operation_state} allows the user +to examine whether any approved (or non-approved) security function is +invoked. + +@showfuncdesc{gnutls_fips140_get_operation_state} diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am index 37ed4eb66e..c3bcac9f3b 100644 --- a/doc/manpages/Makefile.am +++ b/doc/manpages/Makefile.am @@ -323,7 +323,12 @@ APIMANS += gnutls_ext_raw_parse.3 APIMANS += gnutls_ext_register.3 APIMANS += gnutls_ext_set_data.3 APIMANS += gnutls_fingerprint.3 +APIMANS += gnutls_fips140_context_deinit.3 +APIMANS += gnutls_fips140_context_init.3 +APIMANS += gnutls_fips140_get_operation_state.3 APIMANS += gnutls_fips140_mode_enabled.3 +APIMANS += gnutls_fips140_pop_context.3 +APIMANS += gnutls_fips140_push_context.3 APIMANS += gnutls_fips140_set_mode.3 APIMANS += gnutls_get_system_config_file.3 APIMANS += gnutls_global_deinit.3 diff --git a/lib/fips.c b/lib/fips.c index 51567953df..457a8c056e 100644 --- a/lib/fips.c +++ b/lib/fips.c @@ -33,6 +33,12 @@ #include "gthreads.h" unsigned int _gnutls_lib_state = LIB_STATE_POWERON; + +struct gnutls_fips140_context_st { + gnutls_fips140_operation_state_t state; + struct gnutls_fips140_context_st *next; +}; + #ifdef ENABLE_FIPS140 #include <dlfcn.h> @@ -46,6 +52,8 @@ unsigned int _gnutls_lib_state = LIB_STATE_POWERON; static gnutls_fips_mode_t _global_fips_mode = -1; static _Thread_local gnutls_fips_mode_t _tfips_mode = -1; +static _Thread_local gnutls_fips140_context_t _tfips_context = NULL; + static int _skip_integrity_checks = 0; /* Returns: @@ -599,3 +607,177 @@ void _gnutls_lib_force_operational(void) { _gnutls_switch_lib_state(LIB_STATE_OPERATIONAL); } + +/** + * gnutls_fips140_context_init: + * @context: location to store @gnutls_fips140_context_t + * + * Create and initialize the FIPS context object. + * + * Returns: 0 upon success, a negative error code otherwise + * + * Since: 3.7.3 + */ +int +gnutls_fips140_context_init(gnutls_fips140_context_t *context) +{ + *context = gnutls_malloc(sizeof(struct gnutls_fips140_context_st)); + if (!*context) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + (*context)->state = GNUTLS_FIPS140_OP_INITIAL; + return 0; +} + +/** + * gnutls_fips140_context_deinit: + * @context: a #gnutls_fips140_context_t + * + * Uninitialize and release the FIPS context @context. + * + * Since: 3.7.3 + */ +void +gnutls_fips140_context_deinit(gnutls_fips140_context_t context) +{ + gnutls_free(context); +} + +/** + * gnutls_fips140_get_operation_state: + * @context: a #gnutls_fips140_context_t + * + * Get the previous operation state of @context in terms of FIPS. + * + * Returns: a #gnutls_fips140_operation_state_t + * + * Since: 3.7.3 + */ +gnutls_fips140_operation_state_t +gnutls_fips140_get_operation_state(gnutls_fips140_context_t context) +{ + return context->state; +} + +/** + * gnutls_fips140_push_context: + * @context: a #gnutls_fips140_context_t + * + * Associate the FIPS @context to the current thread, diverting the + * currently active context. If a cryptographic operation is ongoing + * in the current thread, e.g., gnutls_aead_cipher_init() is called + * but gnutls_aead_cipher_deinit() is not yet called, it returns an + * error %GNUTLS_E_INVALID_REQUEST. + * + * The operation state of @context will be reset to + * %GNUTLS_FIPS140_OP_INITIAL. + * + * Returns: 0 upon success, a negative error code otherwise + * + * Since: 3.7.3 + */ +int +gnutls_fips140_push_context(gnutls_fips140_context_t context) +{ +#ifdef ENABLE_FIPS140 + context->next = _tfips_context; + _tfips_context = context; + + context->state = GNUTLS_FIPS140_OP_INITIAL; + return 0; +#else + return GNUTLS_E_INVALID_REQUEST; +#endif +} + +/** + * gnutls_fips140_pop_context: + * + * Dissociate the FIPS context currently + * active on the current thread, reverting to the previously active + * context. If a cryptographic operation is ongoing in the current + * thread, e.g., gnutls_aead_cipher_init() is called but + * gnutls_aead_cipher_deinit() is not yet called, it returns an error + * %GNUTLS_E_INVALID_REQUEST. + * + * Returns: 0 upon success, a negative error code otherwise + * + * Since: 3.7.3 + */ +int +gnutls_fips140_pop_context(void) +{ +#ifdef ENABLE_FIPS140 + if (!_tfips_context) { + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } + + _tfips_context = _tfips_context->next; + return 0; +#else + return GNUTLS_E_INVALID_REQUEST; +#endif +} + +static inline const char * +operation_state_to_string(gnutls_fips140_operation_state_t state) +{ + switch (state) { + case GNUTLS_FIPS140_OP_INITIAL: + return "initial"; + case GNUTLS_FIPS140_OP_APPROVED: + return "approved"; + case GNUTLS_FIPS140_OP_NOT_APPROVED: + return "not-approved"; + case GNUTLS_FIPS140_OP_ERROR: + return "error"; + default: + /*NOTREACHED*/ + assert(0); + return NULL; + } +} + +gnutls_fips140_operation_state_t +_gnutls_transit_fips_state(gnutls_fips140_operation_state_t current, + gnutls_fips140_operation_state_t next) +{ + switch (current) { + case GNUTLS_FIPS140_OP_INITIAL: + /* initial can be transitioned to any state */ + _gnutls_debug_log("FIPS140-2 operation mode switched from initial to %s\n", + operation_state_to_string(next)); + return next; + case GNUTLS_FIPS140_OP_APPROVED: + /* approved can only be transitioned to not-approved */ + if (next == GNUTLS_FIPS140_OP_NOT_APPROVED) { + _gnutls_debug_log("FIPS140-2 operation mode switched from approved to %s\n", + operation_state_to_string(next)); + return next; + } + FALLTHROUGH; + default: + /* other transitions are prohibited */ + if (next != current) { + _gnutls_debug_log("FIPS140-2 operation mode cannot be switched from %s to %s\n", + operation_state_to_string(current), + operation_state_to_string(next)); + } + return current; + } +} + +void +_gnutls_switch_fips_state(gnutls_fips140_operation_state_t state) +{ +#ifdef ENABLE_FIPS140 + if (!_tfips_context) { + _gnutls_debug_log("FIPS140-2 context is not set\n"); + return; + } + _tfips_context->state = + _gnutls_transit_fips_state(_tfips_context->state, state); +#else + (void)state; +#endif +} diff --git a/lib/fips.h b/lib/fips.h index f76f24da75..cb5ff6a5d3 100644 --- a/lib/fips.h +++ b/lib/fips.h @@ -41,6 +41,12 @@ typedef enum { extern unsigned int _gnutls_lib_state; extern gnutls_crypto_rnd_st _gnutls_fips_rnd_ops; +gnutls_fips140_operation_state_t +_gnutls_transit_fips_state(gnutls_fips140_operation_state_t current, + gnutls_fips140_operation_state_t next); + +void _gnutls_switch_fips_state(gnutls_fips140_operation_state_t state); + inline static void _gnutls_switch_lib_state(gnutls_lib_state_t state) { @@ -89,85 +95,105 @@ void _gnutls_lib_force_operational(void); } \ }} -inline -static unsigned is_mac_algo_forbidden(gnutls_mac_algorithm_t algo) +inline static bool +is_mac_algo_forbidden_in_fips(gnutls_mac_algorithm_t algo) +{ + switch (algo) { + case GNUTLS_MAC_SHA1: + case GNUTLS_MAC_SHA256: + case GNUTLS_MAC_SHA384: + case GNUTLS_MAC_SHA512: + case GNUTLS_MAC_SHA224: + case GNUTLS_MAC_SHA3_224: + case GNUTLS_MAC_SHA3_256: + case GNUTLS_MAC_SHA3_384: + case GNUTLS_MAC_SHA3_512: + case GNUTLS_MAC_AES_CMAC_128: + case GNUTLS_MAC_AES_CMAC_256: + case GNUTLS_MAC_AES_GMAC_128: + case GNUTLS_MAC_AES_GMAC_192: + case GNUTLS_MAC_AES_GMAC_256: + return false; + default: + return true; + } +} + +inline static bool +is_mac_algo_forbidden(gnutls_mac_algorithm_t algo) { 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: - case GNUTLS_MAC_SHA384: - case GNUTLS_MAC_SHA512: - case GNUTLS_MAC_SHA224: - case GNUTLS_MAC_SHA3_224: - case GNUTLS_MAC_SHA3_256: - case GNUTLS_MAC_SHA3_384: - case GNUTLS_MAC_SHA3_512: - case GNUTLS_MAC_AES_CMAC_128: - case GNUTLS_MAC_AES_CMAC_256: - case GNUTLS_MAC_AES_GMAC_128: - case GNUTLS_MAC_AES_GMAC_192: - case GNUTLS_MAC_AES_GMAC_256: - 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; + if (_gnutls_get_lib_state() != LIB_STATE_SELFTEST && + is_mac_algo_forbidden_in_fips(algo)) { + switch (mode) { + case GNUTLS_FIPS140_LOG: + _gnutls_audit_log(NULL, + "fips140-2: allowing access to %s\n", + gnutls_mac_get_name(algo)); + FALLTHROUGH; + case GNUTLS_FIPS140_DISABLED: + case GNUTLS_FIPS140_LAX: + return false; + default: + return true; } } - return 0; + return false; +} + +inline static bool +is_cipher_algo_forbidden_in_fips(gnutls_cipher_algorithm_t algo) +{ + switch (algo) { + case GNUTLS_CIPHER_AES_128_CBC: + case GNUTLS_CIPHER_AES_256_CBC: + case GNUTLS_CIPHER_AES_192_CBC: + case GNUTLS_CIPHER_AES_128_GCM: + case GNUTLS_CIPHER_AES_192_GCM: + case GNUTLS_CIPHER_AES_256_GCM: + case GNUTLS_CIPHER_AES_128_CCM: + case GNUTLS_CIPHER_AES_256_CCM: + case GNUTLS_CIPHER_3DES_CBC: + case GNUTLS_CIPHER_AES_128_CCM_8: + case GNUTLS_CIPHER_AES_256_CCM_8: + case GNUTLS_CIPHER_AES_128_CFB8: + case GNUTLS_CIPHER_AES_192_CFB8: + case GNUTLS_CIPHER_AES_256_CFB8: + case GNUTLS_CIPHER_AES_128_XTS: + case GNUTLS_CIPHER_AES_256_XTS: + return false; + default: + return true; + } } -inline -static unsigned is_cipher_algo_forbidden(gnutls_cipher_algorithm_t algo) +inline static bool +is_cipher_algo_forbidden(gnutls_cipher_algorithm_t algo) { 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_CIPHER_AES_128_CBC: - case GNUTLS_CIPHER_AES_256_CBC: - case GNUTLS_CIPHER_AES_192_CBC: - case GNUTLS_CIPHER_AES_128_GCM: - case GNUTLS_CIPHER_AES_192_GCM: - case GNUTLS_CIPHER_AES_256_GCM: - case GNUTLS_CIPHER_AES_128_CCM: - case GNUTLS_CIPHER_AES_256_CCM: - case GNUTLS_CIPHER_3DES_CBC: - case GNUTLS_CIPHER_AES_128_CCM_8: - case GNUTLS_CIPHER_AES_256_CCM_8: - case GNUTLS_CIPHER_AES_128_CFB8: - case GNUTLS_CIPHER_AES_192_CFB8: - case GNUTLS_CIPHER_AES_256_CFB8: - case GNUTLS_CIPHER_AES_128_XTS: - case GNUTLS_CIPHER_AES_256_XTS: - 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; + if (_gnutls_get_lib_state() != LIB_STATE_SELFTEST && + is_cipher_algo_forbidden_in_fips(algo)) { + switch (mode) { + case GNUTLS_FIPS140_LOG: + _gnutls_audit_log(NULL, "fips140-2: allowing access to %s\n", + gnutls_cipher_get_name(algo)); + FALLTHROUGH; + case GNUTLS_FIPS140_DISABLED: + case GNUTLS_FIPS140_LAX: + return false; + default: + return true; } } - return 0; + return false; } #else -# define is_mac_algo_forbidden(x) 0 -# define is_cipher_algo_forbidden(x) 0 +# define is_mac_algo_forbidden_in_fips(x) false +# define is_mac_algo_forbidden(x) false +# define is_cipher_algo_forbidden_in_fips(x) false +# define is_cipher_algo_forbidden(x) false # define FIPS_RULE(condition, ret_error, ...) #endif diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 1e883aa8eb..0e96be81e8 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -3348,6 +3348,45 @@ void gnutls_fips140_set_mode(gnutls_fips_mode_t mode, unsigned flags); gnutls_fips140_set_mode(GNUTLS_FIPS140_STRICT, GNUTLS_FIPS140_SET_MODE_THREAD); \ } while(0) +typedef struct gnutls_fips140_context_st *gnutls_fips140_context_t; + +int gnutls_fips140_context_init(gnutls_fips140_context_t *context); +void gnutls_fips140_context_deinit(gnutls_fips140_context_t context); + +/** + * gnutls_fips140_operation_state_t: + * @GNUTLS_FIPS140_OP_INITIAL: no previous operation has done + * @GNUTLS_FIPS140_OP_APPROVED: the previous operation was FIPS approved + * @GNUTLS_FIPS140_OP_NOT_APPROVED: the previous operation was not FIPS approved + * @GNUTLS_FIPS140_OP_ERROR: the previous operation caused an error regardless of FIPS + * + * The FIPS operation state set by the preceding operation. + * + * There are state transition rules among the enum values: + * - When the context is attached to a thread, it will be set to reset + * to the %GNUTLS_FIPS140_OP_INITIAL state + * - From the %GNUTLS_FIPS140_OP_INITIAL state, the context can + * transition to either %GNUTLS_FIPS140_OP_APPROVED, + * %GNUTLS_FIPS140_OP_NOT_APPROVED, or %GNUTLS_FIPS140_OP_ERROR + * - From the %GNUTLS_FIPS140_OP_APPROVED state, the context can + * transition to %GNUTLS_FIPS140_OP_NOT_APPROVED + * - All other transitions are prohibited. + * + * Since: 3.7.3 + */ +typedef enum { + GNUTLS_FIPS140_OP_INITIAL, + GNUTLS_FIPS140_OP_APPROVED, + GNUTLS_FIPS140_OP_NOT_APPROVED, + GNUTLS_FIPS140_OP_ERROR +} gnutls_fips140_operation_state_t; + +gnutls_fips140_operation_state_t +gnutls_fips140_get_operation_state(gnutls_fips140_context_t context); + +int gnutls_fips140_push_context(gnutls_fips140_context_t context); +int gnutls_fips140_pop_context(void); + /* 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 e60f10104a..40a3984cbf 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1370,6 +1370,11 @@ GNUTLS_3_7_3 gnutls_digest_set_secure; gnutls_protocol_set_enabled; gnutls_transport_is_ktls_enabled; + gnutls_fips140_context_init; + gnutls_fips140_context_deinit; + gnutls_fips140_get_operation_state; + gnutls_fips140_push_context; + gnutls_fips140_pop_context; local: *; } GNUTLS_3_7_2; |