VirtualBox

source: vbox/trunk/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.cpp

Last change on this file was 104220, checked in by vboxsync, 2 months ago

Windows/Host Installer: Check permissions of target directory when installing. Added a new testcase for this [build fix]. bugref:10616

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 98.4 KB
Line 
1/* $Id: VBoxInstallHelper.cpp 104220 2024-04-08 10:06:01Z vboxsync $ */
2/** @file
3 * VBoxInstallHelper - Various helper routines for Windows host installer.
4 */
5
6/*
7 * Copyright (C) 2008-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#if defined(VBOX_WITH_NETFLT) || defined(VBOX_WITH_NETADP)
33# include "VBox/VBoxNetCfg-win.h"
34# include "VBox/VBoxDrvCfg-win.h"
35#endif
36
37#define _WIN32_DCOM
38#include <iprt/win/windows.h>
39
40#include <aclapi.h>
41#include <msi.h>
42#include <msiquery.h>
43
44#include <shellapi.h>
45#define INITGUID
46#include <guiddef.h>
47#include <cfgmgr32.h>
48#include <devguid.h>
49#include <sddl.h> /* For ConvertSidToStringSidW. */
50
51#include <iprt/win/objbase.h>
52#include <iprt/win/setupapi.h>
53#include <iprt/win/shlobj.h>
54
55#include <VBox/version.h>
56
57#include <iprt/assert.h>
58#include <iprt/alloca.h>
59#include <iprt/dir.h>
60#include <iprt/err.h>
61#include <iprt/file.h>
62#include <iprt/mem.h>
63#include <iprt/path.h> /* RTPATH_MAX, RTPATH_IS_SLASH */
64#include <iprt/string.h> /* RT_ZERO */
65#include <iprt/stream.h>
66#include <iprt/thread.h>
67#include <iprt/utf16.h>
68
69#include "VBoxCommon.h"
70#ifndef VBOX_OSE
71# include "internal/VBoxSerial.h"
72#endif
73
74
75/*********************************************************************************************************************************
76* Defined Constants And Macros *
77*********************************************************************************************************************************/
78#ifdef DEBUG
79# define NonStandardAssert(_expr) Assert(_expr)
80#else
81# define NonStandardAssert(_expr) do{ }while(0)
82#endif
83
84#define MY_WTEXT_HLP(a_str) L##a_str
85#define MY_WTEXT(a_str) MY_WTEXT_HLP(a_str)
86
87
88/*********************************************************************************************************************************
89* Internal structures *
90*********************************************************************************************************************************/
91/**
92 * Structure for keeping a target's directory security context.
93 */
94typedef struct TGTDIRSECCTX
95{
96 /** Initialized status. */
97 bool fInitialized;
98 /** Handle of the target's parent directory.
99 *
100 * Kept open while the context is around and initialized. */
101 RTDIR hParentDir;
102 /** Absolute (resolved) path of the target directory. */
103 char szTargetDirAbs[RTPATH_MAX];
104 /** Access mask which is forbidden for an ACE of type ACCESS_ALLOWED_ACE_TYPE. */
105 uint32_t fAccessMaskForbidden;
106 /** Array of well-known SIDs which are forbidden. */
107 PSID *paWellKnownSidsForbidden;
108 /** Number of entries in \a paWellKnownSidsForbidden. */
109 size_t cWellKnownSidsForbidden;
110} TGTDIRSECCTX;
111/** Pointer to a target's directory security context. */
112typedef TGTDIRSECCTX *PTGTDIRSECCTX;
113
114
115/*********************************************************************************************************************************
116* Prototypes *
117*********************************************************************************************************************************/
118static void destroyTargetDirSecurityCtx(PTGTDIRSECCTX pCtx);
119
120
121/*********************************************************************************************************************************
122* Globals *
123*********************************************************************************************************************************/
124static uint32_t g_cRef = 0;
125/** Our target directory security context.
126 *
127 * Has to be global in order to keep it around as long as the DLL is being loaded. */
128static TGTDIRSECCTX g_TargetDirSecCtx = { 0 };
129
130
131/**
132 * DLL entry point.
133 */
134BOOL WINAPI DllMain(HANDLE hInst, ULONG uReason, LPVOID pReserved)
135{
136 RT_NOREF(hInst, uReason, pReserved);
137
138#ifdef DEBUG
139 WCHAR wszMsg[128];
140 RTUtf16Printf(wszMsg, RT_ELEMENTS(wszMsg), "DllMain: hInst=%#x, uReason=%u (PID %u), g_cRef=%RU32\n",
141 hInst, uReason, GetCurrentProcessId(), g_cRef);
142 OutputDebugStringW(wszMsg);
143#endif
144
145 switch (uReason)
146 {
147 case DLL_PROCESS_ATTACH:
148 {
149 g_cRef++;
150#if 0
151 /*
152 * This is a trick for allowing the debugger to be attached, don't know if
153 * there is an official way to do that, but this is a pretty efficient.
154 *
155 * Monitor the debug output in DbgView and be ready to start windbg when
156 * the message below appear. This will happen 3-4 times during install,
157 * and 2-3 times during uninstall.
158 *
159 * Note! The DIFxApp.DLL will automatically trigger breakpoints when a
160 * debugger is attached. Just continue on these.
161 */
162 RTUtf16Printf(wszMsg, RT_ELEMENTS(wszMsg), "Waiting for debugger to attach: windbg -g -G -p %u\n", GetCurrentProcessId());
163 for (unsigned i = 0; i < 128 && !IsDebuggerPresent(); i++)
164 {
165 OutputDebugStringW(wszMsg);
166 Sleep(1001);
167 }
168 Sleep(1002);
169 __debugbreak();
170#endif
171 break;
172 }
173
174 case DLL_PROCESS_DETACH:
175 {
176 g_cRef--;
177 break;
178 }
179
180 default:
181 break;
182 }
183
184 return TRUE;
185}
186
187/**
188 * Format a log message and print it to whatever is there (i.e. to the MSI log).
189 *
190 * UTF-16 strings are formatted using '%ls' (lowercase).
191 * ANSI strings are formatted using '%s' (uppercase).
192 *
193 * @returns VBox status code.
194 * @param hInstall MSI installer handle. Optional and can be NULL.
195 * @param pszFmt Format string.
196 * @param ... Variable arguments for format string.
197 */
198static int logStringF(MSIHANDLE hInstall, const char *pszFmt, ...)
199{
200 RTUTF16 wszVa[RTPATH_MAX + 256];
201 va_list va;
202 va_start(va, pszFmt);
203 ssize_t cwc = RTUtf16PrintfV(wszVa, RT_ELEMENTS(wszVa), pszFmt, va);
204 va_end(va);
205
206 RTUTF16 wszMsg[RTPATH_MAX + 256];
207 cwc = RTUtf16Printf(wszMsg, sizeof(wszMsg), "VBoxInstallHelper: %ls", wszVa);
208 if (cwc <= 0)
209 return VERR_BUFFER_OVERFLOW;
210
211#ifdef DEBUG
212 OutputDebugStringW(wszMsg);
213#endif
214#ifdef TESTCASE
215 RTPrintf("%ls\n", wszMsg);
216#endif
217 PMSIHANDLE hMSI = MsiCreateRecord(2 /* cParms */);
218 if (hMSI)
219 {
220 MsiRecordSetStringW(hMSI, 0, wszMsg);
221 MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_INFO), hMSI);
222 MsiCloseHandle(hMSI);
223 }
224
225 return cwc < RT_ELEMENTS(wszVa) ? VINF_SUCCESS : VERR_BUFFER_OVERFLOW;
226}
227
228UINT __stdcall IsSerialCheckNeeded(MSIHANDLE hModule)
229{
230#ifndef VBOX_OSE
231 /*BOOL fRet =*/ serialCheckNeeded(hModule);
232#else
233 RT_NOREF(hModule);
234#endif
235 return ERROR_SUCCESS;
236}
237
238UINT __stdcall CheckSerial(MSIHANDLE hModule)
239{
240#ifndef VBOX_OSE
241 /*BOOL bRet =*/ serialIsValid(hModule);
242#else
243 RT_NOREF(hModule);
244#endif
245 return ERROR_SUCCESS;
246}
247
248/**
249 * Initializes a target security context.
250 *
251 * @returns VBox status code.
252 * @param pCtx Target directory security context to initialize.
253 * @param hModule Windows installer module handle.
254 * @param pszPath Target directory path to use.
255 */
256static int initTargetDirSecurityCtx(PTGTDIRSECCTX pCtx, MSIHANDLE hModule, const char *pszPath)
257{
258 if (pCtx->fInitialized)
259 return VINF_SUCCESS;
260
261#ifdef DEBUG
262 logStringF(hModule, "initTargetDirSecurityCtx: pszPath=%s\n", pszPath);
263#endif
264
265 char szPathTemp[RTPATH_MAX];
266 int vrc = RTStrCopy(szPathTemp, sizeof(szPathTemp), pszPath);
267 if (RT_FAILURE(vrc))
268 return vrc;
269
270 /* Try to find a parent path which exists. */
271 char szPathParentAbs[RTPATH_MAX] = { 0 };
272 for (int i = 0; i < 256; i++) /* Failsafe counter. */
273 {
274 RTPathStripTrailingSlash(szPathTemp);
275 RTPathStripFilename(szPathTemp);
276 vrc = RTPathReal(szPathTemp, szPathParentAbs, sizeof(szPathParentAbs));
277 if (RT_SUCCESS(vrc))
278 break;
279 }
280
281 if (RT_FAILURE(vrc))
282 {
283 logStringF(hModule, "initTargetDirSecurityCtx: No existing / valid parent directory found (%Rrc), giving up\n", vrc);
284 return vrc;
285 }
286
287 RTDIR hParentDir;
288 vrc = RTDirOpen(&hParentDir, szPathParentAbs);
289 if (RT_FAILURE(vrc))
290 {
291 logStringF(hModule, "initTargetDirSecurityCtx: Locking parent directory '%s' failed with %Rrc\n", szPathParentAbs, vrc);
292 return vrc;
293 }
294
295#ifdef DEBUG
296 logStringF(hModule, "initTargetDirSecurityCtx: Locked parent directory '%s'\n", szPathParentAbs);
297#endif
298
299 char szPathTargetAbs[RTPATH_MAX];
300 vrc = RTPathReal(pszPath, szPathTargetAbs, sizeof(szPathTargetAbs));
301 if (RT_FAILURE(vrc))
302 vrc = RTStrCopy(szPathTargetAbs, sizeof(szPathTargetAbs), pszPath);
303 if (RT_FAILURE(vrc))
304 {
305 logStringF(hModule, "initTargetDirSecurityCtx: Failed to resolve absolute target path (%Rrc)\n", vrc);
306 return vrc;
307 }
308
309#ifdef DEBUG
310 logStringF(hModule, "initTargetDirSecurityCtx: szPathTargetAbs=%s, szPathParentAbs=%s\n", szPathTargetAbs, szPathParentAbs);
311#endif
312
313 /* Target directory validation. */
314 if ( !RTStrCmp(szPathTargetAbs, szPathParentAbs) /* Don't allow installation into root directories. */
315 || RTStrStr(szPathTargetAbs, ".."))
316 {
317 logStringF(hModule, "initTargetDirSecurityCtx: Directory '%s' invalid", szPathTargetAbs);
318 vrc = VERR_INVALID_NAME;
319 }
320
321 if (RT_SUCCESS(vrc))
322 {
323 RTFSOBJINFO fsObjInfo;
324 vrc = RTPathQueryInfo(szPathParentAbs, &fsObjInfo, RTFSOBJATTRADD_NOTHING);
325 if (RT_SUCCESS(vrc))
326 {
327 if (RTFS_IS_DIRECTORY(fsObjInfo.Attr.fMode)) /* No symlinks or other fun stuff. */
328 {
329 static WELL_KNOWN_SID_TYPE aForbiddenWellKnownSids[] =
330 {
331 WinNullSid,
332 WinWorldSid,
333 WinAuthenticatedUserSid,
334 WinBuiltinUsersSid,
335 WinBuiltinGuestsSid,
336 WinBuiltinPowerUsersSid
337 };
338
339 pCtx->paWellKnownSidsForbidden = (PSID *)RTMemAlloc(sizeof(PSID) * RT_ELEMENTS(aForbiddenWellKnownSids));
340 AssertPtrReturn(pCtx->paWellKnownSidsForbidden, VERR_NO_MEMORY);
341
342 size_t i = 0;
343 for(; i < RT_ELEMENTS(aForbiddenWellKnownSids); i++)
344 {
345 pCtx->paWellKnownSidsForbidden[i] = RTMemAlloc(SECURITY_MAX_SID_SIZE);
346 AssertPtrBreakStmt(pCtx->paWellKnownSidsForbidden, vrc = VERR_NO_MEMORY);
347 DWORD cbSid = SECURITY_MAX_SID_SIZE;
348 if (!CreateWellKnownSid(aForbiddenWellKnownSids[i], NULL, pCtx->paWellKnownSidsForbidden[i], &cbSid))
349 {
350 vrc = RTErrConvertFromWin32(GetLastError());
351 logStringF(hModule, "initTargetDirSecurityCtx: Creating SID (index %zu) failed with %Rrc\n", i, vrc);
352 break;
353 }
354 }
355
356 if (RT_SUCCESS(vrc))
357 {
358 vrc = RTStrCopy(pCtx->szTargetDirAbs, sizeof(pCtx->szTargetDirAbs), szPathTargetAbs);
359 if (RT_SUCCESS(vrc))
360 {
361 pCtx->fInitialized = true;
362 pCtx->hParentDir = hParentDir;
363 pCtx->cWellKnownSidsForbidden = i;
364 pCtx->fAccessMaskForbidden = FILE_WRITE_DATA
365 | FILE_APPEND_DATA
366 | FILE_WRITE_ATTRIBUTES
367 | FILE_WRITE_EA;
368
369 RTFILE fh;
370 RTFileOpen(&fh, "c:\\temp\\targetdir.ctx", RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE | RTFILE_O_WRITE);
371 RTFileClose(fh);
372
373 return VINF_SUCCESS;
374 }
375 }
376 }
377 else
378 vrc = VERR_INVALID_NAME;
379 }
380 }
381
382 RTDirClose(hParentDir);
383
384 while (pCtx->cWellKnownSidsForbidden--)
385 {
386 RTMemFree(pCtx->paWellKnownSidsForbidden[pCtx->cWellKnownSidsForbidden]);
387 pCtx->paWellKnownSidsForbidden[pCtx->cWellKnownSidsForbidden] = NULL;
388 }
389
390 logStringF(hModule, "initTargetDirSecurityCtx: Initialization failed failed with %Rrc\n", vrc);
391 return vrc;
392}
393
394/**
395 * Destroys a target security context.
396 *
397 * @returns VBox status code.
398 * @param pCtx Target directory security context to destroy.
399 */
400static void destroyTargetDirSecurityCtx(PTGTDIRSECCTX pCtx)
401{
402 if ( !pCtx
403 || !pCtx->fInitialized)
404 return;
405
406 if (pCtx->hParentDir != NIL_RTDIR)
407 {
408 RTDirClose(pCtx->hParentDir);
409 pCtx->hParentDir = NIL_RTDIR;
410 }
411 RT_ZERO(pCtx->szTargetDirAbs);
412
413 for (size_t i = 0; i < pCtx->cWellKnownSidsForbidden; i++)
414 RTMemFree(pCtx->paWellKnownSidsForbidden[i]);
415 pCtx->cWellKnownSidsForbidden = 0;
416
417 RTMemFree(pCtx->paWellKnownSidsForbidden);
418 pCtx->paWellKnownSidsForbidden = NULL;
419
420 RTFileDelete("c:\\temp\\targetdir.ctx");
421
422 logStringF(NULL, "destroyTargetDirSecurityCtx\n");
423}
424
425#ifdef DEBUG
426/**
427 * Returns a stingified version of an ACE type.
428 *
429 * @returns Stingified version of an ACE type.
430 * @param uType ACE type.
431 */
432inline const char *dbgAceTypeToString(uint8_t uType)
433{
434 switch (uType)
435 {
436 RT_CASE_RET_STR(ACCESS_ALLOWED_ACE_TYPE);
437 RT_CASE_RET_STR(ACCESS_DENIED_ACE_TYPE);
438 RT_CASE_RET_STR(SYSTEM_AUDIT_ACE_TYPE);
439 RT_CASE_RET_STR(SYSTEM_ALARM_ACE_TYPE);
440 default: break;
441 }
442
443 return "<Invalid>";
444}
445
446/**
447 * Returns an allocated string for a SID containing the user/domain name.
448 *
449 * @returns Allocated string (UTF-8). Must be free'd using RTStrFree().
450 * @param pSid SID to return allocated string for.
451 */
452inline char *dbgSidToNameA(const PSID pSid)
453{
454 char *pszName = NULL;
455 int vrc = VINF_SUCCESS;
456
457 LPWSTR pwszSid = NULL;
458 if (ConvertSidToStringSid(pSid, &pwszSid))
459 {
460 SID_NAME_USE SidNameUse;
461
462 WCHAR wszUser[MAX_PATH];
463 DWORD cbUser = sizeof(wszUser);
464 WCHAR wszDomain[MAX_PATH];
465 DWORD cbDomain = sizeof(wszDomain);
466 if (LookupAccountSid(NULL, pSid, wszUser, &cbUser, wszDomain, &cbDomain, &SidNameUse))
467 {
468 RTUTF16 wszName[RTPATH_MAX];
469 if (RTUtf16Printf(wszName, RT_ELEMENTS(wszName), "%ls%s%ls (%ls)",
470 wszUser, wszDomain[0] == L'\0' ? "" : "\\", wszDomain, pwszSid))
471 {
472 vrc = RTUtf16ToUtf8(wszName, &pszName);
473 }
474 else
475 vrc = VERR_NO_MEMORY;
476 }
477 else
478 vrc = RTStrAPrintf(&pszName, "<Lookup Error>");
479
480 LocalFree(pwszSid);
481 }
482 else
483 vrc = VERR_NOT_FOUND;
484
485 return RT_SUCCESS(vrc) ? pszName : "<Invalid>";
486}
487#endif /* DEBUG */
488
489/**
490 * Checks a single target path whether it's safe to use or not.
491 *
492 * We check if the given path is owned by "NT Service\TrustedInstaller" and therefore assume that it's safe to use.
493 *
494 * @returns VBox status code. On error the path should be considered unsafe.
495 * @retval VERR_INVALID_NAME if the given path is considered unsafe.
496 * @retval VINF_SUCCESS if the given path is found to be safe to use.
497 * @param hModule Windows installer module handle.
498 * @param pszPath Path to check.
499 */
500static int checkTargetDirOne(MSIHANDLE hModule, PTGTDIRSECCTX pCtx, const char *pszPath)
501{
502 logStringF(hModule, "checkTargetDirOne: Checking '%s' ...", pszPath);
503
504 PRTUTF16 pwszPath;
505 int vrc = RTStrToUtf16(pszPath, &pwszPath);
506 if (RT_FAILURE(vrc))
507 return vrc;
508
509 PACL pDacl = NULL;
510 PSECURITY_DESCRIPTOR pSecurityDescriptor = { 0 };
511 DWORD dwErr = GetNamedSecurityInfo(pwszPath, SE_FILE_OBJECT, GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
512 NULL, NULL, NULL, NULL, &pSecurityDescriptor);
513 if (dwErr == ERROR_SUCCESS)
514 {
515 BOOL fDaclPresent = FALSE;
516 BOOL fDaclDefaultedIgnored = FALSE;
517 if (GetSecurityDescriptorDacl(pSecurityDescriptor, &fDaclPresent,
518 &pDacl, &fDaclDefaultedIgnored))
519 {
520 if ( !fDaclPresent
521 || !pDacl)
522 {
523 /* Bail out early if the DACL isn't provided or is missing. */
524 vrc = VERR_INVALID_NAME;
525 }
526 else
527 {
528 ACL_SIZE_INFORMATION aclSizeInfo;
529 RT_ZERO(aclSizeInfo);
530 if (GetAclInformation(pDacl, &aclSizeInfo, sizeof(aclSizeInfo), AclSizeInformation))
531 {
532 for(DWORD idxACE = 0; idxACE < aclSizeInfo.AceCount; idxACE++)
533 {
534 ACE_HEADER *pAceHdr = NULL;
535 if (GetAce(pDacl, idxACE, (LPVOID *)&pAceHdr))
536 {
537#ifdef DEBUG
538 logStringF(hModule, "checkTargetDirOne: ACE type=%s, flags=%#x, size=%#x",
539 dbgAceTypeToString(pAceHdr->AceType), pAceHdr->AceFlags, pAceHdr->AceSize);
540#endif
541 /* Note: We print the ACEs in canonoical order. */
542 switch (pAceHdr->AceType)
543 {
544 case ACCESS_ALLOWED_ACE_TYPE: /* We're only interested in the ALLOW ACE. */
545 {
546 ACCESS_ALLOWED_ACE const *pAce = (ACCESS_ALLOWED_ACE *)pAceHdr;
547 PSID const pSid = (PSID)&pAce->SidStart;
548#ifdef DEBUG
549 char *pszSid = dbgSidToNameA(pSid);
550 logStringF(hModule, "checkTargetDirOne:\t%s fMask=%#x", pszSid, pAce->Mask);
551 RTStrFree(pszSid);
552#endif
553 /* We check the flags here first for performance reasons. */
554 if ((pAce->Mask & pCtx->fAccessMaskForbidden) == pCtx->fAccessMaskForbidden)
555 {
556 for (size_t idxSID = 0; idxSID < pCtx->cWellKnownSidsForbidden; idxSID++)
557 {
558 PSID const pSidForbidden = pCtx->paWellKnownSidsForbidden[idxSID];
559 bool const fForbidden = EqualSid(pSid, pSidForbidden);
560#ifdef DEBUG
561 char *pszName = dbgSidToNameA(pSidForbidden);
562 logStringF(hModule, "checkTargetDirOne:\t%s : %s",
563 fForbidden ? "** FORBIDDEN **" : "ALLOWED ", pszName);
564 RTStrFree(pszName);
565#endif /* DEBUG */
566 if (fForbidden)
567 {
568 vrc = VERR_INVALID_NAME;
569 break;
570 }
571 }
572 }
573
574 break;
575 }
576#ifdef DEBUG
577 case ACCESS_DENIED_ACE_TYPE: /* We're only interested in the ALLOW ACE. */
578 {
579 ACCESS_DENIED_ACE const *pAce = (ACCESS_DENIED_ACE *)pAceHdr;
580
581 LPWSTR pwszSid = NULL;
582 ConvertSidToStringSid((PSID)&pAce->SidStart, &pwszSid);
583
584 logStringF(hModule, "checkTargetDirOne:\t%ls fMask=%#x (generic %#x specific %#x)",
585 pwszSid ? pwszSid : L"<Allocation Error>", pAce->Mask);
586
587 LocalFree(pwszSid);
588 break;
589 }
590#endif /* DEBUG */
591 default:
592 /* Ignore everything else. */
593 break;
594 }
595 }
596 else
597 dwErr = GetLastError();
598
599 /* No point in checking further if we failed somewhere above. */
600 if (RT_FAILURE(vrc))
601 break;
602
603 } /* for ACE */
604 }
605 else
606 dwErr = GetLastError();
607 }
608 }
609 else
610 dwErr = GetLastError();
611
612 LocalFree(pSecurityDescriptor);
613 }
614 else
615 dwErr = GetLastError();
616
617 if (RT_SUCCESS(vrc))
618 vrc = RTErrConvertFromWin32(dwErr);
619
620#ifdef DEBUG
621 logStringF(hModule, "checkTargetDirOne: Returning %Rrc", vrc);
622#endif
623
624 if ( RT_FAILURE(vrc)
625 && vrc != VERR_INVALID_NAME)
626 logStringF(hModule, "checkTargetDirOne: Failed with %Rrc (%#x)", vrc, dwErr);
627
628 return vrc;
629}
630
631/**
632 * Checks whether the path in the public property INSTALLDIR has the correct ACL permissions and returns whether
633 * it's valid or not.
634 *
635 * Called from the MSI installer as a custom action.
636 *
637 * @returns Success status (acccording to MSI custom actions).
638 * @retval ERROR_SUCCESS if checking the target directory turned out to be valid.
639 * @retval ERROR_NO_NET_OR_BAD_PATH is the target directory is invalid.
640 * @param hModule Windows installer module handle.
641 *
642 * @note Sets private property VBox_Target_Dir_Is_Valid to "1" (true) if the given target path is valid,
643 * or "0" (false) if it is not. An empty target directory is considered to be valid (i.e. INSTALLDIR not set yet).
644 *
645 * @sa @bugref{10616}
646 */
647UINT __stdcall CheckTargetDir(MSIHANDLE hModule)
648{
649 char *pszTargetDir;
650
651 int vrc = VBoxGetMsiPropUtf8(hModule, "INSTALLDIR", &pszTargetDir);
652 if (RT_SUCCESS(vrc))
653 {
654 logStringF(hModule, "CheckTargetDir: Checking target directory '%s' ...", pszTargetDir);
655
656 if (!RTStrNLen(pszTargetDir, RTPATH_MAX))
657 {
658 logStringF(hModule, "CheckTargetDir: No INSTALLDIR set (yet), skipping ...");
659 VBoxSetMsiProp(hModule, L"VBox_Target_Dir_Is_Valid", L"1");
660 }
661 else
662 {
663 union
664 {
665 RTPATHPARSED Parsed;
666 uint8_t ab[RTPATH_MAX];
667 } u;
668
669 vrc = RTPathParse(pszTargetDir, &u.Parsed, sizeof(u), RTPATH_STR_F_STYLE_DOS);
670 if (RT_SUCCESS(vrc))
671 {
672 if (u.Parsed.fProps & RTPATH_PROP_DOTDOT_REFS)
673 vrc = VERR_INVALID_PARAMETER;
674 if (RT_SUCCESS(vrc))
675 {
676 vrc = initTargetDirSecurityCtx(&g_TargetDirSecCtx, hModule, pszTargetDir);
677 if (RT_SUCCESS(vrc))
678 {
679 uint16_t idxComp = u.Parsed.cComps;
680 char szPathToCheck[RTPATH_MAX];
681 while (idxComp > 1) /* We traverse backwards from INSTALLDIR and leave out the root (e.g. C:\"). */
682 {
683 u.Parsed.cComps = idxComp;
684 vrc = RTPathParsedReassemble(pszTargetDir, &u.Parsed, RTPATH_STR_F_STYLE_DOS,
685 szPathToCheck, sizeof(szPathToCheck));
686 if (RT_FAILURE(vrc))
687 break;
688 if (RTDirExists(szPathToCheck))
689 {
690 vrc = checkTargetDirOne(hModule, &g_TargetDirSecCtx, szPathToCheck);
691 if (RT_FAILURE(vrc))
692 break;
693 }
694 else
695 logStringF(hModule, "CheckTargetDir: Path '%s' does not exist (yet)", szPathToCheck);
696 idxComp--;
697 }
698
699 destroyTargetDirSecurityCtx(&g_TargetDirSecCtx);
700 }
701 else
702 logStringF(hModule, "CheckTargetDir: initTargetDirSecurityCtx failed with %Rrc\n", vrc);
703
704 if (RT_SUCCESS(vrc))
705 VBoxSetMsiProp(hModule, L"VBox_Target_Dir_Is_Valid", L"1");
706 }
707 }
708 else
709 logStringF(hModule, "CheckTargetDir: Parsing path failed with %Rrc", vrc);
710 }
711
712 RTStrFree(pszTargetDir);
713 }
714
715 if (RT_FAILURE(vrc)) /* On failure (or when in doubt), mark the installation directory as invalid. */
716 {
717 logStringF(hModule, "CheckTargetDir: Checking failed with %Rrc", vrc);
718 VBoxSetMsiProp(hModule, L"VBox_Target_Dir_Is_Valid", L"0");
719 }
720
721 /* Return back outcome to the MSI engine. */
722 return RT_SUCCESS(vrc) ? ERROR_SUCCESS : ERROR_NO_NET_OR_BAD_PATH;
723}
724
725/**
726 * Runs an executable on the OS.
727 *
728 * @returns Windows error code.
729 * @param hModule Windows installer module handle.
730 * @param pwszImage The executable to run.
731 * @param pwszArgs The arguments (command line w/o executable).
732 */
733static UINT procRun(MSIHANDLE hModule, const wchar_t *pwszImage, wchar_t const *pwszArgs)
734{
735 /*
736 * Construct a full command line.
737 */
738 size_t const cwcImage = RTUtf16Len(pwszImage);
739 size_t const cwcArgs = RTUtf16Len(pwszArgs);
740
741 wchar_t *pwszCmdLine = (wchar_t *)alloca((1 + cwcImage + 1 + 1 + cwcArgs + 1) * sizeof(wchar_t));
742 pwszCmdLine[0] = '"';
743 memcpy(&pwszCmdLine[1], pwszImage, cwcImage * sizeof(wchar_t));
744 pwszCmdLine[1 + cwcImage] = '"';
745 pwszCmdLine[1 + cwcImage + 1] = ' ';
746 memcpy(&pwszCmdLine[1 + cwcImage + 1 + 1], pwszArgs, (cwcArgs + 1) * sizeof(wchar_t));
747
748 /*
749 * Construct startup info.
750 */
751 STARTUPINFOW StartupInfo;
752 RT_ZERO(StartupInfo);
753 StartupInfo.cb = sizeof(StartupInfo);
754 StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
755 StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
756 StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
757 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
758#ifndef DEBUG
759 StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
760 StartupInfo.wShowWindow = SW_HIDE;
761#endif
762
763 /*
764 * Start it.
765 */
766 UINT rcWin;
767 PROCESS_INFORMATION ChildInfo = { NULL, NULL, 0, 0 };
768 if (CreateProcessW(pwszImage, pwszCmdLine, NULL /*pProcessAttribs*/, NULL /*pThreadAttribs*/, TRUE /*fInheritHandles*/,
769 0 /*fFlags*/, NULL /*pwszEnv*/, NULL /*pwszCwd*/, &StartupInfo, &ChildInfo))
770 {
771 logStringF(hModule, "procRun: Info: Started process %u: %ls", ChildInfo.dwProcessId, pwszCmdLine);
772 CloseHandle(ChildInfo.hThread);
773 DWORD const dwWait = WaitForSingleObject(ChildInfo.hProcess, RT_MS_30SEC);
774 DWORD dwExitCode = 0xf00dface;
775 if (GetExitCodeProcess(ChildInfo.hProcess, &dwExitCode))
776 {
777 if (dwExitCode == 0)
778 {
779 logStringF(hModule, "procRun: Info: Process '%ls' terminated exit code zero", pwszCmdLine);
780 rcWin = ERROR_SUCCESS;
781 }
782 else
783 {
784 logStringF(hModule, "procRun: Process '%ls' terminated with non-zero exit code: %u (%#x)",
785 pwszCmdLine, dwExitCode, dwExitCode);
786 rcWin = ERROR_GEN_FAILURE;
787 }
788 }
789 else
790 {
791 rcWin = GetLastError();
792 logStringF(hModule, "procRun: Process '%ls' is probably still running: rcWin=%u dwWait=%u (%#x)",
793 pwszCmdLine, rcWin, dwWait, dwWait);
794 }
795 }
796 else
797 {
798 rcWin = GetLastError();
799 logStringF(hModule, "procRun: Creating process '%ls' failed: rcWin=%u\n", pwszCmdLine, rcWin);
800 }
801 return rcWin;
802}
803
804/**
805 * Tries to retrieve the Python installation path on the system, extended version.
806 *
807 * @returns Windows error code.
808 * @param hModule Windows installer module handle.
809 * @param hKeyRoot Registry root key to use, e.g. HKEY_LOCAL_MACHINE.
810 * @param pwszPythonPath Buffer to return the path for python.exe in.
811 * @param cwcPythonPath Buffer size in UTF-16 units.
812 * @param fReturnExe Return the path to python.exe if true, otherwise
813 * just the python install directory.
814 */
815static UINT getPythonPathEx(MSIHANDLE hModule, HKEY hKeyRoot, wchar_t *pwszPythonPath, size_t cwcPythonPath, bool fReturnExe)
816{
817 *pwszPythonPath = '\0';
818
819 /*
820 * Enumerate the subkeys of python core installation key.
821 *
822 * Note: The loop ASSUMES that later found versions are higher, e.g. newer
823 * Python versions. For now we always go by the newest version.
824 */
825 HKEY hKeyPythonCore = NULL;
826 LSTATUS dwErr = RegOpenKeyExW(hKeyRoot, L"SOFTWARE\\Python\\PythonCore", 0, KEY_READ, &hKeyPythonCore);
827 if (dwErr != ERROR_SUCCESS)
828 return dwErr;
829
830 UINT rcWinRet = ERROR_PATH_NOT_FOUND;
831 for (DWORD i = 0; i < 16384; ++i)
832 {
833 static wchar_t const s_wszInstallPath[] = L"\\InstallPath";
834 static wchar_t const s_wszPythonExe[] = L"python.exe";
835
836 /* Get key name: */
837 wchar_t wszBuf[RTPATH_MAX + RT_MAX(RT_ELEMENTS(s_wszInstallPath), RT_ELEMENTS(s_wszPythonExe)) + 2];
838 DWORD cwcKeyNm = RTPATH_MAX;
839 DWORD dwKeyType = REG_SZ;
840 dwErr = RegEnumKeyExW(hKeyPythonCore, i, wszBuf, &cwcKeyNm, NULL, NULL, NULL, NULL);
841 if (dwErr == ERROR_NO_MORE_ITEMS)
842 break;
843 if (dwErr != ERROR_SUCCESS)
844 continue;
845 if (dwKeyType != REG_SZ)
846 continue;
847 if (cwcKeyNm == 0)
848 continue;
849 NonStandardAssert(cwcKeyNm <= sizeof(wszBuf));
850
851 /* Try Open the InstallPath subkey: */
852 memcpy(&wszBuf[cwcKeyNm], s_wszInstallPath, sizeof(s_wszInstallPath));
853
854 HKEY hKeyInstallPath = NULL;
855 dwErr = RegOpenKeyExW(hKeyPythonCore, wszBuf, 0, KEY_READ, &hKeyInstallPath);
856 if (dwErr != ERROR_SUCCESS)
857 continue;
858
859 /* Query the value. We double buffer this so we don't overwrite an okay
860 return value with this. Use the smaller of cwcPythonPath and wszValue
861 so RegQueryValueExW can do all the buffer overflow checking for us.
862 For paranoid reasons, we reserve a space for a terminator as well as
863 a slash. (ASSUMES reasonably sized output buffer.) */
864 NonStandardAssert(cwcPythonPath > RT_ELEMENTS(s_wszPythonExe) + 16);
865 DWORD cbValue = (DWORD)RT_MIN( cwcPythonPath * sizeof(wchar_t)
866 - (fReturnExe ? sizeof(s_wszInstallPath) - sizeof(wchar_t) * 2 : sizeof(wchar_t) * 2),
867 RTPATH_MAX * sizeof(wchar_t));
868 DWORD dwValueType = REG_SZ;
869 dwErr = RegQueryValueExW(hKeyInstallPath, L"", NULL, &dwValueType, (LPBYTE)wszBuf, &cbValue);
870 RegCloseKey(hKeyInstallPath);
871 if ( dwErr == ERROR_SUCCESS
872 && dwValueType == REG_SZ
873 && cbValue >= sizeof(L"C:\\") - sizeof(L""))
874 {
875 /* Find length in wchar_t unit w/o terminator: */
876 DWORD cwc = cbValue / sizeof(wchar_t);
877 while (cwc > 0 && wszBuf[cwc - 1] == '\0')
878 cwc--;
879 wszBuf[cwc] = '\0';
880 if (cwc > 2)
881 {
882 /* Check if the path leads to a directory with a python.exe file in it. */
883 if (!RTPATH_IS_SLASH(wszBuf[cwc - 1]))
884 wszBuf[cwc++] = '\\';
885 memcpy(&wszBuf[cwc], s_wszPythonExe, sizeof(s_wszPythonExe));
886 DWORD const fAttribs = GetFileAttributesW(wszBuf);
887 if (fAttribs != INVALID_FILE_ATTRIBUTES)
888 {
889 if (!(fAttribs & FILE_ATTRIBUTE_DIRECTORY))
890 {
891 /* Okay, we found something that can be returned. */
892 if (fReturnExe)
893 cwc += RT_ELEMENTS(s_wszPythonExe) - 1;
894 wszBuf[cwc] = '\0';
895 logStringF(hModule, "getPythonPath: Found: \"%ls\"", wszBuf);
896
897 NonStandardAssert(cwcPythonPath > cwc);
898 memcpy(pwszPythonPath, wszBuf, cwc * sizeof(wchar_t));
899 pwszPythonPath[cwc] = '\0';
900 rcWinRet = ERROR_SUCCESS;
901 }
902 else
903 logStringF(hModule, "getPythonPath: Warning: Skipping \"%ls\": is a directory (%#x)", wszBuf, fAttribs);
904 }
905 else
906 logStringF(hModule, "getPythonPath: Warning: Skipping \"%ls\": Does not exist (%u)", wszBuf, GetLastError());
907 }
908 }
909 }
910
911 RegCloseKey(hKeyPythonCore);
912 if (rcWinRet != ERROR_SUCCESS)
913 logStringF(hModule, "getPythonPath: Unable to find python");
914 return rcWinRet;
915}
916
917/**
918 * Retrieves the absolute path of the Python installation.
919 *
920 * @returns Windows error code.
921 * @param hModule Windows installer module handle.
922 * @param pwszPythonPath Buffer to return the path for python.exe in.
923 * @param cwcPythonPath Buffer size in UTF-16 units.
924 * @param fReturnExe Return the path to python.exe if true, otherwise
925 * just the python install directory.
926 */
927static UINT getPythonPath(MSIHANDLE hModule, wchar_t *pwszPythonPath, size_t cwcPythonPath, bool fReturnExe = false)
928{
929 UINT rcWin = getPythonPathEx(hModule, HKEY_LOCAL_MACHINE, pwszPythonPath, cwcPythonPath, fReturnExe);
930 if (rcWin != ERROR_SUCCESS)
931 rcWin = getPythonPathEx(hModule, HKEY_CURRENT_USER, pwszPythonPath, cwcPythonPath, fReturnExe);
932 return rcWin;
933}
934
935/**
936 * Retrieves the absolute path of the Python executable.
937 *
938 * @returns Windows error code.
939 * @param hModule Windows installer module handle.
940 * @param pwszPythonExe Buffer to return the path for python.exe in.
941 * @param cwcPythonExe Buffer size in UTF-16 units.
942 */
943static UINT getPythonExe(MSIHANDLE hModule, wchar_t *pwszPythonExe, size_t cwcPythonExe)
944{
945 return getPythonPath(hModule, pwszPythonExe, cwcPythonExe, true /*fReturnExe*/);
946}
947
948/**
949 * Checks if all dependencies for running the VBox Python API bindings are met.
950 *
951 * @returns VBox status code, or error if depedencies are not met.
952 * @param hModule Windows installer module handle.
953 * @param pwszPythonExe Path to Python interpreter image (.exe).
954 */
955static int checkPythonDependencies(MSIHANDLE hModule, const wchar_t *pwszPythonExe)
956{
957 /*
958 * Check if importing the win32api module works.
959 * This is a prerequisite for setting up the VBox API.
960 */
961 logStringF(hModule, "checkPythonDependencies: Checking for win32api extensions ...");
962
963 UINT rcWin = procRun(hModule, pwszPythonExe, L"-c \"import win32api\"");
964 if (rcWin == ERROR_SUCCESS)
965 logStringF(hModule, "checkPythonDependencies: win32api found\n");
966 else
967 logStringF(hModule, "checkPythonDependencies: Importing win32api failed with %u (%#x)\n", rcWin, rcWin);
968
969 return rcWin;
970}
971
972/**
973 * Checks for a valid Python installation on the system.
974 *
975 * Called from the MSI installer as custom action.
976 *
977 * @returns Always ERROR_SUCCESS.
978 * Sets public property VBOX_PYTHON_INSTALLED to "0" (false) or "1" (success).
979 * Sets public property VBOX_PYTHON_PATH to the Python installation path (if found).
980 *
981 * @param hModule Windows installer module handle.
982 */
983UINT __stdcall IsPythonInstalled(MSIHANDLE hModule)
984{
985 wchar_t wszPythonPath[RTPATH_MAX];
986 UINT rcWin = getPythonPath(hModule, wszPythonPath, RTPATH_MAX);
987 if (rcWin == ERROR_SUCCESS)
988 {
989 logStringF(hModule, "IsPythonInstalled: Python installation found at \"%ls\"", wszPythonPath);
990 VBoxSetMsiProp(hModule, L"VBOX_PYTHON_PATH", wszPythonPath);
991 VBoxSetMsiProp(hModule, L"VBOX_PYTHON_INSTALLED", L"1");
992 }
993 else
994 {
995 logStringF(hModule, "IsPythonInstalled: Error: No suitable Python installation found (%u), skipping installation.", rcWin);
996 logStringF(hModule, "IsPythonInstalled: Python seems not to be installed; please download + install the Python Core package.");
997 VBoxSetMsiProp(hModule, L"VBOX_PYTHON_INSTALLED", L"0");
998 }
999
1000 return ERROR_SUCCESS; /* Never return failure. */
1001}
1002
1003/**
1004 * Checks if all dependencies for running the VBox Python API bindings are met.
1005 *
1006 * Called from the MSI installer as custom action.
1007 *
1008 * @returns Always ERROR_SUCCESS.
1009 * Sets public property VBOX_PYTHON_DEPS_INSTALLED to "0" (false) or "1" (success).
1010 *
1011 * @param hModule Windows installer module handle.
1012 */
1013UINT __stdcall ArePythonAPIDepsInstalled(MSIHANDLE hModule)
1014{
1015 wchar_t wszPythonExe[RTPATH_MAX];
1016 UINT dwErr = getPythonExe(hModule, wszPythonExe, RTPATH_MAX);
1017 if (dwErr == ERROR_SUCCESS)
1018 {
1019 dwErr = checkPythonDependencies(hModule, wszPythonExe);
1020 if (dwErr == ERROR_SUCCESS)
1021 logStringF(hModule, "ArePythonAPIDepsInstalled: Dependencies look good.");
1022 }
1023
1024 if (dwErr != ERROR_SUCCESS)
1025 logStringF(hModule, "ArePythonAPIDepsInstalled: Failed with dwErr=%u", dwErr);
1026
1027 VBoxSetMsiProp(hModule, L"VBOX_PYTHON_DEPS_INSTALLED", dwErr == ERROR_SUCCESS ? L"1" : L"0");
1028 return ERROR_SUCCESS; /* Never return failure. */
1029}
1030
1031/**
1032 * Checks if all required MS CRTs (Visual Studio Redistributable Package) are installed on the system.
1033 *
1034 * Called from the MSI installer as custom action.
1035 *
1036 * @returns Always ERROR_SUCCESS.
1037 * Sets public property VBOX_MSCRT_INSTALLED to "" (false, to use "NOT" in WiX) or "1" (success).
1038 *
1039 * Also exposes public properties VBOX_MSCRT_VER_MIN + VBOX_MSCRT_VER_MAJ strings
1040 * with the most recent MSCRT version detected.
1041 *
1042 * @param hModule Windows installer module handle.
1043 *
1044 * @sa https://docs.microsoft.com/en-us/cpp/windows/redistributing-visual-cpp-files?view=msvc-170
1045 */
1046UINT __stdcall IsMSCRTInstalled(MSIHANDLE hModule)
1047{
1048 HKEY hKeyVS = NULL;
1049 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1050 L"SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\X64",
1051 0, KEY_READ, &hKeyVS);
1052 if (lrc == ERROR_SUCCESS)
1053 {
1054 DWORD dwVal = 0;
1055 DWORD cbVal = sizeof(dwVal);
1056 DWORD dwValueType = REG_DWORD; /** @todo r=bird: output only parameter, optional, so pointless. */
1057 lrc = RegQueryValueExW(hKeyVS, L"Installed", NULL, &dwValueType, (LPBYTE)&dwVal, &cbVal);
1058 if (lrc == ERROR_SUCCESS)
1059 {
1060 if (dwVal >= 1)
1061 {
1062 DWORD dwMaj = 0; /** @todo r=bird: It's purdent to initialize values if you don't bother to check the type and size! */
1063 lrc = RegQueryValueExW(hKeyVS, L"Major", NULL, &dwValueType, (LPBYTE)&dwMaj, &cbVal);
1064 if (lrc == ERROR_SUCCESS)
1065 {
1066 VBoxSetMsiPropDWORD(hModule, L"VBOX_MSCRT_VER_MAJ", dwMaj);
1067
1068 DWORD dwMin = 0;
1069 lrc = RegQueryValueExW(hKeyVS, L"Minor", NULL, &dwValueType, (LPBYTE)&dwMin, &cbVal);
1070 if (lrc == ERROR_SUCCESS)
1071 {
1072 VBoxSetMsiPropDWORD(hModule, L"VBOX_MSCRT_VER_MIN", dwMin);
1073
1074 logStringF(hModule, "IsMSCRTInstalled: Found v%u.%u\n", dwMaj, dwMin);
1075
1076 /* Check for at least 2019. */
1077 if (dwMaj > 14 || (dwMaj == 14 && dwMin >= 20))
1078 VBoxSetMsiProp(hModule, L"VBOX_MSCRT_INSTALLED", L"1");
1079 }
1080 else
1081 logStringF(hModule, "IsMSCRTInstalled: Found, but 'Minor' key not present (lrc=%d)", lrc);
1082 }
1083 else
1084 logStringF(hModule, "IsMSCRTInstalled: Found, but 'Major' key not present (lrc=%d)", lrc);
1085 }
1086 else
1087 {
1088 logStringF(hModule, "IsMSCRTInstalled: Found, but not marked as installed");
1089 lrc = ERROR_NOT_INSTALLED;
1090 }
1091 }
1092 else
1093 logStringF(hModule, "IsMSCRTInstalled: Found, but 'Installed' key not present (lrc=%d)", lrc);
1094 }
1095
1096 if (lrc != ERROR_SUCCESS)
1097 logStringF(hModule, "IsMSCRTInstalled: Failed with lrc=%ld", lrc);
1098
1099 return ERROR_SUCCESS; /* Never return failure. */
1100}
1101
1102/**
1103 * Checks if the running OS is (at least) Windows 10 (e.g. >= build 10000).
1104 *
1105 * Called from the MSI installer as custom action.
1106 *
1107 * @returns Always ERROR_SUCCESS.
1108 * Sets public property VBOX_IS_WINDOWS_10 to "" (empty / false) or "1" (success).
1109 *
1110 * @param hModule Windows installer module handle.
1111 */
1112UINT __stdcall IsWindows10(MSIHANDLE hModule)
1113{
1114 /*
1115 * Note: We cannot use RtlGetVersion() / GetVersionExW() here, as the Windows Installer service
1116 * all shims this, unfortunately. So we have to go another route by querying the major version
1117 * number from the registry.
1118 */
1119 HKEY hKeyCurVer = NULL;
1120 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKeyCurVer);
1121 if (lrc == ERROR_SUCCESS)
1122 {
1123 DWORD dwVal = 0;
1124 DWORD cbVal = sizeof(dwVal);
1125 DWORD dwValueType = REG_DWORD; /** @todo r=bird: Again, the type is an optional output parameter. pointless to init or pass it unless you check. */
1126 lrc = RegQueryValueExW(hKeyCurVer, L"CurrentMajorVersionNumber", NULL, &dwValueType, (LPBYTE)&dwVal, &cbVal);
1127 if (lrc == ERROR_SUCCESS)
1128 {
1129 logStringF(hModule, "IsWindows10/CurrentMajorVersionNumber: %u", dwVal);
1130
1131 VBoxSetMsiProp(hModule, L"VBOX_IS_WINDOWS_10", dwVal >= 10 ? L"1" : L"");
1132 }
1133 else
1134 logStringF(hModule, "IsWindows10/RegOpenKeyExW: Error reading CurrentMajorVersionNumber (%ld)", lrc);
1135
1136 RegCloseKey(hKeyCurVer);
1137 }
1138 else
1139 logStringF(hModule, "IsWindows10/RegOpenKeyExW: Error opening CurrentVersion key (%ld)", lrc);
1140
1141 return ERROR_SUCCESS; /* Never return failure. */
1142}
1143
1144/**
1145 * Installs and compiles the VBox Python bindings.
1146 *
1147 * Called from the MSI installer as custom action.
1148 *
1149 * @returns Always ERROR_SUCCESS.
1150 * Sets public property VBOX_API_INSTALLED to "0" (false) or "1" (success).
1151 *
1152 * @param hModule Windows installer module handle.
1153 */
1154UINT __stdcall InstallPythonAPI(MSIHANDLE hModule)
1155{
1156 logStringF(hModule, "InstallPythonAPI: Checking for installed Python environment(s) ...");
1157
1158 /** @todo r=bird: Can't we get the VBOX_PYTHON_PATH property here? */
1159 wchar_t wszPythonExe[RTPATH_MAX];
1160 UINT rcWin = getPythonExe(hModule, wszPythonExe, RTPATH_MAX);
1161 if (rcWin != ERROR_SUCCESS)
1162 {
1163 VBoxSetMsiProp(hModule, L"VBOX_API_INSTALLED", L"0");
1164 return ERROR_SUCCESS;
1165 }
1166
1167 /*
1168 * Set up the VBox API.
1169 */
1170 /* Get the VBox API setup string. */
1171 WCHAR wszVBoxPythonInstallerPath[RTPATH_MAX];
1172 rcWin = VBoxGetMsiProp(hModule, L"CustomActionData", wszVBoxPythonInstallerPath, RT_ELEMENTS(wszVBoxPythonInstallerPath));
1173 if (rcWin == ERROR_SUCCESS)
1174 {
1175 /* Make sure our current working directory is the VBox installation path. */
1176 if (SetCurrentDirectoryW(wszVBoxPythonInstallerPath))
1177 {
1178 /* Set required environment variables. */
1179 /** @todo r=andy: That can't be right!
1180 *
1181 * r=bird: The variable probably isn't used because VBOX_MSI_INSTALL_PATH is
1182 * set by VBoxMergeApp.wxi. */
1183 if (SetEnvironmentVariableW(L"VBOX_INSTALL_PATH", wszVBoxPythonInstallerPath))
1184 {
1185 logStringF(hModule, "InstallPythonAPI: Invoking vboxapisetup.py in \"%ls\" ...", wszVBoxPythonInstallerPath);
1186
1187 rcWin = procRun(hModule, wszPythonExe, L"vboxapisetup.py install");
1188 if (rcWin == ERROR_SUCCESS)
1189 {
1190 logStringF(hModule, "InstallPythonAPI: Installation of vboxapisetup.py successful");
1191
1192 /*
1193 * Do some sanity checking if the VBox API works.
1194 */
1195 logStringF(hModule, "InstallPythonAPI: Validating VBox API ...");
1196
1197 rcWin = procRun(hModule, wszPythonExe, L"-c \"from vboxapi import VirtualBoxManager\"");
1198 if (rcWin == ERROR_SUCCESS)
1199 {
1200 logStringF(hModule, "InstallPythonAPI: VBox API looks good.");
1201 VBoxSetMsiProp(hModule, L"VBOX_API_INSTALLED", L"1");
1202 return ERROR_SUCCESS;
1203 }
1204
1205 /* failed */
1206 logStringF(hModule, "InstallPythonAPI: Validating VBox API failed with %u (%#x)", rcWin, rcWin);
1207 }
1208 else
1209 logStringF(hModule, "InstallPythonAPI: Calling vboxapisetup.py failed with %u (%#x)", rcWin, rcWin);
1210 }
1211 else
1212 logStringF(hModule, "InstallPythonAPI: Could set environment variable VBOX_INSTALL_PATH: LastError=%u",
1213 GetLastError());
1214 }
1215 else
1216 logStringF(hModule, "InstallPythonAPI: Could set working directory to \"%ls\": LastError=%u",
1217 wszVBoxPythonInstallerPath, GetLastError());
1218 }
1219 else
1220 logStringF(hModule, "InstallPythonAPI: Unable to retrieve VBox installation directory: rcWin=%u (%#x)", rcWin, rcWin);
1221
1222 VBoxSetMsiProp(hModule, L"VBOX_API_INSTALLED", L"0");
1223 logStringF(hModule, "InstallPythonAPI: Installation failed");
1224 return ERROR_SUCCESS; /* Do not fail here. */
1225}
1226
1227static LONG installBrandingValue(MSIHANDLE hModule,
1228 const WCHAR *pwszFileName,
1229 const WCHAR *pwszSection,
1230 const WCHAR *pwszValue)
1231{
1232 LONG rc;
1233 WCHAR wszValue[MAX_PATH];
1234 if (GetPrivateProfileStringW(pwszSection, pwszValue, NULL, wszValue, sizeof(wszValue), pwszFileName) > 0)
1235 {
1236 WCHAR wszKey[MAX_PATH + 64];
1237 if (RTUtf16ICmpAscii(pwszSection, "General") != 0)
1238 RTUtf16Printf(wszKey, RT_ELEMENTS(wszKey), "SOFTWARE\\%s\\VirtualBox\\Branding\\%ls", VBOX_VENDOR_SHORT, pwszSection);
1239 else
1240 RTUtf16Printf(wszKey, RT_ELEMENTS(wszKey), "SOFTWARE\\%s\\VirtualBox\\Branding", VBOX_VENDOR_SHORT);
1241
1242 HKEY hkBranding = NULL;
1243 rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszKey, 0, KEY_WRITE, &hkBranding);
1244 if (rc == ERROR_SUCCESS)
1245 {
1246 rc = RegSetValueExW(hkBranding,
1247 pwszValue,
1248 NULL,
1249 REG_SZ,
1250 (BYTE *)wszValue,
1251 (DWORD)RTUtf16Len(wszValue));
1252 if (rc != ERROR_SUCCESS)
1253 logStringF(hModule, "InstallBranding: Could not write value %s! Error %d", pwszValue, rc);
1254 RegCloseKey(hkBranding);
1255 }
1256 }
1257 else
1258 rc = ERROR_NOT_FOUND;
1259 return rc;
1260}
1261
1262/**
1263 * @note Both paths strings must have an extra terminator.
1264 */
1265static UINT CopyDir(MSIHANDLE hModule, const WCHAR *pwszzDstDir, const WCHAR *pwszzSrcDir)
1266{
1267 NonStandardAssert(pwszzDstDir[RTUtf16Len(pwszzDstDir) + 1] == '\0');
1268 NonStandardAssert(pwszzSrcDir[RTUtf16Len(pwszzSrcDir) + 1] == '\0');
1269
1270 SHFILEOPSTRUCTW s = {0};
1271 s.hwnd = NULL;
1272 s.wFunc = FO_COPY;
1273 s.pTo = pwszzDstDir;
1274 s.pFrom = pwszzSrcDir;
1275 s.fFlags = FOF_SILENT
1276 | FOF_NOCONFIRMATION
1277 | FOF_NOCONFIRMMKDIR
1278 | FOF_NOERRORUI;
1279
1280 logStringF(hModule, "CopyDir: pwszzDstDir=%ls, pwszzSrcDir=%ls", pwszzDstDir, pwszzSrcDir);
1281 int r = SHFileOperationW(&s);
1282 if (r == 0)
1283 return ERROR_SUCCESS;
1284 logStringF(hModule, "CopyDir: Copy operation returned status %#x", r);
1285 return ERROR_GEN_FAILURE;
1286}
1287
1288/**
1289 * @note The directory string must have two zero terminators!
1290 */
1291static UINT RemoveDir(MSIHANDLE hModule, const WCHAR *pwszzDstDir)
1292{
1293 NonStandardAssert(pwszzDstDir[RTUtf16Len(pwszzDstDir) + 1] == '\0');
1294
1295 SHFILEOPSTRUCTW s = {0};
1296 s.hwnd = NULL;
1297 s.wFunc = FO_DELETE;
1298 s.pFrom = pwszzDstDir;
1299 s.fFlags = FOF_SILENT
1300 | FOF_NOCONFIRMATION
1301 | FOF_NOCONFIRMMKDIR
1302 | FOF_NOERRORUI;
1303
1304 logStringF(hModule, "RemoveDir: pwszzDstDir=%ls", pwszzDstDir);
1305 int r = SHFileOperationW(&s);
1306 if (r == 0)
1307 return ERROR_SUCCESS;
1308 logStringF(hModule, "RemoveDir: Remove operation returned status %#x", r);
1309 return ERROR_GEN_FAILURE;
1310}
1311
1312/**
1313 * @note Both paths strings must have an extra terminator.
1314 */
1315static UINT RenameDir(MSIHANDLE hModule, const WCHAR *pwszzDstDir, const WCHAR *pwszzSrcDir)
1316{
1317 NonStandardAssert(pwszzDstDir[RTUtf16Len(pwszzDstDir) + 1] == '\0');
1318 NonStandardAssert(pwszzSrcDir[RTUtf16Len(pwszzSrcDir) + 1] == '\0');
1319
1320 SHFILEOPSTRUCTW s = {0};
1321 s.hwnd = NULL;
1322 s.wFunc = FO_RENAME;
1323 s.pTo = pwszzDstDir;
1324 s.pFrom = pwszzSrcDir;
1325 s.fFlags = FOF_SILENT
1326 | FOF_NOCONFIRMATION
1327 | FOF_NOCONFIRMMKDIR
1328 | FOF_NOERRORUI;
1329
1330 logStringF(hModule, "RenameDir: pwszzDstDir=%ls, pwszzSrcDir=%ls", pwszzDstDir, pwszzSrcDir);
1331 int r = SHFileOperationW(&s);
1332 if (r == 0)
1333 return ERROR_SUCCESS;
1334 logStringF(hModule, "RenameDir: Rename operation returned status %#x", r);
1335 return ERROR_GEN_FAILURE;
1336}
1337
1338/** RTPathAppend-like function. */
1339static UINT AppendToPath(wchar_t *pwszPath, size_t cwcPath, const wchar_t *pwszAppend, bool fDoubleTerm = false)
1340{
1341 size_t cwcCurPath = RTUtf16Len(pwszPath);
1342 size_t cwcSlash = cwcCurPath > 1 && RTPATH_IS_SLASH(pwszPath[cwcCurPath - 1]) ? 0 : 1;
1343 while (RTPATH_IS_SLASH(*pwszAppend))
1344 pwszAppend++;
1345 size_t cwcAppend = RTUtf16Len(pwszAppend);
1346 if (cwcCurPath + cwcCurPath + cwcAppend + fDoubleTerm < cwcPath)
1347 {
1348 if (cwcSlash)
1349 pwszPath[cwcCurPath++] = '\\';
1350 memcpy(&pwszPath[cwcCurPath], pwszAppend, (cwcAppend + 1) * sizeof(wchar_t));
1351 if (fDoubleTerm)
1352 pwszPath[cwcCurPath + cwcAppend + 1] = '\0';
1353 return ERROR_SUCCESS;
1354 }
1355 return ERROR_BUFFER_OVERFLOW;
1356}
1357
1358/** RTPathJoin-like function. */
1359static UINT JoinPaths(wchar_t *pwszPath, size_t cwcPath, wchar_t *pwszPath1, const wchar_t *pwszAppend, bool fDoubleTerm = false)
1360{
1361 size_t cwcCurPath = RTUtf16Len(pwszPath1);
1362 if (cwcCurPath < cwcPath)
1363 {
1364 memcpy(pwszPath, pwszPath1, (cwcCurPath + 1) * sizeof(wchar_t));
1365 return AppendToPath(pwszPath, cwcPath, pwszAppend, fDoubleTerm);
1366 }
1367 return ERROR_BUFFER_OVERFLOW;
1368}
1369
1370UINT __stdcall UninstallBranding(MSIHANDLE hModule)
1371{
1372 logStringF(hModule, "UninstallBranding: Handling branding file ...");
1373
1374 WCHAR wszPath[RTPATH_MAX];
1375 UINT rc = VBoxGetMsiProp(hModule, L"CustomActionData", wszPath, RT_ELEMENTS(wszPath));
1376 if (rc == ERROR_SUCCESS)
1377 {
1378 size_t const cwcPath = RTUtf16Len(wszPath);
1379 rc = AppendToPath(wszPath, RTPATH_MAX, L"custom", true /*fDoubleTerm*/);
1380 if (rc == ERROR_SUCCESS)
1381 rc = RemoveDir(hModule, wszPath);
1382
1383 /* Check for .custom directory from a failed install and remove it. */
1384 wszPath[cwcPath] = '\0';
1385 rc = AppendToPath(wszPath, RTPATH_MAX, L".custom", true /*fDoubleTerm*/);
1386 if (rc == ERROR_SUCCESS)
1387 rc = RemoveDir(hModule, wszPath);
1388 }
1389
1390 logStringF(hModule, "UninstallBranding: Handling done. (rc=%u (ignored))", rc);
1391 return ERROR_SUCCESS; /* Do not fail here. */
1392}
1393
1394UINT __stdcall InstallBranding(MSIHANDLE hModule)
1395{
1396 logStringF(hModule, "InstallBranding: Handling branding file ...");
1397
1398 /*
1399 * Get the paths.
1400 */
1401 wchar_t wszSrcPath[RTPATH_MAX];
1402 UINT rc = VBoxGetMsiProp(hModule, L"SOURCEDIR", wszSrcPath, RT_ELEMENTS(wszSrcPath));
1403 if (rc == ERROR_SUCCESS)
1404 {
1405 wchar_t wszDstPath[RTPATH_MAX];
1406 rc = VBoxGetMsiProp(hModule, L"CustomActionData", wszDstPath, RT_ELEMENTS(wszDstPath) - 1);
1407 if (rc == ERROR_SUCCESS)
1408 {
1409 /*
1410 * First we copy the src\.custom dir to the target.
1411 */
1412 rc = AppendToPath(wszSrcPath, RT_ELEMENTS(wszSrcPath) - 1, L".custom", true /*fDoubleTerm*/);
1413 if (rc == ERROR_SUCCESS)
1414 {
1415 rc = CopyDir(hModule, wszDstPath, wszSrcPath);
1416 if (rc == ERROR_SUCCESS)
1417 {
1418 /*
1419 * The rename the '.custom' directory we now got in the target area to 'custom'.
1420 */
1421 rc = JoinPaths(wszSrcPath, RT_ELEMENTS(wszSrcPath), wszDstPath, L".custom", true /*fDoubleTerm*/);
1422 if (rc == ERROR_SUCCESS)
1423 {
1424 rc = AppendToPath(wszDstPath, RT_ELEMENTS(wszDstPath), L"custom", true /*fDoubleTerm*/);
1425 if (rc == ERROR_SUCCESS)
1426 rc = RenameDir(hModule, wszDstPath, wszSrcPath);
1427 }
1428 }
1429 }
1430 }
1431 }
1432
1433 logStringF(hModule, "InstallBranding: Handling done. (rc=%u (ignored))", rc);
1434 return ERROR_SUCCESS; /* Do not fail here. */
1435}
1436
1437#if defined(VBOX_WITH_NETFLT) || defined(VBOX_WITH_NETADP)
1438
1439/** @todo should use some real VBox app name */
1440#define VBOX_NETCFG_APP_NAME L"VirtualBox Installer"
1441#define VBOX_NETCFG_MAX_RETRIES 10
1442#define NETFLT_PT_INF_REL_PATH L"VBoxNetFlt.inf"
1443#define NETFLT_MP_INF_REL_PATH L"VBoxNetFltM.inf"
1444#define NETFLT_ID L"sun_VBoxNetFlt" /** @todo Needs to be changed (?). */
1445#define NETADP_ID L"sun_VBoxNetAdp" /** @todo Needs to be changed (?). */
1446
1447#define NETLWF_INF_NAME L"VBoxNetLwf.inf"
1448
1449static MSIHANDLE g_hCurrentModule = NULL;
1450
1451static UINT _uninstallNetFlt(MSIHANDLE hModule);
1452static UINT _uninstallNetLwf(MSIHANDLE hModule);
1453
1454static VOID vboxDrvLoggerCallback(VBOXDRVCFG_LOG_SEVERITY_T enmSeverity, char *pszMsg, void *pvContext)
1455{
1456 RT_NOREF1(pvContext);
1457 switch (enmSeverity)
1458 {
1459 case VBOXDRVCFG_LOG_SEVERITY_FLOW:
1460 case VBOXDRVCFG_LOG_SEVERITY_REGULAR:
1461 break;
1462 case VBOXDRVCFG_LOG_SEVERITY_REL:
1463 if (g_hCurrentModule)
1464 logStringF(g_hCurrentModule, "%s", pszMsg);
1465 break;
1466 default:
1467 break;
1468 }
1469}
1470
1471static DECLCALLBACK(void) netCfgLoggerCallback(const char *pszString)
1472{
1473 if (g_hCurrentModule)
1474 logStringF(g_hCurrentModule, "%s", pszString);
1475}
1476
1477static VOID netCfgLoggerDisable()
1478{
1479 if (g_hCurrentModule)
1480 {
1481 VBoxNetCfgWinSetLogging(NULL);
1482 g_hCurrentModule = NULL;
1483 }
1484}
1485
1486static VOID netCfgLoggerEnable(MSIHANDLE hModule)
1487{
1488 NonStandardAssert(hModule);
1489
1490 if (g_hCurrentModule)
1491 netCfgLoggerDisable();
1492
1493 g_hCurrentModule = hModule;
1494
1495 VBoxNetCfgWinSetLogging(netCfgLoggerCallback);
1496 /* uncomment next line if you want to add logging information from VBoxDrvCfg.cpp */
1497// VBoxDrvCfgLoggerSet(vboxDrvLoggerCallback, NULL);
1498}
1499
1500static UINT errorConvertFromHResult(MSIHANDLE hModule, HRESULT hr)
1501{
1502 UINT uRet;
1503 switch (hr)
1504 {
1505 case S_OK:
1506 uRet = ERROR_SUCCESS;
1507 break;
1508
1509 case NETCFG_S_REBOOT:
1510 {
1511 logStringF(hModule, "Reboot required, setting REBOOT property to \"force\"");
1512 HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
1513 if (hr2 != ERROR_SUCCESS)
1514 logStringF(hModule, "Failed to set REBOOT property, error = %#x", hr2);
1515 uRet = ERROR_SUCCESS; /* Never fail here. */
1516 break;
1517 }
1518
1519 default:
1520 logStringF(hModule, "Converting unhandled HRESULT (%#x) to ERROR_GEN_FAILURE", hr);
1521 uRet = ERROR_GEN_FAILURE;
1522 }
1523 return uRet;
1524}
1525
1526static MSIHANDLE createNetCfgLockedMsgRecord(MSIHANDLE hModule)
1527{
1528 MSIHANDLE hRecord = MsiCreateRecord(2);
1529 if (hRecord)
1530 {
1531 UINT uErr = MsiRecordSetInteger(hRecord, 1, 25001);
1532 if (uErr != ERROR_SUCCESS)
1533 {
1534 logStringF(hModule, "createNetCfgLockedMsgRecord: MsiRecordSetInteger failed, error = %#x", uErr);
1535 MsiCloseHandle(hRecord);
1536 hRecord = NULL;
1537 }
1538 }
1539 else
1540 logStringF(hModule, "createNetCfgLockedMsgRecord: Failed to create a record");
1541
1542 return hRecord;
1543}
1544
1545static UINT doNetCfgInit(MSIHANDLE hModule, INetCfg **ppnc, BOOL bWrite)
1546{
1547 MSIHANDLE hMsg = NULL;
1548 UINT uErr = ERROR_GEN_FAILURE;
1549 int MsgResult;
1550 int cRetries = 0;
1551
1552 do
1553 {
1554 LPWSTR lpszLockedBy;
1555 HRESULT hr = VBoxNetCfgWinQueryINetCfg(ppnc, bWrite, VBOX_NETCFG_APP_NAME, 10000, &lpszLockedBy);
1556 if (hr != NETCFG_E_NO_WRITE_LOCK)
1557 {
1558 if (FAILED(hr))
1559 logStringF(hModule, "doNetCfgInit: VBoxNetCfgWinQueryINetCfg failed, error = %#x", hr);
1560 uErr = errorConvertFromHResult(hModule, hr);
1561 break;
1562 }
1563
1564 /* hr == NETCFG_E_NO_WRITE_LOCK */
1565
1566 if (!lpszLockedBy)
1567 {
1568 logStringF(hModule, "doNetCfgInit: lpszLockedBy == NULL, breaking");
1569 break;
1570 }
1571
1572 /* on vista the 6to4svc.dll periodically maintains the lock for some reason,
1573 * if this is the case, increase the wait period by retrying multiple times
1574 * NOTE: we could alternatively increase the wait timeout,
1575 * however it seems unneeded for most cases, e.g. in case some network connection property
1576 * dialog is opened, it would be better to post a notification to the user as soon as possible
1577 * rather than waiting for a longer period of time before displaying it */
1578 if ( cRetries < VBOX_NETCFG_MAX_RETRIES
1579 && RTUtf16ICmpAscii(lpszLockedBy, "6to4svc.dll") == 0)
1580 {
1581 cRetries++;
1582 logStringF(hModule, "doNetCfgInit: lpszLockedBy is 6to4svc.dll, retrying %d out of %d", cRetries, VBOX_NETCFG_MAX_RETRIES);
1583 MsgResult = IDRETRY;
1584 }
1585 else
1586 {
1587 if (!hMsg)
1588 {
1589 hMsg = createNetCfgLockedMsgRecord(hModule);
1590 if (!hMsg)
1591 {
1592 logStringF(hModule, "doNetCfgInit: Failed to create a message record, breaking");
1593 CoTaskMemFree(lpszLockedBy);
1594 break;
1595 }
1596 }
1597
1598 UINT rTmp = MsiRecordSetStringW(hMsg, 2, lpszLockedBy);
1599 NonStandardAssert(rTmp == ERROR_SUCCESS);
1600 if (rTmp != ERROR_SUCCESS)
1601 {
1602 logStringF(hModule, "doNetCfgInit: MsiRecordSetStringW failed, error = #%x", rTmp);
1603 CoTaskMemFree(lpszLockedBy);
1604 break;
1605 }
1606
1607 MsgResult = MsiProcessMessage(hModule, (INSTALLMESSAGE)(INSTALLMESSAGE_USER | MB_RETRYCANCEL), hMsg);
1608 NonStandardAssert(MsgResult == IDRETRY || MsgResult == IDCANCEL);
1609 logStringF(hModule, "doNetCfgInit: MsiProcessMessage returned (%#x)", MsgResult);
1610 }
1611 CoTaskMemFree(lpszLockedBy);
1612 } while(MsgResult == IDRETRY);
1613
1614 if (hMsg)
1615 MsiCloseHandle(hMsg);
1616
1617 return uErr;
1618}
1619#endif /* defined(VBOX_WITH_NETFLT) || defined(VBOX_WITH_NETADP) */
1620
1621#ifdef VBOX_WITH_NETFLT
1622static UINT vboxNetFltQueryInfArray(MSIHANDLE hModule, OUT LPWSTR pwszPtInf, DWORD cwcPtInf,
1623 OUT LPWSTR pwszMpInf, DWORD cwcMpInf)
1624{
1625 DWORD cwcEffBuf = cwcPtInf - RT_MAX(sizeof(NETFLT_PT_INF_REL_PATH), sizeof(NETFLT_MP_INF_REL_PATH)) / sizeof(WCHAR);
1626 UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", pwszPtInf, &cwcEffBuf);
1627 if ( uErr == ERROR_SUCCESS
1628 && cwcEffBuf > 0)
1629 {
1630 int vrc = RTUtf16Copy(pwszMpInf, cwcMpInf, pwszPtInf);
1631 AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
1632
1633 vrc = RTUtf16Cat(pwszPtInf, cwcPtInf, NETFLT_PT_INF_REL_PATH);
1634 AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
1635 logStringF(hModule, "vboxNetFltQueryInfArray: INF 1: %ls", pwszPtInf);
1636
1637 vrc = RTUtf16Cat(pwszMpInf, cwcMpInf, NETFLT_MP_INF_REL_PATH);
1638 AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
1639 logStringF(hModule, "vboxNetFltQueryInfArray: INF 2: %ls", pwszMpInf);
1640 }
1641 else if (uErr != ERROR_SUCCESS)
1642 logStringF(hModule, "vboxNetFltQueryInfArray: MsiGetPropertyW failed, error = %#x", uErr);
1643 else
1644 {
1645 logStringF(hModule, "vboxNetFltQueryInfArray: Empty installation directory");
1646 uErr = ERROR_GEN_FAILURE;
1647 }
1648
1649 return uErr;
1650}
1651
1652static UINT _uninstallNetFlt(MSIHANDLE hModule)
1653{
1654 INetCfg *pNetCfg;
1655 UINT uErr;
1656
1657 netCfgLoggerEnable(hModule);
1658
1659 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
1660
1661 __try
1662 {
1663 logStringF(hModule, "Uninstalling NetFlt");
1664
1665 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
1666 if (uErr == ERROR_SUCCESS)
1667 {
1668 HRESULT hr = VBoxNetCfgWinNetFltUninstall(pNetCfg);
1669 if (hr != S_OK)
1670 logStringF(hModule, "UninstallNetFlt: VBoxNetCfgWinUninstallComponent failed, error = %#x", hr);
1671
1672 uErr = errorConvertFromHResult(hModule, hr);
1673
1674 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
1675
1676 logStringF(hModule, "Uninstalling NetFlt done, error = %#x", uErr);
1677 }
1678 else
1679 logStringF(hModule, "UninstallNetFlt: doNetCfgInit failed, error = %#x", uErr);
1680 }
1681 __finally
1682 {
1683 if (bOldIntMode)
1684 {
1685 /* The prev mode != FALSE, i.e. non-interactive. */
1686 SetupSetNonInteractiveMode(bOldIntMode);
1687 }
1688 netCfgLoggerDisable();
1689 }
1690
1691 /* Never fail the uninstall even if we did not succeed. */
1692 return ERROR_SUCCESS;
1693}
1694#endif /* VBOX_WITH_NETFLT */
1695
1696UINT __stdcall UninstallNetFlt(MSIHANDLE hModule)
1697{
1698#ifdef VBOX_WITH_NETFLT
1699 _uninstallNetLwf(hModule);
1700 return _uninstallNetFlt(hModule);
1701#else
1702 RT_NOREF(hModule);
1703 return ERROR_SUCCESS;
1704#endif
1705}
1706
1707#ifdef VBOX_WITH_NETFLT
1708static UINT _installNetFlt(MSIHANDLE hModule)
1709{
1710 UINT uErr;
1711 INetCfg *pNetCfg;
1712
1713 netCfgLoggerEnable(hModule);
1714
1715 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
1716
1717 __try
1718 {
1719
1720 logStringF(hModule, "InstallNetFlt: Installing NetFlt");
1721
1722 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
1723 if (uErr == ERROR_SUCCESS)
1724 {
1725 WCHAR wszPtInf[MAX_PATH];
1726 WCHAR wszMpInf[MAX_PATH];
1727 uErr = vboxNetFltQueryInfArray(hModule, wszPtInf, RT_ELEMENTS(wszPtInf), wszMpInf, RT_ELEMENTS(wszMpInf));
1728 if (uErr == ERROR_SUCCESS)
1729 {
1730 LPCWSTR const apwszInfs[] = { wszPtInf, wszMpInf };
1731 HRESULT hr = VBoxNetCfgWinNetFltInstall(pNetCfg, &apwszInfs[0], RT_ELEMENTS(apwszInfs));
1732 if (FAILED(hr))
1733 logStringF(hModule, "InstallNetFlt: VBoxNetCfgWinNetFltInstall failed, error = %#x", hr);
1734
1735 uErr = errorConvertFromHResult(hModule, hr);
1736 }
1737 else
1738 logStringF(hModule, "InstallNetFlt: vboxNetFltQueryInfArray failed, error = %#x", uErr);
1739
1740 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
1741
1742 logStringF(hModule, "InstallNetFlt: Done");
1743 }
1744 else
1745 logStringF(hModule, "InstallNetFlt: doNetCfgInit failed, error = %#x", uErr);
1746 }
1747 __finally
1748 {
1749 if (bOldIntMode)
1750 {
1751 /* The prev mode != FALSE, i.e. non-interactive. */
1752 SetupSetNonInteractiveMode(bOldIntMode);
1753 }
1754 netCfgLoggerDisable();
1755 }
1756
1757 /* Never fail the install even if we did not succeed. */
1758 return ERROR_SUCCESS;
1759}
1760#endif /* VBOX_WITH_NETFLT */
1761
1762UINT __stdcall InstallNetFlt(MSIHANDLE hModule)
1763{
1764#ifdef VBOX_WITH_NETFLT
1765 _uninstallNetLwf(hModule);
1766 return _installNetFlt(hModule);
1767#else
1768 RT_NOREF(hModule);
1769 return ERROR_SUCCESS;
1770#endif
1771}
1772
1773#ifdef VBOX_WITH_NETFLT
1774static UINT _uninstallNetLwf(MSIHANDLE hModule)
1775{
1776 INetCfg *pNetCfg;
1777 UINT uErr;
1778
1779 netCfgLoggerEnable(hModule);
1780
1781 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
1782
1783 __try
1784 {
1785 logStringF(hModule, "Uninstalling NetLwf");
1786
1787 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
1788 if (uErr == ERROR_SUCCESS)
1789 {
1790 HRESULT hr = VBoxNetCfgWinNetLwfUninstall(pNetCfg);
1791 if (hr != S_OK)
1792 logStringF(hModule, "UninstallNetLwf: VBoxNetCfgWinUninstallComponent failed, error = %#x", hr);
1793
1794 uErr = errorConvertFromHResult(hModule, hr);
1795
1796 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
1797
1798 logStringF(hModule, "Uninstalling NetLwf done, error = %#x", uErr);
1799 }
1800 else
1801 logStringF(hModule, "UninstallNetLwf: doNetCfgInit failed, error = %#x", uErr);
1802 }
1803 __finally
1804 {
1805 if (bOldIntMode)
1806 {
1807 /* The prev mode != FALSE, i.e. non-interactive. */
1808 SetupSetNonInteractiveMode(bOldIntMode);
1809 }
1810 netCfgLoggerDisable();
1811 }
1812
1813 /* Never fail the uninstall even if we did not succeed. */
1814 return ERROR_SUCCESS;
1815}
1816#endif /* VBOX_WITH_NETFLT */
1817
1818UINT __stdcall UninstallNetLwf(MSIHANDLE hModule)
1819{
1820#ifdef VBOX_WITH_NETFLT
1821 _uninstallNetFlt(hModule);
1822 return _uninstallNetLwf(hModule);
1823#else
1824 RT_NOREF(hModule);
1825 return ERROR_SUCCESS;
1826#endif
1827}
1828
1829#ifdef VBOX_WITH_NETFLT
1830static UINT _installNetLwf(MSIHANDLE hModule)
1831{
1832 UINT uErr;
1833 INetCfg *pNetCfg;
1834
1835 netCfgLoggerEnable(hModule);
1836
1837 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
1838
1839 __try
1840 {
1841
1842 logStringF(hModule, "InstallNetLwf: Installing NetLwf");
1843
1844 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
1845 if (uErr == ERROR_SUCCESS)
1846 {
1847 WCHAR wszInf[MAX_PATH];
1848 DWORD cwcInf = RT_ELEMENTS(wszInf) - sizeof(NETLWF_INF_NAME) - 1;
1849 uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszInf, &cwcInf);
1850 if (uErr == ERROR_SUCCESS)
1851 {
1852 if (cwcInf)
1853 {
1854 if (wszInf[cwcInf - 1] != L'\\')
1855 {
1856 wszInf[cwcInf++] = L'\\';
1857 wszInf[cwcInf] = L'\0';
1858 }
1859
1860 int vrc = RTUtf16Cat(wszInf, RT_ELEMENTS(wszInf), NETLWF_INF_NAME);
1861 AssertRC(vrc);
1862
1863 HRESULT hr = VBoxNetCfgWinNetLwfInstall(pNetCfg, wszInf);
1864 if (FAILED(hr))
1865 logStringF(hModule, "InstallNetLwf: VBoxNetCfgWinNetLwfInstall failed, error = %#x", hr);
1866
1867 uErr = errorConvertFromHResult(hModule, hr);
1868 }
1869 else
1870 {
1871 logStringF(hModule, "vboxNetFltQueryInfArray: Empty installation directory");
1872 uErr = ERROR_GEN_FAILURE;
1873 }
1874 }
1875 else
1876 logStringF(hModule, "vboxNetFltQueryInfArray: MsiGetPropertyW failed, error = %#x", uErr);
1877
1878 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
1879
1880 logStringF(hModule, "InstallNetLwf: Done");
1881 }
1882 else
1883 logStringF(hModule, "InstallNetLwf: doNetCfgInit failed, error = %#x", uErr);
1884 }
1885 __finally
1886 {
1887 if (bOldIntMode)
1888 {
1889 /* The prev mode != FALSE, i.e. non-interactive. */
1890 SetupSetNonInteractiveMode(bOldIntMode);
1891 }
1892 netCfgLoggerDisable();
1893 }
1894
1895 /* Never fail the install even if we did not succeed. */
1896 return ERROR_SUCCESS;
1897}
1898#endif /* VBOX_WITH_NETFLT */
1899
1900UINT __stdcall InstallNetLwf(MSIHANDLE hModule)
1901{
1902#ifdef VBOX_WITH_NETFLT
1903 _uninstallNetFlt(hModule);
1904 return _installNetLwf(hModule);
1905#else
1906 RT_NOREF(hModule);
1907 return ERROR_SUCCESS;
1908#endif
1909}
1910
1911
1912#if 0 /** @todo r=andy Remove this? */
1913static BOOL RenameHostOnlyConnectionsCallback(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pContext)
1914{
1915 WCHAR DevName[256];
1916 DWORD winEr;
1917
1918 if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, pDev,
1919 SPDRP_FRIENDLYNAME , /* IN DWORD Property,*/
1920 NULL, /*OUT PDWORD PropertyRegDataType, OPTIONAL*/
1921 (PBYTE)DevName, /*OUT PBYTE PropertyBuffer,*/
1922 sizeof(DevName), /* IN DWORD PropertyBufferSize,*/
1923 NULL /*OUT PDWORD RequiredSize OPTIONAL*/
1924 ))
1925 {
1926 HKEY hKey = SetupDiOpenDevRegKey(hDevInfo, pDev,
1927 DICS_FLAG_GLOBAL, /* IN DWORD Scope,*/
1928 0, /*IN DWORD HwProfile, */
1929 DIREG_DRV, /* IN DWORD KeyType, */
1930 KEY_READ /*IN REGSAM samDesired*/
1931 );
1932 NonStandardAssert(hKey != INVALID_HANDLE_VALUE);
1933 if (hKey != INVALID_HANDLE_VALUE)
1934 {
1935 WCHAR guid[50];
1936 DWORD cbGuid=sizeof(guid);
1937 winEr = RegQueryValueExW(hKey,
1938 L"NetCfgInstanceId", /*__in_opt LPCTSTR lpValueName,*/
1939 NULL, /*__reserved LPDWORD lpReserved,*/
1940 NULL, /*__out_opt LPDWORD lpType,*/
1941 (LPBYTE)guid, /*__out_opt LPBYTE lpData,*/
1942 &cbGuid /*guid__inout_opt LPDWORD lpcbData*/
1943 );
1944 NonStandardAssert(winEr == ERROR_SUCCESS);
1945 if (winEr == ERROR_SUCCESS)
1946 {
1947 WCHAR ConnectoinName[128];
1948 ULONG cbName = sizeof(ConnectoinName);
1949
1950 HRESULT hr = VBoxNetCfgWinGenHostonlyConnectionName(DevName, ConnectoinName, &cbName);
1951 NonStandardAssert(hr == S_OK);
1952 if (SUCCEEDED(hr))
1953 {
1954 hr = VBoxNetCfgWinRenameConnection(guid, ConnectoinName);
1955 NonStandardAssert(hr == S_OK);
1956 }
1957 }
1958 }
1959 RegCloseKey(hKey);
1960 }
1961 else
1962 {
1963 NonStandardAssert(0);
1964 }
1965
1966 return TRUE;
1967}
1968#endif /* 0 */
1969
1970#ifdef VBOX_WITH_NETADP
1971static UINT _createHostOnlyInterface(MSIHANDLE hModule, LPCWSTR pwszId, LPCWSTR pwszInfName)
1972{
1973 netCfgLoggerEnable(hModule);
1974
1975 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
1976
1977 logStringF(hModule, "CreateHostOnlyInterface: Creating host-only interface");
1978
1979 HRESULT hr = E_FAIL;
1980 GUID guid;
1981 WCHAR wszMpInf[MAX_PATH];
1982 DWORD cwcMpInf = RT_ELEMENTS(wszMpInf) - (DWORD)RTUtf16Len(pwszInfName) - 1 - 1;
1983 LPCWSTR pwszInfPath = NULL;
1984 bool fIsFile = false;
1985 UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszMpInf, &cwcMpInf);
1986 if (uErr == ERROR_SUCCESS)
1987 {
1988 if (cwcMpInf)
1989 {
1990 logStringF(hModule, "CreateHostOnlyInterface: NetAdpDir property = %ls", wszMpInf);
1991 if (wszMpInf[cwcMpInf - 1] != L'\\')
1992 {
1993 wszMpInf[cwcMpInf++] = L'\\';
1994 wszMpInf[cwcMpInf] = L'\0';
1995 }
1996
1997 int vrc = RTUtf16Cat(wszMpInf, RT_ELEMENTS(wszMpInf), pwszInfName);
1998 AssertRC(vrc);
1999
2000 pwszInfPath = wszMpInf;
2001 fIsFile = true;
2002
2003 logStringF(hModule, "CreateHostOnlyInterface: Resulting INF path = %ls", pwszInfPath);
2004 }
2005 else
2006 logStringF(hModule, "CreateHostOnlyInterface: VBox installation path is empty");
2007 }
2008 else
2009 logStringF(hModule, "CreateHostOnlyInterface: Unable to retrieve VBox installation path, error = %#x", uErr);
2010
2011 /* Make sure the inf file is installed. */
2012 if (pwszInfPath != NULL && fIsFile)
2013 {
2014 logStringF(hModule, "CreateHostOnlyInterface: Calling VBoxDrvCfgInfInstall(%ls)", pwszInfPath);
2015 hr = VBoxDrvCfgInfInstall(pwszInfPath);
2016 logStringF(hModule, "CreateHostOnlyInterface: VBoxDrvCfgInfInstall returns %#x", hr);
2017 if (FAILED(hr))
2018 logStringF(hModule, "CreateHostOnlyInterface: Failed to install INF file, error = %#x", hr);
2019 }
2020
2021 if (SUCCEEDED(hr))
2022 {
2023 //first, try to update Host Only Network Interface
2024 BOOL fRebootRequired = FALSE;
2025 hr = VBoxNetCfgWinUpdateHostOnlyNetworkInterface(pwszInfPath, &fRebootRequired, pwszId);
2026 if (SUCCEEDED(hr))
2027 {
2028 if (fRebootRequired)
2029 {
2030 logStringF(hModule, "CreateHostOnlyInterface: Reboot required for update, setting REBOOT property to force");
2031 HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
2032 if (hr2 != ERROR_SUCCESS)
2033 logStringF(hModule, "CreateHostOnlyInterface: Failed to set REBOOT property for update, error = %#x", hr2);
2034 }
2035 }
2036 else
2037 {
2038 //in fail case call CreateHostOnlyInterface
2039 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinUpdateHostOnlyNetworkInterface failed, hr = %#x", hr);
2040 logStringF(hModule, "CreateHostOnlyInterface: calling VBoxNetCfgWinCreateHostOnlyNetworkInterface");
2041# ifdef VBOXNETCFG_DELAYEDRENAME
2042 BSTR devId;
2043 hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsFile, NULL, &guid, &devId, NULL);
2044# else /* !VBOXNETCFG_DELAYEDRENAME */
2045 hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsFile, NULL, &guid, NULL, NULL);
2046# endif /* !VBOXNETCFG_DELAYEDRENAME */
2047 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinCreateHostOnlyNetworkInterface returns %#x", hr);
2048 if (SUCCEEDED(hr))
2049 {
2050 ULONG ip = inet_addr("192.168.56.1");
2051 ULONG mask = inet_addr("255.255.255.0");
2052 logStringF(hModule, "CreateHostOnlyInterface: calling VBoxNetCfgWinEnableStaticIpConfig");
2053 hr = VBoxNetCfgWinEnableStaticIpConfig(&guid, ip, mask);
2054 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinEnableStaticIpConfig returns %#x", hr);
2055 if (FAILED(hr))
2056 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinEnableStaticIpConfig failed, error = %#x", hr);
2057# ifdef VBOXNETCFG_DELAYEDRENAME
2058 hr = VBoxNetCfgWinRenameHostOnlyConnection(&guid, devId, NULL);
2059 if (FAILED(hr))
2060 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinRenameHostOnlyConnection failed, error = %#x", hr);
2061 SysFreeString(devId);
2062# endif /* VBOXNETCFG_DELAYEDRENAME */
2063 }
2064 else
2065 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinCreateHostOnlyNetworkInterface failed, error = %#x", hr);
2066 }
2067 }
2068
2069 if (SUCCEEDED(hr))
2070 logStringF(hModule, "CreateHostOnlyInterface: Creating host-only interface done");
2071
2072 /* Restore original setup mode. */
2073 logStringF(hModule, "CreateHostOnlyInterface: Almost done...");
2074 if (fSetupModeInteractive)
2075 SetupSetNonInteractiveMode(fSetupModeInteractive);
2076
2077 netCfgLoggerDisable();
2078
2079 logStringF(hModule, "CreateHostOnlyInterface: Returns success (ignoring all failures)");
2080 /* Never fail the install even if we did not succeed. */
2081 return ERROR_SUCCESS;
2082}
2083#endif /* VBOX_WITH_NETADP */
2084
2085UINT __stdcall CreateHostOnlyInterface(MSIHANDLE hModule)
2086{
2087#ifdef VBOX_WITH_NETADP
2088 return _createHostOnlyInterface(hModule, NETADP_ID, L"VBoxNetAdp.inf");
2089#else
2090 RT_NOREF(hModule);
2091 return ERROR_SUCCESS;
2092#endif
2093}
2094
2095UINT __stdcall Ndis6CreateHostOnlyInterface(MSIHANDLE hModule)
2096{
2097#ifdef VBOX_WITH_NETADP
2098# if 0 /* Trick for allowing the debugger to be attached. */
2099 for (unsigned i = 0; i < 128 && !IsDebuggerPresent(); i++)
2100 {
2101 logStringF(hModule, "Waiting for debugger to attach: windbg -p %u", GetCurrentProcessId());
2102 Sleep(1001);
2103 }
2104 Sleep(1002);
2105 __debugbreak();
2106# endif
2107 return _createHostOnlyInterface(hModule, NETADP_ID, L"VBoxNetAdp6.inf");
2108#else /* !VBOX_WITH_NETADP */
2109 RT_NOREF(hModule);
2110 return ERROR_SUCCESS;
2111#endif
2112}
2113
2114#ifdef VBOX_WITH_NETADP
2115static UINT _removeHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszId)
2116{
2117 netCfgLoggerEnable(hModule);
2118
2119 logStringF(hModule, "RemoveHostOnlyInterfaces: Removing all host-only interfaces");
2120
2121 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2122
2123 HRESULT hr = VBoxNetCfgWinRemoveAllNetDevicesOfId(pwszId);
2124 if (SUCCEEDED(hr))
2125 {
2126 hr = VBoxDrvCfgInfUninstallAllSetupDi(&GUID_DEVCLASS_NET, L"Net", pwszId, SUOI_FORCEDELETE/* could be SUOI_FORCEDELETE */);
2127 if (FAILED(hr))
2128 logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstalled successfully, but failed to remove INF files");
2129 else
2130 logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstalled successfully");
2131 }
2132 else
2133 logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstall failed, hr = %#x", hr);
2134
2135 /* Restore original setup mode. */
2136 if (fSetupModeInteractive)
2137 SetupSetNonInteractiveMode(fSetupModeInteractive);
2138
2139 netCfgLoggerDisable();
2140
2141 /* Never fail the uninstall even if we did not succeed. */
2142 return ERROR_SUCCESS;
2143}
2144#endif /* VBOX_WITH_NETADP */
2145
2146UINT __stdcall RemoveHostOnlyInterfaces(MSIHANDLE hModule)
2147{
2148#ifdef VBOX_WITH_NETADP
2149 return _removeHostOnlyInterfaces(hModule, NETADP_ID);
2150#else
2151 RT_NOREF(hModule);
2152 return ERROR_SUCCESS;
2153#endif
2154}
2155
2156#ifdef VBOX_WITH_NETADP
2157static UINT _stopHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszId)
2158{
2159 netCfgLoggerEnable(hModule);
2160
2161 logStringF(hModule, "StopHostOnlyInterfaces: Stopping all host-only interfaces");
2162
2163 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2164
2165 HRESULT hr = VBoxNetCfgWinPropChangeAllNetDevicesOfId(pwszId, VBOXNECTFGWINPROPCHANGE_TYPE_DISABLE);
2166 if (SUCCEEDED(hr))
2167 logStringF(hModule, "StopHostOnlyInterfaces: Disabling host interfaces was successful, hr = %#x", hr);
2168 else
2169 logStringF(hModule, "StopHostOnlyInterfaces: Disabling host interfaces failed, hr = %#x", hr);
2170
2171 /* Restore original setup mode. */
2172 if (fSetupModeInteractive)
2173 SetupSetNonInteractiveMode(fSetupModeInteractive);
2174
2175 netCfgLoggerDisable();
2176
2177 /* Never fail the uninstall even if we did not succeed. */
2178 return ERROR_SUCCESS;
2179}
2180#endif /* VBOX_WITH_NETADP */
2181
2182UINT __stdcall StopHostOnlyInterfaces(MSIHANDLE hModule)
2183{
2184#ifdef VBOX_WITH_NETADP
2185 return _stopHostOnlyInterfaces(hModule, NETADP_ID);
2186#else
2187 RT_NOREF(hModule);
2188 return ERROR_SUCCESS;
2189#endif
2190}
2191
2192#ifdef VBOX_WITH_NETADP
2193static UINT _updateHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszInfName, LPCWSTR pwszId)
2194{
2195 netCfgLoggerEnable(hModule);
2196
2197 logStringF(hModule, "UpdateHostOnlyInterfaces: Updating all host-only interfaces");
2198
2199 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2200
2201 WCHAR wszMpInf[MAX_PATH];
2202 DWORD cwcMpInf = RT_ELEMENTS(wszMpInf) - (DWORD)RTUtf16Len(pwszInfName) - 1 - 1;
2203 LPCWSTR pwszInfPath = NULL;
2204 bool fIsFile = false;
2205 UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszMpInf, &cwcMpInf);
2206 if (uErr == ERROR_SUCCESS)
2207 {
2208 if (cwcMpInf)
2209 {
2210 logStringF(hModule, "UpdateHostOnlyInterfaces: NetAdpDir property = %ls", wszMpInf);
2211 if (wszMpInf[cwcMpInf - 1] != L'\\')
2212 {
2213 wszMpInf[cwcMpInf++] = L'\\';
2214 wszMpInf[cwcMpInf] = L'\0';
2215 }
2216
2217 int vrc = RTUtf16Cat(wszMpInf, RT_ELEMENTS(wszMpInf), pwszInfName);
2218 AssertRC(vrc);
2219 pwszInfPath = wszMpInf;
2220 fIsFile = true;
2221
2222 logStringF(hModule, "UpdateHostOnlyInterfaces: Resulting INF path = %ls", pwszInfPath);
2223
2224 DWORD attrFile = GetFileAttributesW(pwszInfPath);
2225 if (attrFile == INVALID_FILE_ATTRIBUTES)
2226 {
2227 DWORD dwErr = GetLastError();
2228 logStringF(hModule, "UpdateHostOnlyInterfaces: File \"%ls\" not found, dwErr=%ld", pwszInfPath, dwErr);
2229 }
2230 else
2231 {
2232 logStringF(hModule, "UpdateHostOnlyInterfaces: File \"%ls\" exists", pwszInfPath);
2233
2234 BOOL fRebootRequired = FALSE;
2235 HRESULT hr = VBoxNetCfgWinUpdateHostOnlyNetworkInterface(pwszInfPath, &fRebootRequired, pwszId);
2236 if (SUCCEEDED(hr))
2237 {
2238 if (fRebootRequired)
2239 {
2240 logStringF(hModule, "UpdateHostOnlyInterfaces: Reboot required, setting REBOOT property to force");
2241 HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
2242 if (hr2 != ERROR_SUCCESS)
2243 logStringF(hModule, "UpdateHostOnlyInterfaces: Failed to set REBOOT property, error = %#x", hr2);
2244 }
2245 }
2246 else
2247 logStringF(hModule, "UpdateHostOnlyInterfaces: VBoxNetCfgWinUpdateHostOnlyNetworkInterface failed, hr = %#x", hr);
2248 }
2249 }
2250 else
2251 logStringF(hModule, "UpdateHostOnlyInterfaces: VBox installation path is empty");
2252 }
2253 else
2254 logStringF(hModule, "UpdateHostOnlyInterfaces: Unable to retrieve VBox installation path, error = %#x", uErr);
2255
2256 /* Restore original setup mode. */
2257 if (fSetupModeInteractive)
2258 SetupSetNonInteractiveMode(fSetupModeInteractive);
2259
2260 netCfgLoggerDisable();
2261
2262 /* Never fail the update even if we did not succeed. */
2263 return ERROR_SUCCESS;
2264}
2265#endif /* VBOX_WITH_NETADP */
2266
2267UINT __stdcall UpdateHostOnlyInterfaces(MSIHANDLE hModule)
2268{
2269#ifdef VBOX_WITH_NETADP
2270 return _updateHostOnlyInterfaces(hModule, L"VBoxNetAdp.inf", NETADP_ID);
2271#else
2272 RT_NOREF(hModule);
2273 return ERROR_SUCCESS;
2274#endif
2275}
2276
2277UINT __stdcall Ndis6UpdateHostOnlyInterfaces(MSIHANDLE hModule)
2278{
2279#ifdef VBOX_WITH_NETADP
2280 return _updateHostOnlyInterfaces(hModule, L"VBoxNetAdp6.inf", NETADP_ID);
2281#else
2282 RT_NOREF(hModule);
2283 return ERROR_SUCCESS;
2284#endif
2285}
2286
2287#ifdef VBOX_WITH_NETADP
2288static UINT _uninstallNetAdp(MSIHANDLE hModule, LPCWSTR pwszId)
2289{
2290 INetCfg *pNetCfg;
2291 UINT uErr;
2292
2293 netCfgLoggerEnable(hModule);
2294
2295 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
2296
2297 __try
2298 {
2299 logStringF(hModule, "Uninstalling NetAdp");
2300
2301 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
2302 if (uErr == ERROR_SUCCESS)
2303 {
2304 HRESULT hr = VBoxNetCfgWinNetAdpUninstall(pNetCfg, pwszId);
2305 if (hr != S_OK)
2306 logStringF(hModule, "UninstallNetAdp: VBoxNetCfgWinUninstallComponent failed, error = %#x", hr);
2307
2308 uErr = errorConvertFromHResult(hModule, hr);
2309
2310 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
2311
2312 logStringF(hModule, "Uninstalling NetAdp done, error = %#x", uErr);
2313 }
2314 else
2315 logStringF(hModule, "UninstallNetAdp: doNetCfgInit failed, error = %#x", uErr);
2316 }
2317 __finally
2318 {
2319 if (bOldIntMode)
2320 {
2321 /* The prev mode != FALSE, i.e. non-interactive. */
2322 SetupSetNonInteractiveMode(bOldIntMode);
2323 }
2324 netCfgLoggerDisable();
2325 }
2326
2327 /* Never fail the uninstall even if we did not succeed. */
2328 return ERROR_SUCCESS;
2329}
2330#endif /* VBOX_WITH_NETADP */
2331
2332UINT __stdcall UninstallNetAdp(MSIHANDLE hModule)
2333{
2334#ifdef VBOX_WITH_NETADP
2335 return _uninstallNetAdp(hModule, NETADP_ID);
2336#else
2337 RT_NOREF(hModule);
2338 return ERROR_SUCCESS;
2339#endif
2340}
2341
2342static bool isTAPDevice(const WCHAR *pwszGUID)
2343{
2344 HKEY hNetcard;
2345 bool bIsTapDevice = false;
2346 LONG lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2347 L"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}",
2348 0, KEY_READ, &hNetcard);
2349 if (lStatus != ERROR_SUCCESS)
2350 return false;
2351
2352 int i = 0;
2353 for (;;)
2354 {
2355 WCHAR wszEnumName[256];
2356 WCHAR wszNetCfgInstanceId[256];
2357 DWORD dwKeyType;
2358 HKEY hNetCardGUID;
2359
2360 DWORD dwLen = sizeof(wszEnumName);
2361 lStatus = RegEnumKeyExW(hNetcard, i, wszEnumName, &dwLen, NULL, NULL, NULL, NULL);
2362 if (lStatus != ERROR_SUCCESS)
2363 break;
2364
2365 lStatus = RegOpenKeyExW(hNetcard, wszEnumName, 0, KEY_READ, &hNetCardGUID);
2366 if (lStatus == ERROR_SUCCESS)
2367 {
2368 dwLen = sizeof(wszNetCfgInstanceId);
2369 lStatus = RegQueryValueExW(hNetCardGUID, L"NetCfgInstanceId", NULL, &dwKeyType, (LPBYTE)wszNetCfgInstanceId, &dwLen);
2370 if ( lStatus == ERROR_SUCCESS
2371 && dwKeyType == REG_SZ)
2372 {
2373 WCHAR wszNetProductName[256];
2374 WCHAR wszNetProviderName[256];
2375
2376 wszNetProductName[0] = 0;
2377 dwLen = sizeof(wszNetProductName);
2378 lStatus = RegQueryValueExW(hNetCardGUID, L"ProductName", NULL, &dwKeyType, (LPBYTE)wszNetProductName, &dwLen);
2379
2380 wszNetProviderName[0] = 0;
2381 dwLen = sizeof(wszNetProviderName);
2382 lStatus = RegQueryValueExW(hNetCardGUID, L"ProviderName", NULL, &dwKeyType, (LPBYTE)wszNetProviderName, &dwLen);
2383
2384 if ( !RTUtf16Cmp(wszNetCfgInstanceId, pwszGUID)
2385 && !RTUtf16Cmp(wszNetProductName, L"VirtualBox TAP Adapter")
2386 && ( (!RTUtf16Cmp(wszNetProviderName, L"innotek GmbH")) /* Legacy stuff. */
2387 || (!RTUtf16Cmp(wszNetProviderName, L"Sun Microsystems, Inc.")) /* Legacy stuff. */
2388 || (!RTUtf16Cmp(wszNetProviderName, MY_WTEXT(VBOX_VENDOR))) /* Reflects current vendor string. */
2389 )
2390 )
2391 {
2392 bIsTapDevice = true;
2393 RegCloseKey(hNetCardGUID);
2394 break;
2395 }
2396 }
2397 RegCloseKey(hNetCardGUID);
2398 }
2399 ++i;
2400 }
2401
2402 RegCloseKey(hNetcard);
2403 return bIsTapDevice;
2404}
2405
2406/** @todo r=andy BUGBUG WTF! Why do we a) set the rc to 0 (success), and b) need this macro at all!?
2407 *
2408 * @todo r=bird: Because it's returning a bool, not int? The return code is
2409 * ignored anyway, both internally in removeNetworkInterface and in it's caller.
2410 * There is similar code in VBoxNetCfg.cpp, which is probably where it was copied from. */
2411#define SetErrBreak(args) \
2412 if (1) { \
2413 rc = 0; \
2414 logStringF args; \
2415 break; \
2416 } else do {} while (0)
2417
2418int removeNetworkInterface(MSIHANDLE hModule, const WCHAR *pwszGUID)
2419{
2420 int rc = 1;
2421 do /* break-loop */
2422 {
2423 WCHAR wszPnPInstanceId[512] = {0};
2424
2425 /* We have to find the device instance ID through a registry search */
2426
2427 HKEY hkeyNetwork = 0;
2428 HKEY hkeyConnection = 0;
2429
2430 do /* break-loop */
2431 {
2432 WCHAR wszRegLocation[256];
2433 RTUtf16Printf(wszRegLocation, RT_ELEMENTS(wszRegLocation),
2434 "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\%ls", pwszGUID);
2435 LONG lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRegLocation, 0, KEY_READ, &hkeyNetwork);
2436 if (lrc != ERROR_SUCCESS || !hkeyNetwork)
2437 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [1]",
2438 wszRegLocation, lrc));
2439
2440 lrc = RegOpenKeyExW(hkeyNetwork, L"Connection", 0, KEY_READ, &hkeyConnection);
2441 if (lrc != ERROR_SUCCESS || !hkeyConnection)
2442 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [2]",
2443 wszRegLocation, lrc));
2444
2445 DWORD len = sizeof(wszPnPInstanceId);
2446 DWORD dwKeyType;
2447 lrc = RegQueryValueExW(hkeyConnection, L"PnPInstanceID", NULL, &dwKeyType, (LPBYTE)&wszPnPInstanceId[0], &len);
2448 if (lrc != ERROR_SUCCESS || dwKeyType != REG_SZ)
2449 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [3]",
2450 wszRegLocation, lrc));
2451 }
2452 while (0);
2453
2454 if (hkeyConnection)
2455 RegCloseKey(hkeyConnection);
2456 if (hkeyNetwork)
2457 RegCloseKey(hkeyNetwork);
2458
2459 /*
2460 * Now we are going to enumerate all network devices and
2461 * wait until we encounter the right device instance ID
2462 */
2463
2464 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2465 BOOL fResult;
2466
2467 do /* break-loop */
2468 {
2469 /* initialize the structure size */
2470 SP_DEVINFO_DATA DeviceInfoData = { sizeof(DeviceInfoData) };
2471
2472 /* copy the net class GUID */
2473 GUID netGuid;
2474 memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof (GUID_DEVCLASS_NET));
2475
2476 /* return a device info set contains all installed devices of the Net class */
2477 hDeviceInfo = SetupDiGetClassDevs(&netGuid, NULL, NULL, DIGCF_PRESENT);
2478 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2479 {
2480 logStringF(hModule, "VBox HostInterfaces: SetupDiGetClassDevs failed (0x%08X)!", GetLastError());
2481 SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
2482 }
2483
2484 /* enumerate the driver info list */
2485 BOOL fFoundDevice = FALSE;
2486 for (DWORD index = 0;; index++)
2487 {
2488 fResult = SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData);
2489 if (!fResult)
2490 {
2491 if (GetLastError() == ERROR_NO_MORE_ITEMS)
2492 break;
2493 continue;
2494 }
2495
2496 /* try to get the hardware ID registry property */
2497 WCHAR *pwszDeviceHwid;
2498 DWORD size = 0;
2499 fResult = SetupDiGetDeviceRegistryProperty(hDeviceInfo,
2500 &DeviceInfoData,
2501 SPDRP_HARDWAREID,
2502 NULL,
2503 NULL,
2504 0,
2505 &size);
2506 if (!fResult)
2507 {
2508 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2509 continue;
2510
2511 pwszDeviceHwid = (WCHAR *)RTMemAllocZ(size);
2512 if (!pwszDeviceHwid)
2513 continue;
2514
2515 fResult = SetupDiGetDeviceRegistryProperty(hDeviceInfo,
2516 &DeviceInfoData,
2517 SPDRP_HARDWAREID,
2518 NULL,
2519 (PBYTE)pwszDeviceHwid,
2520 size,
2521 &size);
2522 if (!fResult)
2523 {
2524 RTMemFree(pwszDeviceHwid);
2525 continue;
2526 }
2527 }
2528 else
2529 {
2530 /* something is wrong. This shouldn't have worked with a NULL buffer */
2531 continue;
2532 }
2533
2534 for (WCHAR *t = pwszDeviceHwid;
2535 *t && t < &pwszDeviceHwid[size / sizeof(WCHAR)];
2536 t += RTUtf16Len(t) + 1)
2537 {
2538 if (RTUtf16ICmpAscii(t, "vboxtap") == 0)
2539 {
2540 /* get the device instance ID */
2541 WCHAR wszDevID[MAX_DEVICE_ID_LEN];
2542 if (CM_Get_Device_IDW(DeviceInfoData.DevInst, wszDevID, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS)
2543 {
2544 /* compare to what we determined before */
2545 if (RTUtf16Cmp(wszDevID, wszPnPInstanceId) == 0)
2546 {
2547 fFoundDevice = TRUE;
2548 break;
2549 }
2550 }
2551 }
2552 }
2553
2554 RTMemFree(pwszDeviceHwid);
2555
2556 if (fFoundDevice)
2557 break;
2558 }
2559
2560 if (fFoundDevice)
2561 {
2562 fResult = SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData);
2563 if (!fResult)
2564 {
2565 logStringF(hModule, "VBox HostInterfaces: SetupDiSetSelectedDevice failed (0x%08X)!", GetLastError());
2566 SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
2567 }
2568
2569 fResult = SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2570 if (!fResult)
2571 {
2572 logStringF(hModule, "VBox HostInterfaces: SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)!", GetLastError());
2573 SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
2574 }
2575 }
2576 else
2577 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network device not found!"));
2578 } while (0);
2579
2580 /* clean up the device info set */
2581 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2582 SetupDiDestroyDeviceInfoList(hDeviceInfo);
2583 } while (0);
2584 return rc;
2585}
2586
2587UINT __stdcall UninstallTAPInstances(MSIHANDLE hModule)
2588{
2589 static const wchar_t s_wszNetworkKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}";
2590 HKEY hCtrlNet;
2591
2592 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_wszNetworkKey, 0, KEY_READ, &hCtrlNet);
2593 if (lrc == ERROR_SUCCESS)
2594 {
2595 logStringF(hModule, "VBox HostInterfaces: Enumerating interfaces ...");
2596 for (int i = 0; ; ++i)
2597 {
2598 WCHAR wszNetworkGUID[256] = { 0 };
2599 DWORD dwLen = (DWORD)sizeof(wszNetworkGUID);
2600 lrc = RegEnumKeyExW(hCtrlNet, i, wszNetworkGUID, &dwLen, NULL, NULL, NULL, NULL);
2601 if (lrc != ERROR_SUCCESS)
2602 {
2603 switch (lrc)
2604 {
2605 case ERROR_NO_MORE_ITEMS:
2606 logStringF(hModule, "VBox HostInterfaces: No interfaces found.");
2607 break;
2608 default:
2609 logStringF(hModule, "VBox HostInterfaces: Enumeration failed: %ld", lrc);
2610 break;
2611 }
2612 break;
2613 }
2614
2615 if (isTAPDevice(wszNetworkGUID))
2616 {
2617 logStringF(hModule, "VBox HostInterfaces: Removing interface \"%ls\" ...", wszNetworkGUID);
2618 removeNetworkInterface(hModule, wszNetworkGUID);
2619 lrc = RegDeleteKeyW(hCtrlNet, wszNetworkGUID);
2620 }
2621 }
2622 RegCloseKey(hCtrlNet);
2623 logStringF(hModule, "VBox HostInterfaces: Removing interfaces done.");
2624 }
2625 return ERROR_SUCCESS;
2626}
2627
2628
2629/**
2630 * This is used to remove the old VBoxDrv service before installation.
2631 *
2632 * The current service name is VBoxSup but the INF file won't remove the old
2633 * one, so we do it manually to try prevent trouble as the device nodes are the
2634 * same and we would fail starting VBoxSup.sys if VBoxDrv.sys is still loading.
2635 *
2636 * Status code is ignored for now as a reboot should fix most potential trouble
2637 * here (and I don't want to break stuff too badly).
2638 *
2639 * @sa @bugref{10162}
2640 */
2641UINT __stdcall UninstallVBoxDrv(MSIHANDLE hModule)
2642{
2643 /*
2644 * Try open the service.
2645 */
2646 SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG | SERVICE_STOP | SERVICE_QUERY_STATUS);
2647 if (hSMgr)
2648 {
2649 SC_HANDLE hService = OpenServiceW(hSMgr, L"VBoxDrv", DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
2650 if (hService)
2651 {
2652 /*
2653 * Try stop it before we delete it.
2654 */
2655 SERVICE_STATUS Status = { 0, 0, 0, 0, 0, 0, 0 };
2656 QueryServiceStatus(hService, &Status);
2657 if (Status.dwCurrentState == SERVICE_STOPPED)
2658 logStringF(hModule, "VBoxDrv: The service old service was already stopped");
2659 else
2660 {
2661 logStringF(hModule, "VBoxDrv: Stopping the service (state %u)", Status.dwCurrentState);
2662 if (ControlService(hService, SERVICE_CONTROL_STOP, &Status))
2663 {
2664 /* waiting for it to stop: */
2665 int iWait = 100;
2666 while (Status.dwCurrentState == SERVICE_STOP_PENDING && iWait-- > 0)
2667 {
2668 Sleep(100);
2669 QueryServiceStatus(hService, &Status);
2670 }
2671
2672 if (Status.dwCurrentState == SERVICE_STOPPED)
2673 logStringF(hModule, "VBoxDrv: Stopped service");
2674 else
2675 logStringF(hModule, "VBoxDrv: Failed to stop the service, status: %u", Status.dwCurrentState);
2676 }
2677 else
2678 {
2679 DWORD const dwErr = GetLastError();
2680 if ( Status.dwCurrentState == SERVICE_STOP_PENDING
2681 && dwErr == ERROR_SERVICE_CANNOT_ACCEPT_CTRL)
2682 logStringF(hModule, "VBoxDrv: Failed to stop the service: stop pending, not accepting control messages");
2683 else
2684 logStringF(hModule, "VBoxDrv: Failed to stop the service: dwErr=%u status=%u", dwErr, Status.dwCurrentState);
2685 }
2686 }
2687
2688 /*
2689 * Delete the service, or at least mark it for deletion.
2690 */
2691 if (DeleteService(hService))
2692 logStringF(hModule, "VBoxDrv: Successfully delete service");
2693 else
2694 logStringF(hModule, "VBoxDrv: Failed to delete the service: %u", GetLastError());
2695
2696 CloseServiceHandle(hService);
2697 }
2698 else
2699 {
2700 DWORD const dwErr = GetLastError();
2701 if (dwErr == ERROR_SERVICE_DOES_NOT_EXIST)
2702 logStringF(hModule, "VBoxDrv: Nothing to do, the old service does not exist");
2703 else
2704 logStringF(hModule, "VBoxDrv: Failed to open the service: %u", dwErr);
2705 }
2706
2707 CloseServiceHandle(hSMgr);
2708 }
2709 else
2710 logStringF(hModule, "VBoxDrv: Failed to open service manager (%u).", GetLastError());
2711
2712 return ERROR_SUCCESS;
2713}
2714
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use