Index: libgcrypt-1.9.1/src/global.c =================================================================== --- libgcrypt-1.9.1.orig/src/global.c +++ libgcrypt-1.9.1/src/global.c @@ -86,7 +86,7 @@ static gpg_err_code_t external_lock_test likely to be called at startup. The suggested way for an application to make sure that this has been called is by using gcry_check_version. */ -static void +static void __attribute__((constructor)) global_init (void) { gcry_error_t err = 0; @@ -134,6 +134,16 @@ global_init (void) if (err) goto fail; + int no_secmem_save; + /* it should be always 0 at this point but let's keep on the safe side */ + no_secmem_save = no_secure_memory; + no_secure_memory = 1; + err = _gcry_fips_run_selftests (0); + no_secure_memory = no_secmem_save; + + if (err) + goto fail; + return; fail: @@ -141,16 +151,6 @@ global_init (void) } -void __attribute__ ((constructor)) _gcry_global_constructor (void) -{ - /* We always need the FSM lock to be functional. */ - _gcry_initialize_fsm_lock (); - - /* We run the integrity check at this point. The remaining - selftests are run before use of the library by application. */ - _gcry_fips_run_selftests (0); -} - /* This function is called by the macro fips_is_operational and makes sure that the minimal initialization has been done. This is far from a perfect solution and hides problems with an improper Index: libgcrypt-1.9.1/src/fips.c =================================================================== --- libgcrypt-1.9.1.orig/src/fips.c +++ libgcrypt-1.9.1/src/fips.c @@ -124,6 +124,7 @@ void _gcry_initialize_fips_mode (int force) { static int done; + gpg_error_t err; /* Make sure we are not accidentally called twice. */ if (done) @@ -213,6 +214,23 @@ _gcry_initialize_fips_mode (int force) /* Yes, we are in FIPS mode. */ FILE *fp; + /* Intitialize the lock to protect the FSM. */ + err = gpgrt_lock_init (&fsm_lock); + if (err) + { + /* If that fails we can't do anything but abort the + * process. We need to use log_info so that the FSM won't + * get involved. */ + log_info ("FATAL: failed to create the FSM lock in libgcrypt: %s\n", + gpg_strerror (err)); +#ifdef HAVE_SYSLOG + syslog (LOG_USER|LOG_ERR, "Libgcrypt error: " + "creating FSM lock failed: %s - abort", + gpg_strerror (err)); +#endif /*HAVE_SYSLOG*/ + abort (); + } + /* If the FIPS force files exists, is readable and has a number != 0 on its first line, we enable the enforced fips mode. */ fp = fopen (FIPS_FORCE_FILE, "r"); @@ -641,6 +659,39 @@ get_library_path(const char *libname, co } #endif +static gpg_error_t +get_hmac_path(char **fname) +{ + char libpath[4096]; + gpg_error_t err; + + if (get_library_path ("libgcrypt.so.20", "gcry_check_version", libpath, sizeof(libpath))) + err = gpg_error_from_syserror (); + else + { + *fname = _gcry_malloc (strlen (libpath) + 1 + 5 + 1 ); + if (!*fname) + err = gpg_error_from_syserror (); + else + { + char *p; + + /* Prefix the basename with a dot. */ + strcpy (*fname, libpath); + p = strrchr (*fname, '/'); + if (p) + p++; + else + p = *fname; + memmove (p+1, p, strlen (p)+1); + *p = '.'; + strcat (*fname, ".hmac"); + err = 0; + } + } + return err; +} + /* Run an integrity check on the binary. Returns 0 on success. */ static int check_binary_integrity (void) @@ -665,25 +716,10 @@ check_binary_integrity (void) err = gpg_error (GPG_ERR_INTERNAL); else { - fname = xtrymalloc (strlen (libpath) + 1 + 5 + 1 ); - if (!fname) - err = gpg_error_from_syserror (); - else + FILE *fp; + err = get_hmac_path(&fname); + if (!err) { - FILE *fp; - char *p; - - /* Prefix the basename with a dot. */ - strcpy (fname, libpath); - p = strrchr (fname, '/'); - if (p) - p++; - else - p = fname; - memmove (p+1, p, strlen (p)+1); - *p = '.'; - strcat (fname, ".hmac"); - /* Open the file. */ fp = fopen (fname, "r"); if (!fp) @@ -734,6 +770,33 @@ check_binary_integrity (void) #endif } +int +can_skip_selftests(void) +{ + char *fname = NULL; + int ret = 0; + + if (fips_mode()) + return 0; + + if (get_hmac_path(&fname)) + return 0; + + /* check the hmac presence */ + if (access(fname, F_OK)) + /* no hmac file is present, don't run the tests */ + if (errno == ENOENT) + ret = 1; + /* otherwise one of these events happened: + * access() returned 0 + * -> run the tests + * some error other than ENOENT occurred + * -> run the tests anyway and let them fail + */ + + xfree(fname); + return ret; +} /* Run the self-tests. If EXTENDED is true, extended versions of the selftest are run, that is more tests than required by FIPS. */ @@ -742,26 +805,13 @@ _gcry_fips_run_selftests (int extended) { enum module_states result = STATE_ERROR; gcry_err_code_t ec = GPG_ERR_SELFTEST_FAILED; - int in_poweron; - - lock_fsm (); - in_poweron = (current_state == STATE_POWERON); - unlock_fsm (); - - fips_new_state (STATE_SELFTEST); - /* We first check the integrity of the binary. - If run from the constructor we are in POWERON state, - we return and finish the remaining selftests before - real use of the library. It will be in the POWERON - state meanwhile. */ - if (in_poweron) - if (check_binary_integrity ()) - goto leave; - - if (in_poweron) + if (can_skip_selftests()) return 0; + if (fips_mode ()) + fips_new_state (STATE_SELFTEST); + if (run_cipher_selftests (extended)) goto leave; @@ -774,6 +824,9 @@ _gcry_fips_run_selftests (int extended) if (run_kdf_selftests (extended)) goto leave; + if (check_binary_integrity ()) + goto leave; + /* Run random tests before the pubkey tests because the latter require random. */ if (run_random_selftests ()) @@ -787,7 +840,8 @@ _gcry_fips_run_selftests (int extended) ec = 0; leave: - fips_new_state (result); + if (fips_mode ()) + fips_new_state (result); return ec; } @@ -843,7 +897,6 @@ fips_new_state (enum module_states new_s { case STATE_POWERON: if (new_state == STATE_INIT - || new_state == STATE_SELFTEST || new_state == STATE_ERROR || new_state == STATE_FATALERROR) ok = 1; @@ -858,8 +911,6 @@ fips_new_state (enum module_states new_s case STATE_SELFTEST: if (new_state == STATE_OPERATIONAL - || new_state == STATE_INIT - || new_state == STATE_SELFTEST || new_state == STATE_ERROR || new_state == STATE_FATALERROR) ok = 1;