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