OpenVPN
openvpnmsica.c
Go to the documentation of this file.
1 /*
2  * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages
3  * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA
4  *
5  * Copyright (C) 2018 Simon Rozman <simon@rozman.si>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2
9  * as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #elif defined(_MSC_VER)
24 #include <config-msvc.h>
25 #endif
26 #include <winsock2.h> /* Must be included _before_ <windows.h> */
27 
28 #include "openvpnmsica.h"
29 #include "msica_op.h"
30 #include "msiex.h"
31 
32 #include "../tapctl/basic.h"
33 #include "../tapctl/error.h"
34 #include "../tapctl/tap.h"
35 
36 #include <windows.h>
37 #include <iphlpapi.h>
38 #include <malloc.h>
39 #include <memory.h>
40 #include <msiquery.h>
41 #include <shellapi.h>
42 #include <shlwapi.h>
43 #include <stdbool.h>
44 #include <stdlib.h>
45 #include <tchar.h>
46 
47 #ifdef _MSC_VER
48 #pragma comment(lib, "advapi32.lib")
49 #pragma comment(lib, "iphlpapi.lib")
50 #pragma comment(lib, "shell32.lib")
51 #pragma comment(lib, "shlwapi.lib")
52 #pragma comment(lib, "version.lib")
53 #endif
54 
55 
60 #define MSICA_INTERFACE_TICK_SIZE (16*1024)
66 static const struct {
67  LPCTSTR szName;
68  TCHAR szSuffix[3];
70 {
71  { TEXT("Commit" ), TEXT("cm") }, /* MSICA_CLEANUP_ACTION_COMMIT */
72  { TEXT("Rollback"), TEXT("rb") }, /* MSICA_CLEANUP_ACTION_ROLLBACK */
73 };
74 
75 
89 static DWORD
91  _In_ MSIHANDLE hInstall,
92  _In_z_ LPCTSTR szProperty,
93  _Out_z_cap_(MAXPATH + 1) LPTSTR szFilename)
94 {
95  DWORD dwResult;
96 
97  if (szFilename == NULL)
98  {
99  return ERROR_BAD_ARGUMENTS;
100  }
101 
102  /* Generate a random filename in the temporary folder. */
103  if (GetTempPath(MAX_PATH + 1, szFilename) == 0)
104  {
105  dwResult = GetLastError();
106  msg(M_NONFATAL | M_ERRNO, "%s: GetTempPath failed", __FUNCTION__);
107  return dwResult;
108  }
109  if (GetTempFileName(szFilename, szProperty, 0, szFilename) == 0)
110  {
111  dwResult = GetLastError();
112  msg(M_NONFATAL | M_ERRNO, "%s: GetTempFileName failed", __FUNCTION__);
113  return dwResult;
114  }
115 
116  /* Store sequence filename to property for deferred custom action. */
117  dwResult = MsiSetProperty(hInstall, szProperty, szFilename);
118  if (dwResult != ERROR_SUCCESS)
119  {
120  SetLastError(dwResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
121  msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szProperty);
122  return dwResult;
123  }
124 
125  /* Generate and store cleanup operation sequence filenames to properties. */
126  LPTSTR szExtension = PathFindExtension(szFilename);
127  TCHAR szFilenameEx[MAX_PATH + 1 /*dash*/ + 2 /*suffix*/ + 1 /*terminator*/];
128  size_t len_property_name = _tcslen(szProperty);
129  for (size_t i = 0; i < MSICA_CLEANUP_ACTION_COUNT; i++)
130  {
131  size_t len_action_name_z = _tcslen(openvpnmsica_cleanup_action_seqs[i].szName) + 1;
132  TCHAR *szPropertyEx = (TCHAR *)malloc((len_property_name + len_action_name_z) * sizeof(TCHAR));
133  if (szPropertyEx == NULL)
134  {
135  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, (len_property_name + len_action_name_z) * sizeof(TCHAR));
136  return ERROR_OUTOFMEMORY;
137  }
138 
139  memcpy(szPropertyEx, szProperty, len_property_name * sizeof(TCHAR));
140  memcpy(szPropertyEx + len_property_name, openvpnmsica_cleanup_action_seqs[i].szName, len_action_name_z * sizeof(TCHAR));
141  _stprintf_s(
142  szFilenameEx, _countof(szFilenameEx),
143  TEXT("%.*s-%.2s%s"),
144  (int)(szExtension - szFilename), szFilename,
146  szExtension);
147  dwResult = MsiSetProperty(hInstall, szPropertyEx, szFilenameEx);
148  if (dwResult != ERROR_SUCCESS)
149  {
150  SetLastError(dwResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
151  msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szPropertyEx);
152  free(szPropertyEx);
153  return dwResult;
154  }
155  free(szPropertyEx);
156  }
157 
158  return ERROR_SUCCESS;
159 }
160 
161 
162 #ifdef _DEBUG
163 
171 static void
172 _openvpnmsica_debug_popup(_In_z_ LPCTSTR szFunctionName)
173 {
174  TCHAR szTitle[0x100], szMessage[0x100+MAX_PATH], szProcessPath[MAX_PATH];
175 
176  /* Compose pop-up title. The dialog title will contain function name to ease the process
177  * locating. Mind that Visual Studio displays window titles on the process list. */
178  _stprintf_s(szTitle, _countof(szTitle), TEXT("%s v%s"), szFunctionName, TEXT(PACKAGE_VERSION));
179 
180  /* Get process name. */
181  GetModuleFileName(NULL, szProcessPath, _countof(szProcessPath));
182  LPCTSTR szProcessName = _tcsrchr(szProcessPath, TEXT('\\'));
183  szProcessName = szProcessName ? szProcessName + 1 : szProcessPath;
184 
185  /* Compose the pop-up message. */
186  _stprintf_s(
187  szMessage, _countof(szMessage),
188  TEXT("The %s process (PID: %u) has started to execute the %s custom action.\r\n")
189  TEXT("\r\n")
190  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")
191  TEXT("\r\n")
192  TEXT("If you are not debugging this custom action, you can safely ignore this message."),
193  szProcessName,
194  GetCurrentProcessId(),
195  szFunctionName);
196 
197  MessageBox(NULL, szMessage, szTitle, MB_OK);
198 }
199 
200 #define openvpnmsica_debug_popup(f) _openvpnmsica_debug_popup(f)
201 #else /* ifdef _DEBUG */
202 #define openvpnmsica_debug_popup(f)
203 #endif /* ifdef _DEBUG */
204 
205 
215 static UINT
217 {
218  UINT uiResult;
219 
220  /* Get Windows version. */
221 #ifdef _MSC_VER
222 #pragma warning(push)
223 #pragma warning(disable: 4996) /* 'GetVersionExW': was declared deprecated. */
224 #endif
225  OSVERSIONINFOEX ver_info = { .dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX) };
226  if (!GetVersionEx((LPOSVERSIONINFO)&ver_info))
227  {
228  uiResult = GetLastError();
229  msg(M_NONFATAL | M_ERRNO, "%s: GetVersionEx() failed", __FUNCTION__);
230  return uiResult;
231  }
232 #ifdef _MSC_VER
233 #pragma warning(pop)
234 #endif
235 
236  /* The Windows version is usually spoofed, check using RtlGetVersion(). */
237  TCHAR szDllPath[0x1000];
238  ExpandEnvironmentStrings(TEXT("%SystemRoot%\\System32\\ntdll.dll"), szDllPath,
239 #ifdef UNICODE
240  _countof(szDllPath)
241 #else
242  _countof(szDllPath) - 1
243 #endif
244  );
245  HMODULE hNtDllModule = LoadLibrary(szDllPath);
246  if (hNtDllModule)
247  {
248  typedef NTSTATUS (WINAPI* fnRtlGetVersion)(PRTL_OSVERSIONINFOW);
249  fnRtlGetVersion RtlGetVersion = (fnRtlGetVersion)GetProcAddress(hNtDllModule, "RtlGetVersion");
250  if (RtlGetVersion)
251  {
252  RTL_OSVERSIONINFOW rtl_ver_info = { .dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW) };
253  if (RtlGetVersion(&rtl_ver_info) == 0)
254  {
255  if (
256  rtl_ver_info.dwMajorVersion > ver_info.dwMajorVersion
257  || rtl_ver_info.dwMajorVersion == ver_info.dwMajorVersion && rtl_ver_info.dwMinorVersion > ver_info.dwMinorVersion
258  || rtl_ver_info.dwMajorVersion == ver_info.dwMajorVersion && rtl_ver_info.dwMinorVersion == ver_info.dwMinorVersion && rtl_ver_info.dwBuildNumber > ver_info.dwBuildNumber)
259  {
260  /* We got RtlGetVersion() and it reported newer version than GetVersionEx(). */
261  ver_info.dwMajorVersion = rtl_ver_info.dwMajorVersion;
262  ver_info.dwMinorVersion = rtl_ver_info.dwMinorVersion;
263  ver_info.dwBuildNumber = rtl_ver_info.dwBuildNumber;
264  ver_info.dwPlatformId = rtl_ver_info.dwPlatformId;
265  }
266  }
267  }
268 
269  FreeLibrary(hNtDllModule);
270  }
271 
272  /* We don't trust RtlGetVersion() either. Check the version resource of kernel32.dll. */
273  ExpandEnvironmentStrings(TEXT("%SystemRoot%\\System32\\kernel32.dll"), szDllPath,
274 #ifdef UNICODE
275  _countof(szDllPath)
276 #else
277  _countof(szDllPath) - 1
278 #endif
279  );
280 
281  DWORD dwHandle;
282  DWORD dwVerInfoSize = GetFileVersionInfoSize(szDllPath, &dwHandle);
283  if (dwVerInfoSize)
284  {
285  LPVOID pVersionInfo = malloc(dwVerInfoSize);
286  if (pVersionInfo)
287  {
288  /* Read version info. */
289  if (GetFileVersionInfo(szDllPath, dwHandle, dwVerInfoSize, pVersionInfo))
290  {
291  /* Get the value for the root block. */
292  UINT uiSize = 0;
293  VS_FIXEDFILEINFO *pVSFixedFileInfo = NULL;
294  if (VerQueryValue(pVersionInfo, TEXT("\\"), &pVSFixedFileInfo, &uiSize) && uiSize && pVSFixedFileInfo)
295  {
296  if (HIWORD(pVSFixedFileInfo->dwProductVersionMS) > ver_info.dwMajorVersion
297  || HIWORD(pVSFixedFileInfo->dwProductVersionMS) == ver_info.dwMajorVersion && LOWORD(pVSFixedFileInfo->dwProductVersionMS) > ver_info.dwMinorVersion
298  || HIWORD(pVSFixedFileInfo->dwProductVersionMS) == ver_info.dwMajorVersion && LOWORD(pVSFixedFileInfo->dwProductVersionMS) == ver_info.dwMinorVersion && HIWORD(pVSFixedFileInfo->dwProductVersionLS) > ver_info.dwBuildNumber)
299  {
300  /* We got kernel32.dll version and it is newer. */
301  ver_info.dwMajorVersion = HIWORD(pVSFixedFileInfo->dwProductVersionMS);
302  ver_info.dwMinorVersion = LOWORD(pVSFixedFileInfo->dwProductVersionMS);
303  ver_info.dwBuildNumber = HIWORD(pVSFixedFileInfo->dwProductVersionLS);
304  }
305  }
306  }
307 
308  free(pVersionInfo);
309  }
310  else
311  {
312  msg(M_NONFATAL, "%s: malloc(%u) failed", __FUNCTION__, dwVerInfoSize);
313  }
314  }
315 
316  uiResult = MsiSetProperty(hInstall, TEXT("DRIVERCERTIFICATION"), ver_info.dwMajorVersion >= 10 ? ver_info.wProductType > VER_NT_WORKSTATION ? TEXT("whql") : TEXT("attsgn") : TEXT(""));
317  if (uiResult != ERROR_SUCCESS)
318  {
319  SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
320  msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"DRIVERCERTIFICATION\") failed", __FUNCTION__);
321  return uiResult;
322  }
323 
324  return ERROR_SUCCESS;
325 }
326 
327 
338 static UINT
340 {
341  UINT uiResult;
342 
343  /* Get Service Control Manager handle. */
344  SC_HANDLE hSCManager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
345  if (hSCManager == NULL)
346  {
347  uiResult = GetLastError();
348  msg(M_NONFATAL | M_ERRNO, "%s: OpenSCManager() failed", __FUNCTION__);
349  return uiResult;
350  }
351 
352  /* Get OpenVPNService service handle. */
353  SC_HANDLE hService = OpenService(hSCManager, TEXT("OpenVPNService"), SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG);
354  if (hService == NULL)
355  {
356  uiResult = GetLastError();
357  if (uiResult == ERROR_SERVICE_DOES_NOT_EXIST)
358  {
359  /* This is not actually an error. */
360  goto cleanup_OpenSCManager;
361  }
362  msg(M_NONFATAL | M_ERRNO, "%s: OpenService(\"OpenVPNService\") failed", __FUNCTION__);
363  goto cleanup_OpenSCManager;
364  }
365 
366  /* Query service status. */
367  SERVICE_STATUS_PROCESS ssp;
368  DWORD dwBufSize;
369  if (QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(ssp), &dwBufSize))
370  {
371  switch (ssp.dwCurrentState)
372  {
373  case SERVICE_START_PENDING:
374  case SERVICE_RUNNING:
375  case SERVICE_STOP_PENDING:
376  case SERVICE_PAUSE_PENDING:
377  case SERVICE_PAUSED:
378  case SERVICE_CONTINUE_PENDING:
379  {
380  /* Service is started (kind of). Set OPENVPNSERVICE property to service PID. */
381  TCHAR szPID[10 /*MAXDWORD in decimal*/ + 1 /*terminator*/];
382  _stprintf_s(
383  szPID, _countof(szPID),
384  TEXT("%u"),
385  ssp.dwProcessId);
386 
387  uiResult = MsiSetProperty(hInstall, TEXT("OPENVPNSERVICE"), szPID);
388  if (uiResult != ERROR_SUCCESS)
389  {
390  SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
391  msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"OPENVPNSERVICE\") failed", __FUNCTION__);
392  }
393 
394  /* We know user is using the service. Skip auto-start setting check. */
395  goto cleanup_OpenService;
396  }
397  break;
398  }
399  }
400  else
401  {
402  uiResult = GetLastError();
403  msg(M_NONFATAL | M_ERRNO, "%s: QueryServiceStatusEx(\"OpenVPNService\") failed", __FUNCTION__);
404  }
405 
406  /* Service is not started. Is it set to auto-start? */
407  /* MSDN describes the maximum buffer size for QueryServiceConfig() to be 8kB. */
408  /* This is small enough to fit on stack. */
409  BYTE _buffer_8k[8192];
410  LPQUERY_SERVICE_CONFIG pQsc = (LPQUERY_SERVICE_CONFIG)_buffer_8k;
411  dwBufSize = sizeof(_buffer_8k);
412  if (!QueryServiceConfig(hService, pQsc, dwBufSize, &dwBufSize))
413  {
414  uiResult = GetLastError();
415  msg(M_NONFATAL | M_ERRNO, "%s: QueryServiceStatusEx(\"QueryServiceConfig\") failed", __FUNCTION__);
416  goto cleanup_OpenService;
417  }
418 
419  if (pQsc->dwStartType <= SERVICE_AUTO_START)
420  {
421  /* Service is set to auto-start. Set OPENVPNSERVICE property to its path. */
422  uiResult = MsiSetProperty(hInstall, TEXT("OPENVPNSERVICE"), pQsc->lpBinaryPathName);
423  if (uiResult != ERROR_SUCCESS)
424  {
425  SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
426  msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"OPENVPNSERVICE\") failed", __FUNCTION__);
427  goto cleanup_OpenService;
428  }
429  }
430 
431  uiResult = ERROR_SUCCESS;
432 
433 cleanup_OpenService:
434  CloseServiceHandle(hService);
435 cleanup_OpenSCManager:
436  CloseServiceHandle(hSCManager);
437  return uiResult;
438 }
439 
440 
441 UINT __stdcall
442 FindSystemInfo(_In_ MSIHANDLE hInstall)
443 {
444 #ifdef _MSC_VER
445 #pragma comment(linker, DLLEXP_EXPORT)
446 #endif
447 
448  openvpnmsica_debug_popup(TEXT(__FUNCTION__));
449 
450  BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
451 
453 
456 
457  if (bIsCoInitialized)
458  {
459  CoUninitialize();
460  }
461  return ERROR_SUCCESS;
462 }
463 
464 
465 UINT __stdcall
466 FindTAPInterfaces(_In_ MSIHANDLE hInstall)
467 {
468 #ifdef _MSC_VER
469 #pragma comment(linker, DLLEXP_EXPORT)
470 #endif
471 
472  openvpnmsica_debug_popup(TEXT(__FUNCTION__));
473 
474  UINT uiResult;
475  BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
476 
478 
479  /* Get all TUN/TAP network interfaces. */
480  struct tap_interface_node *pInterfaceList = NULL;
481  uiResult = tap_list_interfaces(NULL, &pInterfaceList, FALSE);
482  if (uiResult != ERROR_SUCCESS)
483  {
484  goto cleanup_CoInitialize;
485  }
486 
487  /* Get IPv4/v6 info for all network interfaces. Actually, we're interested in link status only: up/down? */
488  PIP_ADAPTER_ADDRESSES pAdapterAdresses = NULL;
489  ULONG ulAdapterAdressesSize = 16*1024;
490  for (size_t iteration = 0; iteration < 2; iteration++)
491  {
492  pAdapterAdresses = (PIP_ADAPTER_ADDRESSES)malloc(ulAdapterAdressesSize);
493  if (pAdapterAdresses == NULL)
494  {
495  msg(M_NONFATAL, "%s: malloc(%u) failed", __FUNCTION__, ulAdapterAdressesSize);
496  uiResult = ERROR_OUTOFMEMORY; goto cleanup_tap_list_interfaces;
497  }
498 
499  ULONG ulResult = GetAdaptersAddresses(
500  AF_UNSPEC,
501  GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_INCLUDE_ALL_INTERFACES,
502  NULL,
503  pAdapterAdresses,
504  &ulAdapterAdressesSize);
505 
506  if (ulResult == ERROR_SUCCESS)
507  {
508  break;
509  }
510 
511  free(pAdapterAdresses);
512  if (ulResult != ERROR_BUFFER_OVERFLOW)
513  {
514  SetLastError(ulResult); /* MSDN does not mention GetAdaptersAddresses() to set GetLastError(). But we do have an error code. Set last error manually. */
515  msg(M_NONFATAL | M_ERRNO, "%s: GetAdaptersAddresses() failed", __FUNCTION__);
516  uiResult = ulResult; goto cleanup_tap_list_interfaces;
517  }
518  }
519 
520  if (pInterfaceList != NULL)
521  {
522  /* Count interfaces. */
523  size_t interface_count = 0;
524  for (struct tap_interface_node *pInterface = pInterfaceList; pInterface; pInterface = pInterface->pNext)
525  {
526  interface_count++;
527  }
528 
529  /* Prepare semicolon delimited list of TAP interface ID(s) and active TAP interface ID(s). */
530  LPTSTR
531  szTAPInterfaces = (LPTSTR)malloc(interface_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(TCHAR)),
532  szTAPInterfacesTail = szTAPInterfaces;
533  if (szTAPInterfaces == NULL)
534  {
535  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, interface_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(TCHAR));
536  uiResult = ERROR_OUTOFMEMORY; goto cleanup_pAdapterAdresses;
537  }
538 
539  LPTSTR
540  szTAPInterfacesActive = (LPTSTR)malloc(interface_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(TCHAR)),
541  szTAPInterfacesActiveTail = szTAPInterfacesActive;
542  if (szTAPInterfacesActive == NULL)
543  {
544  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, interface_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(TCHAR));
545  uiResult = ERROR_OUTOFMEMORY; goto cleanup_szTAPInterfaces;
546  }
547 
548  for (struct tap_interface_node *pInterface = pInterfaceList; pInterface; pInterface = pInterface->pNext)
549  {
550  /* Convert interface GUID to UTF-16 string. (LPOLESTR defaults to LPWSTR) */
551  LPOLESTR szInterfaceId = NULL;
552  StringFromIID((REFIID)&pInterface->guid, &szInterfaceId);
553 
554  /* Append to the list of TAP interface ID(s). */
555  if (szTAPInterfaces < szTAPInterfacesTail)
556  {
557  *(szTAPInterfacesTail++) = TEXT(';');
558  }
559  memcpy(szTAPInterfacesTail, szInterfaceId, 38 * sizeof(TCHAR));
560  szTAPInterfacesTail += 38;
561 
562  /* If this interface is active (connected), add it to the list of active TAP interface ID(s). */
563  for (PIP_ADAPTER_ADDRESSES p = pAdapterAdresses; p; p = p->Next)
564  {
565  OLECHAR szId[38 /*GUID*/ + 1 /*terminator*/];
566  GUID guid;
567  if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, p->AdapterName, -1, szId, _countof(szId)) > 0
568  && SUCCEEDED(IIDFromString(szId, &guid))
569  && memcmp(&guid, &pInterface->guid, sizeof(GUID)) == 0)
570  {
571  if (p->OperStatus == IfOperStatusUp)
572  {
573  /* This TAP interface is active (connected). */
574  if (szTAPInterfacesActive < szTAPInterfacesActiveTail)
575  {
576  *(szTAPInterfacesActiveTail++) = TEXT(';');
577  }
578  memcpy(szTAPInterfacesActiveTail, szInterfaceId, 38 * sizeof(TCHAR));
579  szTAPInterfacesActiveTail += 38;
580  }
581  break;
582  }
583  }
584  CoTaskMemFree(szInterfaceId);
585  }
586  szTAPInterfacesTail [0] = 0;
587  szTAPInterfacesActiveTail[0] = 0;
588 
589  /* Set Installer TAPINTERFACES property. */
590  uiResult = MsiSetProperty(hInstall, TEXT("TAPINTERFACES"), szTAPInterfaces);
591  if (uiResult != ERROR_SUCCESS)
592  {
593  SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
594  msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"TAPINTERFACES\") failed", __FUNCTION__);
595  goto cleanup_szTAPInterfacesActive;
596  }
597 
598  /* Set Installer ACTIVETAPINTERFACES property. */
599  uiResult = MsiSetProperty(hInstall, TEXT("ACTIVETAPINTERFACES"), szTAPInterfacesActive);
600  if (uiResult != ERROR_SUCCESS)
601  {
602  SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
603  msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"ACTIVETAPINTERFACES\") failed", __FUNCTION__);
604  goto cleanup_szTAPInterfacesActive;
605  }
606 
607 cleanup_szTAPInterfacesActive:
608  free(szTAPInterfacesActive);
609 cleanup_szTAPInterfaces:
610  free(szTAPInterfaces);
611  }
612  else
613  {
614  uiResult = ERROR_SUCCESS;
615  }
616 
617 cleanup_pAdapterAdresses:
618  free(pAdapterAdresses);
619 cleanup_tap_list_interfaces:
620  tap_free_interface_list(pInterfaceList);
621 cleanup_CoInitialize:
622  if (bIsCoInitialized)
623  {
624  CoUninitialize();
625  }
626  return uiResult;
627 }
628 
629 
630 UINT __stdcall
631 CloseOpenVPNGUI(_In_ MSIHANDLE hInstall)
632 {
633 #ifdef _MSC_VER
634 #pragma comment(linker, DLLEXP_EXPORT)
635 #endif
636  UNREFERENCED_PARAMETER(hInstall); /* This CA is does not interact with MSI session (report errors, access properties, tables, etc.). */
637 
638  openvpnmsica_debug_popup(TEXT(__FUNCTION__));
639 
640  /* Find OpenVPN GUI window. */
641  HWND hWnd = FindWindow(TEXT("OpenVPN-GUI"), NULL);
642  if (hWnd)
643  {
644  /* Ask it to close and wait for 100ms. Unfortunately, this will succeed only for recent OpenVPN GUI that do not run elevated. */
645  SendMessage(hWnd, WM_CLOSE, 0, 0);
646  Sleep(100);
647  }
648 
649  return ERROR_SUCCESS;
650 }
651 
652 
653 UINT __stdcall
654 StartOpenVPNGUI(_In_ MSIHANDLE hInstall)
655 {
656 #ifdef _MSC_VER
657 #pragma comment(linker, DLLEXP_EXPORT)
658 #endif
659 
660  openvpnmsica_debug_popup(TEXT(__FUNCTION__));
661 
662  UINT uiResult;
663  BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
664 
666 
667  /* Create and populate a MSI record. */
668  MSIHANDLE hRecord = MsiCreateRecord(1);
669  if (!hRecord)
670  {
671  uiResult = ERROR_INVALID_HANDLE;
672  msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__);
673  goto cleanup_CoInitialize;
674  }
675  uiResult = MsiRecordSetString(hRecord, 0, TEXT("\"[#bin.openvpn_gui.exe]\""));
676  if (uiResult != ERROR_SUCCESS)
677  {
678  SetLastError(uiResult); /* MSDN does not mention MsiRecordSetString() to set GetLastError(). But we do have an error code. Set last error manually. */
679  msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordSetString failed", __FUNCTION__);
680  goto cleanup_MsiCreateRecord;
681  }
682 
683  /* Format string. */
684  TCHAR szStackBuf[MAX_PATH];
685  DWORD dwPathSize = _countof(szStackBuf);
686  LPTSTR szPath = szStackBuf;
687  uiResult = MsiFormatRecord(hInstall, hRecord, szPath, &dwPathSize);
688  if (uiResult == ERROR_MORE_DATA)
689  {
690  /* Allocate buffer on heap (+1 for terminator), and retry. */
691  szPath = (LPTSTR)malloc((++dwPathSize) * sizeof(TCHAR));
692  if (szPath == NULL)
693  {
694  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwPathSize * sizeof(TCHAR));
695  uiResult = ERROR_OUTOFMEMORY; goto cleanup_MsiCreateRecord;
696  }
697 
698  uiResult = MsiFormatRecord(hInstall, hRecord, szPath, &dwPathSize);
699  }
700  if (uiResult != ERROR_SUCCESS)
701  {
702  SetLastError(uiResult); /* MSDN does not mention MsiFormatRecord() to set GetLastError(). But we do have an error code. Set last error manually. */
703  msg(M_NONFATAL | M_ERRNO, "%s: MsiFormatRecord failed", __FUNCTION__);
704  goto cleanup_malloc_szPath;
705  }
706 
707  /* Launch the OpenVPN GUI. */
708  SHELLEXECUTEINFO sei = {
709  .cbSize = sizeof(SHELLEXECUTEINFO),
710  .fMask = SEE_MASK_FLAG_NO_UI, /* Don't show error UI, we'll display it. */
711  .lpFile = szPath,
712  .nShow = SW_SHOWNORMAL
713  };
714  if (!ShellExecuteEx(&sei))
715  {
716  uiResult = GetLastError();
717  msg(M_NONFATAL | M_ERRNO, "%s: ShellExecuteEx(%s) failed", __FUNCTION__, szPath);
718  goto cleanup_malloc_szPath;
719  }
720 
721  uiResult = ERROR_SUCCESS;
722 
723 cleanup_malloc_szPath:
724  if (szPath != szStackBuf)
725  {
726  free(szPath);
727  }
728 cleanup_MsiCreateRecord:
729  MsiCloseHandle(hRecord);
730 cleanup_CoInitialize:
731  if (bIsCoInitialized)
732  {
733  CoUninitialize();
734  }
735  return uiResult;
736 }
737 
738 
739 UINT __stdcall
740 EvaluateTAPInterfaces(_In_ MSIHANDLE hInstall)
741 {
742 #ifdef _MSC_VER
743 #pragma comment(linker, DLLEXP_EXPORT)
744 #endif
745 
746  openvpnmsica_debug_popup(TEXT(__FUNCTION__));
747 
748  UINT uiResult;
749  BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
750 
752 
753  /* List of deferred custom actions EvaluateTAPInterfaces prepares operation sequence for. */
754  static const LPCTSTR szActionNames[] =
755  {
756  TEXT("InstallTAPInterfaces"),
757  TEXT("UninstallTAPInterfaces"),
758  };
759  struct msica_op_seq exec_seq[_countof(szActionNames)];
760  for (size_t i = 0; i < _countof(szActionNames); i++)
761  {
762  msica_op_seq_init(&exec_seq[i]);
763  }
764 
765  {
766  /* Check and store the rollback enabled state. */
767  TCHAR szValue[128];
768  DWORD dwLength = _countof(szValue);
769  bool enable_rollback = MsiGetProperty(hInstall, TEXT("RollbackDisabled"), szValue, &dwLength) == ERROR_SUCCESS ?
770  _ttoi(szValue) || _totlower(szValue[0]) == TEXT('y') ? false : true :
771  true;
772  for (size_t i = 0; i < _countof(szActionNames); i++)
773  {
775  &exec_seq[i],
778  0,
779  NULL,
780  enable_rollback));
781  }
782  }
783 
784  /* Open MSI database. */
785  MSIHANDLE hDatabase = MsiGetActiveDatabase(hInstall);
786  if (hDatabase == 0)
787  {
788  msg(M_NONFATAL, "%s: MsiGetActiveDatabase failed", __FUNCTION__);
789  uiResult = ERROR_INVALID_HANDLE; goto cleanup_exec_seq;
790  }
791 
792  /* Check if TAPInterface table exists. If it doesn't exist, there's nothing to do. */
793  switch (MsiDatabaseIsTablePersistent(hDatabase, TEXT("TAPInterface")))
794  {
795  case MSICONDITION_FALSE:
796  case MSICONDITION_TRUE: break;
797 
798  default:
799  uiResult = ERROR_SUCCESS;
800  goto cleanup_hDatabase;
801  }
802 
803  /* Prepare a query to get a list/view of interfaces. */
804  MSIHANDLE hViewST = 0;
805  LPCTSTR szQuery = TEXT("SELECT `Interface`,`DisplayName`,`Condition`,`Component_` FROM `TAPInterface`");
806  uiResult = MsiDatabaseOpenView(hDatabase, szQuery, &hViewST);
807  if (uiResult != ERROR_SUCCESS)
808  {
809  SetLastError(uiResult); /* MSDN does not mention MsiDatabaseOpenView() to set GetLastError(). But we do have an error code. Set last error manually. */
810  msg(M_NONFATAL | M_ERRNO, "%s: MsiDatabaseOpenView(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szQuery);
811  goto cleanup_hDatabase;
812  }
813 
814  /* Execute query! */
815  uiResult = MsiViewExecute(hViewST, 0);
816  if (uiResult != ERROR_SUCCESS)
817  {
818  SetLastError(uiResult); /* MSDN does not mention MsiViewExecute() to set GetLastError(). But we do have an error code. Set last error manually. */
819  msg(M_NONFATAL | M_ERRNO, "%s: MsiViewExecute(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szQuery);
820  goto cleanup_hViewST;
821  }
822 
823  /* Create a record to report progress with. */
824  MSIHANDLE hRecordProg = MsiCreateRecord(2);
825  if (!hRecordProg)
826  {
827  uiResult = ERROR_INVALID_HANDLE;
828  msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__);
829  goto cleanup_hViewST_close;
830  }
831 
832  for (;;)
833  {
834  /* Fetch one record from the view. */
835  MSIHANDLE hRecord = 0;
836  uiResult = MsiViewFetch(hViewST, &hRecord);
837  if (uiResult == ERROR_NO_MORE_ITEMS)
838  {
839  uiResult = ERROR_SUCCESS;
840  break;
841  }
842  else if (uiResult != ERROR_SUCCESS)
843  {
844  SetLastError(uiResult); /* MSDN does not mention MsiViewFetch() to set GetLastError(). But we do have an error code. Set last error manually. */
845  msg(M_NONFATAL | M_ERRNO, "%s: MsiViewFetch failed", __FUNCTION__);
846  goto cleanup_hRecordProg;
847  }
848 
849  INSTALLSTATE iInstalled, iAction;
850  {
851  /* Read interface component ID (`Component_` is field #4). */
852  LPTSTR szValue = NULL;
853  uiResult = msi_get_record_string(hRecord, 4, &szValue);
854  if (uiResult != ERROR_SUCCESS)
855  {
856  goto cleanup_hRecord;
857  }
858 
859  /* Get the component state. */
860  uiResult = MsiGetComponentState(hInstall, szValue, &iInstalled, &iAction);
861  if (uiResult != ERROR_SUCCESS)
862  {
863  SetLastError(uiResult); /* MSDN does not mention MsiGetComponentState() to set GetLastError(). But we do have an error code. Set last error manually. */
864  msg(M_NONFATAL | M_ERRNO, "%s: MsiGetComponentState(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szValue);
865  free(szValue);
866  goto cleanup_hRecord;
867  }
868  free(szValue);
869  }
870 
871  /* Get interface display name (`DisplayName` is field #2). */
872  LPTSTR szDisplayName = NULL;
873  uiResult = msi_format_field(hInstall, hRecord, 2, &szDisplayName);
874  if (uiResult != ERROR_SUCCESS)
875  {
876  goto cleanup_hRecord;
877  }
878 
879  if (iAction > INSTALLSTATE_BROKEN)
880  {
881  if (iAction >= INSTALLSTATE_LOCAL)
882  {
883  /* Read and evaluate interface condition (`Condition` is field #3). */
884  LPTSTR szValue = NULL;
885  uiResult = msi_get_record_string(hRecord, 3, &szValue);
886  if (uiResult != ERROR_SUCCESS)
887  {
888  goto cleanup_szDisplayName;
889  }
890 #ifdef __GNUC__
891 /*
892  * warning: enumeration value ‘MSICONDITION_TRUE’ not handled in switch
893  * warning: enumeration value ‘MSICONDITION_NONE’ not handled in switch
894  */
895 #pragma GCC diagnostic push
896 #pragma GCC diagnostic ignored "-Wswitch"
897 #endif
898  switch (MsiEvaluateCondition(hInstall, szValue))
899  {
900  case MSICONDITION_FALSE:
901  free(szValue);
902  goto cleanup_szDisplayName;
903 
904  case MSICONDITION_ERROR:
905  uiResult = ERROR_INVALID_FIELD;
906  msg(M_NONFATAL | M_ERRNO, "%s: MsiEvaluateCondition(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szValue);
907  free(szValue);
908  goto cleanup_szDisplayName;
909  }
910 #ifdef __GNUC__
911 #pragma GCC diagnostic pop
912 #endif
913  free(szValue);
914 
915  /* Component is or should be installed. Schedule interface creation. */
917  &exec_seq[0],
921  NULL,
922  szDisplayName));
923  }
924  else
925  {
926  /* Component is installed, but should be degraded to advertised/removed. Schedule interface deletition. */
928  &exec_seq[1],
932  NULL,
933  szDisplayName));
934  }
935 
936  /* The amount of tick space to add for each interface to progress indicator. */
937  MsiRecordSetInteger(hRecordProg, 1, 3 /* OP3 = Add ticks to the expected total number of progress of the progress bar */);
938  MsiRecordSetInteger(hRecordProg, 2, MSICA_INTERFACE_TICK_SIZE);
939  if (MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL)
940  {
941  uiResult = ERROR_INSTALL_USEREXIT;
942  goto cleanup_szDisplayName;
943  }
944  }
945 
946 cleanup_szDisplayName:
947  free(szDisplayName);
948 cleanup_hRecord:
949  MsiCloseHandle(hRecord);
950  if (uiResult != ERROR_SUCCESS)
951  {
952  goto cleanup_hRecordProg;
953  }
954  }
955 
956  /*
957  * Write sequence files.
958  * The InstallTAPInterfaces and UninstallTAPInterfaces are deferred custom actions, thus all this information
959  * will be unavailable to them. Therefore save all required operations and their info to sequence files.
960  */
961  TCHAR szSeqFilename[_countof(szActionNames)][MAX_PATH + 1];
962  for (size_t i = 0; i < _countof(szActionNames); i++)
963  {
964  szSeqFilename[i][0] = 0;
965  }
966  for (size_t i = 0; i < _countof(szActionNames); i++)
967  {
968  uiResult = openvpnmsica_setup_sequence_filename(hInstall, szActionNames[i], szSeqFilename[i]);
969  if (uiResult != ERROR_SUCCESS)
970  {
971  goto cleanup_szSeqFilename;
972  }
973  HANDLE hSeqFile = CreateFile(
974  szSeqFilename[i],
975  GENERIC_WRITE,
976  FILE_SHARE_READ,
977  NULL,
978  CREATE_ALWAYS,
979  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
980  NULL);
981  if (hSeqFile == INVALID_HANDLE_VALUE)
982  {
983  uiResult = GetLastError();
984  msg(M_NONFATAL | M_ERRNO, "%s: CreateFile(\"%.*" PRIsLPTSTR "\") failed", __FUNCTION__, _countof(szSeqFilename[i]), szSeqFilename[i]);
985  goto cleanup_szSeqFilename;
986  }
987  uiResult = msica_op_seq_save(&exec_seq[i], hSeqFile);
988  CloseHandle(hSeqFile);
989  if (uiResult != ERROR_SUCCESS)
990  {
991  goto cleanup_szSeqFilename;
992  }
993  }
994 
995  uiResult = ERROR_SUCCESS;
996 
997 cleanup_szSeqFilename:
998  if (uiResult != ERROR_SUCCESS)
999  {
1000  /* Clean-up sequence files. */
1001  for (size_t i = _countof(szActionNames); i--;)
1002  {
1003  if (szSeqFilename[i][0])
1004  {
1005  DeleteFile(szSeqFilename[i]);
1006  }
1007  }
1008  }
1009 cleanup_hRecordProg:
1010  MsiCloseHandle(hRecordProg);
1011 cleanup_hViewST_close:
1012  MsiViewClose(hViewST);
1013 cleanup_hViewST:
1014  MsiCloseHandle(hViewST);
1015 cleanup_hDatabase:
1016  MsiCloseHandle(hDatabase);
1017 cleanup_exec_seq:
1018  for (size_t i = 0; i < _countof(szActionNames); i++)
1019  {
1020  msica_op_seq_free(&exec_seq[i]);
1021  }
1022  if (bIsCoInitialized)
1023  {
1024  CoUninitialize();
1025  }
1026  return uiResult;
1027 }
1028 
1029 
1030 UINT __stdcall
1031 ProcessDeferredAction(_In_ MSIHANDLE hInstall)
1032 {
1033 #ifdef _MSC_VER
1034 #pragma comment(linker, DLLEXP_EXPORT)
1035 #endif
1036 
1037  openvpnmsica_debug_popup(TEXT(__FUNCTION__));
1038 
1039  UINT uiResult;
1040  BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
1041 
1043 
1044  BOOL bIsCleanup = MsiGetMode(hInstall, MSIRUNMODE_COMMIT) || MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK);
1045 
1046  /* Get sequence filename and open the file. */
1047  LPTSTR szSeqFilename = NULL;
1048  uiResult = msi_get_string(hInstall, TEXT("CustomActionData"), &szSeqFilename);
1049  if (uiResult != ERROR_SUCCESS)
1050  {
1051  goto cleanup_CoInitialize;
1052  }
1053  struct msica_op_seq seq = { .head = NULL, .tail = NULL };
1054  {
1055  HANDLE hSeqFile = CreateFile(
1056  szSeqFilename,
1057  GENERIC_READ,
1058  FILE_SHARE_READ,
1059  NULL,
1060  OPEN_EXISTING,
1061  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
1062  NULL);
1063  if (hSeqFile == INVALID_HANDLE_VALUE)
1064  {
1065  uiResult = GetLastError();
1066  if (uiResult == ERROR_FILE_NOT_FOUND && bIsCleanup)
1067  {
1068  /*
1069  * Sequence file not found and this is rollback/commit action. Either of the following scenarios are possible:
1070  * - The delayed action failed to save the rollback/commit sequence to file. The delayed action performed cleanup itself. No further operation is required.
1071  * - Somebody removed the rollback/commit file between delayed action and rollback/commit action. No further operation is possible.
1072  */
1073  uiResult = ERROR_SUCCESS;
1074  goto cleanup_szSeqFilename;
1075  }
1076  msg(M_NONFATAL | M_ERRNO, "%s: CreateFile(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szSeqFilename);
1077  goto cleanup_szSeqFilename;
1078  }
1079 
1080  /* Load sequence. */
1081  uiResult = msica_op_seq_load(&seq, hSeqFile);
1082  CloseHandle(hSeqFile);
1083  if (uiResult != ERROR_SUCCESS)
1084  {
1085  goto cleanup_seq;
1086  }
1087  }
1088 
1089  /* Prepare session context. */
1090  struct msica_session session;
1092  &session,
1093  hInstall,
1094  bIsCleanup, /* In case of commit/rollback, continue sequence on error, to do as much cleanup as possible. */
1095  false);
1096 
1097  /* Execute sequence. */
1098  uiResult = msica_op_seq_process(&seq, &session);
1099  if (!bIsCleanup)
1100  {
1101  /*
1102  * Save cleanup scripts of delayed action regardless of action's execution status.
1103  * Rollback action MUST be scheduled in InstallExecuteSequence before this action! Otherwise cleanup won't be performed in case this action execution failed.
1104  */
1105  DWORD dwResultEx; /* Don't overwrite uiResult. */
1106  LPCTSTR szExtension = PathFindExtension(szSeqFilename);
1107  TCHAR szFilenameEx[MAX_PATH + 1 /*dash*/ + 2 /*suffix*/ + 1 /*terminator*/];
1108  for (size_t i = 0; i < MSICA_CLEANUP_ACTION_COUNT; i++)
1109  {
1110  _stprintf_s(
1111  szFilenameEx, _countof(szFilenameEx),
1112  TEXT("%.*s-%.2s%s"),
1113  (int)(szExtension - szSeqFilename), szSeqFilename,
1115  szExtension);
1116 
1117  /* After commit, delete rollback file. After rollback, delete commit file. */
1119  &session.seq_cleanup[MSICA_CLEANUP_ACTION_COUNT - 1 - i],
1122  0,
1123  NULL,
1124  szFilenameEx));
1125  }
1126  for (size_t i = 0; i < MSICA_CLEANUP_ACTION_COUNT; i++)
1127  {
1128  _stprintf_s(
1129  szFilenameEx, _countof(szFilenameEx),
1130  TEXT("%.*s-%.2s%s"),
1131  (int)(szExtension - szSeqFilename), szSeqFilename,
1133  szExtension);
1134 
1135  /* Save the cleanup sequence file. */
1136  HANDLE hSeqFile = CreateFile(
1137  szFilenameEx,
1138  GENERIC_WRITE,
1139  FILE_SHARE_READ,
1140  NULL,
1141  CREATE_ALWAYS,
1142  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
1143  NULL);
1144  if (hSeqFile == INVALID_HANDLE_VALUE)
1145  {
1146  dwResultEx = GetLastError();
1147  msg(M_NONFATAL | M_ERRNO, "%s: CreateFile(\"%.*" PRIsLPTSTR "\") failed", __FUNCTION__, _countof(szFilenameEx), szFilenameEx);
1148  goto cleanup_session;
1149  }
1150  dwResultEx = msica_op_seq_save(&session.seq_cleanup[i], hSeqFile);
1151  CloseHandle(hSeqFile);
1152  if (dwResultEx != ERROR_SUCCESS)
1153  {
1154  goto cleanup_session;
1155  }
1156  }
1157 
1158 cleanup_session:
1159  if (dwResultEx != ERROR_SUCCESS)
1160  {
1161  /* The commit and/or rollback scripts were not written to file successfully. Perform the cleanup immediately. */
1162  struct msica_session session_cleanup;
1164  &session_cleanup,
1165  hInstall,
1166  true,
1167  false);
1168  msica_op_seq_process(&session.seq_cleanup[MSICA_CLEANUP_ACTION_ROLLBACK], &session_cleanup);
1169 
1170  szExtension = PathFindExtension(szSeqFilename);
1171  for (size_t i = 0; i < MSICA_CLEANUP_ACTION_COUNT; i++)
1172  {
1173  _stprintf_s(
1174  szFilenameEx, _countof(szFilenameEx),
1175  TEXT("%.*s-%.2s%s"),
1176  (int)(szExtension - szSeqFilename), szSeqFilename,
1178  szExtension);
1179  DeleteFile(szFilenameEx);
1180  }
1181  }
1182  }
1183  else
1184  {
1185  /* No cleanup after cleanup support. */
1186  uiResult = ERROR_SUCCESS;
1187  }
1188 
1189  for (size_t i = MSICA_CLEANUP_ACTION_COUNT; i--;)
1190  {
1191  msica_op_seq_free(&session.seq_cleanup[i]);
1192  }
1193  DeleteFile(szSeqFilename);
1194 cleanup_seq:
1195  msica_op_seq_free(&seq);
1196 cleanup_szSeqFilename:
1197  free(szSeqFilename);
1198 cleanup_CoInitialize:
1199  if (bIsCoInitialized)
1200  {
1201  CoUninitialize();
1202  }
1203  return uiResult;
1204 }
#define M_NONFATAL
Definition: error.h:95
DWORD msica_op_seq_load(_Inout_ struct msica_op_seq *seq, _In_ HANDLE hFile)
Loads the operation sequence from the file.
Definition: msica_op.c:362
TCHAR szSuffix[3]
Name of the cleanup action.
Definition: openvpnmsica.c:68
struct msica_op_seq seq_cleanup[MSICA_CLEANUP_ACTION_COUNT]
Is rollback enabled?
Definition: msica_op.h:387
UINT __stdcall CloseOpenVPNGUI(_In_ MSIHANDLE hInstall)
Find OpenVPN GUI window and send it a WM_CLOSE message.
Definition: openvpnmsica.c:631
UINT __stdcall ProcessDeferredAction(_In_ MSIHANDLE hInstall)
Perform scheduled deferred action.
UINT __stdcall StartOpenVPNGUI(_In_ MSIHANDLE hInstall)
Launches OpenVPN GUI.
Definition: openvpnmsica.c:654
UINT msi_get_record_string(_In_ MSIHANDLE hRecord, _In_ unsigned int iField, _Out_ LPTSTR *pszValue)
Gets MSI record string value.
Definition: msiex.c:98
struct msica_op * head
Definition: msica_op.h:76
LPCTSTR szName
Definition: openvpnmsica.c:67
DWORD msica_op_seq_process(_Inout_ const struct msica_op_seq *seq, _Inout_ struct msica_session *session)
Executes all operations in sequence.
Definition: msica_op.c:963
static const struct @12 openvpnmsica_cleanup_action_seqs[MSICA_CLEANUP_ACTION_COUNT]
Cleanup actions.
#define PRIsLPTSTR
Definition: basic.h:29
Execution session.
Definition: msica_op.h:382
Operation sequence.
Definition: msica_op.h:74
#define MSICA_CLEANUP_ACTION_ROLLBACK
Definition: msica_op.h:375
void openvpnmsica_session_init(_Inout_ struct msica_session *session, _In_ MSIHANDLE hInstall, _In_ bool continue_on_error, _In_ bool rollback_enabled)
Initializes execution session.
Definition: msica_op.c:946
static UINT openvpnmsica_set_driver_certification(_In_ MSIHANDLE hInstall)
Detects Windows version and sets DRIVERCERTIFICATION property to "", "whql", or "attsgn" accordingly...
Definition: openvpnmsica.c:216
#define false
Definition: simple.c:63
#define MSICA_CLEANUP_ACTION_COUNT
Definition: msica_op.h:376
#define openvpnmsica_debug_popup(f)
Definition: openvpnmsica.c:202
struct msica_op * msica_op_create_bool(_In_ enum msica_op_type type, _In_ int ticks, _In_opt_ struct msica_op *next, _In_ bool value)
Allocates and fills a new msica_op_bool operation.
Definition: msica_op.c:75
Create TAP/TUN interface | msica_op_string.
Definition: msica_op.h:52
#define malloc
Definition: cmocka.c:1795
#define MSICA_INTERFACE_TICK_SIZE
Local constants.
Definition: openvpnmsica.c:60
#define _In_
Definition: basic.h:34
struct tap_interface_node * pNext
Interface name.
Definition: tap.h:106
void msica_op_seq_init(_Inout_ struct msica_op_seq *seq)
Initializes operation sequence.
Definition: msica_op.c:54
DWORD msica_op_seq_save(_In_ const struct msica_op_seq *seq, _In_ HANDLE hFile)
Saves the operation sequence to the file.
Definition: msica_op.c:297
static DWORD openvpnmsica_setup_sequence_filename(_In_ MSIHANDLE hInstall, _In_z_ LPCTSTR szProperty, _Out_z_cap_(MAXPATH+1) LPTSTR szFilename)
Creates a new sequence file in the current user&#39;s temporary folder and sets MSI property to its absol...
Definition: openvpnmsica.c:90
DWORD tap_list_interfaces(_In_opt_ HWND hwndParent, _Out_ struct tap_interface_node **ppInterface, _In_ BOOL bAll)
Creates a list of available network interfaces.
Definition: tap.c:954
#define M_ERRNO
Definition: error.h:99
#define msg
Definition: error.h:173
void msica_op_seq_add_tail(_Inout_ struct msica_op_seq *seq, _Inout_ struct msica_op *operation)
Appends operation(s) to the end of the operation sequence.
Definition: msica_op.c:270
UINT msi_get_string(_In_ MSIHANDLE hInstall, _In_z_ LPCTSTR szName, _Out_ LPTSTR *pszValue)
Gets MSI property value.
Definition: msiex.c:40
UINT __stdcall FindSystemInfo(_In_ MSIHANDLE hInstall)
Determines Windows information:
Definition: openvpnmsica.c:442
Network interface list node.
Definition: tap.h:100
UINT __stdcall FindTAPInterfaces(_In_ MSIHANDLE hInstall)
Find existing TAP interfaces and set TAPINTERFACES property with semicolon delimited list of installe...
Definition: openvpnmsica.c:466
#define _Out_z_cap_(n)
Definition: basic.h:52
#define OPENVPNMSICA_SAVE_MSI_SESSION(hInstall)
Set MSI session handle in thread local storage.
Definition: openvpnmsica.h:54
Enable/disable rollback | msica_op_bool.
Definition: msica_op.h:51
struct msica_op * msica_op_create_string(_In_ enum msica_op_type type, _In_ int ticks, _In_opt_ struct msica_op *next, _In_z_ LPCTSTR value)
Allocates and fills a new msica_op_string operation.
Definition: msica_op.c:105
void tap_free_interface_list(_In_ struct tap_interface_node *pInterfaceList)
Frees a list of network interfaces.
Definition: tap.c:1163
#define _In_z_
Definition: basic.h:40
UINT __stdcall EvaluateTAPInterfaces(_In_ MSIHANDLE hInstall)
Evaluate the TAPInterface table of the MSI package database and prepare a list of TAP interfaces to i...
Definition: openvpnmsica.c:740
static UINT openvpnmsica_set_openvpnserv_state(_In_ MSIHANDLE hInstall)
Detects if the OpenVPNService service is in use (running or paused) and sets OPENVPNSERVICE to the se...
Definition: openvpnmsica.c:339
#define M_FATAL
Definition: error.h:94
Rename TAP/TUN interface | msica_op_guid_string.
Definition: msica_op.h:55
#define PACKAGE_VERSION
Definition: config.h:742
#define free
Definition: cmocka.c:1850
#define true
Definition: simple.c:62
void msica_op_seq_free(_Inout_ struct msica_op_seq *seq)
Frees operation sequence.
Definition: msica_op.c:62
UINT msi_format_field(_In_ MSIHANDLE hInstall, _In_ MSIHANDLE hRecord, _In_ unsigned int iField, _Out_ LPTSTR *pszValue)
Formats MSI record field.
Definition: msiex.c:214
#define MAXPATH