Skip to contentSkip to conversation

Delete TAP interface before the TAP driver is uninstalled

PATCH In review Simon Rozman 2 participants 6 messages Nov 12, 2018 – Jan 17, 2019

lore
Share & tools
lore
#0 lore raw

PATCH 1/3 Delete TAP interface before the TAP driver is uninstalled

The previous version of MSI installer did:
- Execution Pass: rename the TAP interface to some temporary name
- Commit/Rollback Pass: delete the TAP interface / rename the interface back to original name

However, the WiX Toolset's Diffx extension to install and remove drivers removed the TAP driver between the execution and commit passes. The TAP driver removal makes all TAP interfaces unavailable and our custom action couldn't find the interface to delete any more.

While the system where OpenVPN was uninstalled didn't have any TAP interfaces any more as expected behaviour, the problem appears after reinstalling the OpenVPN. Some residue TAP interface registry keys remain on the system, causing the TAP interface to reappear as "Ethernet NN" interface next time the TAP driver is installed. This causes TAP interfaces to accumulate when cycling install-uninstall-install...

Therefore, it is better to remove the TAP interfaces before the TAP driver is removed, and reinstall the TAP interface back should the rollback be required. Though it won't be exactly the same interface again.

I wonder if the WiX Diffx extension supports execute/commit/rollback feature of MSI in the first place.

---
 src/openvpnmsica/msica_op.c | 87 ++++++++++++++-----------------------
 1 file changed, 33 insertions(+), 54 deletions(-)
src/openvpnmsica/msica_op.c +33 -54
index 8e9a3832..2ce69444 100644
@@ -454,62 +454,41 @@ msica_op_tap_interface_delete(
 
     DWORD dwResult;
 
-    if (session->rollback_enabled)
-    {
-        int count = 0;
-
-        do {
-            /* Rename the interface to keep it as a backup. */
-            TCHAR szNameBackup[10/*"Interface "*/ + 10/*maximum int*/ + 1/*terminator*/];
-            _stprintf_s(
-                szNameBackup, _countof(szNameBackup),
-                TEXT("Interface %i"),
-                ++count);
-            for (struct tap_interface_node *pInterfaceOther = pInterfaceList; ; pInterfaceOther = pInterfaceOther->pNext)
-            {
-                if (pInterfaceOther == NULL)
-                {
-                    /* No interface with a same name found. All clear to rename the interface. */
-                    dwResult = tap_set_interface_name(&pInterface->guid, szNameBackup);
-                    break;
-                }
-                else if (_tcsicmp(szNameBackup, pInterfaceOther->szName) == 0)
-                {
-                    /* Interface with a same name found. Duplicate interface names are not allowed. */
-                    dwResult = ERROR_ALREADY_EXISTS;
-                    break;
-                }
-            }
-        } while (dwResult == ERROR_ALREADY_EXISTS);
+    /* Delete the interface. */
+    BOOL bRebootRequired = FALSE;
+    dwResult = tap_delete_interface(NULL, &pInterface->guid, &bRebootRequired);
+    if (bRebootRequired)
+        MsiSetMode(session->hInstall, MSIRUNMODE_REBOOTATEND, TRUE);
 
-        if (dwResult == ERROR_SUCCESS) {
-            /* Schedule rollback action to rename the interface back. */
-            msica_op_seq_add_head(
-                &session->seq_cleanup[MSICA_CLEANUP_ACTION_ROLLBACK],
-                msica_op_create_guid_string(
-                    msica_op_tap_interface_set_name,
-                    0,
-                    NULL,
-                    &pInterface->guid,
-                    pInterface->szName));
-
-            /* Schedule commit action to delete the interface. */
-            msica_op_seq_add_tail(
-                &session->seq_cleanup[MSICA_CLEANUP_ACTION_COMMIT],
-                msica_op_create_guid(
-                    msica_op_tap_interface_delete_by_guid,
-                    0,
-                    NULL,
-                    &pInterface->guid));
-        }
-    }
-    else
+    if (session->rollback_enabled)
     {
-        /* Delete the interface. */
-        BOOL bRebootRequired = FALSE;
-        dwResult = tap_delete_interface(NULL, &pInterface->guid, &bRebootRequired);
-        if (bRebootRequired)
-            MsiSetMode(session->hInstall, MSIRUNMODE_REBOOTATEND, TRUE);
+        /*
+        Schedule rollback action to create the interface back. Though it won't be exactly the same interface again.
+
+        The previous version of this function did:
+        - Execution Pass:       rename the interface to some temporary name
+        - Commit/Rollback Pass: delete the interface / rename the interface back to original name
+
+        However, the WiX Toolset's Diffx extension to install and remove drivers removed the TAP driver between the
+        execution and commit passes. TAP driver removal makes all TAP interfaces unavailable and our CA couldn't find
+        the interface to delete any more.
+
+        While the system where OpenVPN was uninstalled didn't have any TAP interfaces any more as expected behaviour,
+        the problem appears after reinstalling the OpenVPN. Some residue TAP interface registry keys remain on the
+        system, causing the TAP interface to reappear as "Ethernet NN" interface next time the TAP driver is
+        installed. This causes TAP interfaces to accumulate over cyclic install-uninstall-install...
+
+        Therefore, it is better to remove the TAP interfaces before the TAP driver is removed, and reinstall the TAP
+        interface back should the rollback be required. I wonder if the WiX Diffx extension supports execute/commit/
+        rollback feature of MSI in the first place.
+        */
+        msica_op_seq_add_head(
+            &session->seq_cleanup[MSICA_CLEANUP_ACTION_ROLLBACK],
+            msica_op_create_string(
+                msica_op_tap_interface_create,
+                0,
+                NULL,
+                pInterface->szName));
     }
 
     return dwResult;
Signature · 1 line
2.19.1.windows.1
#1 lore raw

PATCH 2/3 Change C++ to C comments

---
 src/openvpnmsica/openvpnmsica.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
src/openvpnmsica/openvpnmsica.c +7 -7
index d1642d6a..99b47bf0 100644
@@ -162,7 +162,7 @@ FindSystemInfo(_In_ MSIHANDLE hInstall)
     struct openvpnmsica_tls_data *s = (struct openvpnmsica_tls_data *)TlsGetValue(openvpnmsica_tlsidx_session);
     s->hInstall = hInstall;
 
-    // Get Windows version.
+    /* Get Windows version. */
     OSVERSIONINFOEX ver_info = { .dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX) };
     if (!GetVersionEx((LPOSVERSIONINFO)&ver_info)) {
         uiResult = GetLastError();
@@ -170,7 +170,7 @@ FindSystemInfo(_In_ MSIHANDLE hInstall)
         goto cleanup_CoInitialize;
     }
 
-    // The Windows version is usually spoofed, check using RtlGetVersion().
+    /* The Windows version is usually spoofed, check using RtlGetVersion(). */
     TCHAR szDllPath[0x1000];
     ExpandEnvironmentStrings(TEXT("%SystemRoot%\\System32\\ntdll.dll"), szDllPath,
 #ifdef UNICODE
@@ -193,7 +193,7 @@ FindSystemInfo(_In_ MSIHANDLE hInstall)
                     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().
+                    /* 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;
@@ -204,7 +204,7 @@ FindSystemInfo(_In_ MSIHANDLE hInstall)
         FreeLibrary(hNtDllModule);
     }
 
-    // We don't trust RtlGetVersion() either. Check the version resource of kernel32.dll.
+    /* We don't trust RtlGetVersion() either. Check the version resource of kernel32.dll. */
     ExpandEnvironmentStrings(TEXT("%SystemRoot%\\System32\\kernel32.dll"), szDllPath,
 #ifdef UNICODE
         _countof(szDllPath)
@@ -220,10 +220,10 @@ FindSystemInfo(_In_ MSIHANDLE hInstall)
         LPVOID pVersionInfo = malloc(dwVerInfoSize);
         if (pVersionInfo)
         {
-            // Read version info.
+            /* Read version info. */
             if (GetFileVersionInfo(szDllPath, dwHandle, dwVerInfoSize, pVersionInfo))
             {
-                // Get the value for the root block.
+                /* Get the value for the root block. */
                 UINT uiSize = 0;
                 VS_FIXEDFILEINFO *pVSFixedFileInfo = NULL;
                 if (VerQueryValue(pVersionInfo, TEXT("\\"), &pVSFixedFileInfo, &uiSize) && uiSize && pVSFixedFileInfo)
@@ -231,7 +231,7 @@ FindSystemInfo(_In_ MSIHANDLE hInstall)
                         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.
+                        /* 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);
Signature · 1 line
2.19.1.windows.1
#3 lore raw

PATCH 3/3 Make MSI custom action debug pop-up more informative

In reply to Simon Rozman

Each MSI custom action pops-up a message box in the _DEBUG version before commencing execution. This opens a time window for developer to attach debugger to the msiexec.exe process, set the breakpoints before custom action proceeds with execution.

While those pop-up dialogs are targeted to a limited audience, they were very sparse. With this patch, they become more informative and they also provide PID of the msiexec.exe process to attach debugger to.

---
 src/openvpnmsica/openvpnmsica.c | 60 ++++++++++++++++++++++++++-------
 1 file changed, 48 insertions(+), 12 deletions(-)
src/openvpnmsica/openvpnmsica.c +48 -12
index 99b47bf0..a2819e62 100644
@@ -144,6 +144,50 @@ openvpnmsica_setup_sequence_filename(
 }
 
 
+#ifdef _DEBUG
+
+/**
+ * Pops up a message box creating a time window to attach a debugger to the installer process in
+ * order to debug custom actions.
+ *
+ * @param szFunctionName  Function name that triggered the pop-up. Displayed in message box's
+ *                        title.
+ */
+static void
+_openvpnmsica_debug_popup(_In_z_ LPCTSTR szFunctionName)
+{
+    TCHAR szTitle[0x100], szMessage[0x100+MAX_PATH], szProcessPath[MAX_PATH];
+
+    /* Compose pop-up title. The dialog title will contain function name to ease the process
+       locating. Mind that Visual Studio displays window titles on the process list. */
+    _stprintf_s(szTitle, _countof(szTitle), TEXT("%s v%s"), szFunctionName, TEXT(PACKAGE_VERSION));
+
+    /* Get process name. */
+    GetModuleFileName(NULL, szProcessPath, _countof(szProcessPath));
+    LPCTSTR szProcessName = _tcsrchr(szProcessPath, TEXT('\\'));
+    szProcessName = szProcessName ? szProcessName + 1 : szProcessPath;
+
+    /* Compose the pop-up message. */
+    _stprintf_s(
+        szMessage, _countof(szMessage),
+        TEXT("The %s process (PID: %u) has started to execute the %s custom action.\r\n")
+        TEXT("\r\n")
+        TEXT("If you would like to debug the custom action, attach a debugger to this process and set breakpoints before dismissing this dialog.\r\n")
+        TEXT("\r\n")
+        TEXT("If you are not debugging this custom action, you can safely ignore this message."),
+        szProcessName,
+        GetCurrentProcessId(),
+        szFunctionName);
+
+    MessageBox(NULL, szMessage, szTitle, MB_OK);
+}
+
+#define openvpnmsica_debug_popup(f) _openvpnmsica_debug_popup(f)
+#else
+#define openvpnmsica_debug_popup(f)
+#endif
+
+
 UINT __stdcall
 FindSystemInfo(_In_ MSIHANDLE hInstall)
 {
@@ -151,9 +195,7 @@ FindSystemInfo(_In_ MSIHANDLE hInstall)
 #pragma comment(linker, DLLEXP_EXPORT)
 #endif
 
-#ifdef _DEBUG
-    MessageBox(NULL, TEXT("Attach debugger!"), TEXT(__FUNCTION__) TEXT(" v")  TEXT(PACKAGE_VERSION), MB_OK);
-#endif
+    openvpnmsica_debug_popup(TEXT(__FUNCTION__));
 
     UINT uiResult;
     BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
@@ -265,9 +307,7 @@ FindTAPInterfaces(_In_ MSIHANDLE hInstall)
 #pragma comment(linker, DLLEXP_EXPORT)
 #endif
 
-#ifdef _DEBUG
-    MessageBox(NULL, TEXT("Attach debugger!"), TEXT(__FUNCTION__) TEXT(" v")  TEXT(PACKAGE_VERSION), MB_OK);
-#endif
+    openvpnmsica_debug_popup(TEXT(__FUNCTION__));
 
     UINT uiResult;
     BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
@@ -371,9 +411,7 @@ EvaluateTAPInterfaces(_In_ MSIHANDLE hInstall)
 #pragma comment(linker, DLLEXP_EXPORT)
 #endif
 
-#ifdef _DEBUG
-    MessageBox(NULL, TEXT("Attach debugger!"), TEXT(__FUNCTION__) TEXT(" v")  TEXT(PACKAGE_VERSION), MB_OK);
-#endif
+    openvpnmsica_debug_popup(TEXT(__FUNCTION__));
 
     UINT uiResult;
     BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
@@ -633,9 +671,7 @@ ProcessDeferredAction(_In_ MSIHANDLE hInstall)
 #pragma comment(linker, DLLEXP_EXPORT)
 #endif
 
-#ifdef _DEBUG
-    MessageBox(NULL, TEXT("Attach debugger!"), TEXT(__FUNCTION__) TEXT(" v")  TEXT(PACKAGE_VERSION), MB_OK);
-#endif
+    openvpnmsica_debug_popup(TEXT(__FUNCTION__));
 
     UINT uiResult;
     BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
Signature · 1 line
2.19.1.windows.1
Acked-by on v1 2/3
#2 lore raw

[Openvpn-devel] [PATCH applied] Re: Change C++ to C comments

In reply to Simon Rozman

"Now that was an easy one" :-)

Your patch has been applied to the master branch.

commit 49cb9f9469e993abc13d317bdcd671822b47fab6

Author: Simon Rozman
Date:   Mon Nov 12 13:22:45 2018 +0100
     Change C++ to C comments

     Acked-by: Gert Doering <[email protected]...>
     Message-Id: <[email protected]...>
     URL: https://www.mail-archive.com/[email protected]/msg17908.html
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering

Acked-by on v1 3/3
#4 lore raw

[Openvpn-devel] [PATCH applied] Re: Make MSI custom action debug pop-up more informative

In reply to Simon Rozman

"Makes sense" :-) - I have only looked at the code and built a non-debug build (because I'm lazy) but that looked reasonable, and there is nothing in the _DEBUG block that looks suspicious.

Your patch has been applied to the master branch.

commit 27556854a512a901dba5118ad18b37f3abf0d440

Author: Simon Rozman
Date:   Mon Nov 12 13:22:46 2018 +0100
     Make MSI custom action debug pop-up more informative

     Acked-by: Gert Doering <[email protected]...>
     Message-Id: <[email protected]...>
     URL: https://www.mail-archive.com/[email protected]/msg17907.html
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering

Acked-by on v1 1/3
#5 lore raw
In reply to Simon Rozman

The explanation of "what could possibly go wrong" sounds logical, and the code change seems to match that (... and has no obvious pitfalls).

Test compiled on ubuntu 16.04 / mingw.

I have not tested this (because I have no idea how to trigger all these scenarios) so I trust you and Tincantech to have tested the variants (automated regression testing of this stuff is going to be interesting).

The comment lines are a bit on the long side ("<80 characters, please") but I've seen an upcoming uncrustify patch, so I hope that will be taken care of.

Your patch has been applied to the master branch.

commit 39c9811e7d3a509405b2986e9684a99054f25923

Author: Simon Rozman
Date:   Mon Nov 12 13:22:44 2018 +0100
     Delete TAP interface before the TAP driver is uninstalled

     Acked-by: Gert Doering <[email protected]...>
     Message-Id: <[email protected]...>
     URL: https://www.mail-archive.com/[email protected]/msg17906.html
     Signed-off-by: Gert Doering <[email protected]...>

--
kind regards,

Gert Doering