VirtualBox

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

最後變更 在這個檔案從104165是 104165,由 vboxsync 提交於 12 月 前

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

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 98.4 KB
 
1/* $Id: VBoxInstallHelper.cpp 104165 2024-04-04 17:26:51Z 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.alldomusa.eu.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 LPWSTR pwszSid = NULL;
581 ConvertSidToStringSid((PSID)&pAce->SidStart, &pwszSid);
582
583 logStringF(hModule, "checkTargetDirOne:\t%ls fMask=%#x (generic %#x specific %#x)",
584 pwszSid ? pwszSid : L"<Allocation Error>", pAce->Mask);
585
586 LocalFree(pwszSid);
587 break;
588 }
589#endif /* DEBUG */
590 default:
591 /* Ignore everything else. */
592 break;
593 }
594 }
595 else
596 dwErr = GetLastError();
597
598 /* No point in checking further if we failed somewhere above. */
599 if (RT_FAILURE(vrc))
600 break;
601
602 } /* for ACE */
603 }
604 else
605 dwErr = GetLastError();
606 }
607 }
608 else
609 dwErr = GetLastError();
610
611 LocalFree(pSecurityDescriptor);
612 }
613 else
614 dwErr = GetLastError();
615
616 if (RT_SUCCESS(vrc))
617 vrc = RTErrConvertFromWin32(dwErr);
618
619#ifdef DEBUG
620 logStringF(hModule, "checkTargetDirOne: Returning %Rrc", vrc);
621#endif
622
623 if ( RT_FAILURE(vrc)
624 && vrc != VERR_INVALID_NAME)
625 logStringF(hModule, "checkTargetDirOne: Failed with %Rrc (%#x)", vrc, dwErr);
626
627 return vrc;
628}
629
630/**
631 * Checks whether the path in the public property INSTALLDIR has the correct ACL permissions and returns whether
632 * it's valid or not.
633 *
634 * Called from the MSI installer as a custom action.
635 *
636 * @returns Success status (acccording to MSI custom actions).
637 * @retval ERROR_SUCCESS if checking the target directory turned out to be valid.
638 * @retval ERROR_NO_NET_OR_BAD_PATH is the target directory is invalid.
639 * @param hModule Windows installer module handle.
640 *
641 * @note Sets private property VBox_Target_Dir_Is_Valid to "1" (true) if the given target path is valid,
642 * or "0" (false) if it is not. An empty target directory is considered to be valid (i.e. INSTALLDIR not set yet).
643 *
644 * @sa @bugref{10616}
645 */
646UINT __stdcall CheckTargetDir(MSIHANDLE hModule)
647{
648 char *pszTargetDir;
649
650 int vrc = VBoxGetMsiPropUtf8(hModule, "INSTALLDIR", &pszTargetDir);
651 if (RT_SUCCESS(vrc))
652 {
653 logStringF(hModule, "CheckTargetDir: Checking target directory '%s' ...", pszTargetDir);
654
655 if (!RTStrNLen(pszTargetDir, RTPATH_MAX))
656 {
657 logStringF(hModule, "CheckTargetDir: No INSTALLDIR set (yet), skipping ...");
658 VBoxSetMsiProp(hModule, L"VBox_Target_Dir_Is_Valid", L"1");
659 }
660 else
661 {
662 union
663 {
664 RTPATHPARSED Parsed;
665 uint8_t ab[RTPATH_MAX];
666 } u;
667
668 vrc = RTPathParse(pszTargetDir, &u.Parsed, sizeof(u), RTPATH_STR_F_STYLE_DOS);
669 if (RT_SUCCESS(vrc))
670 {
671 if (u.Parsed.fProps & RTPATH_PROP_DOTDOT_REFS)
672 vrc = VERR_INVALID_PARAMETER;
673 if (RT_SUCCESS(vrc))
674 {
675 vrc = initTargetDirSecurityCtx(&g_TargetDirSecCtx, hModule, pszTargetDir);
676 if (RT_SUCCESS(vrc))
677 {
678 uint16_t idxComp = u.Parsed.cComps;
679 char szPathToCheck[RTPATH_MAX];
680 while (idxComp > 1) /* We traverse backwards from INSTALLDIR and leave out the root (e.g. C:\"). */
681 {
682 u.Parsed.cComps = idxComp;
683 vrc = RTPathParsedReassemble(pszTargetDir, &u.Parsed, RTPATH_STR_F_STYLE_DOS,
684 szPathToCheck, sizeof(szPathToCheck));
685 if (RT_FAILURE(vrc))
686 break;
687 if (RTDirExists(szPathToCheck))
688 {
689 vrc = checkTargetDirOne(hModule, &g_TargetDirSecCtx, szPathToCheck);
690 if (RT_FAILURE(vrc))
691 break;
692 }
693 else
694 logStringF(hModule, "CheckTargetDir: Path '%s' does not exist (yet)", szPathToCheck);
695 idxComp--;
696 }
697
698 destroyTargetDirSecurityCtx(&g_TargetDirSecCtx);
699 }
700 else
701 logStringF(hModule, "CheckTargetDir: initTargetDirSecurityCtx failed with %Rrc\n", vrc);
702
703 if (RT_SUCCESS(vrc))
704 VBoxSetMsiProp(hModule, L"VBox_Target_Dir_Is_Valid", L"1");
705 }
706 }
707 else
708 logStringF(hModule, "CheckTargetDir: Parsing path failed with %Rrc", vrc);
709 }
710
711 RTStrFree(pszTargetDir);
712 }
713
714 if (RT_FAILURE(vrc)) /* On failure (or when in doubt), mark the installation directory as invalid. */
715 {
716 logStringF(hModule, "CheckTargetDir: Checking failed with %Rrc", vrc);
717 VBoxSetMsiProp(hModule, L"VBox_Target_Dir_Is_Valid", L"0");
718 }
719
720 /* Return back outcome to the MSI engine. */
721 return RT_SUCCESS(vrc) ? ERROR_SUCCESS : ERROR_NO_NET_OR_BAD_PATH;
722}
723
724/**
725 * Runs an executable on the OS.
726 *
727 * @returns Windows error code.
728 * @param hModule Windows installer module handle.
729 * @param pwszImage The executable to run.
730 * @param pwszArgs The arguments (command line w/o executable).
731 */
732static UINT procRun(MSIHANDLE hModule, const wchar_t *pwszImage, wchar_t const *pwszArgs)
733{
734 /*
735 * Construct a full command line.
736 */
737 size_t const cwcImage = RTUtf16Len(pwszImage);
738 size_t const cwcArgs = RTUtf16Len(pwszArgs);
739
740 wchar_t *pwszCmdLine = (wchar_t *)alloca((1 + cwcImage + 1 + 1 + cwcArgs + 1) * sizeof(wchar_t));
741 pwszCmdLine[0] = '"';
742 memcpy(&pwszCmdLine[1], pwszImage, cwcImage * sizeof(wchar_t));
743 pwszCmdLine[1 + cwcImage] = '"';
744 pwszCmdLine[1 + cwcImage + 1] = ' ';
745 memcpy(&pwszCmdLine[1 + cwcImage + 1 + 1], pwszArgs, (cwcArgs + 1) * sizeof(wchar_t));
746
747 /*
748 * Construct startup info.
749 */
750 STARTUPINFOW StartupInfo;
751 RT_ZERO(StartupInfo);
752 StartupInfo.cb = sizeof(StartupInfo);
753 StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
754 StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
755 StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
756 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
757#ifndef DEBUG
758 StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
759 StartupInfo.wShowWindow = SW_HIDE;
760#endif
761
762 /*
763 * Start it.
764 */
765 UINT rcWin;
766 PROCESS_INFORMATION ChildInfo = { NULL, NULL, 0, 0 };
767 if (CreateProcessW(pwszImage, pwszCmdLine, NULL /*pProcessAttribs*/, NULL /*pThreadAttribs*/, TRUE /*fInheritHandles*/,
768 0 /*fFlags*/, NULL /*pwszEnv*/, NULL /*pwszCwd*/, &StartupInfo, &ChildInfo))
769 {
770 logStringF(hModule, "procRun: Info: Started process %u: %ls", ChildInfo.dwProcessId, pwszCmdLine);
771 CloseHandle(ChildInfo.hThread);
772 DWORD const dwWait = WaitForSingleObject(ChildInfo.hProcess, RT_MS_30SEC);
773 DWORD dwExitCode = 0xf00dface;
774 if (GetExitCodeProcess(ChildInfo.hProcess, &dwExitCode))
775 {
776 if (dwExitCode == 0)
777 {
778 logStringF(hModule, "procRun: Info: Process '%ls' terminated exit code zero", pwszCmdLine);
779 rcWin = ERROR_SUCCESS;
780 }
781 else
782 {
783 logStringF(hModule, "procRun: Process '%ls' terminated with non-zero exit code: %u (%#x)",
784 pwszCmdLine, dwExitCode, dwExitCode);
785 rcWin = ERROR_GEN_FAILURE;
786 }
787 }
788 else
789 {
790 rcWin = GetLastError();
791 logStringF(hModule, "procRun: Process '%ls' is probably still running: rcWin=%u dwWait=%u (%#x)",
792 pwszCmdLine, rcWin, dwWait, dwWait);
793 }
794 }
795 else
796 {
797 rcWin = GetLastError();
798 logStringF(hModule, "procRun: Creating process '%ls' failed: rcWin=%u\n", pwszCmdLine, rcWin);
799 }
800 return rcWin;
801}
802
803/**
804 * Tries to retrieve the Python installation path on the system, extended version.
805 *
806 * @returns Windows error code.
807 * @param hModule Windows installer module handle.
808 * @param hKeyRoot Registry root key to use, e.g. HKEY_LOCAL_MACHINE.
809 * @param pwszPythonPath Buffer to return the path for python.exe in.
810 * @param cwcPythonPath Buffer size in UTF-16 units.
811 * @param fReturnExe Return the path to python.exe if true, otherwise
812 * just the python install directory.
813 */
814static UINT getPythonPathEx(MSIHANDLE hModule, HKEY hKeyRoot, wchar_t *pwszPythonPath, size_t cwcPythonPath, bool fReturnExe)
815{
816 *pwszPythonPath = '\0';
817
818 /*
819 * Enumerate the subkeys of python core installation key.
820 *
821 * Note: The loop ASSUMES that later found versions are higher, e.g. newer
822 * Python versions. For now we always go by the newest version.
823 */
824 HKEY hKeyPythonCore = NULL;
825 LSTATUS dwErr = RegOpenKeyExW(hKeyRoot, L"SOFTWARE\\Python\\PythonCore", 0, KEY_READ, &hKeyPythonCore);
826 if (dwErr != ERROR_SUCCESS)
827 return dwErr;
828
829 UINT rcWinRet = ERROR_PATH_NOT_FOUND;
830 for (DWORD i = 0; i < 16384; ++i)
831 {
832 static wchar_t const s_wszInstallPath[] = L"\\InstallPath";
833 static wchar_t const s_wszPythonExe[] = L"python.exe";
834
835 /* Get key name: */
836 wchar_t wszBuf[RTPATH_MAX + RT_MAX(RT_ELEMENTS(s_wszInstallPath), RT_ELEMENTS(s_wszPythonExe)) + 2];
837 DWORD cwcKeyNm = RTPATH_MAX;
838 DWORD dwKeyType = REG_SZ;
839 dwErr = RegEnumKeyExW(hKeyPythonCore, i, wszBuf, &cwcKeyNm, NULL, NULL, NULL, NULL);
840 if (dwErr == ERROR_NO_MORE_ITEMS)
841 break;
842 if (dwErr != ERROR_SUCCESS)
843 continue;
844 if (dwKeyType != REG_SZ)
845 continue;
846 if (cwcKeyNm == 0)
847 continue;
848 NonStandardAssert(cwcKeyNm <= sizeof(wszBuf));
849
850 /* Try Open the InstallPath subkey: */
851 memcpy(&wszBuf[cwcKeyNm], s_wszInstallPath, sizeof(s_wszInstallPath));
852
853 HKEY hKeyInstallPath = NULL;
854 dwErr = RegOpenKeyExW(hKeyPythonCore, wszBuf, 0, KEY_READ, &hKeyInstallPath);
855 if (dwErr != ERROR_SUCCESS)
856 continue;
857
858 /* Query the value. We double buffer this so we don't overwrite an okay
859 return value with this. Use the smaller of cwcPythonPath and wszValue
860 so RegQueryValueExW can do all the buffer overflow checking for us.
861 For paranoid reasons, we reserve a space for a terminator as well as
862 a slash. (ASSUMES reasonably sized output buffer.) */
863 NonStandardAssert(cwcPythonPath > RT_ELEMENTS(s_wszPythonExe) + 16);
864 DWORD cbValue = (DWORD)RT_MIN( cwcPythonPath * sizeof(wchar_t)
865 - (fReturnExe ? sizeof(s_wszInstallPath) - sizeof(wchar_t) * 2 : sizeof(wchar_t) * 2),
866 RTPATH_MAX * sizeof(wchar_t));
867 DWORD dwValueType = REG_SZ;
868 dwErr = RegQueryValueExW(hKeyInstallPath, L"", NULL, &dwValueType, (LPBYTE)wszBuf, &cbValue);
869 RegCloseKey(hKeyInstallPath);
870 if ( dwErr == ERROR_SUCCESS
871 && dwValueType == REG_SZ
872 && cbValue >= sizeof(L"C:\\") - sizeof(L""))
873 {
874 /* Find length in wchar_t unit w/o terminator: */
875 DWORD cwc = cbValue / sizeof(wchar_t);
876 while (cwc > 0 && wszBuf[cwc - 1] == '\0')
877 cwc--;
878 wszBuf[cwc] = '\0';
879 if (cwc > 2)
880 {
881 /* Check if the path leads to a directory with a python.exe file in it. */
882 if (!RTPATH_IS_SLASH(wszBuf[cwc - 1]))
883 wszBuf[cwc++] = '\\';
884 memcpy(&wszBuf[cwc], s_wszPythonExe, sizeof(s_wszPythonExe));
885 DWORD const fAttribs = GetFileAttributesW(wszBuf);
886 if (fAttribs != INVALID_FILE_ATTRIBUTES)
887 {
888 if (!(fAttribs & FILE_ATTRIBUTE_DIRECTORY))
889 {
890 /* Okay, we found something that can be returned. */
891 if (fReturnExe)
892 cwc += RT_ELEMENTS(s_wszPythonExe) - 1;
893 wszBuf[cwc] = '\0';
894 logStringF(hModule, "getPythonPath: Found: \"%ls\"", wszBuf);
895
896 NonStandardAssert(cwcPythonPath > cwc);
897 memcpy(pwszPythonPath, wszBuf, cwc * sizeof(wchar_t));
898 pwszPythonPath[cwc] = '\0';
899 rcWinRet = ERROR_SUCCESS;
900 }
901 else
902 logStringF(hModule, "getPythonPath: Warning: Skipping \"%ls\": is a directory (%#x)", wszBuf, fAttribs);
903 }
904 else
905 logStringF(hModule, "getPythonPath: Warning: Skipping \"%ls\": Does not exist (%u)", wszBuf, GetLastError());
906 }
907 }
908 }
909
910 RegCloseKey(hKeyPythonCore);
911 if (rcWinRet != ERROR_SUCCESS)
912 logStringF(hModule, "getPythonPath: Unable to find python");
913 return rcWinRet;
914}
915
916/**
917 * Retrieves the absolute path of the Python installation.
918 *
919 * @returns Windows error code.
920 * @param hModule Windows installer module handle.
921 * @param pwszPythonPath Buffer to return the path for python.exe in.
922 * @param cwcPythonPath Buffer size in UTF-16 units.
923 * @param fReturnExe Return the path to python.exe if true, otherwise
924 * just the python install directory.
925 */
926static UINT getPythonPath(MSIHANDLE hModule, wchar_t *pwszPythonPath, size_t cwcPythonPath, bool fReturnExe = false)
927{
928 UINT rcWin = getPythonPathEx(hModule, HKEY_LOCAL_MACHINE, pwszPythonPath, cwcPythonPath, fReturnExe);
929 if (rcWin != ERROR_SUCCESS)
930 rcWin = getPythonPathEx(hModule, HKEY_CURRENT_USER, pwszPythonPath, cwcPythonPath, fReturnExe);
931 return rcWin;
932}
933
934/**
935 * Retrieves the absolute path of the Python executable.
936 *
937 * @returns Windows error code.
938 * @param hModule Windows installer module handle.
939 * @param pwszPythonExe Buffer to return the path for python.exe in.
940 * @param cwcPythonExe Buffer size in UTF-16 units.
941 */
942static UINT getPythonExe(MSIHANDLE hModule, wchar_t *pwszPythonExe, size_t cwcPythonExe)
943{
944 return getPythonPath(hModule, pwszPythonExe, cwcPythonExe, true /*fReturnExe*/);
945}
946
947/**
948 * Checks if all dependencies for running the VBox Python API bindings are met.
949 *
950 * @returns VBox status code, or error if depedencies are not met.
951 * @param hModule Windows installer module handle.
952 * @param pwszPythonExe Path to Python interpreter image (.exe).
953 */
954static int checkPythonDependencies(MSIHANDLE hModule, const wchar_t *pwszPythonExe)
955{
956 /*
957 * Check if importing the win32api module works.
958 * This is a prerequisite for setting up the VBox API.
959 */
960 logStringF(hModule, "checkPythonDependencies: Checking for win32api extensions ...");
961
962 UINT rcWin = procRun(hModule, pwszPythonExe, L"-c \"import win32api\"");
963 if (rcWin == ERROR_SUCCESS)
964 logStringF(hModule, "checkPythonDependencies: win32api found\n");
965 else
966 logStringF(hModule, "checkPythonDependencies: Importing win32api failed with %u (%#x)\n", rcWin, rcWin);
967
968 return rcWin;
969}
970
971/**
972 * Checks for a valid Python installation on the system.
973 *
974 * Called from the MSI installer as custom action.
975 *
976 * @returns Always ERROR_SUCCESS.
977 * Sets public property VBOX_PYTHON_INSTALLED to "0" (false) or "1" (success).
978 * Sets public property VBOX_PYTHON_PATH to the Python installation path (if found).
979 *
980 * @param hModule Windows installer module handle.
981 */
982UINT __stdcall IsPythonInstalled(MSIHANDLE hModule)
983{
984 wchar_t wszPythonPath[RTPATH_MAX];
985 UINT rcWin = getPythonPath(hModule, wszPythonPath, RTPATH_MAX);
986 if (rcWin == ERROR_SUCCESS)
987 {
988 logStringF(hModule, "IsPythonInstalled: Python installation found at \"%ls\"", wszPythonPath);
989 VBoxSetMsiProp(hModule, L"VBOX_PYTHON_PATH", wszPythonPath);
990 VBoxSetMsiProp(hModule, L"VBOX_PYTHON_INSTALLED", L"1");
991 }
992 else
993 {
994 logStringF(hModule, "IsPythonInstalled: Error: No suitable Python installation found (%u), skipping installation.", rcWin);
995 logStringF(hModule, "IsPythonInstalled: Python seems not to be installed; please download + install the Python Core package.");
996 VBoxSetMsiProp(hModule, L"VBOX_PYTHON_INSTALLED", L"0");
997 }
998
999 return ERROR_SUCCESS; /* Never return failure. */
1000}
1001
1002/**
1003 * Checks if all dependencies for running the VBox Python API bindings are met.
1004 *
1005 * Called from the MSI installer as custom action.
1006 *
1007 * @returns Always ERROR_SUCCESS.
1008 * Sets public property VBOX_PYTHON_DEPS_INSTALLED to "0" (false) or "1" (success).
1009 *
1010 * @param hModule Windows installer module handle.
1011 */
1012UINT __stdcall ArePythonAPIDepsInstalled(MSIHANDLE hModule)
1013{
1014 wchar_t wszPythonExe[RTPATH_MAX];
1015 UINT dwErr = getPythonExe(hModule, wszPythonExe, RTPATH_MAX);
1016 if (dwErr == ERROR_SUCCESS)
1017 {
1018 dwErr = checkPythonDependencies(hModule, wszPythonExe);
1019 if (dwErr == ERROR_SUCCESS)
1020 logStringF(hModule, "ArePythonAPIDepsInstalled: Dependencies look good.");
1021 }
1022
1023 if (dwErr != ERROR_SUCCESS)
1024 logStringF(hModule, "ArePythonAPIDepsInstalled: Failed with dwErr=%u", dwErr);
1025
1026 VBoxSetMsiProp(hModule, L"VBOX_PYTHON_DEPS_INSTALLED", dwErr == ERROR_SUCCESS ? L"1" : L"0");
1027 return ERROR_SUCCESS; /* Never return failure. */
1028}
1029
1030/**
1031 * Checks if all required MS CRTs (Visual Studio Redistributable Package) are installed on the system.
1032 *
1033 * Called from the MSI installer as custom action.
1034 *
1035 * @returns Always ERROR_SUCCESS.
1036 * Sets public property VBOX_MSCRT_INSTALLED to "" (false, to use "NOT" in WiX) or "1" (success).
1037 *
1038 * Also exposes public properties VBOX_MSCRT_VER_MIN + VBOX_MSCRT_VER_MAJ strings
1039 * with the most recent MSCRT version detected.
1040 *
1041 * @param hModule Windows installer module handle.
1042 *
1043 * @sa https://docs.microsoft.com/en-us/cpp/windows/redistributing-visual-cpp-files?view=msvc-170
1044 */
1045UINT __stdcall IsMSCRTInstalled(MSIHANDLE hModule)
1046{
1047 HKEY hKeyVS = NULL;
1048 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1049 L"SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\X64",
1050 0, KEY_READ, &hKeyVS);
1051 if (lrc == ERROR_SUCCESS)
1052 {
1053 DWORD dwVal = 0;
1054 DWORD cbVal = sizeof(dwVal);
1055 DWORD dwValueType = REG_DWORD; /** @todo r=bird: output only parameter, optional, so pointless. */
1056 lrc = RegQueryValueExW(hKeyVS, L"Installed", NULL, &dwValueType, (LPBYTE)&dwVal, &cbVal);
1057 if (lrc == ERROR_SUCCESS)
1058 {
1059 if (dwVal >= 1)
1060 {
1061 DWORD dwMaj = 0; /** @todo r=bird: It's purdent to initialize values if you don't bother to check the type and size! */
1062 lrc = RegQueryValueExW(hKeyVS, L"Major", NULL, &dwValueType, (LPBYTE)&dwMaj, &cbVal);
1063 if (lrc == ERROR_SUCCESS)
1064 {
1065 VBoxSetMsiPropDWORD(hModule, L"VBOX_MSCRT_VER_MAJ", dwMaj);
1066
1067 DWORD dwMin = 0;
1068 lrc = RegQueryValueExW(hKeyVS, L"Minor", NULL, &dwValueType, (LPBYTE)&dwMin, &cbVal);
1069 if (lrc == ERROR_SUCCESS)
1070 {
1071 VBoxSetMsiPropDWORD(hModule, L"VBOX_MSCRT_VER_MIN", dwMin);
1072
1073 logStringF(hModule, "IsMSCRTInstalled: Found v%u.%u\n", dwMaj, dwMin);
1074
1075 /* Check for at least 2019. */
1076 if (dwMaj > 14 || (dwMaj == 14 && dwMin >= 20))
1077 VBoxSetMsiProp(hModule, L"VBOX_MSCRT_INSTALLED", L"1");
1078 }
1079 else
1080 logStringF(hModule, "IsMSCRTInstalled: Found, but 'Minor' key not present (lrc=%d)", lrc);
1081 }
1082 else
1083 logStringF(hModule, "IsMSCRTInstalled: Found, but 'Major' key not present (lrc=%d)", lrc);
1084 }
1085 else
1086 {
1087 logStringF(hModule, "IsMSCRTInstalled: Found, but not marked as installed");
1088 lrc = ERROR_NOT_INSTALLED;
1089 }
1090 }
1091 else
1092 logStringF(hModule, "IsMSCRTInstalled: Found, but 'Installed' key not present (lrc=%d)", lrc);
1093 }
1094
1095 if (lrc != ERROR_SUCCESS)
1096 logStringF(hModule, "IsMSCRTInstalled: Failed with lrc=%ld", lrc);
1097
1098 return ERROR_SUCCESS; /* Never return failure. */
1099}
1100
1101/**
1102 * Checks if the running OS is (at least) Windows 10 (e.g. >= build 10000).
1103 *
1104 * Called from the MSI installer as custom action.
1105 *
1106 * @returns Always ERROR_SUCCESS.
1107 * Sets public property VBOX_IS_WINDOWS_10 to "" (empty / false) or "1" (success).
1108 *
1109 * @param hModule Windows installer module handle.
1110 */
1111UINT __stdcall IsWindows10(MSIHANDLE hModule)
1112{
1113 /*
1114 * Note: We cannot use RtlGetVersion() / GetVersionExW() here, as the Windows Installer service
1115 * all shims this, unfortunately. So we have to go another route by querying the major version
1116 * number from the registry.
1117 */
1118 HKEY hKeyCurVer = NULL;
1119 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKeyCurVer);
1120 if (lrc == ERROR_SUCCESS)
1121 {
1122 DWORD dwVal = 0;
1123 DWORD cbVal = sizeof(dwVal);
1124 DWORD dwValueType = REG_DWORD; /** @todo r=bird: Again, the type is an optional output parameter. pointless to init or pass it unless you check. */
1125 lrc = RegQueryValueExW(hKeyCurVer, L"CurrentMajorVersionNumber", NULL, &dwValueType, (LPBYTE)&dwVal, &cbVal);
1126 if (lrc == ERROR_SUCCESS)
1127 {
1128 logStringF(hModule, "IsWindows10/CurrentMajorVersionNumber: %u", dwVal);
1129
1130 VBoxSetMsiProp(hModule, L"VBOX_IS_WINDOWS_10", dwVal >= 10 ? L"1" : L"");
1131 }
1132 else
1133 logStringF(hModule, "IsWindows10/RegOpenKeyExW: Error reading CurrentMajorVersionNumber (%ld)", lrc);
1134
1135 RegCloseKey(hKeyCurVer);
1136 }
1137 else
1138 logStringF(hModule, "IsWindows10/RegOpenKeyExW: Error opening CurrentVersion key (%ld)", lrc);
1139
1140 return ERROR_SUCCESS; /* Never return failure. */
1141}
1142
1143/**
1144 * Installs and compiles the VBox Python bindings.
1145 *
1146 * Called from the MSI installer as custom action.
1147 *
1148 * @returns Always ERROR_SUCCESS.
1149 * Sets public property VBOX_API_INSTALLED to "0" (false) or "1" (success).
1150 *
1151 * @param hModule Windows installer module handle.
1152 */
1153UINT __stdcall InstallPythonAPI(MSIHANDLE hModule)
1154{
1155 logStringF(hModule, "InstallPythonAPI: Checking for installed Python environment(s) ...");
1156
1157 /** @todo r=bird: Can't we get the VBOX_PYTHON_PATH property here? */
1158 wchar_t wszPythonExe[RTPATH_MAX];
1159 UINT rcWin = getPythonExe(hModule, wszPythonExe, RTPATH_MAX);
1160 if (rcWin != ERROR_SUCCESS)
1161 {
1162 VBoxSetMsiProp(hModule, L"VBOX_API_INSTALLED", L"0");
1163 return ERROR_SUCCESS;
1164 }
1165
1166 /*
1167 * Set up the VBox API.
1168 */
1169 /* Get the VBox API setup string. */
1170 WCHAR wszVBoxPythonInstallerPath[RTPATH_MAX];
1171 rcWin = VBoxGetMsiProp(hModule, L"CustomActionData", wszVBoxPythonInstallerPath, RT_ELEMENTS(wszVBoxPythonInstallerPath));
1172 if (rcWin == ERROR_SUCCESS)
1173 {
1174 /* Make sure our current working directory is the VBox installation path. */
1175 if (SetCurrentDirectoryW(wszVBoxPythonInstallerPath))
1176 {
1177 /* Set required environment variables. */
1178 /** @todo r=andy: That can't be right!
1179 *
1180 * r=bird: The variable probably isn't used because VBOX_MSI_INSTALL_PATH is
1181 * set by VBoxMergeApp.wxi. */
1182 if (SetEnvironmentVariableW(L"VBOX_INSTALL_PATH", wszVBoxPythonInstallerPath))
1183 {
1184 logStringF(hModule, "InstallPythonAPI: Invoking vboxapisetup.py in \"%ls\" ...", wszVBoxPythonInstallerPath);
1185
1186 rcWin = procRun(hModule, wszPythonExe, L"vboxapisetup.py install");
1187 if (rcWin == ERROR_SUCCESS)
1188 {
1189 logStringF(hModule, "InstallPythonAPI: Installation of vboxapisetup.py successful");
1190
1191 /*
1192 * Do some sanity checking if the VBox API works.
1193 */
1194 logStringF(hModule, "InstallPythonAPI: Validating VBox API ...");
1195
1196 rcWin = procRun(hModule, wszPythonExe, L"-c \"from vboxapi import VirtualBoxManager\"");
1197 if (rcWin == ERROR_SUCCESS)
1198 {
1199 logStringF(hModule, "InstallPythonAPI: VBox API looks good.");
1200 VBoxSetMsiProp(hModule, L"VBOX_API_INSTALLED", L"1");
1201 return ERROR_SUCCESS;
1202 }
1203
1204 /* failed */
1205 logStringF(hModule, "InstallPythonAPI: Validating VBox API failed with %u (%#x)", rcWin, rcWin);
1206 }
1207 else
1208 logStringF(hModule, "InstallPythonAPI: Calling vboxapisetup.py failed with %u (%#x)", rcWin, rcWin);
1209 }
1210 else
1211 logStringF(hModule, "InstallPythonAPI: Could set environment variable VBOX_INSTALL_PATH: LastError=%u",
1212 GetLastError());
1213 }
1214 else
1215 logStringF(hModule, "InstallPythonAPI: Could set working directory to \"%ls\": LastError=%u",
1216 wszVBoxPythonInstallerPath, GetLastError());
1217 }
1218 else
1219 logStringF(hModule, "InstallPythonAPI: Unable to retrieve VBox installation directory: rcWin=%u (%#x)", rcWin, rcWin);
1220
1221 VBoxSetMsiProp(hModule, L"VBOX_API_INSTALLED", L"0");
1222 logStringF(hModule, "InstallPythonAPI: Installation failed");
1223 return ERROR_SUCCESS; /* Do not fail here. */
1224}
1225
1226static LONG installBrandingValue(MSIHANDLE hModule,
1227 const WCHAR *pwszFileName,
1228 const WCHAR *pwszSection,
1229 const WCHAR *pwszValue)
1230{
1231 LONG rc;
1232 WCHAR wszValue[MAX_PATH];
1233 if (GetPrivateProfileStringW(pwszSection, pwszValue, NULL, wszValue, sizeof(wszValue), pwszFileName) > 0)
1234 {
1235 WCHAR wszKey[MAX_PATH + 64];
1236 if (RTUtf16ICmpAscii(pwszSection, "General") != 0)
1237 RTUtf16Printf(wszKey, RT_ELEMENTS(wszKey), "SOFTWARE\\%s\\VirtualBox\\Branding\\%ls", VBOX_VENDOR_SHORT, pwszSection);
1238 else
1239 RTUtf16Printf(wszKey, RT_ELEMENTS(wszKey), "SOFTWARE\\%s\\VirtualBox\\Branding", VBOX_VENDOR_SHORT);
1240
1241 HKEY hkBranding = NULL;
1242 rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszKey, 0, KEY_WRITE, &hkBranding);
1243 if (rc == ERROR_SUCCESS)
1244 {
1245 rc = RegSetValueExW(hkBranding,
1246 pwszValue,
1247 NULL,
1248 REG_SZ,
1249 (BYTE *)wszValue,
1250 (DWORD)RTUtf16Len(wszValue));
1251 if (rc != ERROR_SUCCESS)
1252 logStringF(hModule, "InstallBranding: Could not write value %s! Error %d", pwszValue, rc);
1253 RegCloseKey(hkBranding);
1254 }
1255 }
1256 else
1257 rc = ERROR_NOT_FOUND;
1258 return rc;
1259}
1260
1261/**
1262 * @note Both paths strings must have an extra terminator.
1263 */
1264static UINT CopyDir(MSIHANDLE hModule, const WCHAR *pwszzDstDir, const WCHAR *pwszzSrcDir)
1265{
1266 NonStandardAssert(pwszzDstDir[RTUtf16Len(pwszzDstDir) + 1] == '\0');
1267 NonStandardAssert(pwszzSrcDir[RTUtf16Len(pwszzSrcDir) + 1] == '\0');
1268
1269 SHFILEOPSTRUCTW s = {0};
1270 s.hwnd = NULL;
1271 s.wFunc = FO_COPY;
1272 s.pTo = pwszzDstDir;
1273 s.pFrom = pwszzSrcDir;
1274 s.fFlags = FOF_SILENT
1275 | FOF_NOCONFIRMATION
1276 | FOF_NOCONFIRMMKDIR
1277 | FOF_NOERRORUI;
1278
1279 logStringF(hModule, "CopyDir: pwszzDstDir=%ls, pwszzSrcDir=%ls", pwszzDstDir, pwszzSrcDir);
1280 int r = SHFileOperationW(&s);
1281 if (r == 0)
1282 return ERROR_SUCCESS;
1283 logStringF(hModule, "CopyDir: Copy operation returned status %#x", r);
1284 return ERROR_GEN_FAILURE;
1285}
1286
1287/**
1288 * @note The directory string must have two zero terminators!
1289 */
1290static UINT RemoveDir(MSIHANDLE hModule, const WCHAR *pwszzDstDir)
1291{
1292 NonStandardAssert(pwszzDstDir[RTUtf16Len(pwszzDstDir) + 1] == '\0');
1293
1294 SHFILEOPSTRUCTW s = {0};
1295 s.hwnd = NULL;
1296 s.wFunc = FO_DELETE;
1297 s.pFrom = pwszzDstDir;
1298 s.fFlags = FOF_SILENT
1299 | FOF_NOCONFIRMATION
1300 | FOF_NOCONFIRMMKDIR
1301 | FOF_NOERRORUI;
1302
1303 logStringF(hModule, "RemoveDir: pwszzDstDir=%ls", pwszzDstDir);
1304 int r = SHFileOperationW(&s);
1305 if (r == 0)
1306 return ERROR_SUCCESS;
1307 logStringF(hModule, "RemoveDir: Remove operation returned status %#x", r);
1308 return ERROR_GEN_FAILURE;
1309}
1310
1311/**
1312 * @note Both paths strings must have an extra terminator.
1313 */
1314static UINT RenameDir(MSIHANDLE hModule, const WCHAR *pwszzDstDir, const WCHAR *pwszzSrcDir)
1315{
1316 NonStandardAssert(pwszzDstDir[RTUtf16Len(pwszzDstDir) + 1] == '\0');
1317 NonStandardAssert(pwszzSrcDir[RTUtf16Len(pwszzSrcDir) + 1] == '\0');
1318
1319 SHFILEOPSTRUCTW s = {0};
1320 s.hwnd = NULL;
1321 s.wFunc = FO_RENAME;
1322 s.pTo = pwszzDstDir;
1323 s.pFrom = pwszzSrcDir;
1324 s.fFlags = FOF_SILENT
1325 | FOF_NOCONFIRMATION
1326 | FOF_NOCONFIRMMKDIR
1327 | FOF_NOERRORUI;
1328
1329 logStringF(hModule, "RenameDir: pwszzDstDir=%ls, pwszzSrcDir=%ls", pwszzDstDir, pwszzSrcDir);
1330 int r = SHFileOperationW(&s);
1331 if (r == 0)
1332 return ERROR_SUCCESS;
1333 logStringF(hModule, "RenameDir: Rename operation returned status %#x", r);
1334 return ERROR_GEN_FAILURE;
1335}
1336
1337/** RTPathAppend-like function. */
1338static UINT AppendToPath(wchar_t *pwszPath, size_t cwcPath, const wchar_t *pwszAppend, bool fDoubleTerm = false)
1339{
1340 size_t cwcCurPath = RTUtf16Len(pwszPath);
1341 size_t cwcSlash = cwcCurPath > 1 && RTPATH_IS_SLASH(pwszPath[cwcCurPath - 1]) ? 0 : 1;
1342 while (RTPATH_IS_SLASH(*pwszAppend))
1343 pwszAppend++;
1344 size_t cwcAppend = RTUtf16Len(pwszAppend);
1345 if (cwcCurPath + cwcCurPath + cwcAppend + fDoubleTerm < cwcPath)
1346 {
1347 if (cwcSlash)
1348 pwszPath[cwcCurPath++] = '\\';
1349 memcpy(&pwszPath[cwcCurPath], pwszAppend, (cwcAppend + 1) * sizeof(wchar_t));
1350 if (fDoubleTerm)
1351 pwszPath[cwcCurPath + cwcAppend + 1] = '\0';
1352 return ERROR_SUCCESS;
1353 }
1354 return ERROR_BUFFER_OVERFLOW;
1355}
1356
1357/** RTPathJoin-like function. */
1358static UINT JoinPaths(wchar_t *pwszPath, size_t cwcPath, wchar_t *pwszPath1, const wchar_t *pwszAppend, bool fDoubleTerm = false)
1359{
1360 size_t cwcCurPath = RTUtf16Len(pwszPath1);
1361 if (cwcCurPath < cwcPath)
1362 {
1363 memcpy(pwszPath, pwszPath1, (cwcCurPath + 1) * sizeof(wchar_t));
1364 return AppendToPath(pwszPath, cwcPath, pwszAppend, fDoubleTerm);
1365 }
1366 return ERROR_BUFFER_OVERFLOW;
1367}
1368
1369UINT __stdcall UninstallBranding(MSIHANDLE hModule)
1370{
1371 logStringF(hModule, "UninstallBranding: Handling branding file ...");
1372
1373 WCHAR wszPath[RTPATH_MAX];
1374 UINT rc = VBoxGetMsiProp(hModule, L"CustomActionData", wszPath, RT_ELEMENTS(wszPath));
1375 if (rc == ERROR_SUCCESS)
1376 {
1377 size_t const cwcPath = RTUtf16Len(wszPath);
1378 rc = AppendToPath(wszPath, RTPATH_MAX, L"custom", true /*fDoubleTerm*/);
1379 if (rc == ERROR_SUCCESS)
1380 rc = RemoveDir(hModule, wszPath);
1381
1382 /* Check for .custom directory from a failed install and remove it. */
1383 wszPath[cwcPath] = '\0';
1384 rc = AppendToPath(wszPath, RTPATH_MAX, L".custom", true /*fDoubleTerm*/);
1385 if (rc == ERROR_SUCCESS)
1386 rc = RemoveDir(hModule, wszPath);
1387 }
1388
1389 logStringF(hModule, "UninstallBranding: Handling done. (rc=%u (ignored))", rc);
1390 return ERROR_SUCCESS; /* Do not fail here. */
1391}
1392
1393UINT __stdcall InstallBranding(MSIHANDLE hModule)
1394{
1395 logStringF(hModule, "InstallBranding: Handling branding file ...");
1396
1397 /*
1398 * Get the paths.
1399 */
1400 wchar_t wszSrcPath[RTPATH_MAX];
1401 UINT rc = VBoxGetMsiProp(hModule, L"SOURCEDIR", wszSrcPath, RT_ELEMENTS(wszSrcPath));
1402 if (rc == ERROR_SUCCESS)
1403 {
1404 wchar_t wszDstPath[RTPATH_MAX];
1405 rc = VBoxGetMsiProp(hModule, L"CustomActionData", wszDstPath, RT_ELEMENTS(wszDstPath) - 1);
1406 if (rc == ERROR_SUCCESS)
1407 {
1408 /*
1409 * First we copy the src\.custom dir to the target.
1410 */
1411 rc = AppendToPath(wszSrcPath, RT_ELEMENTS(wszSrcPath) - 1, L".custom", true /*fDoubleTerm*/);
1412 if (rc == ERROR_SUCCESS)
1413 {
1414 rc = CopyDir(hModule, wszDstPath, wszSrcPath);
1415 if (rc == ERROR_SUCCESS)
1416 {
1417 /*
1418 * The rename the '.custom' directory we now got in the target area to 'custom'.
1419 */
1420 rc = JoinPaths(wszSrcPath, RT_ELEMENTS(wszSrcPath), wszDstPath, L".custom", true /*fDoubleTerm*/);
1421 if (rc == ERROR_SUCCESS)
1422 {
1423 rc = AppendToPath(wszDstPath, RT_ELEMENTS(wszDstPath), L"custom", true /*fDoubleTerm*/);
1424 if (rc == ERROR_SUCCESS)
1425 rc = RenameDir(hModule, wszDstPath, wszSrcPath);
1426 }
1427 }
1428 }
1429 }
1430 }
1431
1432 logStringF(hModule, "InstallBranding: Handling done. (rc=%u (ignored))", rc);
1433 return ERROR_SUCCESS; /* Do not fail here. */
1434}
1435
1436#if defined(VBOX_WITH_NETFLT) || defined(VBOX_WITH_NETADP)
1437
1438/** @todo should use some real VBox app name */
1439#define VBOX_NETCFG_APP_NAME L"VirtualBox Installer"
1440#define VBOX_NETCFG_MAX_RETRIES 10
1441#define NETFLT_PT_INF_REL_PATH L"VBoxNetFlt.inf"
1442#define NETFLT_MP_INF_REL_PATH L"VBoxNetFltM.inf"
1443#define NETFLT_ID L"sun_VBoxNetFlt" /** @todo Needs to be changed (?). */
1444#define NETADP_ID L"sun_VBoxNetAdp" /** @todo Needs to be changed (?). */
1445
1446#define NETLWF_INF_NAME L"VBoxNetLwf.inf"
1447
1448static MSIHANDLE g_hCurrentModule = NULL;
1449
1450static UINT _uninstallNetFlt(MSIHANDLE hModule);
1451static UINT _uninstallNetLwf(MSIHANDLE hModule);
1452
1453static VOID vboxDrvLoggerCallback(VBOXDRVCFG_LOG_SEVERITY_T enmSeverity, char *pszMsg, void *pvContext)
1454{
1455 RT_NOREF1(pvContext);
1456 switch (enmSeverity)
1457 {
1458 case VBOXDRVCFG_LOG_SEVERITY_FLOW:
1459 case VBOXDRVCFG_LOG_SEVERITY_REGULAR:
1460 break;
1461 case VBOXDRVCFG_LOG_SEVERITY_REL:
1462 if (g_hCurrentModule)
1463 logStringF(g_hCurrentModule, "%s", pszMsg);
1464 break;
1465 default:
1466 break;
1467 }
1468}
1469
1470static DECLCALLBACK(void) netCfgLoggerCallback(const char *pszString)
1471{
1472 if (g_hCurrentModule)
1473 logStringF(g_hCurrentModule, "%s", pszString);
1474}
1475
1476static VOID netCfgLoggerDisable()
1477{
1478 if (g_hCurrentModule)
1479 {
1480 VBoxNetCfgWinSetLogging(NULL);
1481 g_hCurrentModule = NULL;
1482 }
1483}
1484
1485static VOID netCfgLoggerEnable(MSIHANDLE hModule)
1486{
1487 NonStandardAssert(hModule);
1488
1489 if (g_hCurrentModule)
1490 netCfgLoggerDisable();
1491
1492 g_hCurrentModule = hModule;
1493
1494 VBoxNetCfgWinSetLogging(netCfgLoggerCallback);
1495 /* uncomment next line if you want to add logging information from VBoxDrvCfg.cpp */
1496// VBoxDrvCfgLoggerSet(vboxDrvLoggerCallback, NULL);
1497}
1498
1499static UINT errorConvertFromHResult(MSIHANDLE hModule, HRESULT hr)
1500{
1501 UINT uRet;
1502 switch (hr)
1503 {
1504 case S_OK:
1505 uRet = ERROR_SUCCESS;
1506 break;
1507
1508 case NETCFG_S_REBOOT:
1509 {
1510 logStringF(hModule, "Reboot required, setting REBOOT property to \"force\"");
1511 HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
1512 if (hr2 != ERROR_SUCCESS)
1513 logStringF(hModule, "Failed to set REBOOT property, error = %#x", hr2);
1514 uRet = ERROR_SUCCESS; /* Never fail here. */
1515 break;
1516 }
1517
1518 default:
1519 logStringF(hModule, "Converting unhandled HRESULT (%#x) to ERROR_GEN_FAILURE", hr);
1520 uRet = ERROR_GEN_FAILURE;
1521 }
1522 return uRet;
1523}
1524
1525static MSIHANDLE createNetCfgLockedMsgRecord(MSIHANDLE hModule)
1526{
1527 MSIHANDLE hRecord = MsiCreateRecord(2);
1528 if (hRecord)
1529 {
1530 UINT uErr = MsiRecordSetInteger(hRecord, 1, 25001);
1531 if (uErr != ERROR_SUCCESS)
1532 {
1533 logStringF(hModule, "createNetCfgLockedMsgRecord: MsiRecordSetInteger failed, error = %#x", uErr);
1534 MsiCloseHandle(hRecord);
1535 hRecord = NULL;
1536 }
1537 }
1538 else
1539 logStringF(hModule, "createNetCfgLockedMsgRecord: Failed to create a record");
1540
1541 return hRecord;
1542}
1543
1544static UINT doNetCfgInit(MSIHANDLE hModule, INetCfg **ppnc, BOOL bWrite)
1545{
1546 MSIHANDLE hMsg = NULL;
1547 UINT uErr = ERROR_GEN_FAILURE;
1548 int MsgResult;
1549 int cRetries = 0;
1550
1551 do
1552 {
1553 LPWSTR lpszLockedBy;
1554 HRESULT hr = VBoxNetCfgWinQueryINetCfg(ppnc, bWrite, VBOX_NETCFG_APP_NAME, 10000, &lpszLockedBy);
1555 if (hr != NETCFG_E_NO_WRITE_LOCK)
1556 {
1557 if (FAILED(hr))
1558 logStringF(hModule, "doNetCfgInit: VBoxNetCfgWinQueryINetCfg failed, error = %#x", hr);
1559 uErr = errorConvertFromHResult(hModule, hr);
1560 break;
1561 }
1562
1563 /* hr == NETCFG_E_NO_WRITE_LOCK */
1564
1565 if (!lpszLockedBy)
1566 {
1567 logStringF(hModule, "doNetCfgInit: lpszLockedBy == NULL, breaking");
1568 break;
1569 }
1570
1571 /* on vista the 6to4svc.dll periodically maintains the lock for some reason,
1572 * if this is the case, increase the wait period by retrying multiple times
1573 * NOTE: we could alternatively increase the wait timeout,
1574 * however it seems unneeded for most cases, e.g. in case some network connection property
1575 * dialog is opened, it would be better to post a notification to the user as soon as possible
1576 * rather than waiting for a longer period of time before displaying it */
1577 if ( cRetries < VBOX_NETCFG_MAX_RETRIES
1578 && RTUtf16ICmpAscii(lpszLockedBy, "6to4svc.dll") == 0)
1579 {
1580 cRetries++;
1581 logStringF(hModule, "doNetCfgInit: lpszLockedBy is 6to4svc.dll, retrying %d out of %d", cRetries, VBOX_NETCFG_MAX_RETRIES);
1582 MsgResult = IDRETRY;
1583 }
1584 else
1585 {
1586 if (!hMsg)
1587 {
1588 hMsg = createNetCfgLockedMsgRecord(hModule);
1589 if (!hMsg)
1590 {
1591 logStringF(hModule, "doNetCfgInit: Failed to create a message record, breaking");
1592 CoTaskMemFree(lpszLockedBy);
1593 break;
1594 }
1595 }
1596
1597 UINT rTmp = MsiRecordSetStringW(hMsg, 2, lpszLockedBy);
1598 NonStandardAssert(rTmp == ERROR_SUCCESS);
1599 if (rTmp != ERROR_SUCCESS)
1600 {
1601 logStringF(hModule, "doNetCfgInit: MsiRecordSetStringW failed, error = #%x", rTmp);
1602 CoTaskMemFree(lpszLockedBy);
1603 break;
1604 }
1605
1606 MsgResult = MsiProcessMessage(hModule, (INSTALLMESSAGE)(INSTALLMESSAGE_USER | MB_RETRYCANCEL), hMsg);
1607 NonStandardAssert(MsgResult == IDRETRY || MsgResult == IDCANCEL);
1608 logStringF(hModule, "doNetCfgInit: MsiProcessMessage returned (%#x)", MsgResult);
1609 }
1610 CoTaskMemFree(lpszLockedBy);
1611 } while(MsgResult == IDRETRY);
1612
1613 if (hMsg)
1614 MsiCloseHandle(hMsg);
1615
1616 return uErr;
1617}
1618#endif /* defined(VBOX_WITH_NETFLT) || defined(VBOX_WITH_NETADP) */
1619
1620#ifdef VBOX_WITH_NETFLT
1621static UINT vboxNetFltQueryInfArray(MSIHANDLE hModule, OUT LPWSTR pwszPtInf, DWORD cwcPtInf,
1622 OUT LPWSTR pwszMpInf, DWORD cwcMpInf)
1623{
1624 DWORD cwcEffBuf = cwcPtInf - RT_MAX(sizeof(NETFLT_PT_INF_REL_PATH), sizeof(NETFLT_MP_INF_REL_PATH)) / sizeof(WCHAR);
1625 UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", pwszPtInf, &cwcEffBuf);
1626 if ( uErr == ERROR_SUCCESS
1627 && cwcEffBuf > 0)
1628 {
1629 int vrc = RTUtf16Copy(pwszMpInf, cwcMpInf, pwszPtInf);
1630 AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
1631
1632 vrc = RTUtf16Cat(pwszPtInf, cwcPtInf, NETFLT_PT_INF_REL_PATH);
1633 AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
1634 logStringF(hModule, "vboxNetFltQueryInfArray: INF 1: %ls", pwszPtInf);
1635
1636 vrc = RTUtf16Cat(pwszMpInf, cwcMpInf, NETFLT_MP_INF_REL_PATH);
1637 AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
1638 logStringF(hModule, "vboxNetFltQueryInfArray: INF 2: %ls", pwszMpInf);
1639 }
1640 else if (uErr != ERROR_SUCCESS)
1641 logStringF(hModule, "vboxNetFltQueryInfArray: MsiGetPropertyW failed, error = %#x", uErr);
1642 else
1643 {
1644 logStringF(hModule, "vboxNetFltQueryInfArray: Empty installation directory");
1645 uErr = ERROR_GEN_FAILURE;
1646 }
1647
1648 return uErr;
1649}
1650
1651static UINT _uninstallNetFlt(MSIHANDLE hModule)
1652{
1653 INetCfg *pNetCfg;
1654 UINT uErr;
1655
1656 netCfgLoggerEnable(hModule);
1657
1658 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
1659
1660 __try
1661 {
1662 logStringF(hModule, "Uninstalling NetFlt");
1663
1664 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
1665 if (uErr == ERROR_SUCCESS)
1666 {
1667 HRESULT hr = VBoxNetCfgWinNetFltUninstall(pNetCfg);
1668 if (hr != S_OK)
1669 logStringF(hModule, "UninstallNetFlt: VBoxNetCfgWinUninstallComponent failed, error = %#x", hr);
1670
1671 uErr = errorConvertFromHResult(hModule, hr);
1672
1673 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
1674
1675 logStringF(hModule, "Uninstalling NetFlt done, error = %#x", uErr);
1676 }
1677 else
1678 logStringF(hModule, "UninstallNetFlt: doNetCfgInit failed, error = %#x", uErr);
1679 }
1680 __finally
1681 {
1682 if (bOldIntMode)
1683 {
1684 /* The prev mode != FALSE, i.e. non-interactive. */
1685 SetupSetNonInteractiveMode(bOldIntMode);
1686 }
1687 netCfgLoggerDisable();
1688 }
1689
1690 /* Never fail the uninstall even if we did not succeed. */
1691 return ERROR_SUCCESS;
1692}
1693#endif /* VBOX_WITH_NETFLT */
1694
1695UINT __stdcall UninstallNetFlt(MSIHANDLE hModule)
1696{
1697#ifdef VBOX_WITH_NETFLT
1698 _uninstallNetLwf(hModule);
1699 return _uninstallNetFlt(hModule);
1700#else
1701 RT_NOREF(hModule);
1702 return ERROR_SUCCESS;
1703#endif
1704}
1705
1706#ifdef VBOX_WITH_NETFLT
1707static UINT _installNetFlt(MSIHANDLE hModule)
1708{
1709 UINT uErr;
1710 INetCfg *pNetCfg;
1711
1712 netCfgLoggerEnable(hModule);
1713
1714 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
1715
1716 __try
1717 {
1718
1719 logStringF(hModule, "InstallNetFlt: Installing NetFlt");
1720
1721 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
1722 if (uErr == ERROR_SUCCESS)
1723 {
1724 WCHAR wszPtInf[MAX_PATH];
1725 WCHAR wszMpInf[MAX_PATH];
1726 uErr = vboxNetFltQueryInfArray(hModule, wszPtInf, RT_ELEMENTS(wszPtInf), wszMpInf, RT_ELEMENTS(wszMpInf));
1727 if (uErr == ERROR_SUCCESS)
1728 {
1729 LPCWSTR const apwszInfs[] = { wszPtInf, wszMpInf };
1730 HRESULT hr = VBoxNetCfgWinNetFltInstall(pNetCfg, &apwszInfs[0], RT_ELEMENTS(apwszInfs));
1731 if (FAILED(hr))
1732 logStringF(hModule, "InstallNetFlt: VBoxNetCfgWinNetFltInstall failed, error = %#x", hr);
1733
1734 uErr = errorConvertFromHResult(hModule, hr);
1735 }
1736 else
1737 logStringF(hModule, "InstallNetFlt: vboxNetFltQueryInfArray failed, error = %#x", uErr);
1738
1739 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
1740
1741 logStringF(hModule, "InstallNetFlt: Done");
1742 }
1743 else
1744 logStringF(hModule, "InstallNetFlt: doNetCfgInit failed, error = %#x", uErr);
1745 }
1746 __finally
1747 {
1748 if (bOldIntMode)
1749 {
1750 /* The prev mode != FALSE, i.e. non-interactive. */
1751 SetupSetNonInteractiveMode(bOldIntMode);
1752 }
1753 netCfgLoggerDisable();
1754 }
1755
1756 /* Never fail the install even if we did not succeed. */
1757 return ERROR_SUCCESS;
1758}
1759#endif /* VBOX_WITH_NETFLT */
1760
1761UINT __stdcall InstallNetFlt(MSIHANDLE hModule)
1762{
1763#ifdef VBOX_WITH_NETFLT
1764 _uninstallNetLwf(hModule);
1765 return _installNetFlt(hModule);
1766#else
1767 RT_NOREF(hModule);
1768 return ERROR_SUCCESS;
1769#endif
1770}
1771
1772#ifdef VBOX_WITH_NETFLT
1773static UINT _uninstallNetLwf(MSIHANDLE hModule)
1774{
1775 INetCfg *pNetCfg;
1776 UINT uErr;
1777
1778 netCfgLoggerEnable(hModule);
1779
1780 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
1781
1782 __try
1783 {
1784 logStringF(hModule, "Uninstalling NetLwf");
1785
1786 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
1787 if (uErr == ERROR_SUCCESS)
1788 {
1789 HRESULT hr = VBoxNetCfgWinNetLwfUninstall(pNetCfg);
1790 if (hr != S_OK)
1791 logStringF(hModule, "UninstallNetLwf: VBoxNetCfgWinUninstallComponent failed, error = %#x", hr);
1792
1793 uErr = errorConvertFromHResult(hModule, hr);
1794
1795 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
1796
1797 logStringF(hModule, "Uninstalling NetLwf done, error = %#x", uErr);
1798 }
1799 else
1800 logStringF(hModule, "UninstallNetLwf: doNetCfgInit failed, error = %#x", uErr);
1801 }
1802 __finally
1803 {
1804 if (bOldIntMode)
1805 {
1806 /* The prev mode != FALSE, i.e. non-interactive. */
1807 SetupSetNonInteractiveMode(bOldIntMode);
1808 }
1809 netCfgLoggerDisable();
1810 }
1811
1812 /* Never fail the uninstall even if we did not succeed. */
1813 return ERROR_SUCCESS;
1814}
1815#endif /* VBOX_WITH_NETFLT */
1816
1817UINT __stdcall UninstallNetLwf(MSIHANDLE hModule)
1818{
1819#ifdef VBOX_WITH_NETFLT
1820 _uninstallNetFlt(hModule);
1821 return _uninstallNetLwf(hModule);
1822#else
1823 RT_NOREF(hModule);
1824 return ERROR_SUCCESS;
1825#endif
1826}
1827
1828#ifdef VBOX_WITH_NETFLT
1829static UINT _installNetLwf(MSIHANDLE hModule)
1830{
1831 UINT uErr;
1832 INetCfg *pNetCfg;
1833
1834 netCfgLoggerEnable(hModule);
1835
1836 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
1837
1838 __try
1839 {
1840
1841 logStringF(hModule, "InstallNetLwf: Installing NetLwf");
1842
1843 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
1844 if (uErr == ERROR_SUCCESS)
1845 {
1846 WCHAR wszInf[MAX_PATH];
1847 DWORD cwcInf = RT_ELEMENTS(wszInf) - sizeof(NETLWF_INF_NAME) - 1;
1848 uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszInf, &cwcInf);
1849 if (uErr == ERROR_SUCCESS)
1850 {
1851 if (cwcInf)
1852 {
1853 if (wszInf[cwcInf - 1] != L'\\')
1854 {
1855 wszInf[cwcInf++] = L'\\';
1856 wszInf[cwcInf] = L'\0';
1857 }
1858
1859 int vrc = RTUtf16Cat(wszInf, RT_ELEMENTS(wszInf), NETLWF_INF_NAME);
1860 AssertRC(vrc);
1861
1862 HRESULT hr = VBoxNetCfgWinNetLwfInstall(pNetCfg, wszInf);
1863 if (FAILED(hr))
1864 logStringF(hModule, "InstallNetLwf: VBoxNetCfgWinNetLwfInstall failed, error = %#x", hr);
1865
1866 uErr = errorConvertFromHResult(hModule, hr);
1867 }
1868 else
1869 {
1870 logStringF(hModule, "vboxNetFltQueryInfArray: Empty installation directory");
1871 uErr = ERROR_GEN_FAILURE;
1872 }
1873 }
1874 else
1875 logStringF(hModule, "vboxNetFltQueryInfArray: MsiGetPropertyW failed, error = %#x", uErr);
1876
1877 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
1878
1879 logStringF(hModule, "InstallNetLwf: Done");
1880 }
1881 else
1882 logStringF(hModule, "InstallNetLwf: doNetCfgInit failed, error = %#x", uErr);
1883 }
1884 __finally
1885 {
1886 if (bOldIntMode)
1887 {
1888 /* The prev mode != FALSE, i.e. non-interactive. */
1889 SetupSetNonInteractiveMode(bOldIntMode);
1890 }
1891 netCfgLoggerDisable();
1892 }
1893
1894 /* Never fail the install even if we did not succeed. */
1895 return ERROR_SUCCESS;
1896}
1897#endif /* VBOX_WITH_NETFLT */
1898
1899UINT __stdcall InstallNetLwf(MSIHANDLE hModule)
1900{
1901#ifdef VBOX_WITH_NETFLT
1902 _uninstallNetFlt(hModule);
1903 return _installNetLwf(hModule);
1904#else
1905 RT_NOREF(hModule);
1906 return ERROR_SUCCESS;
1907#endif
1908}
1909
1910
1911#if 0 /** @todo r=andy Remove this? */
1912static BOOL RenameHostOnlyConnectionsCallback(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pContext)
1913{
1914 WCHAR DevName[256];
1915 DWORD winEr;
1916
1917 if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, pDev,
1918 SPDRP_FRIENDLYNAME , /* IN DWORD Property,*/
1919 NULL, /*OUT PDWORD PropertyRegDataType, OPTIONAL*/
1920 (PBYTE)DevName, /*OUT PBYTE PropertyBuffer,*/
1921 sizeof(DevName), /* IN DWORD PropertyBufferSize,*/
1922 NULL /*OUT PDWORD RequiredSize OPTIONAL*/
1923 ))
1924 {
1925 HKEY hKey = SetupDiOpenDevRegKey(hDevInfo, pDev,
1926 DICS_FLAG_GLOBAL, /* IN DWORD Scope,*/
1927 0, /*IN DWORD HwProfile, */
1928 DIREG_DRV, /* IN DWORD KeyType, */
1929 KEY_READ /*IN REGSAM samDesired*/
1930 );
1931 NonStandardAssert(hKey != INVALID_HANDLE_VALUE);
1932 if (hKey != INVALID_HANDLE_VALUE)
1933 {
1934 WCHAR guid[50];
1935 DWORD cbGuid=sizeof(guid);
1936 winEr = RegQueryValueExW(hKey,
1937 L"NetCfgInstanceId", /*__in_opt LPCTSTR lpValueName,*/
1938 NULL, /*__reserved LPDWORD lpReserved,*/
1939 NULL, /*__out_opt LPDWORD lpType,*/
1940 (LPBYTE)guid, /*__out_opt LPBYTE lpData,*/
1941 &cbGuid /*guid__inout_opt LPDWORD lpcbData*/
1942 );
1943 NonStandardAssert(winEr == ERROR_SUCCESS);
1944 if (winEr == ERROR_SUCCESS)
1945 {
1946 WCHAR ConnectoinName[128];
1947 ULONG cbName = sizeof(ConnectoinName);
1948
1949 HRESULT hr = VBoxNetCfgWinGenHostonlyConnectionName(DevName, ConnectoinName, &cbName);
1950 NonStandardAssert(hr == S_OK);
1951 if (SUCCEEDED(hr))
1952 {
1953 hr = VBoxNetCfgWinRenameConnection(guid, ConnectoinName);
1954 NonStandardAssert(hr == S_OK);
1955 }
1956 }
1957 }
1958 RegCloseKey(hKey);
1959 }
1960 else
1961 {
1962 NonStandardAssert(0);
1963 }
1964
1965 return TRUE;
1966}
1967#endif /* 0 */
1968
1969#ifdef VBOX_WITH_NETADP
1970static UINT _createHostOnlyInterface(MSIHANDLE hModule, LPCWSTR pwszId, LPCWSTR pwszInfName)
1971{
1972 netCfgLoggerEnable(hModule);
1973
1974 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
1975
1976 logStringF(hModule, "CreateHostOnlyInterface: Creating host-only interface");
1977
1978 HRESULT hr = E_FAIL;
1979 GUID guid;
1980 WCHAR wszMpInf[MAX_PATH];
1981 DWORD cwcMpInf = RT_ELEMENTS(wszMpInf) - (DWORD)RTUtf16Len(pwszInfName) - 1 - 1;
1982 LPCWSTR pwszInfPath = NULL;
1983 bool fIsFile = false;
1984 UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszMpInf, &cwcMpInf);
1985 if (uErr == ERROR_SUCCESS)
1986 {
1987 if (cwcMpInf)
1988 {
1989 logStringF(hModule, "CreateHostOnlyInterface: NetAdpDir property = %ls", wszMpInf);
1990 if (wszMpInf[cwcMpInf - 1] != L'\\')
1991 {
1992 wszMpInf[cwcMpInf++] = L'\\';
1993 wszMpInf[cwcMpInf] = L'\0';
1994 }
1995
1996 int vrc = RTUtf16Cat(wszMpInf, RT_ELEMENTS(wszMpInf), pwszInfName);
1997 AssertRC(vrc);
1998
1999 pwszInfPath = wszMpInf;
2000 fIsFile = true;
2001
2002 logStringF(hModule, "CreateHostOnlyInterface: Resulting INF path = %ls", pwszInfPath);
2003 }
2004 else
2005 logStringF(hModule, "CreateHostOnlyInterface: VBox installation path is empty");
2006 }
2007 else
2008 logStringF(hModule, "CreateHostOnlyInterface: Unable to retrieve VBox installation path, error = %#x", uErr);
2009
2010 /* Make sure the inf file is installed. */
2011 if (pwszInfPath != NULL && fIsFile)
2012 {
2013 logStringF(hModule, "CreateHostOnlyInterface: Calling VBoxDrvCfgInfInstall(%ls)", pwszInfPath);
2014 hr = VBoxDrvCfgInfInstall(pwszInfPath);
2015 logStringF(hModule, "CreateHostOnlyInterface: VBoxDrvCfgInfInstall returns %#x", hr);
2016 if (FAILED(hr))
2017 logStringF(hModule, "CreateHostOnlyInterface: Failed to install INF file, error = %#x", hr);
2018 }
2019
2020 if (SUCCEEDED(hr))
2021 {
2022 //first, try to update Host Only Network Interface
2023 BOOL fRebootRequired = FALSE;
2024 hr = VBoxNetCfgWinUpdateHostOnlyNetworkInterface(pwszInfPath, &fRebootRequired, pwszId);
2025 if (SUCCEEDED(hr))
2026 {
2027 if (fRebootRequired)
2028 {
2029 logStringF(hModule, "CreateHostOnlyInterface: Reboot required for update, setting REBOOT property to force");
2030 HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
2031 if (hr2 != ERROR_SUCCESS)
2032 logStringF(hModule, "CreateHostOnlyInterface: Failed to set REBOOT property for update, error = %#x", hr2);
2033 }
2034 }
2035 else
2036 {
2037 //in fail case call CreateHostOnlyInterface
2038 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinUpdateHostOnlyNetworkInterface failed, hr = %#x", hr);
2039 logStringF(hModule, "CreateHostOnlyInterface: calling VBoxNetCfgWinCreateHostOnlyNetworkInterface");
2040# ifdef VBOXNETCFG_DELAYEDRENAME
2041 BSTR devId;
2042 hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsFile, NULL, &guid, &devId, NULL);
2043# else /* !VBOXNETCFG_DELAYEDRENAME */
2044 hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsFile, NULL, &guid, NULL, NULL);
2045# endif /* !VBOXNETCFG_DELAYEDRENAME */
2046 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinCreateHostOnlyNetworkInterface returns %#x", hr);
2047 if (SUCCEEDED(hr))
2048 {
2049 ULONG ip = inet_addr("192.168.56.1");
2050 ULONG mask = inet_addr("255.255.255.0");
2051 logStringF(hModule, "CreateHostOnlyInterface: calling VBoxNetCfgWinEnableStaticIpConfig");
2052 hr = VBoxNetCfgWinEnableStaticIpConfig(&guid, ip, mask);
2053 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinEnableStaticIpConfig returns %#x", hr);
2054 if (FAILED(hr))
2055 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinEnableStaticIpConfig failed, error = %#x", hr);
2056# ifdef VBOXNETCFG_DELAYEDRENAME
2057 hr = VBoxNetCfgWinRenameHostOnlyConnection(&guid, devId, NULL);
2058 if (FAILED(hr))
2059 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinRenameHostOnlyConnection failed, error = %#x", hr);
2060 SysFreeString(devId);
2061# endif /* VBOXNETCFG_DELAYEDRENAME */
2062 }
2063 else
2064 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinCreateHostOnlyNetworkInterface failed, error = %#x", hr);
2065 }
2066 }
2067
2068 if (SUCCEEDED(hr))
2069 logStringF(hModule, "CreateHostOnlyInterface: Creating host-only interface done");
2070
2071 /* Restore original setup mode. */
2072 logStringF(hModule, "CreateHostOnlyInterface: Almost done...");
2073 if (fSetupModeInteractive)
2074 SetupSetNonInteractiveMode(fSetupModeInteractive);
2075
2076 netCfgLoggerDisable();
2077
2078 logStringF(hModule, "CreateHostOnlyInterface: Returns success (ignoring all failures)");
2079 /* Never fail the install even if we did not succeed. */
2080 return ERROR_SUCCESS;
2081}
2082#endif /* VBOX_WITH_NETADP */
2083
2084UINT __stdcall CreateHostOnlyInterface(MSIHANDLE hModule)
2085{
2086#ifdef VBOX_WITH_NETADP
2087 return _createHostOnlyInterface(hModule, NETADP_ID, L"VBoxNetAdp.inf");
2088#else
2089 RT_NOREF(hModule);
2090 return ERROR_SUCCESS;
2091#endif
2092}
2093
2094UINT __stdcall Ndis6CreateHostOnlyInterface(MSIHANDLE hModule)
2095{
2096#ifdef VBOX_WITH_NETADP
2097# if 0 /* Trick for allowing the debugger to be attached. */
2098 for (unsigned i = 0; i < 128 && !IsDebuggerPresent(); i++)
2099 {
2100 logStringF(hModule, "Waiting for debugger to attach: windbg -p %u", GetCurrentProcessId());
2101 Sleep(1001);
2102 }
2103 Sleep(1002);
2104 __debugbreak();
2105# endif
2106 return _createHostOnlyInterface(hModule, NETADP_ID, L"VBoxNetAdp6.inf");
2107#else /* !VBOX_WITH_NETADP */
2108 RT_NOREF(hModule);
2109 return ERROR_SUCCESS;
2110#endif
2111}
2112
2113#ifdef VBOX_WITH_NETADP
2114static UINT _removeHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszId)
2115{
2116 netCfgLoggerEnable(hModule);
2117
2118 logStringF(hModule, "RemoveHostOnlyInterfaces: Removing all host-only interfaces");
2119
2120 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2121
2122 HRESULT hr = VBoxNetCfgWinRemoveAllNetDevicesOfId(pwszId);
2123 if (SUCCEEDED(hr))
2124 {
2125 hr = VBoxDrvCfgInfUninstallAllSetupDi(&GUID_DEVCLASS_NET, L"Net", pwszId, SUOI_FORCEDELETE/* could be SUOI_FORCEDELETE */);
2126 if (FAILED(hr))
2127 logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstalled successfully, but failed to remove INF files");
2128 else
2129 logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstalled successfully");
2130 }
2131 else
2132 logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstall failed, hr = %#x", hr);
2133
2134 /* Restore original setup mode. */
2135 if (fSetupModeInteractive)
2136 SetupSetNonInteractiveMode(fSetupModeInteractive);
2137
2138 netCfgLoggerDisable();
2139
2140 /* Never fail the uninstall even if we did not succeed. */
2141 return ERROR_SUCCESS;
2142}
2143#endif /* VBOX_WITH_NETADP */
2144
2145UINT __stdcall RemoveHostOnlyInterfaces(MSIHANDLE hModule)
2146{
2147#ifdef VBOX_WITH_NETADP
2148 return _removeHostOnlyInterfaces(hModule, NETADP_ID);
2149#else
2150 RT_NOREF(hModule);
2151 return ERROR_SUCCESS;
2152#endif
2153}
2154
2155#ifdef VBOX_WITH_NETADP
2156static UINT _stopHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszId)
2157{
2158 netCfgLoggerEnable(hModule);
2159
2160 logStringF(hModule, "StopHostOnlyInterfaces: Stopping all host-only interfaces");
2161
2162 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2163
2164 HRESULT hr = VBoxNetCfgWinPropChangeAllNetDevicesOfId(pwszId, VBOXNECTFGWINPROPCHANGE_TYPE_DISABLE);
2165 if (SUCCEEDED(hr))
2166 logStringF(hModule, "StopHostOnlyInterfaces: Disabling host interfaces was successful, hr = %#x", hr);
2167 else
2168 logStringF(hModule, "StopHostOnlyInterfaces: Disabling host interfaces failed, hr = %#x", hr);
2169
2170 /* Restore original setup mode. */
2171 if (fSetupModeInteractive)
2172 SetupSetNonInteractiveMode(fSetupModeInteractive);
2173
2174 netCfgLoggerDisable();
2175
2176 /* Never fail the uninstall even if we did not succeed. */
2177 return ERROR_SUCCESS;
2178}
2179#endif /* VBOX_WITH_NETADP */
2180
2181UINT __stdcall StopHostOnlyInterfaces(MSIHANDLE hModule)
2182{
2183#ifdef VBOX_WITH_NETADP
2184 return _stopHostOnlyInterfaces(hModule, NETADP_ID);
2185#else
2186 RT_NOREF(hModule);
2187 return ERROR_SUCCESS;
2188#endif
2189}
2190
2191#ifdef VBOX_WITH_NETADP
2192static UINT _updateHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszInfName, LPCWSTR pwszId)
2193{
2194 netCfgLoggerEnable(hModule);
2195
2196 logStringF(hModule, "UpdateHostOnlyInterfaces: Updating all host-only interfaces");
2197
2198 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2199
2200 WCHAR wszMpInf[MAX_PATH];
2201 DWORD cwcMpInf = RT_ELEMENTS(wszMpInf) - (DWORD)RTUtf16Len(pwszInfName) - 1 - 1;
2202 LPCWSTR pwszInfPath = NULL;
2203 bool fIsFile = false;
2204 UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszMpInf, &cwcMpInf);
2205 if (uErr == ERROR_SUCCESS)
2206 {
2207 if (cwcMpInf)
2208 {
2209 logStringF(hModule, "UpdateHostOnlyInterfaces: NetAdpDir property = %ls", wszMpInf);
2210 if (wszMpInf[cwcMpInf - 1] != L'\\')
2211 {
2212 wszMpInf[cwcMpInf++] = L'\\';
2213 wszMpInf[cwcMpInf] = L'\0';
2214 }
2215
2216 int vrc = RTUtf16Cat(wszMpInf, RT_ELEMENTS(wszMpInf), pwszInfName);
2217 AssertRC(vrc);
2218 pwszInfPath = wszMpInf;
2219 fIsFile = true;
2220
2221 logStringF(hModule, "UpdateHostOnlyInterfaces: Resulting INF path = %ls", pwszInfPath);
2222
2223 DWORD attrFile = GetFileAttributesW(pwszInfPath);
2224 if (attrFile == INVALID_FILE_ATTRIBUTES)
2225 {
2226 DWORD dwErr = GetLastError();
2227 logStringF(hModule, "UpdateHostOnlyInterfaces: File \"%ls\" not found, dwErr=%ld", pwszInfPath, dwErr);
2228 }
2229 else
2230 {
2231 logStringF(hModule, "UpdateHostOnlyInterfaces: File \"%ls\" exists", pwszInfPath);
2232
2233 BOOL fRebootRequired = FALSE;
2234 HRESULT hr = VBoxNetCfgWinUpdateHostOnlyNetworkInterface(pwszInfPath, &fRebootRequired, pwszId);
2235 if (SUCCEEDED(hr))
2236 {
2237 if (fRebootRequired)
2238 {
2239 logStringF(hModule, "UpdateHostOnlyInterfaces: Reboot required, setting REBOOT property to force");
2240 HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
2241 if (hr2 != ERROR_SUCCESS)
2242 logStringF(hModule, "UpdateHostOnlyInterfaces: Failed to set REBOOT property, error = %#x", hr2);
2243 }
2244 }
2245 else
2246 logStringF(hModule, "UpdateHostOnlyInterfaces: VBoxNetCfgWinUpdateHostOnlyNetworkInterface failed, hr = %#x", hr);
2247 }
2248 }
2249 else
2250 logStringF(hModule, "UpdateHostOnlyInterfaces: VBox installation path is empty");
2251 }
2252 else
2253 logStringF(hModule, "UpdateHostOnlyInterfaces: Unable to retrieve VBox installation path, error = %#x", uErr);
2254
2255 /* Restore original setup mode. */
2256 if (fSetupModeInteractive)
2257 SetupSetNonInteractiveMode(fSetupModeInteractive);
2258
2259 netCfgLoggerDisable();
2260
2261 /* Never fail the update even if we did not succeed. */
2262 return ERROR_SUCCESS;
2263}
2264#endif /* VBOX_WITH_NETADP */
2265
2266UINT __stdcall UpdateHostOnlyInterfaces(MSIHANDLE hModule)
2267{
2268#ifdef VBOX_WITH_NETADP
2269 return _updateHostOnlyInterfaces(hModule, L"VBoxNetAdp.inf", NETADP_ID);
2270#else
2271 RT_NOREF(hModule);
2272 return ERROR_SUCCESS;
2273#endif
2274}
2275
2276UINT __stdcall Ndis6UpdateHostOnlyInterfaces(MSIHANDLE hModule)
2277{
2278#ifdef VBOX_WITH_NETADP
2279 return _updateHostOnlyInterfaces(hModule, L"VBoxNetAdp6.inf", NETADP_ID);
2280#else
2281 RT_NOREF(hModule);
2282 return ERROR_SUCCESS;
2283#endif
2284}
2285
2286#ifdef VBOX_WITH_NETADP
2287static UINT _uninstallNetAdp(MSIHANDLE hModule, LPCWSTR pwszId)
2288{
2289 INetCfg *pNetCfg;
2290 UINT uErr;
2291
2292 netCfgLoggerEnable(hModule);
2293
2294 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
2295
2296 __try
2297 {
2298 logStringF(hModule, "Uninstalling NetAdp");
2299
2300 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
2301 if (uErr == ERROR_SUCCESS)
2302 {
2303 HRESULT hr = VBoxNetCfgWinNetAdpUninstall(pNetCfg, pwszId);
2304 if (hr != S_OK)
2305 logStringF(hModule, "UninstallNetAdp: VBoxNetCfgWinUninstallComponent failed, error = %#x", hr);
2306
2307 uErr = errorConvertFromHResult(hModule, hr);
2308
2309 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
2310
2311 logStringF(hModule, "Uninstalling NetAdp done, error = %#x", uErr);
2312 }
2313 else
2314 logStringF(hModule, "UninstallNetAdp: doNetCfgInit failed, error = %#x", uErr);
2315 }
2316 __finally
2317 {
2318 if (bOldIntMode)
2319 {
2320 /* The prev mode != FALSE, i.e. non-interactive. */
2321 SetupSetNonInteractiveMode(bOldIntMode);
2322 }
2323 netCfgLoggerDisable();
2324 }
2325
2326 /* Never fail the uninstall even if we did not succeed. */
2327 return ERROR_SUCCESS;
2328}
2329#endif /* VBOX_WITH_NETADP */
2330
2331UINT __stdcall UninstallNetAdp(MSIHANDLE hModule)
2332{
2333#ifdef VBOX_WITH_NETADP
2334 return _uninstallNetAdp(hModule, NETADP_ID);
2335#else
2336 RT_NOREF(hModule);
2337 return ERROR_SUCCESS;
2338#endif
2339}
2340
2341static bool isTAPDevice(const WCHAR *pwszGUID)
2342{
2343 HKEY hNetcard;
2344 bool bIsTapDevice = false;
2345 LONG lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2346 L"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}",
2347 0, KEY_READ, &hNetcard);
2348 if (lStatus != ERROR_SUCCESS)
2349 return false;
2350
2351 int i = 0;
2352 for (;;)
2353 {
2354 WCHAR wszEnumName[256];
2355 WCHAR wszNetCfgInstanceId[256];
2356 DWORD dwKeyType;
2357 HKEY hNetCardGUID;
2358
2359 DWORD dwLen = sizeof(wszEnumName);
2360 lStatus = RegEnumKeyExW(hNetcard, i, wszEnumName, &dwLen, NULL, NULL, NULL, NULL);
2361 if (lStatus != ERROR_SUCCESS)
2362 break;
2363
2364 lStatus = RegOpenKeyExW(hNetcard, wszEnumName, 0, KEY_READ, &hNetCardGUID);
2365 if (lStatus == ERROR_SUCCESS)
2366 {
2367 dwLen = sizeof(wszNetCfgInstanceId);
2368 lStatus = RegQueryValueExW(hNetCardGUID, L"NetCfgInstanceId", NULL, &dwKeyType, (LPBYTE)wszNetCfgInstanceId, &dwLen);
2369 if ( lStatus == ERROR_SUCCESS
2370 && dwKeyType == REG_SZ)
2371 {
2372 WCHAR wszNetProductName[256];
2373 WCHAR wszNetProviderName[256];
2374
2375 wszNetProductName[0] = 0;
2376 dwLen = sizeof(wszNetProductName);
2377 lStatus = RegQueryValueExW(hNetCardGUID, L"ProductName", NULL, &dwKeyType, (LPBYTE)wszNetProductName, &dwLen);
2378
2379 wszNetProviderName[0] = 0;
2380 dwLen = sizeof(wszNetProviderName);
2381 lStatus = RegQueryValueExW(hNetCardGUID, L"ProviderName", NULL, &dwKeyType, (LPBYTE)wszNetProviderName, &dwLen);
2382
2383 if ( !RTUtf16Cmp(wszNetCfgInstanceId, pwszGUID)
2384 && !RTUtf16Cmp(wszNetProductName, L"VirtualBox TAP Adapter")
2385 && ( (!RTUtf16Cmp(wszNetProviderName, L"innotek GmbH")) /* Legacy stuff. */
2386 || (!RTUtf16Cmp(wszNetProviderName, L"Sun Microsystems, Inc.")) /* Legacy stuff. */
2387 || (!RTUtf16Cmp(wszNetProviderName, MY_WTEXT(VBOX_VENDOR))) /* Reflects current vendor string. */
2388 )
2389 )
2390 {
2391 bIsTapDevice = true;
2392 RegCloseKey(hNetCardGUID);
2393 break;
2394 }
2395 }
2396 RegCloseKey(hNetCardGUID);
2397 }
2398 ++i;
2399 }
2400
2401 RegCloseKey(hNetcard);
2402 return bIsTapDevice;
2403}
2404
2405/** @todo r=andy BUGBUG WTF! Why do we a) set the rc to 0 (success), and b) need this macro at all!?
2406 *
2407 * @todo r=bird: Because it's returning a bool, not int? The return code is
2408 * ignored anyway, both internally in removeNetworkInterface and in it's caller.
2409 * There is similar code in VBoxNetCfg.cpp, which is probably where it was copied from. */
2410#define SetErrBreak(args) \
2411 if (1) { \
2412 rc = 0; \
2413 logStringF args; \
2414 break; \
2415 } else do {} while (0)
2416
2417int removeNetworkInterface(MSIHANDLE hModule, const WCHAR *pwszGUID)
2418{
2419 int rc = 1;
2420 do /* break-loop */
2421 {
2422 WCHAR wszPnPInstanceId[512] = {0};
2423
2424 /* We have to find the device instance ID through a registry search */
2425
2426 HKEY hkeyNetwork = 0;
2427 HKEY hkeyConnection = 0;
2428
2429 do /* break-loop */
2430 {
2431 WCHAR wszRegLocation[256];
2432 RTUtf16Printf(wszRegLocation, RT_ELEMENTS(wszRegLocation),
2433 "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\%ls", pwszGUID);
2434 LONG lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRegLocation, 0, KEY_READ, &hkeyNetwork);
2435 if (lrc != ERROR_SUCCESS || !hkeyNetwork)
2436 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [1]",
2437 wszRegLocation, lrc));
2438
2439 lrc = RegOpenKeyExW(hkeyNetwork, L"Connection", 0, KEY_READ, &hkeyConnection);
2440 if (lrc != ERROR_SUCCESS || !hkeyConnection)
2441 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [2]",
2442 wszRegLocation, lrc));
2443
2444 DWORD len = sizeof(wszPnPInstanceId);
2445 DWORD dwKeyType;
2446 lrc = RegQueryValueExW(hkeyConnection, L"PnPInstanceID", NULL, &dwKeyType, (LPBYTE)&wszPnPInstanceId[0], &len);
2447 if (lrc != ERROR_SUCCESS || dwKeyType != REG_SZ)
2448 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [3]",
2449 wszRegLocation, lrc));
2450 }
2451 while (0);
2452
2453 if (hkeyConnection)
2454 RegCloseKey(hkeyConnection);
2455 if (hkeyNetwork)
2456 RegCloseKey(hkeyNetwork);
2457
2458 /*
2459 * Now we are going to enumerate all network devices and
2460 * wait until we encounter the right device instance ID
2461 */
2462
2463 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2464 BOOL fResult;
2465
2466 do /* break-loop */
2467 {
2468 /* initialize the structure size */
2469 SP_DEVINFO_DATA DeviceInfoData = { sizeof(DeviceInfoData) };
2470
2471 /* copy the net class GUID */
2472 GUID netGuid;
2473 memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof (GUID_DEVCLASS_NET));
2474
2475 /* return a device info set contains all installed devices of the Net class */
2476 hDeviceInfo = SetupDiGetClassDevs(&netGuid, NULL, NULL, DIGCF_PRESENT);
2477 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2478 {
2479 logStringF(hModule, "VBox HostInterfaces: SetupDiGetClassDevs failed (0x%08X)!", GetLastError());
2480 SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
2481 }
2482
2483 /* enumerate the driver info list */
2484 BOOL fFoundDevice = FALSE;
2485 for (DWORD index = 0;; index++)
2486 {
2487 fResult = SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData);
2488 if (!fResult)
2489 {
2490 if (GetLastError() == ERROR_NO_MORE_ITEMS)
2491 break;
2492 continue;
2493 }
2494
2495 /* try to get the hardware ID registry property */
2496 WCHAR *pwszDeviceHwid;
2497 DWORD size = 0;
2498 fResult = SetupDiGetDeviceRegistryProperty(hDeviceInfo,
2499 &DeviceInfoData,
2500 SPDRP_HARDWAREID,
2501 NULL,
2502 NULL,
2503 0,
2504 &size);
2505 if (!fResult)
2506 {
2507 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2508 continue;
2509
2510 pwszDeviceHwid = (WCHAR *)RTMemAllocZ(size);
2511 if (!pwszDeviceHwid)
2512 continue;
2513
2514 fResult = SetupDiGetDeviceRegistryProperty(hDeviceInfo,
2515 &DeviceInfoData,
2516 SPDRP_HARDWAREID,
2517 NULL,
2518 (PBYTE)pwszDeviceHwid,
2519 size,
2520 &size);
2521 if (!fResult)
2522 {
2523 RTMemFree(pwszDeviceHwid);
2524 continue;
2525 }
2526 }
2527 else
2528 {
2529 /* something is wrong. This shouldn't have worked with a NULL buffer */
2530 continue;
2531 }
2532
2533 for (WCHAR *t = pwszDeviceHwid;
2534 *t && t < &pwszDeviceHwid[size / sizeof(WCHAR)];
2535 t += RTUtf16Len(t) + 1)
2536 {
2537 if (RTUtf16ICmpAscii(t, "vboxtap") == 0)
2538 {
2539 /* get the device instance ID */
2540 WCHAR wszDevID[MAX_DEVICE_ID_LEN];
2541 if (CM_Get_Device_IDW(DeviceInfoData.DevInst, wszDevID, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS)
2542 {
2543 /* compare to what we determined before */
2544 if (RTUtf16Cmp(wszDevID, wszPnPInstanceId) == 0)
2545 {
2546 fFoundDevice = TRUE;
2547 break;
2548 }
2549 }
2550 }
2551 }
2552
2553 RTMemFree(pwszDeviceHwid);
2554
2555 if (fFoundDevice)
2556 break;
2557 }
2558
2559 if (fFoundDevice)
2560 {
2561 fResult = SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData);
2562 if (!fResult)
2563 {
2564 logStringF(hModule, "VBox HostInterfaces: SetupDiSetSelectedDevice failed (0x%08X)!", GetLastError());
2565 SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
2566 }
2567
2568 fResult = SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2569 if (!fResult)
2570 {
2571 logStringF(hModule, "VBox HostInterfaces: SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)!", GetLastError());
2572 SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
2573 }
2574 }
2575 else
2576 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network device not found!"));
2577 } while (0);
2578
2579 /* clean up the device info set */
2580 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2581 SetupDiDestroyDeviceInfoList(hDeviceInfo);
2582 } while (0);
2583 return rc;
2584}
2585
2586UINT __stdcall UninstallTAPInstances(MSIHANDLE hModule)
2587{
2588 static const wchar_t s_wszNetworkKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}";
2589 HKEY hCtrlNet;
2590
2591 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_wszNetworkKey, 0, KEY_READ, &hCtrlNet);
2592 if (lrc == ERROR_SUCCESS)
2593 {
2594 logStringF(hModule, "VBox HostInterfaces: Enumerating interfaces ...");
2595 for (int i = 0; ; ++i)
2596 {
2597 WCHAR wszNetworkGUID[256] = { 0 };
2598 DWORD dwLen = (DWORD)sizeof(wszNetworkGUID);
2599 lrc = RegEnumKeyExW(hCtrlNet, i, wszNetworkGUID, &dwLen, NULL, NULL, NULL, NULL);
2600 if (lrc != ERROR_SUCCESS)
2601 {
2602 switch (lrc)
2603 {
2604 case ERROR_NO_MORE_ITEMS:
2605 logStringF(hModule, "VBox HostInterfaces: No interfaces found.");
2606 break;
2607 default:
2608 logStringF(hModule, "VBox HostInterfaces: Enumeration failed: %ld", lrc);
2609 break;
2610 }
2611 break;
2612 }
2613
2614 if (isTAPDevice(wszNetworkGUID))
2615 {
2616 logStringF(hModule, "VBox HostInterfaces: Removing interface \"%ls\" ...", wszNetworkGUID);
2617 removeNetworkInterface(hModule, wszNetworkGUID);
2618 lrc = RegDeleteKeyW(hCtrlNet, wszNetworkGUID);
2619 }
2620 }
2621 RegCloseKey(hCtrlNet);
2622 logStringF(hModule, "VBox HostInterfaces: Removing interfaces done.");
2623 }
2624 return ERROR_SUCCESS;
2625}
2626
2627
2628/**
2629 * This is used to remove the old VBoxDrv service before installation.
2630 *
2631 * The current service name is VBoxSup but the INF file won't remove the old
2632 * one, so we do it manually to try prevent trouble as the device nodes are the
2633 * same and we would fail starting VBoxSup.sys if VBoxDrv.sys is still loading.
2634 *
2635 * Status code is ignored for now as a reboot should fix most potential trouble
2636 * here (and I don't want to break stuff too badly).
2637 *
2638 * @sa @bugref{10162}
2639 */
2640UINT __stdcall UninstallVBoxDrv(MSIHANDLE hModule)
2641{
2642 /*
2643 * Try open the service.
2644 */
2645 SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG | SERVICE_STOP | SERVICE_QUERY_STATUS);
2646 if (hSMgr)
2647 {
2648 SC_HANDLE hService = OpenServiceW(hSMgr, L"VBoxDrv", DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
2649 if (hService)
2650 {
2651 /*
2652 * Try stop it before we delete it.
2653 */
2654 SERVICE_STATUS Status = { 0, 0, 0, 0, 0, 0, 0 };
2655 QueryServiceStatus(hService, &Status);
2656 if (Status.dwCurrentState == SERVICE_STOPPED)
2657 logStringF(hModule, "VBoxDrv: The service old service was already stopped");
2658 else
2659 {
2660 logStringF(hModule, "VBoxDrv: Stopping the service (state %u)", Status.dwCurrentState);
2661 if (ControlService(hService, SERVICE_CONTROL_STOP, &Status))
2662 {
2663 /* waiting for it to stop: */
2664 int iWait = 100;
2665 while (Status.dwCurrentState == SERVICE_STOP_PENDING && iWait-- > 0)
2666 {
2667 Sleep(100);
2668 QueryServiceStatus(hService, &Status);
2669 }
2670
2671 if (Status.dwCurrentState == SERVICE_STOPPED)
2672 logStringF(hModule, "VBoxDrv: Stopped service");
2673 else
2674 logStringF(hModule, "VBoxDrv: Failed to stop the service, status: %u", Status.dwCurrentState);
2675 }
2676 else
2677 {
2678 DWORD const dwErr = GetLastError();
2679 if ( Status.dwCurrentState == SERVICE_STOP_PENDING
2680 && dwErr == ERROR_SERVICE_CANNOT_ACCEPT_CTRL)
2681 logStringF(hModule, "VBoxDrv: Failed to stop the service: stop pending, not accepting control messages");
2682 else
2683 logStringF(hModule, "VBoxDrv: Failed to stop the service: dwErr=%u status=%u", dwErr, Status.dwCurrentState);
2684 }
2685 }
2686
2687 /*
2688 * Delete the service, or at least mark it for deletion.
2689 */
2690 if (DeleteService(hService))
2691 logStringF(hModule, "VBoxDrv: Successfully delete service");
2692 else
2693 logStringF(hModule, "VBoxDrv: Failed to delete the service: %u", GetLastError());
2694
2695 CloseServiceHandle(hService);
2696 }
2697 else
2698 {
2699 DWORD const dwErr = GetLastError();
2700 if (dwErr == ERROR_SERVICE_DOES_NOT_EXIST)
2701 logStringF(hModule, "VBoxDrv: Nothing to do, the old service does not exist");
2702 else
2703 logStringF(hModule, "VBoxDrv: Failed to open the service: %u", dwErr);
2704 }
2705
2706 CloseServiceHandle(hSMgr);
2707 }
2708 else
2709 logStringF(hModule, "VBoxDrv: Failed to open service manager (%u).", GetLastError());
2710
2711 return ERROR_SUCCESS;
2712}
2713
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette