W32 build fixes for gnupg-2.0.19

LRN lrn1986 at gmail.com
Sat Jan 26 04:47:18 CET 2013


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Subj is attached.
If you need patches against git master HEAD, just ask.

0001-fix-regex-test-on.mingw32.all.patch:
NULL is defined in stddef.h by gcc. Include stddef.h.
(regex.h here is provided by libgnurx. That might explain
why it does not include stddef.h itself, while GNU Libc regex.h does
(presumably, otherwise this test would have been failing for you too)).

0002-fix-stringhelp-on.mingw32.patch:
First, it moves home_buffer assignment outside of the #ifdef, since
the use of xstrdup() has nothing to do with PWD.
Second, it adds W32-only code that will use USERPROFILE instead of
HOME if HOME is not defined (not really necessary in MSys, but you can
never be too careful).

0003-fix-tmpfile-on.mingw32.patch:
OK, this one is _big_. Kind of.
I don't know why (the original code looked sound enough), but
previously gnupg_tmpfile() failed during `make check' (i.e. it tried
to create a file 10 times, failed 10 times and returned ENOENT).
I decided to replace it with a sane implementation that generates
template randomly.
Then i discovered that i can't link to libgcrypt, and that gnupg's
minimal target W32 platform is W2000 (0x500), which means that
CryptoAPI is not available at compile time. So i added runtime
detection. Which is why things got so big.


With these patches gnupg-2.0.19 compiled with MinGW/MSys, AND `make
check' passes all tests successfully (i did test all 3 versions of the
random generator).
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (MingW32)
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iQEcBAEBAgAGBQJRA1HFAAoJEOs4Jb6SI2Cwt/wH/R+OMnklEUYfdBoHiSorcft8
uddfKKhnV9gEnoG1gvdQBr8UX2zLSofvIGIvHy0Ah7MFRSTPnDrtn8KpmsaZN7Yd
2R9JpgxUbGlXLvpAAG9emoWgr0jrHGlwuJfPe3zUOAJNdN/H9xXMkalikCcnSW/Y
sszKQXLwJ+pMXRATcgLdb0QqU79I3GlYy/VLU3doqVWdFZXnUrb+ELeq5aDx9Ty4
pHn2mvOOMzLwwav0J9YONeT2UfTNW8cNvvcLwKxTiqnOdO0KM/OWUs10Wq7aIHzi
V1Kc6wo2AGtnk+4BcHegZd2adEhD3UH3/2x90xwXF0qrhE7DlQCfbX9RfthYXT4=
=Ecxy
-----END PGP SIGNATURE-----
-------------- next part --------------
--- gnupg-2.0.19/configure.ac.orig	2013-01-26 04:55:46 +0400
+++ gnupg-2.0.19/configure.ac	2013-01-26 04:56:07 +0400
@@ -1173,6 +1173,7 @@
        AC_TRY_RUN([
 #include <unistd.h>
 #include <regex.h>
+#include <stddef.h>
 main() { regex_t blah ; regmatch_t p; p.rm_eo = p.rm_eo; return regcomp(&blah, "foo.*bar", REG_NOSUB) || regexec (&blah, "foobar", 0, NULL, 0); }],
        gnupg_cv_regex_broken=no, gnupg_cv_regex_broken=yes, gnupg_cv_regex_broken=yes))
 
-------------- next part --------------
--- gnupg-2.0.19/jnlib/t-stringhelp.c.orig	2013-01-26 04:57:13 +0400
+++ gnupg-2.0.19/jnlib/t-stringhelp.c	2013-01-26 05:01:15 +0400
@@ -43,9 +43,9 @@
     {
       char *home = getenv("HOME");
       
-#if defined(HAVE_GETPWUID) && defined(HAVE_PWD_H)
       if(home)
         home_buffer = xstrdup (home);
+#if defined(HAVE_GETPWUID) && defined(HAVE_PWD_H)
       else
         {
           struct passwd *pwd;
--- gnupg-2.0.19/jnlib/t-stringhelp.c.orig	2013-01-26 05:01:15 +0400
+++ gnupg-2.0.19/jnlib/t-stringhelp.c	2013-01-26 05:01:26 +0400
@@ -43,6 +43,10 @@
     {
       char *home = getenv("HOME");
       
+#if defined(_WIN32)
+      if (NULL == home)
+        home = getenv ("USERPROFILE");
+#endif
       if(home)
         home_buffer = xstrdup (home);
 #if defined(HAVE_GETPWUID) && defined(HAVE_PWD_H)
-------------- next part --------------
--- gnupg-2.0.19/common/sysutils.c.orig	2013-01-26 04:57:13 +0400
+++ gnupg-2.0.19/common/sysutils.c	2013-01-26 06:51:57 +0400
@@ -45,6 +45,7 @@
 #ifdef HAVE_W32_SYSTEM
 # define WINVER 0x0500  /* Required for AllowSetForegroundWindow.  */
 # include <windows.h>
+# include <gcrypt.h>
 #endif
 #ifdef HAVE_PTH      
 # include <pth.h>
@@ -320,7 +321,131 @@
 #endif
 }
 
+#if defined(HAVE_W32_SYSTEM)
+typedef ULONG HCRYPTPROV;
+#define PROV_RSA_FULL 1
+#define CRYPT_VERIFYCONTEXT 0xF0000000
+
+/* W32 version of gcry_randomize ().
+   If you ever decide to link libgommon to libgcrypt, use gcry_randomize()
+   instead.
+
+   This function is not exactly thread-safe, as two threads calling
+   it simultaneously for the first time will both try to initialize random,
+   and things will get messy.
+ */
+void
+w32_randomize (unsigned char *buffer, size_t length)
+{
+  static int initialized_random = 0;
+  static int use_nt_random = 0;
+  static int use_xp_random = 0;
+  /* This LoadLibrary madness is, basically, to be able to compile
+   * code for W2000 only (WINVER==0x500), but be able to use
+   * XP-and-newer API at runtime.
+   * If you ever bump minimally supported version to XP,
+   * then just use CryptoAPI directly (don't forget to #include <Wincrypt.h>).
+   * In that case it is also possible to make it thread-safe - 
+   * just obtain and release crypto provider context in every call.
+   */
+  static HMODULE advapi32_dll = NULL;
+  static BOOL __stdcall (*Loaded_CryptGenRandom)(HCRYPTPROV hProv, DWORD dwLen,
+      BYTE *pbBuffer);
+  static BYTE __stdcall (*Loaded_RtlGenRandom)(PVOID RandomBuffer,
+      ULONG RandomBufferLength);
+
+  BOOL b;
+  static HCRYPTPROV cryptoprov;
+
+  int i;
 
+  if (!initialized_random)
+  {
+    OSVERSIONINFOEXA w32_ver;
+    w32_ver.dwOSVersionInfoSize = sizeof (w32_ver);
+    b = GetVersionExA ((LPOSVERSIONINFOA) &w32_ver);
+    if (b)
+    {
+      advapi32_dll = LoadLibrary ("Advapi32.dll");
+      if (advapi32_dll)
+      {
+        char just_checking_that_it_works;
+        if (w32_ver.dwMajorVersion >= 5 && w32_ver.dwMinorVersion >= 1)
+        {
+          /* XP/2003 or newer */
+          BOOL WINAPI (*Loaded_CryptAcquireContext) (HCRYPTPROV *phProv,
+              LPCTSTR pszContainer, LPCTSTR pszProvider, DWORD dwProvType,
+              DWORD dwFlags);
+          BOOL WINAPI (*Loaded_CryptReleaseContext) (HCRYPTPROV hProv,
+              DWORD dwFlags);
+          Loaded_CryptAcquireContext = GetProcAddress (advapi32_dll,
+              "CryptAcquireContextA");
+          Loaded_CryptGenRandom = GetProcAddress (advapi32_dll,
+              "CryptGenRandom");
+          Loaded_CryptReleaseContext = GetProcAddress (advapi32_dll,
+              "CryptReleaseContext");
+          if (Loaded_CryptAcquireContext && Loaded_CryptGenRandom &&
+              Loaded_CryptReleaseContext)
+          {
+            b = Loaded_CryptAcquireContext (&cryptoprov, NULL, NULL,
+                PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
+            if (b)
+            {
+              b = Loaded_CryptGenRandom (cryptoprov, 1, &just_checking_that_it_works);
+              if (b)
+                use_xp_random = 1;
+                /* And just as we can't free the library, we can't release the
+                 * context.
+                 */
+              else
+                Loaded_CryptReleaseContext (cryptoprov, 0);
+            }
+          }
+        }
+        if (!(w32_ver.dwMajorVersion >= 5 && w32_ver.dwMinorVersion >= 1) ||
+            !use_xp_random)
+        {
+          Loaded_RtlGenRandom = (BYTE __stdcall (*)(PVOID RandomBuffer,
+              ULONG RandomBufferLength)) GetProcAddress (advapi32_dll,
+              "SystemFunction036");
+          if (Loaded_RtlGenRandom)
+          {
+            if (Loaded_RtlGenRandom (&just_checking_that_it_works, 1))
+              use_nt_random = 1;
+          }
+        }
+        /* Since we don't have any cleanup hooks, we can't FreeLibrary().
+         * OS will unload it for us on exit, so it's probably ok.
+         * The only case when we can cleanup is when we failed to get
+         * functions out of the library.
+         */
+        if (!use_nt_random && !use_xp_random)
+          FreeLibrary (advapi32_dll);
+      }
+    }
+    /* Since we fall back to CRT random, initialize it now unconditionally */
+    srand (time (NULL));
+    initialized_random = 1;
+  }
+  if (use_xp_random)
+  {
+    if (Loaded_CryptGenRandom (cryptoprov, length, buffer))
+      return;
+    use_xp_random = 0;
+  }
+  else if (use_nt_random)
+  {
+    if (Loaded_RtlGenRandom (buffer, length))
+      return;
+    use_nt_random = 0;
+  }
+  if (!use_xp_random && !use_nt_random);
+  {
+    for (i = 0; i < length; i++)
+      buffer[i] = rand () % 256;
+  }
+}
+#endif
 
 /* Replacement for tmpfile().  This is required because the tmpfile
    function of Windows' runtime library is broken, insecure, ignores
@@ -330,14 +454,13 @@
 gnupg_tmpfile (void)
 {
 #ifdef HAVE_W32_SYSTEM
-  int attempts, n;
+  int n;
   char buffer[MAX_PATH+7+12+1];
   char *name, *p;
   HANDLE file;
-  int pid = GetCurrentProcessId ();
-  unsigned int value;
   int i;
   SECURITY_ATTRIBUTES sec_attr;
+  DWORD w32_error;
 
   memset (&sec_attr, 0, sizeof sec_attr );
   sec_attr.nLength = sizeof sec_attr;
@@ -357,15 +480,31 @@
   CreateDirectory (buffer, NULL);
   *p++ = '\\';
   name = p;
-  for (attempts=0; attempts < 10; attempts++)
+  /* This might have problems:
+     1) Lots of processes are calling tmpfile() with the same template,
+       thus exausting the namespace (this function will get slower and slower,
+       until finally there will be no free names). It will become unstuck once
+       a new name is available (i.e. some files are deleted).
+       Possible fix: add exponential Sleep() to prevent it from busylooping. Or
+         just go back to the old try-10-times-then-fail logic. Depends on
+         what the caller wants. Waiting indefinitely for a resource (filename)
+         to become available might be a valid strategy after all...
+     2) Some malicious process reads this process' memory, learns the filename
+       and creates files before this process does (then removes them, once this
+       process fails to create existing file). Effect is like an extreme case of (1).
+       Can't be fixed (other than just failing after N attempts).
+     This code will NOT fail due to exausting available names if it is the only
+     process creating temporary files with this template, because it will run
+     out of fds (_open_osfhandle() will fail) long before it runs out of names.
+   */
+  do
     {
+      char allowed_chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-+=[](){}`~!@#%^&;'";
+      unsigned char name_random[8];
       p = name;
-      value = (GetTickCount () ^ ((pid<<16) & 0xffff0000));
-      for (i=0; i < 8; i++)
-        {
-          *p++ = tohex (((value >> 28) & 0x0f));
-          value <<= 4;
-        }
+      w32_randomize (name_random, 8);
+      for (i = 0; i < 8; i++)
+        *p++ = allowed_chars[name_random[i] % sizeof (allowed_chars)];
       strcpy (p, ".tmp");
       file = CreateFile (buffer,
                          GENERIC_READ | GENERIC_WRITE,
@@ -374,6 +513,7 @@
                          CREATE_NEW,
                          FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
                          NULL);
+      w32_error = GetLastError ();
       if (file != INVALID_HANDLE_VALUE)
         {
           FILE *fp;
@@ -393,8 +533,7 @@
             }
           return fp;
         }
-      Sleep (1); /* One ms as this is the granularity of GetTickCount.  */
-    }
+    } while (w32_error == ERROR_FILE_EXISTS);
   errno = ENOENT;
   return NULL;
 #else /*!HAVE_W32_SYSTEM*/


More information about the Gnupg-devel mailing list