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