Hi Simon,

This is painful to read, and I bet it was even more painful to write. I am 
sorry it came to this, though it does look like it should(TM) work.

>From what I gather, the OS version should be checked in the 
CustomAction's MSI execution conditions instead of in the CustomAction 
itself. The appropriate information can then be passed in based on that:

https://blogs.msdn.microsoft.com/cjacks/2009/05/06/why-custom-actions-get-a-windows-vista-version-lie-on-windows-7/

It might be somewhat more convenient to add the PID to the debug 
MessageBox call, but it is probably MUCH more convenient to use the 
CustomAction debugging facility built into the MSI service itself:

https://docs.microsoft.com/en-us/windows/desktop/Msi/debugging-custom-actions

Thanks,
Jon

-----Original Message-----
From: Simon Rozman <si...@rozman.si> 
Sent: Tuesday, October 16, 2018 3:26 AM
To: openvpn-devel@lists.sourceforge.net
Subject: [Openvpn-devel] [PATCH 4/5] Add MSI custom action for reliable Windows 
10 detection

This patch introduces a `FindSystemInfo()` MSI custom action to reliably
detect Windows 10. The MSI built-in properties for Windows version
detection depend on bootstrapper's manifest. We could provide our own
Windows 10 compatible EXE bootstrapper, but that would cover the
Windows 10 detection in the `InstallUISequence` only. The
`InstallExecuteSequence` is launched by msiexec.exe which we cannot
tamper with would still report `VersionNT` as Windows 8 (603).
---
 src/openvpnmsica/Makefile.am    |   2 +-
 src/openvpnmsica/openvpnmsica.c | 124 ++++++++++++++++++++++++++++++--
 src/openvpnmsica/openvpnmsica.h |  15 ++++
 3 files changed, 136 insertions(+), 5 deletions(-)

diff --git a/src/openvpnmsica/Makefile.am b/src/openvpnmsica/Makefile.am
index d46170b4..ecca74bc 100644
--- a/src/openvpnmsica/Makefile.am
+++ b/src/openvpnmsica/Makefile.am
@@ -41,7 +41,7 @@ libopenvpnmsica_la_CFLAGS = \
        -municode -D_UNICODE \
        -UNTDDI_VERSION -U_WIN32_WINNT \
        -D_WIN32_WINNT=_WIN32_WINNT_VISTA
-libopenvpnmsica_la_LDFLAGS = -ladvapi32 -lole32 -lmsi -lsetupapi -lshlwapi 
-no-undefined -avoid-version
+libopenvpnmsica_la_LDFLAGS = -ladvapi32 -lole32 -lmsi -lsetupapi -lshlwapi 
-lversion -no-undefined -avoid-version
 endif
 
 libopenvpnmsica_la_SOURCES = \
diff --git a/src/openvpnmsica/openvpnmsica.c b/src/openvpnmsica/openvpnmsica.c
index 3b90ce05..d1642d6a 100644
--- a/src/openvpnmsica/openvpnmsica.c
+++ b/src/openvpnmsica/openvpnmsica.c
@@ -36,13 +36,15 @@
 #include <memory.h>
 #include <msiquery.h>
 #include <shlwapi.h>
-#ifdef _MSC_VER
-#pragma comment(lib, "shlwapi.lib")
-#endif
 #include <stdbool.h>
 #include <stdlib.h>
 #include <tchar.h>
 
+#ifdef _MSC_VER
+#pragma comment(lib, "shlwapi.lib")
+#pragma comment(lib, "version.lib")
+#endif
+
 
 /**
  * Local constants
@@ -119,7 +121,7 @@ openvpnmsica_setup_sequence_filename(
     {
         size_t len_action_name_z = 
_tcslen(openvpnmsica_cleanup_action_seqs[i].szName) + 1;
         TCHAR *szPropertyEx = (TCHAR*)malloc((len_property_name + 
len_action_name_z) * sizeof(TCHAR));
-        memcpy(szPropertyEx                    , szProperty                    
     , len_property_name * sizeof(TCHAR));
+        memcpy(szPropertyEx                    , szProperty                    
            , len_property_name * sizeof(TCHAR));
         memcpy(szPropertyEx + len_property_name, 
openvpnmsica_cleanup_action_seqs[i].szName, len_action_name_z * sizeof(TCHAR));
         _stprintf_s(
             szFilenameEx, _countof(szFilenameEx),
@@ -142,6 +144,120 @@ openvpnmsica_setup_sequence_filename(
 }
 
 
+UINT __stdcall
+FindSystemInfo(_In_ MSIHANDLE hInstall)
+{
+#ifdef _MSC_VER
+#pragma comment(linker, DLLEXP_EXPORT)
+#endif
+
+#ifdef _DEBUG
+    MessageBox(NULL, TEXT("Attach debugger!"), TEXT(__FUNCTION__) TEXT(" v")  
TEXT(PACKAGE_VERSION), MB_OK);
+#endif
+
+    UINT uiResult;
+    BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
+
+    /* Set MSI session handle in TLS. */
+    struct openvpnmsica_tls_data *s = (struct openvpnmsica_tls_data 
*)TlsGetValue(openvpnmsica_tlsidx_session);
+    s->hInstall = hInstall;
+
+    // Get Windows version.
+    OSVERSIONINFOEX ver_info = { .dwOSVersionInfoSize = 
sizeof(OSVERSIONINFOEX) };
+    if (!GetVersionEx((LPOSVERSIONINFO)&ver_info)) {
+        uiResult = GetLastError();
+        msg(M_NONFATAL | M_ERRNO, "%s: GetVersionEx() failed", __FUNCTION__);
+        goto cleanup_CoInitialize;
+    }
+
+    // The Windows version is usually spoofed, check using RtlGetVersion().
+    TCHAR szDllPath[0x1000];
+    ExpandEnvironmentStrings(TEXT("%SystemRoot%\\System32\\ntdll.dll"), 
szDllPath,
+#ifdef UNICODE
+        _countof(szDllPath)
+#else
+        _countof(szDllPath) - 1
+#endif
+    );
+    HMODULE hNtDllModule = LoadLibrary(szDllPath);
+    if (hNtDllModule)
+    {
+        typedef NTSTATUS (WINAPI* fnRtlGetVersion)(PRTL_OSVERSIONINFOW);
+        fnRtlGetVersion RtlGetVersion = 
(fnRtlGetVersion)GetProcAddress(hNtDllModule, "RtlGetVersion");
+        if (RtlGetVersion)
+        {
+            RTL_OSVERSIONINFOW rtl_ver_info = { .dwOSVersionInfoSize = 
sizeof(RTL_OSVERSIONINFOW) };
+            if (RtlGetVersion(&rtl_ver_info) == 0)
+                if (
+                    rtl_ver_info.dwMajorVersion >  ver_info.dwMajorVersion ||
+                    rtl_ver_info.dwMajorVersion == ver_info.dwMajorVersion && 
rtl_ver_info.dwMinorVersion >  ver_info.dwMinorVersion ||
+                    rtl_ver_info.dwMajorVersion == ver_info.dwMajorVersion && 
rtl_ver_info.dwMinorVersion == ver_info.dwMinorVersion && 
rtl_ver_info.dwBuildNumber > ver_info.dwBuildNumber)
+                {
+                    // We got RtlGetVersion() and it reported newer version 
than GetVersionEx().
+                    ver_info.dwMajorVersion = rtl_ver_info.dwMajorVersion;
+                    ver_info.dwMinorVersion = rtl_ver_info.dwMinorVersion;
+                    ver_info.dwBuildNumber  = rtl_ver_info.dwBuildNumber;
+                    ver_info.dwPlatformId   = rtl_ver_info.dwPlatformId;
+                }
+        }
+
+        FreeLibrary(hNtDllModule);
+    }
+
+    // We don't trust RtlGetVersion() either. Check the version resource of 
kernel32.dll.
+    ExpandEnvironmentStrings(TEXT("%SystemRoot%\\System32\\kernel32.dll"), 
szDllPath,
+#ifdef UNICODE
+        _countof(szDllPath)
+#else
+        _countof(szDllPath)-1
+#endif
+    );
+
+    DWORD dwHandle;
+    DWORD dwVerInfoSize = GetFileVersionInfoSize(szDllPath, &dwHandle);
+    if (dwVerInfoSize)
+    {
+        LPVOID pVersionInfo = malloc(dwVerInfoSize);
+        if (pVersionInfo)
+        {
+            // Read version info.
+            if (GetFileVersionInfo(szDllPath, dwHandle, dwVerInfoSize, 
pVersionInfo))
+            {
+                // Get the value for the root block.
+                UINT uiSize = 0;
+                VS_FIXEDFILEINFO *pVSFixedFileInfo = NULL;
+                if (VerQueryValue(pVersionInfo, TEXT("\\"), &pVSFixedFileInfo, 
&uiSize) && uiSize && pVSFixedFileInfo)
+                    if (HIWORD(pVSFixedFileInfo->dwProductVersionMS) >  
ver_info.dwMajorVersion ||
+                        HIWORD(pVSFixedFileInfo->dwProductVersionMS) == 
ver_info.dwMajorVersion && LOWORD(pVSFixedFileInfo->dwProductVersionMS) >  
ver_info.dwMinorVersion ||
+                        HIWORD(pVSFixedFileInfo->dwProductVersionMS) == 
ver_info.dwMajorVersion && LOWORD(pVSFixedFileInfo->dwProductVersionMS) == 
ver_info.dwMinorVersion && HIWORD(pVSFixedFileInfo->dwProductVersionLS) > 
ver_info.dwBuildNumber)
+                    {
+                        // We got kernel32.dll version and it is newer.
+                        ver_info.dwMajorVersion = 
HIWORD(pVSFixedFileInfo->dwProductVersionMS);
+                        ver_info.dwMinorVersion = 
LOWORD(pVSFixedFileInfo->dwProductVersionMS);
+                        ver_info.dwBuildNumber  = 
HIWORD(pVSFixedFileInfo->dwProductVersionLS);
+                    }
+            }
+
+            free(pVersionInfo);
+        }
+    }
+
+    uiResult = MsiSetProperty(hInstall, TEXT("DriverCertification"), 
ver_info.dwMajorVersion >= 10 ? ver_info.wProductType > VER_NT_WORKSTATION ? 
TEXT("whql") : TEXT("attsgn") : TEXT(""));
+    if (uiResult != ERROR_SUCCESS)
+    {
+        SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to 
set GetLastError(). But we do have an error code. Set last error manually. */
+        msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"TAPINTERFACES\") 
failed", __FUNCTION__);
+        goto cleanup_CoInitialize;
+    }
+
+    uiResult = ERROR_SUCCESS;
+
+cleanup_CoInitialize:
+    if (bIsCoInitialized) CoUninitialize();
+    return uiResult;
+}
+
+
 UINT __stdcall
 FindTAPInterfaces(_In_ MSIHANDLE hInstall)
 {
diff --git a/src/openvpnmsica/openvpnmsica.h b/src/openvpnmsica/openvpnmsica.h
index bb8e28ec..da145062 100644
--- a/src/openvpnmsica/openvpnmsica.h
+++ b/src/openvpnmsica/openvpnmsica.h
@@ -63,6 +63,21 @@ extern "C" {
 #endif
 
 
+/**
+ * Determines Windows information:
+ * - Sets `DriverCertification` MSI property to "", "attsgn" or "whql"
+ *   according to the driver certification required by the running version of
+ *   Windows.
+ *
+ * @param hInstall      Handle to the installation provided to the DLL custom 
action
+ *
+ * @return ERROR_SUCCESS on success; An error code otherwise
+ *         See: 
https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmsdn.microsoft.com%2Fen-us%2Flibrary%2Fwindows%2Fdesktop%2Faa368072.aspx&amp;data=02%7C01%7Cjkunkee%40microsoft.com%7Cd520a31814634dced1dc08d63351eb58%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636752824275192379&amp;sdata=2DLeMsL5JHIFmh07Fh6zqZTGt117xy%2Bu2TY3acNpaH8%3D&amp;reserved=0
+ */
+DLLEXP_DECL UINT __stdcall
+FindSystemInfo(_In_ MSIHANDLE hInstall);
+
+
 /**
  * Find existing TAP interfaces and set TAPINTERFACES property with semicolon 
delimited list
  * of installed TAP interface GUIDs.
-- 
2.19.0.windows.1



_______________________________________________
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Flists.sourceforge.net%2Flists%2Flistinfo%2Fopenvpn-devel&amp;data=02%7C01%7Cjkunkee%40microsoft.com%7Cd520a31814634dced1dc08d63351eb58%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636752824275192379&amp;sdata=Upy1p7tESRCzuEPIBUpkxr3YnZpFFrr8zpQybDyaY1Q%3D&amp;reserved=0


_______________________________________________
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to