OpenVPN
msica_op.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 
26 #include "msica_op.h"
27 #include "../tapctl/error.h"
28 #include "../tapctl/tap.h"
29 
30 #include <windows.h>
31 #include <malloc.h>
32 #include <msiquery.h>
33 #include <objbase.h>
34 
35 #ifdef _MSC_VER
36 #pragma comment(lib, "msi.lib")
37 #pragma comment(lib, "ole32.lib")
38 #endif
39 
40 
45 {
47  int ticks;
48  DWORD size_data;
49 };
50 
51 
52 void
54 {
55  seq->head = NULL;
56  seq->tail = NULL;
57 }
58 
59 
60 void
62 {
63  while (seq->head)
64  {
65  struct msica_op *op = seq->head;
66  seq->head = seq->head->next;
67  free(op);
68  }
69  seq->tail = NULL;
70 }
71 
72 
73 struct msica_op *
75  _In_ enum msica_op_type type,
76  _In_ int ticks,
77  _In_opt_ struct msica_op *next,
78  _In_ bool value)
79 {
80  if (MSICA_OP_TYPE_DATA(type) != 0x1)
81  {
82  msg(M_NONFATAL, "%s: Operation data type not bool (%x)", __FUNCTION__, MSICA_OP_TYPE_DATA(type));
83  return NULL;
84  }
85 
86  /* Create and fill operation struct. */
87  struct msica_op_bool *op = (struct msica_op_bool *)malloc(sizeof(struct msica_op_bool));
88  op->base.type = type;
89  op->base.ticks = ticks;
90  op->base.next = next;
91  op->value = value;
92 
93  return &op->base;
94 }
95 
96 
97 struct msica_op *
99  _In_ enum msica_op_type type,
100  _In_ int ticks,
101  _In_opt_ struct msica_op *next,
102  _In_z_ LPCTSTR value)
103 {
104  if (MSICA_OP_TYPE_DATA(type) != 0x2)
105  {
106  msg(M_NONFATAL, "%s: Operation data type not string (%x)", __FUNCTION__, MSICA_OP_TYPE_DATA(type));
107  return NULL;
108  }
109 
110  /* Create and fill operation struct. */
111  size_t value_size = (_tcslen(value) + 1) * sizeof(TCHAR);
112  struct msica_op_string *op = (struct msica_op_string *)malloc(sizeof(struct msica_op_string) + value_size);
113  op->base.type = type;
114  op->base.ticks = ticks;
115  op->base.next = next;
116  memcpy(op->value, value, value_size);
117 
118  return &op->base;
119 }
120 
121 
122 struct msica_op *
124  _In_ enum msica_op_type type,
125  _In_ int ticks,
126  _In_opt_ struct msica_op *next,
127  _In_ va_list arglist)
128 {
129  if (MSICA_OP_TYPE_DATA(type) != 0x3)
130  {
131  msg(M_NONFATAL, "%s: Operation data type not multi-string (%x)", __FUNCTION__, MSICA_OP_TYPE_DATA(type));
132  return NULL;
133  }
134 
135  /* Calculate required space first. */
136  LPCTSTR str;
137  size_t value_size = 1;
138  for (va_list a = arglist; (str = va_arg(a, LPCTSTR)) != NULL; value_size += _tcslen(str) + 1)
139  {
140  }
141  value_size *= sizeof(TCHAR);
142 
143  /* Create and fill operation struct. */
144  struct msica_op_multistring *op = (struct msica_op_multistring *)malloc(sizeof(struct msica_op_multistring) + value_size);
145  op->base.type = type;
146  op->base.ticks = ticks;
147  op->base.next = next;
148  LPTSTR value = op->value;
149  for (va_list a = arglist; (str = va_arg(a, LPCTSTR)) != NULL;)
150  {
151  size_t size = _tcslen(str) + 1;
152  memcpy(value, str, size*sizeof(TCHAR));
153  value += size;
154  }
155  value[0] = 0;
156 
157  return &op->base;
158 }
159 
160 
161 struct msica_op *
163  _In_ enum msica_op_type type,
164  _In_ int ticks,
165  _In_opt_ struct msica_op *next,
166  _In_ const GUID *value)
167 {
168  if (MSICA_OP_TYPE_DATA(type) != 0x4)
169  {
170  msg(M_NONFATAL, "%s: Operation data type not GUID (%x)", __FUNCTION__, MSICA_OP_TYPE_DATA(type));
171  return NULL;
172  }
173 
174  /* Create and fill operation struct. */
175  struct msica_op_guid *op = (struct msica_op_guid *)malloc(sizeof(struct msica_op_guid));
176  op->base.type = type;
177  op->base.ticks = ticks;
178  op->base.next = next;
179  memcpy(&op->value, value, sizeof(GUID));
180 
181  return &op->base;
182 }
183 
184 
185 struct msica_op *
187  _In_ enum msica_op_type type,
188  _In_ int ticks,
189  _In_opt_ struct msica_op *next,
190  _In_ const GUID *value_guid,
191  _In_z_ LPCTSTR value_str)
192 {
193  if (MSICA_OP_TYPE_DATA(type) != 0x5)
194  {
195  msg(M_NONFATAL, "%s: Operation data type not GUID-string (%x)", __FUNCTION__, MSICA_OP_TYPE_DATA(type));
196  return NULL;
197  }
198 
199  /* Create and fill operation struct. */
200  size_t value_str_size = (_tcslen(value_str) + 1) * sizeof(TCHAR);
201  struct msica_op_guid_string *op = (struct msica_op_guid_string *)malloc(sizeof(struct msica_op_guid_string) + value_str_size);
202  op->base.type = type;
203  op->base.ticks = ticks;
204  op->base.next = next;
205  memcpy(&op->value_guid, value_guid, sizeof(GUID));
206  memcpy(op->value_str, value_str, value_str_size);
207 
208  return &op->base;
209 }
210 
211 
212 void
214  _Inout_ struct msica_op_seq *seq,
215  _Inout_ struct msica_op *operation)
216 {
217  /* Insert list in the head. */
218  struct msica_op *op;
219  for (op = operation; op->next; op = op->next)
220  {
221  }
222  op->next = seq->head;
223 
224  /* Update head (and tail). */
225  seq->head = operation;
226  if (seq->tail == NULL)
227  {
228  seq->tail = op;
229  }
230 }
231 
232 
233 void
235  _Inout_ struct msica_op_seq *seq,
236  _Inout_ struct msica_op *operation)
237 {
238  /* Append list to the tail. */
239  struct msica_op *op;
240  for (op = operation; op->next; op = op->next)
241  {
242  }
243  if (seq->tail)
244  {
245  seq->tail->next = operation;
246  }
247  else
248  {
249  seq->head = operation;
250  }
251  seq->tail = op;
252 }
253 
254 
255 DWORD
257  _In_ const struct msica_op_seq *seq,
258  _In_ HANDLE hFile)
259 {
260  DWORD dwWritten;
261  for (const struct msica_op *op = seq->head; op; op = op->next)
262  {
263  struct msica_op_hdr hdr;
264  hdr.type = op->type;
265  hdr.ticks = op->ticks;
266 
267  /* Calculate size of data. */
268  switch (MSICA_OP_TYPE_DATA(op->type))
269  {
270  case 0x1: /* msica_op_bool */
271  hdr.size_data = sizeof(struct msica_op_bool) - sizeof(struct msica_op);
272  break;
273 
274  case 0x2: /* msica_op_string */
275  hdr.size_data =
276  sizeof(struct msica_op_string) - sizeof(struct msica_op)
277  +(DWORD)(_tcslen(((struct msica_op_string *)op)->value) + 1) * sizeof(TCHAR);
278  break;
279 
280  case 0x3: /* msica_op_multistring */
281  {
282  LPCTSTR str;
283  for (str = ((struct msica_op_multistring *)op)->value; str[0]; str += _tcslen(str) + 1)
284  {
285  }
286  hdr.size_data =
287  sizeof(struct msica_op_multistring) - sizeof(struct msica_op)
288  +(DWORD)(str + 1 - ((struct msica_op_multistring *)op)->value) * sizeof(TCHAR);
289  break;
290  }
291 
292  case 0x4: /* msica_op_guid */
293  hdr.size_data = sizeof(struct msica_op_guid) - sizeof(struct msica_op);
294  break;
295 
296  case 0x5: /* msica_op_guid_string */
297  hdr.size_data =
298  sizeof(struct msica_op_guid_string) - sizeof(struct msica_op)
299  +(DWORD)(_tcslen(((struct msica_op_guid_string *)op)->value_str) + 1) * sizeof(TCHAR);
300  break;
301 
302  default:
303  msg(M_NONFATAL, "%s: Unknown operation data type (%x)", __FUNCTION__, MSICA_OP_TYPE_DATA(op->type));
304  return ERROR_BAD_ARGUMENTS;
305  }
306 
307  if (!WriteFile(hFile, &hdr, sizeof(struct msica_op_hdr), &dwWritten, NULL)
308  || !WriteFile(hFile, op + 1, hdr.size_data, &dwWritten, NULL))
309  {
310  DWORD dwResult = GetLastError();
311  msg(M_NONFATAL | M_ERRNO, "%s: WriteFile failed", __FUNCTION__);
312  return dwResult;
313  }
314  }
315 
316  return ERROR_SUCCESS;
317 }
318 
319 
320 DWORD
322  _Inout_ struct msica_op_seq *seq,
323  _In_ HANDLE hFile)
324 {
325  DWORD dwRead;
326 
327  seq->head = seq->tail = NULL;
328 
329  for (;;)
330  {
331  struct msica_op_hdr hdr;
332  if (!ReadFile(hFile, &hdr, sizeof(struct msica_op_hdr), &dwRead, NULL))
333  {
334  DWORD dwResult = GetLastError();
335  msg(M_NONFATAL | M_ERRNO, "%s: ReadFile failed", __FUNCTION__);
336  return dwResult;
337  }
338  else if (dwRead == 0)
339  {
340  /* EOF */
341  return ERROR_SUCCESS;
342  }
343  else if (dwRead < sizeof(struct msica_op_hdr))
344  {
345  msg(M_NONFATAL, "%s: Incomplete ReadFile", __FUNCTION__);
346  return ERROR_INVALID_DATA;
347  }
348  struct msica_op *op = (struct msica_op *)malloc(sizeof(struct msica_op) + hdr.size_data);
349  op->type = hdr.type;
350  op->ticks = hdr.ticks;
351  op->next = NULL;
352  if (!ReadFile(hFile, op + 1, hdr.size_data, &dwRead, NULL))
353  {
354  DWORD dwResult = GetLastError();
355  msg(M_NONFATAL | M_ERRNO, "%s: ReadFile failed", __FUNCTION__);
356  free(op);
357  return dwResult;
358  }
359  else if (dwRead < hdr.size_data)
360  {
361  msg(M_NONFATAL, "%s: Incomplete ReadFile", __FUNCTION__);
362  return ERROR_INVALID_DATA;
363  }
364  msica_op_seq_add_tail(seq, op);
365  }
366 }
367 
368 
369 static DWORD
371  _Inout_ const struct msica_op_string *op,
372  _Inout_ struct msica_session *session)
373 {
374  if (op == NULL || session == NULL)
375  {
376  return ERROR_BAD_ARGUMENTS;
377  }
378 
379  {
380  /* Report the name of the interface to installer. */
381  MSIHANDLE hRecord = MsiCreateRecord(3);
382  MsiRecordSetString(hRecord, 1, TEXT("Creating TAP interface"));
383  MsiRecordSetString(hRecord, 2, op->value);
384  int iResult = MsiProcessMessage(session->hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord);
385  MsiCloseHandle(hRecord);
386  if (iResult == IDCANCEL)
387  {
388  return ERROR_INSTALL_USEREXIT;
389  }
390  }
391 
392  /* Get available network interfaces. */
393  struct tap_interface_node *pInterfaceList = NULL;
394  DWORD dwResult = tap_list_interfaces(NULL, &pInterfaceList);
395  if (dwResult == ERROR_SUCCESS)
396  {
397  /* Does interface exist? */
398  for (struct tap_interface_node *pInterfaceOther = pInterfaceList;; pInterfaceOther = pInterfaceOther->pNext)
399  {
400  if (pInterfaceOther == NULL)
401  {
402  /* No interface with a same name found. Create one. */
403  BOOL bRebootRequired = FALSE;
404  GUID guidInterface;
405  dwResult = tap_create_interface(NULL, NULL, &bRebootRequired, &guidInterface);
406  if (dwResult == ERROR_SUCCESS)
407  {
408  /* Set interface name. */
409  dwResult = tap_set_interface_name(&guidInterface, op->value);
410  if (dwResult == ERROR_SUCCESS)
411  {
412  if (session->rollback_enabled)
413  {
414  /* Order rollback action to delete it. */
416  &session->seq_cleanup[MSICA_CLEANUP_ACTION_ROLLBACK],
419  0,
420  NULL,
421  &guidInterface));
422  }
423  }
424  else
425  {
426  tap_delete_interface(NULL, &guidInterface, &bRebootRequired);
427  }
428 
429  if (bRebootRequired)
430  {
431  MsiSetMode(session->hInstall, MSIRUNMODE_REBOOTATEND, TRUE);
432  }
433  }
434  break;
435  }
436  else if (_tcsicmp(op->value, pInterfaceOther->szName) == 0)
437  {
438  /* Interface with a same name found. */
439  for (LPCTSTR hwid = pInterfaceOther->szzHardwareIDs;; hwid += _tcslen(hwid) + 1)
440  {
441  if (hwid[0] == 0)
442  {
443  /* This is not a TAP interface. */
444  msg(M_NONFATAL, "%s: Interface with name \"%" PRIsLPTSTR "\" already exists", __FUNCTION__, pInterfaceOther->szName);
445  dwResult = ERROR_ALREADY_EXISTS;
446  break;
447  }
448  else if (
449  _tcsicmp(hwid, TEXT(TAP_WIN_COMPONENT_ID)) == 0
450  || _tcsicmp(hwid, TEXT("root\\") TEXT(TAP_WIN_COMPONENT_ID)) == 0)
451  {
452  /* This is a TAP interface. We already got what we wanted! */
453  dwResult = ERROR_SUCCESS;
454  break;
455  }
456  }
457  break;
458  }
459  }
460 
461  tap_free_interface_list(pInterfaceList);
462  }
463 
464  return dwResult;
465 }
466 
467 
468 static DWORD
470  _In_ struct tap_interface_node *pInterfaceList,
471  _In_ struct tap_interface_node *pInterface,
472  _Inout_ struct msica_session *session)
473 {
474  if (pInterfaceList == NULL || pInterface == NULL || session == NULL)
475  {
476  return ERROR_BAD_ARGUMENTS;
477  }
478 
479  DWORD dwResult;
480 
481  /* Delete the interface. */
482  BOOL bRebootRequired = FALSE;
483  dwResult = tap_delete_interface(NULL, &pInterface->guid, &bRebootRequired);
484  if (bRebootRequired)
485  {
486  MsiSetMode(session->hInstall, MSIRUNMODE_REBOOTATEND, TRUE);
487  }
488 
489  if (session->rollback_enabled)
490  {
491  /*
492  * Schedule rollback action to create the interface back. Though it won't be exactly the same interface again.
493  *
494  * The previous version of this function did:
495  * - Execution Pass: rename the interface to some temporary name
496  * - Commit/Rollback Pass: delete the interface / rename the interface back to original name
497  *
498  * However, the WiX Toolset's Diffx extension to install and remove drivers removed the TAP driver between the
499  * execution and commit passes. TAP driver removal makes all TAP interfaces unavailable and our CA couldn't find
500  * the interface to delete any more.
501  *
502  * While the system where OpenVPN was uninstalled didn't have any TAP interfaces any more as expected behaviour,
503  * the problem appears after reinstalling the OpenVPN. Some residue TAP interface registry keys remain on the
504  * system, causing the TAP interface to reappear as "Ethernet NN" interface next time the TAP driver is
505  * installed. This causes TAP interfaces to accumulate over cyclic install-uninstall-install...
506  *
507  * Therefore, it is better to remove the TAP interfaces before the TAP driver is removed, and reinstall the TAP
508  * interface back should the rollback be required. I wonder if the WiX Diffx extension supports execute/commit/
509  * rollback feature of MSI in the first place.
510  */
512  &session->seq_cleanup[MSICA_CLEANUP_ACTION_ROLLBACK],
515  0,
516  NULL,
517  pInterface->szName));
518  }
519 
520  return dwResult;
521 }
522 
523 
524 static DWORD
526  _Inout_ const struct msica_op_string *op,
527  _Inout_ struct msica_session *session)
528 {
529  if (op == NULL || session == NULL)
530  {
531  return ERROR_BAD_ARGUMENTS;
532  }
533 
534  {
535  /* Report the name of the interface to installer. */
536  MSIHANDLE hRecord = MsiCreateRecord(3);
537  MsiRecordSetString(hRecord, 1, TEXT("Deleting interface"));
538  MsiRecordSetString(hRecord, 2, op->value);
539  int iResult = MsiProcessMessage(session->hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord);
540  MsiCloseHandle(hRecord);
541  if (iResult == IDCANCEL)
542  {
543  return ERROR_INSTALL_USEREXIT;
544  }
545  }
546 
547  /* Get available network interfaces. */
548  struct tap_interface_node *pInterfaceList = NULL;
549  DWORD dwResult = tap_list_interfaces(NULL, &pInterfaceList);
550  if (dwResult == ERROR_SUCCESS)
551  {
552  /* Does interface exist? */
553  for (struct tap_interface_node *pInterface = pInterfaceList;; pInterface = pInterface->pNext)
554  {
555  if (pInterface == NULL)
556  {
557  /* Interface not found. We already got what we wanted! */
558  dwResult = ERROR_SUCCESS;
559  break;
560  }
561  else if (_tcsicmp(op->value, pInterface->szName) == 0)
562  {
563  /* Interface found. */
565  pInterfaceList,
566  pInterface,
567  session);
568  break;
569  }
570  }
571 
572  tap_free_interface_list(pInterfaceList);
573  }
574 
575  return dwResult;
576 }
577 
578 
579 static DWORD
581  _Inout_ const struct msica_op_guid *op,
582  _Inout_ struct msica_session *session)
583 {
584  if (op == NULL || session == NULL)
585  {
586  return ERROR_BAD_ARGUMENTS;
587  }
588 
589  {
590  /* Report the GUID of the interface to installer. */
591  MSIHANDLE hRecord = MsiCreateRecord(3);
592  LPOLESTR szInterfaceId = NULL;
593  StringFromIID((REFIID)&op->value, &szInterfaceId);
594  MsiRecordSetString(hRecord, 1, TEXT("Deleting interface"));
595  MsiRecordSetString(hRecord, 2, szInterfaceId);
596  int iResult = MsiProcessMessage(session->hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord);
597  CoTaskMemFree(szInterfaceId);
598  MsiCloseHandle(hRecord);
599  if (iResult == IDCANCEL)
600  {
601  return ERROR_INSTALL_USEREXIT;
602  }
603  }
604 
605  /* Get available network interfaces. */
606  struct tap_interface_node *pInterfaceList = NULL;
607  DWORD dwResult = tap_list_interfaces(NULL, &pInterfaceList);
608  if (dwResult == ERROR_SUCCESS)
609  {
610  /* Does interface exist? */
611  for (struct tap_interface_node *pInterface = pInterfaceList;; pInterface = pInterface->pNext)
612  {
613  if (pInterface == NULL)
614  {
615  /* Interface not found. We already got what we wanted! */
616  dwResult = ERROR_SUCCESS;
617  break;
618  }
619  else if (memcmp(&op->value, &pInterface->guid, sizeof(GUID)) == 0)
620  {
621  /* Interface found. */
623  pInterfaceList,
624  pInterface,
625  session);
626  break;
627  }
628  }
629 
630  tap_free_interface_list(pInterfaceList);
631  }
632 
633  return dwResult;
634 }
635 
636 
637 static DWORD
639  _Inout_ const struct msica_op_guid_string *op,
640  _Inout_ struct msica_session *session)
641 {
642  if (op == NULL || session == NULL)
643  {
644  return ERROR_BAD_ARGUMENTS;
645  }
646 
647  {
648  /* Report the GUID of the interface to installer. */
649  MSIHANDLE hRecord = MsiCreateRecord(3);
650  LPOLESTR szInterfaceId = NULL;
651  StringFromIID((REFIID)&op->value_guid, &szInterfaceId);
652  MsiRecordSetString(hRecord, 1, TEXT("Setting interface name"));
653  MsiRecordSetString(hRecord, 2, szInterfaceId);
654  MsiRecordSetString(hRecord, 3, op->value_str);
655  int iResult = MsiProcessMessage(session->hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord);
656  CoTaskMemFree(szInterfaceId);
657  MsiCloseHandle(hRecord);
658  if (iResult == IDCANCEL)
659  {
660  return ERROR_INSTALL_USEREXIT;
661  }
662  }
663 
664  /* Get available network interfaces. */
665  struct tap_interface_node *pInterfaceList = NULL;
666  DWORD dwResult = tap_list_interfaces(NULL, &pInterfaceList);
667  if (dwResult == ERROR_SUCCESS)
668  {
669  /* Does interface exist? */
670  for (struct tap_interface_node *pInterface = pInterfaceList;; pInterface = pInterface->pNext)
671  {
672  if (pInterface == NULL)
673  {
674  /* Interface not found. */
675  LPOLESTR szInterfaceId = NULL;
676  StringFromIID((REFIID)&op->value_guid, &szInterfaceId);
677  msg(M_NONFATAL, "%s: %" PRIsLPOLESTR " interface not found", __FUNCTION__, szInterfaceId);
678  CoTaskMemFree(szInterfaceId);
679  dwResult = ERROR_FILE_NOT_FOUND;
680  break;
681  }
682  else if (memcmp(&op->value_guid, &pInterface->guid, sizeof(GUID)) == 0)
683  {
684  /* Interface found. */
685  for (struct tap_interface_node *pInterfaceOther = pInterfaceList;; pInterfaceOther = pInterfaceOther->pNext)
686  {
687  if (pInterfaceOther == NULL)
688  {
689  /* No other interface with a same name found. All clear to rename the interface. */
690  dwResult = tap_set_interface_name(&pInterface->guid, op->value_str);
691  if (dwResult == ERROR_SUCCESS)
692  {
693  if (session->rollback_enabled)
694  {
695  /* Order rollback action to rename it back. */
697  &session->seq_cleanup[MSICA_CLEANUP_ACTION_ROLLBACK],
700  0,
701  NULL,
702  &pInterface->guid,
703  pInterface->szName));
704  }
705  }
706  break;
707  }
708  else if (_tcsicmp(op->value_str, pInterfaceOther->szName) == 0)
709  {
710  /* Interface with a same name found. Duplicate interface names are not allowed. */
711  msg(M_NONFATAL, "%s: Interface with name \"%" PRIsLPTSTR "\" already exists", __FUNCTION__, pInterfaceOther->szName);
712  dwResult = ERROR_ALREADY_EXISTS;
713  break;
714  }
715  }
716  break;
717  }
718  }
719 
720  tap_free_interface_list(pInterfaceList);
721  }
722 
723  return dwResult;
724 }
725 
726 
727 static DWORD
729  _Inout_ const struct msica_op_string *op,
730  _Inout_ struct msica_session *session)
731 {
732  if (op == NULL || session == NULL)
733  {
734  return ERROR_BAD_ARGUMENTS;
735  }
736 
737  {
738  /* Report the name of the file to installer. */
739  MSIHANDLE hRecord = MsiCreateRecord(3);
740  MsiRecordSetString(hRecord, 1, TEXT("Deleting file"));
741  MsiRecordSetString(hRecord, 2, op->value);
742  int iResult = MsiProcessMessage(session->hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord);
743  MsiCloseHandle(hRecord);
744  if (iResult == IDCANCEL)
745  {
746  return ERROR_INSTALL_USEREXIT;
747  }
748  }
749 
750  DWORD dwResult;
751 
752  if (session->rollback_enabled)
753  {
754  size_t sizeNameBackupLenZ = _tcslen(op->value) + 7 /*" (orig "*/ + 10 /*maximum int*/ + 1 /*")"*/ + 1 /*terminator*/;
755  LPTSTR szNameBackup = (LPTSTR)malloc(sizeNameBackupLenZ * sizeof(TCHAR));
756  int count = 0;
757 
758  do
759  {
760  /* Rename the file to make a backup. */
761  _stprintf_s(
762  szNameBackup, sizeNameBackupLenZ,
763  TEXT("%s (orig %i)"),
764  op->value,
765  ++count);
766  dwResult = MoveFile(op->value, szNameBackup) ? ERROR_SUCCESS : GetLastError();
767  } while (dwResult == ERROR_ALREADY_EXISTS);
768 
769  if (dwResult == ERROR_SUCCESS)
770  {
771  /* Schedule rollback action to restore from backup. */
773  &session->seq_cleanup[MSICA_CLEANUP_ACTION_ROLLBACK],
776  0,
777  NULL,
778  szNameBackup,
779  op->value,
780  NULL));
781 
782  /* Schedule commit action to delete the backup. */
784  &session->seq_cleanup[MSICA_CLEANUP_ACTION_COMMIT],
787  0,
788  NULL,
789  szNameBackup));
790  }
791  else if (dwResult == ERROR_FILE_NOT_FOUND) /* File does not exist: We already got what we wanted! */
792  {
793  dwResult = ERROR_SUCCESS;
794  }
795  else
796  {
797  msg(M_NONFATAL | M_ERRNO, "%s: MoveFile(\"%" PRIsLPTSTR "\", \"%" PRIsLPTSTR "\") failed", __FUNCTION__, op->value, szNameBackup);
798  }
799 
800  free(szNameBackup);
801  }
802  else
803  {
804  /* Delete the file. */
805  dwResult = DeleteFile(op->value) ? ERROR_SUCCESS : GetLastError();
806  if (dwResult == ERROR_FILE_NOT_FOUND) /* File does not exist: We already got what we wanted! */
807  {
808  dwResult = ERROR_SUCCESS;
809  }
810  else if (dwResult != ERROR_SUCCESS)
811  {
812  msg(M_NONFATAL | M_ERRNO, "%s: DeleteFile(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, op->value);
813  }
814  }
815 
816  return dwResult;
817 }
818 
819 
820 static DWORD
822  _Inout_ const struct msica_op_multistring *op,
823  _Inout_ struct msica_session *session)
824 {
825  if (op == NULL || session == NULL)
826  {
827  return ERROR_BAD_ARGUMENTS;
828  }
829 
830  /* Get source filename. */
831  LPCTSTR szNameSrc = op->value;
832  if (szNameSrc[0] == 0)
833  {
834  return ERROR_BAD_ARGUMENTS;
835  }
836 
837  /* Get destination filename. */
838  LPCTSTR szNameDst = szNameSrc + _tcslen(szNameSrc) + 1;
839  if (szNameDst[0] == 0)
840  {
841  return ERROR_BAD_ARGUMENTS;
842  }
843 
844  {
845  /* Report the name of the files to installer. */
846  MSIHANDLE hRecord = MsiCreateRecord(3);
847  MsiRecordSetString(hRecord, 1, TEXT("Moving file"));
848  MsiRecordSetString(hRecord, 2, szNameSrc);
849  MsiRecordSetString(hRecord, 3, szNameDst);
850  int iResult = MsiProcessMessage(session->hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord);
851  MsiCloseHandle(hRecord);
852  if (iResult == IDCANCEL)
853  {
854  return ERROR_INSTALL_USEREXIT;
855  }
856  }
857 
858  DWORD dwResult = MoveFile(szNameSrc, szNameDst) ? ERROR_SUCCESS : GetLastError();
859  if (dwResult == ERROR_SUCCESS)
860  {
861  if (session->rollback_enabled)
862  {
863  /* Order rollback action to move it back. */
865  &session->seq_cleanup[MSICA_CLEANUP_ACTION_ROLLBACK],
868  0,
869  NULL,
870  szNameDst,
871  szNameSrc,
872  NULL));
873  }
874  }
875  else
876  {
877  msg(M_NONFATAL | M_ERRNO, "%s: MoveFile(\"%" PRIsLPTSTR "\", \"%" PRIsLPTSTR "\") failed", __FUNCTION__, szNameSrc, szNameDst);
878  }
879 
880  return dwResult;
881 }
882 
883 
884 void
886  _Inout_ struct msica_session *session,
887  _In_ MSIHANDLE hInstall,
888  _In_ bool continue_on_error,
889  _In_ bool rollback_enabled)
890 {
891  session->hInstall = hInstall;
892  session->continue_on_error = continue_on_error;
893  session->rollback_enabled = rollback_enabled;
894  for (size_t i = 0; i < MSICA_CLEANUP_ACTION_COUNT; i++)
895  {
896  msica_op_seq_init(&session->seq_cleanup[i]);
897  }
898 }
899 
900 
901 DWORD
903  _Inout_ const struct msica_op_seq *seq,
904  _Inout_ struct msica_session *session)
905 {
906  DWORD dwResult;
907 
908  if (seq == NULL || session == NULL)
909  {
910  return ERROR_BAD_ARGUMENTS;
911  }
912 
913  /* Tell the installer to use explicit progress messages. */
914  MSIHANDLE hRecordProg = MsiCreateRecord(3);
915  MsiRecordSetInteger(hRecordProg, 1, 1);
916  MsiRecordSetInteger(hRecordProg, 2, 1);
917  MsiRecordSetInteger(hRecordProg, 3, 0);
918  MsiProcessMessage(session->hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg);
919 
920  /* Prepare hRecordProg for progress messages. */
921  MsiRecordSetInteger(hRecordProg, 1, 2);
922  MsiRecordSetInteger(hRecordProg, 3, 0);
923 
924  for (const struct msica_op *op = seq->head; op; op = op->next)
925  {
926  switch (op->type)
927  {
929  session->rollback_enabled = ((const struct msica_op_bool *)op)->value;
930  dwResult = ERROR_SUCCESS;
931  break;
932 
934  dwResult = msica_op_tap_interface_create_exec((const struct msica_op_string *)op, session);
935  break;
936 
938  dwResult = msica_op_tap_interface_delete_by_name_exec((const struct msica_op_string *)op, session);
939  break;
940 
942  dwResult = msica_op_tap_interface_delete_by_guid_exec((const struct msica_op_guid *)op, session);
943  break;
944 
946  dwResult = msica_op_tap_interface_set_name_exec((const struct msica_op_guid_string *)op, session);
947  break;
948 
950  dwResult = msica_op_file_delete_exec((const struct msica_op_string *)op, session);
951  break;
952 
953  case msica_op_file_move:
954  dwResult = msica_op_file_move_exec((const struct msica_op_multistring *)op, session);
955  break;
956 
957  default:
958  msg(M_NONFATAL, "%s: Unknown operation type (%x)", __FUNCTION__, op->type);
959  dwResult = ERROR_FILE_NOT_FOUND;
960  }
961 
962  if (!session->continue_on_error && dwResult != ERROR_SUCCESS)
963  {
964  /* Operation failed. It should have sent error message to Installer. Therefore, just quit here. */
965  goto cleanup_hRecordProg;
966  }
967 
968  /* Report progress and check for user cancellation. */
969  MsiRecordSetInteger(hRecordProg, 2, op->ticks);
970  if (MsiProcessMessage(session->hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL)
971  {
972  dwResult = ERROR_INSTALL_USEREXIT;
973  goto cleanup_hRecordProg;
974  }
975  }
976 
977  dwResult = ERROR_SUCCESS;
978 
979 cleanup_hRecordProg:
980  MsiCloseHandle(hRecordProg);
981  return dwResult;
982 }
#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
struct msica_op base
Definition: msica_op.h:202
static DWORD msica_op_tap_interface_set_name_exec(_Inout_ const struct msica_op_guid_string *op, _Inout_ struct msica_session *session)
Definition: msica_op.c:638
static DWORD msica_op_tap_interface_delete(_In_ struct tap_interface_node *pInterfaceList, _In_ struct tap_interface_node *pInterface, _Inout_ struct msica_session *session)
Definition: msica_op.c:469
msica_op_type
Operation types.
Definition: msica_op.h:47
Operation data.
Definition: msica_op.h:62
Delete TAP/TUN interface | msica_op_string.
Definition: msica_op.h:52
TCHAR value[]
Common operation data.
Definition: msica_op.h:170
DWORD tap_delete_interface(_In_opt_ HWND hwndParent, _In_ LPCGUID pguidInterface, _Inout_ LPBOOL pbRebootRequired)
Deletes a TUN/TAP interface.
Definition: tap.c:709
DWORD tap_set_interface_name(_In_ LPCGUID pguidInterface, _In_ LPCTSTR szName)
Sets interface name.
Definition: tap.c:834
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
#define PRIsLPTSTR
Definition: basic.h:28
static DWORD msica_op_tap_interface_create_exec(_Inout_ const struct msica_op_string *op, _Inout_ struct msica_session *session)
Definition: msica_op.c:370
static DWORD msica_op_tap_interface_delete_by_guid_exec(_Inout_ const struct msica_op_guid *op, _Inout_ struct msica_session *session)
Definition: msica_op.c:580
Operation data (bool, 0x1)
Definition: msica_op.h:101
struct msica_op base
Definition: msica_op.h:169
Execution session.
Definition: msica_op.h:381
Operation sequence.
Definition: msica_op.h:73
TCHAR value_str[]
Operation data GUID.
Definition: msica_op.h:237
DWORD size_data
Number of ticks on the progress indicator this operation represents.
Definition: msica_op.c:48
enum msica_op_type type
Definition: msica_op.h:64
#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
GUID value
Common operation data.
Definition: msica_op.h:203
#define MSICA_CLEANUP_ACTION_COUNT
Definition: msica_op.h:375
Operation data (guid-string, 0x5)
Definition: msica_op.h:233
static DWORD msica_op_tap_interface_delete_by_name_exec(_Inout_ const struct msica_op_string *op, _Inout_ struct msica_session *session)
Definition: msica_op.c:525
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
int ticks
Action type.
Definition: msica_op.c:47
Create TAP/TUN interface | msica_op_string.
Definition: msica_op.h:51
Operation data (GUID, 0x4)
Definition: msica_op.h:200
TCHAR value[]
Common operation data.
Definition: msica_op.h:137
#define malloc
Definition: cmocka.c:1795
struct msica_op * msica_op_create_multistring_va(_In_ enum msica_op_type type, _In_ int ticks, _In_opt_ struct msica_op *next, _In_ va_list arglist)
Allocates and fills a new msica_op_multistring operation.
Definition: msica_op.c:123
#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
static DWORD msica_op_file_delete_exec(_Inout_ const struct msica_op_string *op, _Inout_ struct msica_session *session)
Definition: msica_op.c:728
#define PRIsLPOLESTR
Definition: basic.h:29
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
#define _Inout_
Definition: basic.h:42
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
Operation data (multi-string, 0x3)
Definition: msica_op.h:167
#define TAP_WIN_COMPONENT_ID
Definition: config.h:790
#define M_ERRNO
Definition: error.h:99
struct msica_op * msica_op_create_guid(_In_ enum msica_op_type type, _In_ int ticks, _In_opt_ struct msica_op *next, _In_ const GUID *value)
Allocates and fills a new msica_op_guid operation.
Definition: msica_op.c:162
#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
Operation data (string, 0x2)
Definition: msica_op.h:134
static struct msica_op * msica_op_create_multistring(_In_ enum msica_op_type type, _In_ int ticks, _In_opt_ struct msica_op *next,...)
Allocates and fills a new msica_op_multistring operation.
Definition: msica_op.h:282
DWORD tap_create_interface(_In_opt_ HWND hwndParent, _In_opt_ LPCTSTR szDeviceDescription, _Inout_ LPBOOL pbRebootRequired, _Out_ LPGUID pguidInterface)
Creates a TUN/TAP interface.
Definition: tap.c:415
Network interface list node.
Definition: tap.h:99
Delete TAP/TUN interface | msica_op_guid.
Definition: msica_op.h:53
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
Delete file | msica_op_string.
Definition: msica_op.h:55
struct msica_op base
Definition: msica_op.h:103
#define _In_opt_
Definition: basic.h:36
#define _In_z_
Definition: basic.h:39
struct msica_op * next
Number of ticks on the progress indicator this operation represents.
Definition: msica_op.h:66
Rename TAP/TUN interface | msica_op_guid_string.
Definition: msica_op.h:54
static DWORD msica_op_file_move_exec(_Inout_ const struct msica_op_multistring *op, _Inout_ struct msica_session *session)
Definition: msica_op.c:821
struct msica_op base
Definition: msica_op.h:136
#define free
Definition: cmocka.c:1850
GUID value_guid
Common operation data.
Definition: msica_op.h:236
#define MSICA_OP_TYPE_DATA(type)
Definition: msica_op.h:41
#define MSICA_CLEANUP_ACTION_COMMIT
Execution session constants.
Definition: msica_op.h:373
void msica_op_seq_free(_Inout_ struct msica_op_seq *seq)
Frees operation sequence.
Definition: msica_op.c:61
struct msica_op * msica_op_create_guid_string(_In_ enum msica_op_type type, _In_ int ticks, _In_opt_ struct msica_op *next, _In_ const GUID *value_guid, _In_z_ LPCTSTR value_str)
Allocates and fills a new msica_op_guid_string operation.
Definition: msica_op.c:186
bool value
Common operation data.
Definition: msica_op.h:104
Operation data persist header.
Definition: msica_op.c:44
int ticks
Operation type.
Definition: msica_op.h:65
enum msica_op_type type
Definition: msica_op.c:46
struct msica_op base
Definition: msica_op.h:235
void msica_op_seq_add_head(_Inout_ struct msica_op_seq *seq, _Inout_ struct msica_op *operation)
Inserts operation(s) to the beginning of the operation sequence.
Definition: msica_op.c:213