[Openvpn-devel,2/4] Extend FindSystemInfo custom action to detect OpenVPNService state

Message ID 20181219202611.2144-2-simon@rozman.si
State New
Headers show
Series
  • [Openvpn-devel,1/4] Make DriverCertification MSI property public
Related show

Commit Message

Simon Rozman Dec. 19, 2018, 8:26 p.m.
---
 src/openvpnmsica/openvpnmsica.c | 168 ++++++++++++++++++++++++++++----
 1 file changed, 151 insertions(+), 17 deletions(-)

Patch

diff --git a/src/openvpnmsica/openvpnmsica.c b/src/openvpnmsica/openvpnmsica.c
index 721bd4f8..810c2858 100644
--- a/src/openvpnmsica/openvpnmsica.c
+++ b/src/openvpnmsica/openvpnmsica.c
@@ -44,6 +44,7 @@ 
 #include <tchar.h>
 
 #ifdef _MSC_VER
+#pragma comment(lib, "advapi32.lib")
 #pragma comment(lib, "iphlpapi.lib")
 #pragma comment(lib, "shell32.lib")
 #pragma comment(lib, "shlwapi.lib")
@@ -193,29 +194,34 @@  _openvpnmsica_debug_popup(_In_z_ LPCTSTR szFunctionName)
 #endif
 
 
-UINT __stdcall
-FindSystemInfo(_In_ MSIHANDLE hInstall)
+/**
+ * Detects Windows version and sets DRIVERCERTIFICATION property to "", "whql", or "attsgn"
+ * accordingly.
+ *
+ * @param hInstall      Handle to the installation provided to the DLL custom action
+ *
+ * @return ERROR_SUCCESS on success; An error code otherwise
+ *         See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx
+ */
+static UINT
+openvpnmsica_set_driver_certification(_In_ MSIHANDLE hInstall)
 {
-#ifdef _MSC_VER
-#pragma comment(linker, DLLEXP_EXPORT)
-#endif
-
-    openvpnmsica_debug_popup(TEXT(__FUNCTION__));
-
     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. */
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4996) /* 'GetVersionExW': was declared deprecated. */
+#endif
     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;
+        return uiResult;
     }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
 
     /* The Windows version is usually spoofed, check using RtlGetVersion(). */
     TCHAR szDllPath[0x1000];
@@ -294,17 +300,145 @@  FindSystemInfo(_In_ MSIHANDLE hInstall)
     {
         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(\"DRIVERCERTIFICATION\") failed", __FUNCTION__);
-        goto cleanup_CoInitialize;
+        return uiResult;
+    }
+
+    return ERROR_SUCCESS;
+}
+
+
+/**
+ * Detects if the OpenVPNService service is in use (running or paused) and sets
+ * OPENVPNSERVICE to the service process PID, or its path if it is set to
+ * auto-start, but not running.
+ *
+ * @param hInstall      Handle to the installation provided to the DLL custom action
+ *
+ * @return ERROR_SUCCESS on success; An error code otherwise
+ *         See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx
+ */
+static UINT
+openvpnmsica_set_openvpnserv_state(_In_ MSIHANDLE hInstall)
+{
+    UINT uiResult;
+
+    /* Get Service Control Manager handle. */
+    SC_HANDLE hSCManager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
+    if (hSCManager == NULL)
+    {
+        uiResult = GetLastError();
+        msg(M_NONFATAL | M_ERRNO, "%s: OpenSCManager() failed", __FUNCTION__);
+        return uiResult;
+    }
+
+    /* Get OpenVPNService service handle. */
+    SC_HANDLE hService = OpenService(hSCManager, TEXT("OpenVPNService"), SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG);
+    if (hService == NULL)
+    {
+        uiResult = GetLastError();
+        if (uiResult == ERROR_SERVICE_DOES_NOT_EXIST) {
+            /* This is not actually an error. */
+            goto cleanup_OpenSCManager;
+        }
+        msg(M_NONFATAL | M_ERRNO, "%s: OpenService(\"OpenVPNService\") failed", __FUNCTION__);
+        goto cleanup_OpenSCManager;
+    }
+
+    /* Query service status. */
+    SERVICE_STATUS_PROCESS ssp;
+    DWORD dwBufSize;
+    if (!QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(ssp), &dwBufSize))
+    {
+        uiResult = GetLastError();
+        msg(M_NONFATAL | M_ERRNO, "%s: QueryServiceStatusEx(\"OpenVPNService\") failed", __FUNCTION__);
+        goto finish_QueryServiceStatusEx;
+    }
+
+    switch (ssp.dwCurrentState)
+    {
+    case SERVICE_START_PENDING:
+    case SERVICE_RUNNING:
+    case SERVICE_STOP_PENDING:
+    case SERVICE_PAUSE_PENDING:
+    case SERVICE_PAUSED:
+    case SERVICE_CONTINUE_PENDING:
+    {
+        /* Set OPENVPNSERVICE property to service PID. */
+        TCHAR szPID[10/*MAXDWORD in decimal*/ + 1/*terminator*/];
+        _stprintf_s(
+            szPID, _countof(szPID),
+            TEXT("%u"),
+            ssp.dwProcessId);
+
+        uiResult = MsiSetProperty(hInstall, TEXT("OPENVPNSERVICE"), szPID);
+        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(\"OPENVPNSERVICE\") failed", __FUNCTION__);
+        }
+        goto cleanup_OpenService;
+    }
+    break;
+    }
+finish_QueryServiceStatusEx:;
+
+    // Service is not started. Is it set to auto-start?
+    // MSDN describes the maximum buffer size for QueryServiceConfig() to be 8kB.
+    // This is small enough to fit on stack.
+    BYTE _buffer_8k[8192];
+    LPQUERY_SERVICE_CONFIG pQsc = (LPQUERY_SERVICE_CONFIG)_buffer_8k;
+    dwBufSize = sizeof(_buffer_8k);
+    if (!QueryServiceConfig(hService, pQsc, dwBufSize, &dwBufSize))
+    {
+        uiResult = GetLastError();
+        msg(M_NONFATAL | M_ERRNO, "%s: QueryServiceStatusEx(\"QueryServiceConfig\") failed", __FUNCTION__);
+        goto cleanup_OpenService;
+    }
+
+    if (pQsc->dwStartType <= SERVICE_AUTO_START)
+    {
+        uiResult = MsiSetProperty(hInstall, TEXT("OPENVPNSERVICE"), pQsc->lpBinaryPathName);
+        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(\"OPENVPNSERVICE\") failed", __FUNCTION__);
+            goto cleanup_OpenService;
+        }
     }
 
     uiResult = ERROR_SUCCESS;
 
-cleanup_CoInitialize:
-    if (bIsCoInitialized) CoUninitialize();
+cleanup_OpenService:
+    CloseServiceHandle(hService);
+cleanup_OpenSCManager:
+    CloseServiceHandle(hSCManager);
     return uiResult;
 }
 
 
+UINT __stdcall
+FindSystemInfo(_In_ MSIHANDLE hInstall)
+{
+#ifdef _MSC_VER
+#pragma comment(linker, DLLEXP_EXPORT)
+#endif
+
+    openvpnmsica_debug_popup(TEXT(__FUNCTION__));
+
+    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;
+
+    openvpnmsica_set_driver_certification(hInstall);
+    openvpnmsica_set_openvpnserv_state(hInstall);
+
+    if (bIsCoInitialized) CoUninitialize();
+    return ERROR_SUCCESS;
+}
+
+
 UINT __stdcall
 FindTAPInterfaces(_In_ MSIHANDLE hInstall)
 {