diff options
author | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2013-11-22 16:25:47 +0100 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2013-11-27 11:41:44 +0100 |
commit | 7ea57bef06f96546a2c61b0463c401813d39c0e8 (patch) | |
tree | fc1354d4146fd8d9db82ad21414cf06b13a7f73c /lib/gnutls_global.c | |
parent | cc29718387be9c7dcbcdc06dd63435ceaf7298a9 (diff) | |
download | gnutls-7ea57bef06f96546a2c61b0463c401813d39c0e8.tar.gz |
Added gnutls_global_init2(). This allows initializing gnutls in a constructor in FIPS140 mode
Diffstat (limited to 'lib/gnutls_global.c')
-rw-r--r-- | lib/gnutls_global.c | 253 |
1 files changed, 171 insertions, 82 deletions
diff --git a/lib/gnutls_global.c b/lib/gnutls_global.c index 8c8bef8db8..57bd116f53 100644 --- a/lib/gnutls_global.c +++ b/lib/gnutls_global.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2012 Free Software Foundation, Inc. + * Copyright (C) 2001-2013 Free Software Foundation, Inc. * * Author: Nikos Mavrogiannopoulos * @@ -111,7 +111,7 @@ void gnutls_global_set_time_function(gnutls_time_func time_func) /** * gnutls_global_set_log_level: - * @level: it's an integer from 0 to 9. + * @level: it's an integer from 0 to 99. * * This is the function that allows you to set the log level. The * level is an integer between 0 and 9. Higher values mean more @@ -167,9 +167,14 @@ gnutls_global_set_mem_functions(gnutls_alloc_function alloc_func, } static int _gnutls_init = 0; +static unsigned int loaded_modules = 0; + +#define GLOBAL_INIT_ALL (GNUTLS_GLOBAL_INIT_MINIMAL|GNUTLS_GLOBAL_INIT_PKCS11|GNUTLS_GLOBAL_INIT_CRYPTO) /** - * gnutls_global_init: + * gnutls_global_init2: + * + * @flags: it's a %GNUTLS_GLOBAL_* flag * * This function performs any required precalculations, detects * the supported CPU capabilities and initializes the underlying @@ -194,113 +199,188 @@ static int _gnutls_init = 0; * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, * otherwise a negative error code is returned. **/ -int gnutls_global_init(void) +int gnutls_global_init2(unsigned int flags) { int result = 0; int res, level; const char* e; - if (_gnutls_init++) - goto out; + _gnutls_init++; - _gnutls_switch_fips_state(FIPS_STATE_INIT); - - e = getenv("GNUTLS_DEBUG_LEVEL"); - if (e != NULL) { - level = atoi(e); - gnutls_global_set_log_level(level); - if (_gnutls_log_func == NULL) - gnutls_global_set_log_function(default_log_func); - _gnutls_debug_log("Enabled GnuTLS logging...\n"); + /* rationalize flags */ + if (flags == GNUTLS_GLOBAL_INIT_ALL) + flags = GLOBAL_INIT_ALL; + + flags &= ~loaded_modules; + + if (flags == 0) { /* The requested were already loaded */ + return 0; + } + + if (!(flags & GNUTLS_GLOBAL_INIT_MINIMAL) && + !(loaded_modules & GNUTLS_GLOBAL_INIT_MINIMAL)) { + /* Must always initialize the minimal before everything else */ + _gnutls_init--; + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); } - if (gl_sockets_startup(SOCKETS_1_1)) - return gnutls_assert_val(GNUTLS_E_FILE_ERROR); + loaded_modules |= flags; - bindtextdomain(PACKAGE, LOCALEDIR); + if (flags & GNUTLS_GLOBAL_INIT_MINIMAL) { + _gnutls_switch_fips_state(FIPS_STATE_INIT); - res = gnutls_crypto_init(); - if (res != 0) { - gnutls_assert(); - return GNUTLS_E_CRYPTO_INIT_FAILED; - } + e = getenv("GNUTLS_DEBUG_LEVEL"); + if (e != NULL) { + level = atoi(e); + gnutls_global_set_log_level(level); + if (_gnutls_log_func == NULL) + gnutls_global_set_log_function(default_log_func); + _gnutls_debug_log("Enabled GnuTLS logging...\n"); + } - _gnutls_register_accel_crypto(); + if (gl_sockets_startup(SOCKETS_1_1)) + return gnutls_assert_val(GNUTLS_E_SOCKETS_INIT_ERROR); - /* initialize ASN.1 parser - * This should not deal with files in the final - * version. - */ - if (asn1_check_version(GNUTLS_MIN_LIBTASN1_VERSION) == NULL) { - gnutls_assert(); - _gnutls_debug_log - ("Checking for libtasn1 failed: %s < %s\n", - asn1_check_version(NULL), - GNUTLS_MIN_LIBTASN1_VERSION); - return GNUTLS_E_INCOMPATIBLE_LIBTASN1_LIBRARY; - } + bindtextdomain(PACKAGE, LOCALEDIR); - res = asn1_array2tree(pkix_asn1_tab, &_gnutls_pkix1_asn, NULL); - if (res != ASN1_SUCCESS) { - result = _gnutls_asn2err(res); - goto out; - } + res = gnutls_crypto_init(); + if (res != 0) { + gnutls_assert(); + return GNUTLS_E_CRYPTO_INIT_FAILED; + } - res = asn1_array2tree(gnutls_asn1_tab, &_gnutls_gnutls_asn, NULL); - if (res != ASN1_SUCCESS) { - result = _gnutls_asn2err(res); - goto out; - } + /* initialize ASN.1 parser + * This should not deal with files in the final + * version. + */ + if (asn1_check_version(GNUTLS_MIN_LIBTASN1_VERSION) == NULL) { + gnutls_assert(); + _gnutls_debug_log + ("Checking for libtasn1 failed: %s < %s\n", + asn1_check_version(NULL), + GNUTLS_MIN_LIBTASN1_VERSION); + return GNUTLS_E_INCOMPATIBLE_LIBTASN1_LIBRARY; + } - /* Initialize the random generator */ - result = _gnutls_rnd_init(); - if (result < 0) { - gnutls_assert(); - goto out; - } + res = asn1_array2tree(pkix_asn1_tab, &_gnutls_pkix1_asn, NULL); + if (res != ASN1_SUCCESS) { + result = _gnutls_asn2err(res); + goto out; + } - /* Initialize the default TLS extensions */ - result = _gnutls_ext_init(); - if (result < 0) { - gnutls_assert(); - goto out; - } + res = asn1_array2tree(gnutls_asn1_tab, &_gnutls_gnutls_asn, NULL); + if (res != ASN1_SUCCESS) { + result = _gnutls_asn2err(res); + goto out; + } + + /* Initialize the random generator */ + result = _gnutls_rnd_init(); + if (result < 0) { + gnutls_assert(); + goto out; + } + + /* Initialize the default TLS extensions */ + result = _gnutls_ext_init(); + if (result < 0) { + gnutls_assert(); + goto out; + } - result = gnutls_mutex_init(&_gnutls_file_mutex); - if (result < 0) { - gnutls_assert(); - goto out; + result = gnutls_mutex_init(&_gnutls_file_mutex); + if (result < 0) { + gnutls_assert(); + goto out; + } + + result = gnutls_system_global_init(); + if (result < 0) { + gnutls_assert(); + goto out; + } + } - result = gnutls_system_global_init(); - if (result < 0) { - gnutls_assert(); - goto out; + if (flags & GNUTLS_GLOBAL_INIT_CRYPTO) { + _gnutls_register_accel_crypto(); + + _gnutls_cryptodev_init(); } + #ifdef ENABLE_PKCS11 - gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_AUTO, NULL); + if (flags & GNUTLS_GLOBAL_INIT_PKCS11) { + gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_AUTO, NULL); + } #endif - _gnutls_cryptodev_init(); - - result = _gnutls_fips_mode_enabled(); - if (result != 0) { - result = _gnutls_fips_perform_self_checks(); - if (_gnutls_get_fips_state() != FIPS_STATE_ZOMBIE) { - if (result < 0) { - gnutls_assert(); - goto out; +#ifdef ENABLE_FIPS140 + /* Perform FIPS140 checks last, so that all modules + * have been loaded */ + if (flags & GNUTLS_GLOBAL_INIT_MINIMAL) { + result = _gnutls_fips_mode_enabled(); + if (result != 0) { + result = _gnutls_fips_perform_self_checks(); + if (_gnutls_get_fips_state() != FIPS_STATE_ZOMBIE) { + if (result < 0) { + gnutls_assert(); + goto out; + } + } else { + result = 0; } - } else { - result = 0; + _gnutls_switch_fips_state(FIPS_STATE_OPERATIONAL); } - _gnutls_switch_fips_state(FIPS_STATE_OPERATIONAL); } +#endif out: return result; } +#ifdef ENABLE_FIPS140 +__attribute__((constructor)) +static void lib_init(void) +{ + if (gnutls_global_init2(GNUTLS_GLOBAL_INIT_MINIMAL|GNUTLS_GLOBAL_INIT_CRYPTO) < 0) { + fprintf(stderr, "Error in GnuTLS initialization"); + abort(); + } +} +#endif + + +/** + * gnutls_global_init: + * + * This function performs any required precalculations, detects + * the supported CPU capabilities and initializes the underlying + * cryptographic backend. In order to free any resources + * taken by this call you should gnutls_global_deinit() + * when gnutls usage is no longer needed. + * + * This function increments a global counter, so that + * gnutls_global_deinit() only releases resources when it has been + * called as many times as gnutls_global_init(). This is useful when + * GnuTLS is used by more than one library in an application. This + * function can be called many times, but will only do something the + * first time. + * + * Note! This function is not thread safe. If two threads call this + * function simultaneously, they can cause a race between checking + * the global counter and incrementing it, causing both threads to + * execute the library initialization code. That could lead to a + * memory leak or even a crash. To handle this, your application should + * invoke this function after aquiring a thread mutex. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + **/ +int gnutls_global_init(void) +{ + return gnutls_global_init2(GNUTLS_GLOBAL_INIT_ALL); +} + /** * gnutls_global_deinit: * @@ -320,14 +400,23 @@ void gnutls_global_deinit(void) asn1_delete_structure(&_gnutls_gnutls_asn); asn1_delete_structure(&_gnutls_pkix1_asn); _gnutls_crypto_deregister(); - _gnutls_cryptodev_deinit(); gnutls_system_global_deinit(); + + if (loaded_modules & GNUTLS_GLOBAL_INIT_CRYPTO) { + _gnutls_cryptodev_deinit(); + } #ifdef ENABLE_PKCS11 - gnutls_pkcs11_deinit(); + if (loaded_modules & GNUTLS_GLOBAL_INIT_PKCS11) { + gnutls_pkcs11_deinit(); + } #endif + gnutls_mutex_deinit(&_gnutls_file_mutex); + loaded_modules = 0; } - _gnutls_init--; + + if (_gnutls_init > 0) + _gnutls_init--; } /* These functions should be elsewere. Kept here for |