File libgcrypt-global_init-constructor.patch of Package libgcrypt.14858

Index: libgcrypt-1.8.2/src/global.c
===================================================================
--- libgcrypt-1.8.2.orig/src/global.c	2020-04-16 21:13:28.252717330 +0200
+++ libgcrypt-1.8.2/src/global.c	2020-04-16 21:13:47.960822991 +0200
@@ -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.8.2/src/fips.c
===================================================================
--- libgcrypt-1.8.2.orig/src/fips.c	2020-04-16 21:13:28.252717330 +0200
+++ libgcrypt-1.8.2/src/fips.c	2020-04-16 21:14:44.781127616 +0200
@@ -125,6 +125,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)
@@ -214,6 +215,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");
@@ -614,10 +632,10 @@ get_library_path(const char *libname, co
     void *dl, *sym;
     int rv = -1;
 
-        dl = dlopen(libname, RTLD_LAZY);
-        if (dl == NULL) {
-            return -1;
-        }       
+    dl = dlopen(libname, RTLD_LAZY);
+    if (dl == NULL) {
+        return -1;
+    }
 
     sym = dlsym(dl, symbolname);
 
@@ -632,6 +650,39 @@ get_library_path(const char *libname, co
     return rv;
 }
 
+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)
@@ -656,25 +707,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;
-              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");
-
+          FILE *fp;
+	  err = get_hmac_path(&fname);
+	  if (!err)
+	    {
               /* Open the file.  */
               fp = fopen (fname, "r");
               if (!fp)
@@ -725,6 +761,32 @@ 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.  */
@@ -733,26 +795,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;
 
@@ -762,6 +811,9 @@ _gcry_fips_run_selftests (int extended)
   if (run_mac_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 ())
@@ -775,7 +827,8 @@ _gcry_fips_run_selftests (int extended)
   ec = 0;
 
  leave:
-  fips_new_state (result);
+  if (fips_mode ())
+    fips_new_state (result);
 
   return ec;
 }
@@ -831,7 +884,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;
@@ -846,8 +898,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;