diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 22:55:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 22:55:45 +0000 |
commit | 04aecf1372d30eb709d8de65152535ab66dcb74a (patch) | |
tree | d1e4d8c453a76465e8b63119314a28d39b474479 /src/VBox/Installer/win/InstallHelper | |
parent | Adding upstream version 7.0.14-dfsg. (diff) | |
download | virtualbox-upstream/7.0.16-dfsg.tar.xz virtualbox-upstream/7.0.16-dfsg.zip |
Adding upstream version 7.0.16-dfsg.upstream/7.0.16-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Installer/win/InstallHelper')
4 files changed, 721 insertions, 105 deletions
diff --git a/src/VBox/Installer/win/InstallHelper/Makefile.kmk b/src/VBox/Installer/win/InstallHelper/Makefile.kmk index cad3c4c3..38525a7d 100644 --- a/src/VBox/Installer/win/InstallHelper/Makefile.kmk +++ b/src/VBox/Installer/win/InstallHelper/Makefile.kmk @@ -33,9 +33,16 @@ DLLS += VBoxInstallHelper VBoxInstallHelper_TEMPLATE = VBoxR3StaticDllNoAsan VBoxInstallHelper_SDKS = ReorderCompilerIncs $(VBOX_WINPSDK) $(VBOX_WINDDK) VBoxInstallHelper_DEFS = _WIN32_WINNT=0x0501 _UNICODE UNICODE VBOX_SVN_REV=$(VBOX_SVN_REV) -ifdef VBOX_WITH_NETFLT +if defined(VBOX_WITH_NETFLT) || defined(VBOX_WITH_NETADP) VBoxInstallHelper_SDKS += VBoxWinNewDevLib - VBoxInstallHelper_DEFS += VBOX_WITH_NETFLT=1 + VBoxInstallHelper_DEFS += \ + $(if $(VBOX_WITH_NETFLT),VBOX_WITH_NETFLT,) \ + $(if $(VBOX_WITH_NETADP),VBOX_WITH_NETADP,) + VBoxInstallHelper_LIBS += \ + $(PATH_STAGE_LIB)/WinNetConfigSharedStatic.lib \ + $(PATH_STAGE_LIB)/VBoxDrvCfgSharedStatic.lib \ + $(PATH_TOOL_$(VBOX_VCC_TOOL)_LIB)/comsupp.lib \ + $(PATH_SDK_$(VBOX_WINPSDK)_LIB)/WbemUuid.Lib endif VBoxInstallHelper_DEPS = $(VBOX_SVN_REV_KMK) VBoxInstallHelper_SOURCES = \ @@ -47,15 +54,8 @@ ifndef VBOX_OSE VBoxInstallHelper_SOURCES += \ internal/VBoxSerial.cpp endif -VBoxInstallHelper_LIBS = \ +VBoxInstallHelper_LIBS += \ $(PATH_SDK_$(VBOX_WINPSDK)_LIB)/Msi.lib -ifdef VBOX_WITH_NETFLT - VBoxInstallHelper_LIBS += \ - $(PATH_STAGE_LIB)/WinNetConfigSharedStatic.lib \ - $(PATH_STAGE_LIB)/VBoxDrvCfgSharedStatic.lib \ - $(PATH_TOOL_$(VBOX_VCC_TOOL)_LIB)/comsupp.lib \ - $(PATH_SDK_$(VBOX_WINPSDK)_LIB)/WbemUuid.Lib -endif if "$(KBUILD_TARGET)" == "win" && defined(VBOX_WITH_TESTCASES) && !defined(VBOX_OSE) include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk diff --git a/src/VBox/Installer/win/InstallHelper/VBoxCommon.cpp b/src/VBox/Installer/win/InstallHelper/VBoxCommon.cpp index f02447bc..d76cb438 100644 --- a/src/VBox/Installer/win/InstallHelper/VBoxCommon.cpp +++ b/src/VBox/Installer/win/InstallHelper/VBoxCommon.cpp @@ -38,27 +38,28 @@ #include <iprt/string.h> #include <iprt/utf16.h> +#include "VBoxCommon.h" + +#ifndef TESTCASE +/** + * Retrieves a MSI property (in UTF-16). + * + * Convenience function for VBoxGetMsiProp(). + * + * @returns VBox status code. + * @param hMsi MSI handle to use. + * @param pwszName Name of property to retrieve. + * @param pwszValueBuf Where to store the allocated value on success. + * @param cwcValueBuf Size (in WCHARs) of \a pwszValueBuf. + */ UINT VBoxGetMsiProp(MSIHANDLE hMsi, const WCHAR *pwszName, WCHAR *pwszValueBuf, DWORD cwcValueBuf) { - RT_BZERO(pwszValueBuf, cwcValueBuf * sizeof(pwszValueBuf[0])); - - /** @todo r=bird: why do we need to query the size first and then the data. - * The API should be perfectly capable of doing that without our help. */ - DWORD cwcNeeded = 0; - UINT uiRet = MsiGetPropertyW(hMsi, pwszName, L"", &cwcNeeded); - if (uiRet == ERROR_MORE_DATA) - { - ++cwcNeeded; /* On output does not include terminating null, so add 1. */ - - if (cwcNeeded > cwcValueBuf) - return ERROR_MORE_DATA; - uiRet = MsiGetPropertyW(hMsi, pwszName, pwszValueBuf, &cwcNeeded); - } - return uiRet; + RT_BZERO(pwszValueBuf, cwcValueBuf * sizeof(WCHAR)); + return MsiGetPropertyW(hMsi, pwszName, pwszValueBuf, &cwcValueBuf); } +#endif -#if 0 /* unused */ /** * Retrieves a MSI property (in UTF-8). * @@ -77,7 +78,7 @@ int VBoxGetMsiPropUtf8(MSIHANDLE hMsi, const char *pcszName, char **ppszValue) if (RT_SUCCESS(rc)) { WCHAR wszValue[1024]; /* 1024 should be enough for everybody (tm). */ - if (VBoxGetMsiProp(hMsi, pwszName, wszValue, sizeof(wszValue)) == ERROR_SUCCESS) + if (VBoxGetMsiProp(hMsi, pwszName, wszValue, RT_ELEMENTS(wszValue)) == ERROR_SUCCESS) rc = RTUtf16ToUtf8(wszValue, ppszValue); else rc = VERR_NOT_FOUND; @@ -87,12 +88,13 @@ int VBoxGetMsiPropUtf8(MSIHANDLE hMsi, const char *pcszName, char **ppszValue) return rc; } -#endif +#ifndef TESTCASE UINT VBoxSetMsiProp(MSIHANDLE hMsi, const WCHAR *pwszName, const WCHAR *pwszValue) { return MsiSetPropertyW(hMsi, pwszName, pwszValue); } +#endif UINT VBoxSetMsiPropDWORD(MSIHANDLE hMsi, const WCHAR *pwszName, DWORD dwVal) { diff --git a/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.cpp b/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.cpp index f82f0848..d4ac6104 100644 --- a/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.cpp +++ b/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.cpp @@ -29,22 +29,24 @@ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ -#ifdef VBOX_WITH_NETFLT +#if defined(VBOX_WITH_NETFLT) || defined(VBOX_WITH_NETADP) # include "VBox/VBoxNetCfg-win.h" # include "VBox/VBoxDrvCfg-win.h" #endif -#include <msi.h> -#include <msiquery.h> - #define _WIN32_DCOM #include <iprt/win/windows.h> +#include <aclapi.h> +#include <msi.h> +#include <msiquery.h> + #include <shellapi.h> #define INITGUID #include <guiddef.h> #include <cfgmgr32.h> #include <devguid.h> +#include <sddl.h> /* For ConvertSidToStringSidW. */ #include <iprt/win/objbase.h> #include <iprt/win/setupapi.h> @@ -54,9 +56,14 @@ #include <iprt/assert.h> #include <iprt/alloca.h> +#include <iprt/dir.h> +#include <iprt/err.h> +#include <iprt/file.h> #include <iprt/mem.h> #include <iprt/path.h> /* RTPATH_MAX, RTPATH_IS_SLASH */ #include <iprt/string.h> /* RT_ZERO */ +#include <iprt/stream.h> +#include <iprt/thread.h> #include <iprt/utf16.h> #include "VBoxCommon.h" @@ -78,6 +85,48 @@ #define MY_WTEXT(a_str) MY_WTEXT_HLP(a_str) +/********************************************************************************************************************************* +* Internal structures * +*********************************************************************************************************************************/ +/** + * Structure for keeping a target's directory security context. + */ +typedef struct TGTDIRSECCTX +{ + /** Initialized status. */ + bool fInitialized; + /** Handle of the target's parent directory. + * + * Kept open while the context is around and initialized. */ + RTDIR hParentDir; + /** Absolute (resolved) path of the target directory. */ + char szTargetDirAbs[RTPATH_MAX]; + /** Access mask which is forbidden for an ACE of type ACCESS_ALLOWED_ACE_TYPE. */ + uint32_t fAccessMaskForbidden; + /** Array of well-known SIDs which are forbidden. */ + PSID *paWellKnownSidsForbidden; + /** Number of entries in \a paWellKnownSidsForbidden. */ + size_t cWellKnownSidsForbidden; +} TGTDIRSECCTX; +/** Pointer to a target's directory security context. */ +typedef TGTDIRSECCTX *PTGTDIRSECCTX; + + +/********************************************************************************************************************************* +* Prototypes * +*********************************************************************************************************************************/ +static void destroyTargetDirSecurityCtx(PTGTDIRSECCTX pCtx); + + +/********************************************************************************************************************************* +* Globals * +*********************************************************************************************************************************/ +static uint32_t g_cRef = 0; +/** Our target directory security context. + * + * Has to be global in order to keep it around as long as the DLL is being loaded. */ +static TGTDIRSECCTX g_TargetDirSecCtx = { 0 }; + /** * DLL entry point. @@ -86,59 +135,94 @@ BOOL WINAPI DllMain(HANDLE hInst, ULONG uReason, LPVOID pReserved) { RT_NOREF(hInst, uReason, pReserved); -#if 0 - /* - * This is a trick for allowing the debugger to be attached, don't know if - * there is an official way to do that, but this is a pretty efficient. - * - * Monitor the debug output in DbgView and be ready to start windbg when - * the message below appear. This will happen 3-4 times during install, - * and 2-3 times during uninstall. - * - * Note! The DIFxApp.DLL will automatically trigger breakpoints when a - * debugger is attached. Just continue on these. - */ - if (uReason == DLL_PROCESS_ATTACH) +#ifdef DEBUG + WCHAR wszMsg[128]; + RTUtf16Printf(wszMsg, RT_ELEMENTS(wszMsg), "DllMain: hInst=%#x, uReason=%u (PID %u), g_cRef=%RU32\n", + hInst, uReason, GetCurrentProcessId(), g_cRef); + OutputDebugStringW(wszMsg); +#endif + + switch (uReason) { - WCHAR wszMsg[128]; - RTUtf16Printf(wszMsg, RT_ELEMENTS(wszMsg), "Waiting for debugger to attach: windbg -g -G -p %u\n", GetCurrentProcessId()); - for (unsigned i = 0; i < 128 && !IsDebuggerPresent(); i++) + case DLL_PROCESS_ATTACH: + { + g_cRef++; +#if 0 + /* + * This is a trick for allowing the debugger to be attached, don't know if + * there is an official way to do that, but this is a pretty efficient. + * + * Monitor the debug output in DbgView and be ready to start windbg when + * the message below appear. This will happen 3-4 times during install, + * and 2-3 times during uninstall. + * + * Note! The DIFxApp.DLL will automatically trigger breakpoints when a + * debugger is attached. Just continue on these. + */ + RTUtf16Printf(wszMsg, RT_ELEMENTS(wszMsg), "Waiting for debugger to attach: windbg -g -G -p %u\n", GetCurrentProcessId()); + for (unsigned i = 0; i < 128 && !IsDebuggerPresent(); i++) + { + OutputDebugStringW(wszMsg); + Sleep(1001); + } + Sleep(1002); + __debugbreak(); +#endif + break; + } + + case DLL_PROCESS_DETACH: { - OutputDebugStringW(wszMsg); - Sleep(1001); + g_cRef--; + break; } - Sleep(1002); - __debugbreak(); + + default: + break; } -#endif return TRUE; } /** - * Format and add message to the MSI log. + * Format a log message and print it to whatever is there (i.e. to the MSI log). * * UTF-16 strings are formatted using '%ls' (lowercase). * ANSI strings are formatted using '%s' (uppercase). + * + * @returns VBox status code. + * @param hInstall MSI installer handle. Optional and can be NULL. + * @param pszFmt Format string. + * @param ... Variable arguments for format string. */ -static UINT logStringF(MSIHANDLE hInstall, const char *pszFmt, ...) +static int logStringF(MSIHANDLE hInstall, const char *pszFmt, ...) { + RTUTF16 wszVa[RTPATH_MAX + 256]; + va_list va; + va_start(va, pszFmt); + ssize_t cwc = RTUtf16PrintfV(wszVa, RT_ELEMENTS(wszVa), pszFmt, va); + va_end(va); + + RTUTF16 wszMsg[RTPATH_MAX + 256]; + cwc = RTUtf16Printf(wszMsg, sizeof(wszMsg), "VBoxInstallHelper: %ls", wszVa); + if (cwc <= 0) + return VERR_BUFFER_OVERFLOW; + +#ifdef DEBUG + OutputDebugStringW(wszMsg); +#endif +#ifdef TESTCASE + RTPrintf("%ls\n", wszMsg); +#endif PMSIHANDLE hMSI = MsiCreateRecord(2 /* cParms */); if (hMSI) { - wchar_t wszBuf[RTPATH_MAX + 256]; - va_list va; - va_start(va, pszFmt); - ssize_t cwc = RTUtf16PrintfV(wszBuf, RT_ELEMENTS(wszBuf), pszFmt, va); - va_end(va); - - MsiRecordSetStringW(hMSI, 0, wszBuf); + MsiRecordSetStringW(hMSI, 0, wszMsg); MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_INFO), hMSI); - MsiCloseHandle(hMSI); - return cwc < RT_ELEMENTS(wszBuf) ? ERROR_SUCCESS : ERROR_BUFFER_OVERFLOW; } - return ERROR_ACCESS_DENIED; + + return cwc < RT_ELEMENTS(wszVa) ? VINF_SUCCESS : VERR_BUFFER_OVERFLOW; } UINT __stdcall IsSerialCheckNeeded(MSIHANDLE hModule) @@ -162,6 +246,483 @@ UINT __stdcall CheckSerial(MSIHANDLE hModule) } /** + * Initializes a target security context. + * + * @returns VBox status code. + * @param pCtx Target directory security context to initialize. + * @param hModule Windows installer module handle. + * @param pszPath Target directory path to use. + */ +static int initTargetDirSecurityCtx(PTGTDIRSECCTX pCtx, MSIHANDLE hModule, const char *pszPath) +{ + if (pCtx->fInitialized) + return VINF_SUCCESS; + +#ifdef DEBUG + logStringF(hModule, "initTargetDirSecurityCtx: pszPath=%s\n", pszPath); +#endif + + char szPathTemp[RTPATH_MAX]; + int vrc = RTStrCopy(szPathTemp, sizeof(szPathTemp), pszPath); + if (RT_FAILURE(vrc)) + return vrc; + + /* Try to find a parent path which exists. */ + char szPathParentAbs[RTPATH_MAX] = { 0 }; + for (int i = 0; i < 256; i++) /* Failsafe counter. */ + { + RTPathStripTrailingSlash(szPathTemp); + RTPathStripFilename(szPathTemp); + vrc = RTPathReal(szPathTemp, szPathParentAbs, sizeof(szPathParentAbs)); + if (RT_SUCCESS(vrc)) + break; + } + + if (RT_FAILURE(vrc)) + { + logStringF(hModule, "initTargetDirSecurityCtx: No existing / valid parent directory found (%Rrc), giving up\n", vrc); + return vrc; + } + + RTDIR hParentDir; + vrc = RTDirOpen(&hParentDir, szPathParentAbs); + if (RT_FAILURE(vrc)) + { + logStringF(hModule, "initTargetDirSecurityCtx: Locking parent directory '%s' failed with %Rrc\n", szPathParentAbs, vrc); + return vrc; + } + +#ifdef DEBUG + logStringF(hModule, "initTargetDirSecurityCtx: Locked parent directory '%s'\n", szPathParentAbs); +#endif + + char szPathTargetAbs[RTPATH_MAX]; + vrc = RTPathReal(pszPath, szPathTargetAbs, sizeof(szPathTargetAbs)); + if (RT_FAILURE(vrc)) + vrc = RTStrCopy(szPathTargetAbs, sizeof(szPathTargetAbs), pszPath); + if (RT_FAILURE(vrc)) + { + logStringF(hModule, "initTargetDirSecurityCtx: Failed to resolve absolute target path (%Rrc)\n", vrc); + return vrc; + } + +#ifdef DEBUG + logStringF(hModule, "initTargetDirSecurityCtx: szPathTargetAbs=%s, szPathParentAbs=%s\n", szPathTargetAbs, szPathParentAbs); +#endif + + /* Target directory validation. */ + if ( !RTStrCmp(szPathTargetAbs, szPathParentAbs) /* Don't allow installation into root directories. */ + || RTStrStr(szPathTargetAbs, "..")) + { + logStringF(hModule, "initTargetDirSecurityCtx: Directory '%s' invalid", szPathTargetAbs); + vrc = VERR_INVALID_NAME; + } + + if (RT_SUCCESS(vrc)) + { + RTFSOBJINFO fsObjInfo; + vrc = RTPathQueryInfo(szPathParentAbs, &fsObjInfo, RTFSOBJATTRADD_NOTHING); + if (RT_SUCCESS(vrc)) + { + if (RTFS_IS_DIRECTORY(fsObjInfo.Attr.fMode)) /* No symlinks or other fun stuff. */ + { + static WELL_KNOWN_SID_TYPE aForbiddenWellKnownSids[] = + { + WinNullSid, + WinWorldSid, + WinAuthenticatedUserSid, + WinBuiltinUsersSid, + WinBuiltinGuestsSid, + WinBuiltinPowerUsersSid + }; + + pCtx->paWellKnownSidsForbidden = (PSID *)RTMemAlloc(sizeof(PSID) * RT_ELEMENTS(aForbiddenWellKnownSids)); + AssertPtrReturn(pCtx->paWellKnownSidsForbidden, VERR_NO_MEMORY); + + size_t i = 0; + for(; i < RT_ELEMENTS(aForbiddenWellKnownSids); i++) + { + pCtx->paWellKnownSidsForbidden[i] = RTMemAlloc(SECURITY_MAX_SID_SIZE); + AssertPtrBreakStmt(pCtx->paWellKnownSidsForbidden, vrc = VERR_NO_MEMORY); + DWORD cbSid = SECURITY_MAX_SID_SIZE; + if (!CreateWellKnownSid(aForbiddenWellKnownSids[i], NULL, pCtx->paWellKnownSidsForbidden[i], &cbSid)) + { + vrc = RTErrConvertFromWin32(GetLastError()); + logStringF(hModule, "initTargetDirSecurityCtx: Creating SID (index %zu) failed with %Rrc\n", i, vrc); + break; + } + } + + if (RT_SUCCESS(vrc)) + { + vrc = RTStrCopy(pCtx->szTargetDirAbs, sizeof(pCtx->szTargetDirAbs), szPathTargetAbs); + if (RT_SUCCESS(vrc)) + { + pCtx->fInitialized = true; + pCtx->hParentDir = hParentDir; + pCtx->cWellKnownSidsForbidden = i; + pCtx->fAccessMaskForbidden = FILE_WRITE_DATA + | FILE_APPEND_DATA + | FILE_WRITE_ATTRIBUTES + | FILE_WRITE_EA; + + RTFILE fh; + RTFileOpen(&fh, "c:\\temp\\targetdir.ctx", RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE | RTFILE_O_WRITE); + RTFileClose(fh); + + return VINF_SUCCESS; + } + } + } + else + vrc = VERR_INVALID_NAME; + } + } + + RTDirClose(hParentDir); + + while (pCtx->cWellKnownSidsForbidden--) + { + RTMemFree(pCtx->paWellKnownSidsForbidden[pCtx->cWellKnownSidsForbidden]); + pCtx->paWellKnownSidsForbidden[pCtx->cWellKnownSidsForbidden] = NULL; + } + + logStringF(hModule, "initTargetDirSecurityCtx: Initialization failed failed with %Rrc\n", vrc); + return vrc; +} + +/** + * Destroys a target security context. + * + * @returns VBox status code. + * @param pCtx Target directory security context to destroy. + */ +static void destroyTargetDirSecurityCtx(PTGTDIRSECCTX pCtx) +{ + if ( !pCtx + || !pCtx->fInitialized) + return; + + if (pCtx->hParentDir != NIL_RTDIR) + { + RTDirClose(pCtx->hParentDir); + pCtx->hParentDir = NIL_RTDIR; + } + RT_ZERO(pCtx->szTargetDirAbs); + + for (size_t i = 0; i < pCtx->cWellKnownSidsForbidden; i++) + RTMemFree(pCtx->paWellKnownSidsForbidden[i]); + pCtx->cWellKnownSidsForbidden = 0; + + RTMemFree(pCtx->paWellKnownSidsForbidden); + pCtx->paWellKnownSidsForbidden = NULL; + + RTFileDelete("c:\\temp\\targetdir.ctx"); + + logStringF(NULL, "destroyTargetDirSecurityCtx\n"); +} + +#ifdef DEBUG +/** + * Returns a stingified version of an ACE type. + * + * @returns Stingified version of an ACE type. + * @param uType ACE type. + */ +inline const char *dbgAceTypeToString(uint8_t uType) +{ + switch (uType) + { + RT_CASE_RET_STR(ACCESS_ALLOWED_ACE_TYPE); + RT_CASE_RET_STR(ACCESS_DENIED_ACE_TYPE); + RT_CASE_RET_STR(SYSTEM_AUDIT_ACE_TYPE); + RT_CASE_RET_STR(SYSTEM_ALARM_ACE_TYPE); + default: break; + } + + return "<Invalid>"; +} + +/** + * Returns an allocated string for a SID containing the user/domain name. + * + * @returns Allocated string (UTF-8). Must be free'd using RTStrFree(). + * @param pSid SID to return allocated string for. + */ +inline char *dbgSidToNameA(const PSID pSid) +{ + char *pszName = NULL; + int vrc = VINF_SUCCESS; + + LPWSTR pwszSid = NULL; + if (ConvertSidToStringSid(pSid, &pwszSid)) + { + SID_NAME_USE SidNameUse; + + WCHAR wszUser[MAX_PATH]; + DWORD cbUser = sizeof(wszUser); + WCHAR wszDomain[MAX_PATH]; + DWORD cbDomain = sizeof(wszDomain); + if (LookupAccountSid(NULL, pSid, wszUser, &cbUser, wszDomain, &cbDomain, &SidNameUse)) + { + RTUTF16 wszName[RTPATH_MAX]; + if (RTUtf16Printf(wszName, RT_ELEMENTS(wszName), "%ls%s%ls (%ls)", + wszUser, wszDomain[0] == L'\0' ? "" : "\\", wszDomain, pwszSid)) + { + vrc = RTUtf16ToUtf8(wszName, &pszName); + } + else + vrc = VERR_NO_MEMORY; + } + else + vrc = RTStrAPrintf(&pszName, "<Lookup Error>"); + + LocalFree(pwszSid); + } + else + vrc = VERR_NOT_FOUND; + + return RT_SUCCESS(vrc) ? pszName : "<Invalid>"; +} +#endif /* DEBUG */ + +/** + * Checks a single target path whether it's safe to use or not. + * + * We check if the given path is owned by "NT Service\TrustedInstaller" and therefore assume that it's safe to use. + * + * @returns VBox status code. On error the path should be considered unsafe. + * @retval VERR_INVALID_NAME if the given path is considered unsafe. + * @retval VINF_SUCCESS if the given path is found to be safe to use. + * @param hModule Windows installer module handle. + * @param pszPath Path to check. + */ +static int checkTargetDirOne(MSIHANDLE hModule, PTGTDIRSECCTX pCtx, const char *pszPath) +{ + logStringF(hModule, "checkTargetDirOne: Checking '%s' ...", pszPath); + + PRTUTF16 pwszPath; + int vrc = RTStrToUtf16(pszPath, &pwszPath); + if (RT_FAILURE(vrc)) + return vrc; + + PACL pDacl = NULL; + PSECURITY_DESCRIPTOR pSecurityDescriptor = { 0 }; + DWORD dwErr = GetNamedSecurityInfo(pwszPath, SE_FILE_OBJECT, GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + NULL, NULL, NULL, NULL, &pSecurityDescriptor); + if (dwErr == ERROR_SUCCESS) + { + BOOL fDaclPresent = FALSE; + BOOL fDaclDefaultedIgnored = FALSE; + if (GetSecurityDescriptorDacl(pSecurityDescriptor, &fDaclPresent, + &pDacl, &fDaclDefaultedIgnored)) + { + if ( !fDaclPresent + || !pDacl) + { + /* Bail out early if the DACL isn't provided or is missing. */ + vrc = VERR_INVALID_NAME; + } + else + { + ACL_SIZE_INFORMATION aclSizeInfo; + RT_ZERO(aclSizeInfo); + if (GetAclInformation(pDacl, &aclSizeInfo, sizeof(aclSizeInfo), AclSizeInformation)) + { + for(DWORD idxACE = 0; idxACE < aclSizeInfo.AceCount; idxACE++) + { + ACE_HEADER *pAceHdr = NULL; + if (GetAce(pDacl, idxACE, (LPVOID *)&pAceHdr)) + { +#ifdef DEBUG + logStringF(hModule, "checkTargetDirOne: ACE type=%s, flags=%#x, size=%#x", + dbgAceTypeToString(pAceHdr->AceType), pAceHdr->AceFlags, pAceHdr->AceSize); +#endif + /* Note: We print the ACEs in canonoical order. */ + switch (pAceHdr->AceType) + { + case ACCESS_ALLOWED_ACE_TYPE: /* We're only interested in the ALLOW ACE. */ + { + ACCESS_ALLOWED_ACE const *pAce = (ACCESS_ALLOWED_ACE *)pAceHdr; + PSID const pSid = (PSID)&pAce->SidStart; +#ifdef DEBUG + char *pszSid = dbgSidToNameA(pSid); + logStringF(hModule, "checkTargetDirOne:\t%s fMask=%#x", pszSid, pAce->Mask); + RTStrFree(pszSid); +#endif + /* We check the flags here first for performance reasons. */ + if ((pAce->Mask & pCtx->fAccessMaskForbidden) == pCtx->fAccessMaskForbidden) + { + for (size_t idxSID = 0; idxSID < pCtx->cWellKnownSidsForbidden; idxSID++) + { + PSID const pSidForbidden = pCtx->paWellKnownSidsForbidden[idxSID]; + bool const fForbidden = EqualSid(pSid, pSidForbidden); +#ifdef DEBUG + char *pszName = dbgSidToNameA(pSidForbidden); + logStringF(hModule, "checkTargetDirOne:\t%s : %s", + fForbidden ? "** FORBIDDEN **" : "ALLOWED ", pszName); + RTStrFree(pszName); +#endif /* DEBUG */ + if (fForbidden) + { + vrc = VERR_INVALID_NAME; + break; + } + } + } + + break; + } +#ifdef DEBUG + case ACCESS_DENIED_ACE_TYPE: /* We're only interested in the ALLOW ACE. */ + { + ACCESS_DENIED_ACE const *pAce = (ACCESS_DENIED_ACE *)pAceHdr; + + LPWSTR pwszSid = NULL; + ConvertSidToStringSid((PSID)&pAce->SidStart, &pwszSid); + + logStringF(hModule, "checkTargetDirOne:\t%ls fMask=%#x (generic %#x specific %#x)", + pwszSid ? pwszSid : L"<Allocation Error>", pAce->Mask); + + LocalFree(pwszSid); + break; + } +#endif /* DEBUG */ + default: + /* Ignore everything else. */ + break; + } + } + else + dwErr = GetLastError(); + + /* No point in checking further if we failed somewhere above. */ + if (RT_FAILURE(vrc)) + break; + + } /* for ACE */ + } + else + dwErr = GetLastError(); + } + } + else + dwErr = GetLastError(); + + LocalFree(pSecurityDescriptor); + } + else + dwErr = GetLastError(); + + if (RT_SUCCESS(vrc)) + vrc = RTErrConvertFromWin32(dwErr); + +#ifdef DEBUG + logStringF(hModule, "checkTargetDirOne: Returning %Rrc", vrc); +#endif + + if ( RT_FAILURE(vrc) + && vrc != VERR_INVALID_NAME) + logStringF(hModule, "checkTargetDirOne: Failed with %Rrc (%#x)", vrc, dwErr); + + return vrc; +} + +/** + * Checks whether the path in the public property INSTALLDIR has the correct ACL permissions and returns whether + * it's valid or not. + * + * Called from the MSI installer as a custom action. + * + * @returns Success status (acccording to MSI custom actions). + * @retval ERROR_SUCCESS if checking the target directory turned out to be valid. + * @retval ERROR_NO_NET_OR_BAD_PATH is the target directory is invalid. + * @param hModule Windows installer module handle. + * + * @note Sets private property VBox_Target_Dir_Is_Valid to "1" (true) if the given target path is valid, + * or "0" (false) if it is not. An empty target directory is considered to be valid (i.e. INSTALLDIR not set yet). + * + * @sa @bugref{10616} + */ +UINT __stdcall CheckTargetDir(MSIHANDLE hModule) +{ + char *pszTargetDir; + + int vrc = VBoxGetMsiPropUtf8(hModule, "INSTALLDIR", &pszTargetDir); + if (RT_SUCCESS(vrc)) + { + logStringF(hModule, "CheckTargetDir: Checking target directory '%s' ...", pszTargetDir); + + if (!RTStrNLen(pszTargetDir, RTPATH_MAX)) + { + logStringF(hModule, "CheckTargetDir: No INSTALLDIR set (yet), skipping ..."); + VBoxSetMsiProp(hModule, L"VBox_Target_Dir_Is_Valid", L"1"); + } + else + { + union + { + RTPATHPARSED Parsed; + uint8_t ab[RTPATH_MAX]; + } u; + + vrc = RTPathParse(pszTargetDir, &u.Parsed, sizeof(u), RTPATH_STR_F_STYLE_DOS); + if (RT_SUCCESS(vrc)) + { + if (u.Parsed.fProps & RTPATH_PROP_DOTDOT_REFS) + vrc = VERR_INVALID_PARAMETER; + if (RT_SUCCESS(vrc)) + { + vrc = initTargetDirSecurityCtx(&g_TargetDirSecCtx, hModule, pszTargetDir); + if (RT_SUCCESS(vrc)) + { + uint16_t idxComp = u.Parsed.cComps; + char szPathToCheck[RTPATH_MAX]; + while (idxComp > 1) /* We traverse backwards from INSTALLDIR and leave out the root (e.g. C:\"). */ + { + u.Parsed.cComps = idxComp; + vrc = RTPathParsedReassemble(pszTargetDir, &u.Parsed, RTPATH_STR_F_STYLE_DOS, + szPathToCheck, sizeof(szPathToCheck)); + if (RT_FAILURE(vrc)) + break; + if (RTDirExists(szPathToCheck)) + { + vrc = checkTargetDirOne(hModule, &g_TargetDirSecCtx, szPathToCheck); + if (RT_FAILURE(vrc)) + break; + } + else + logStringF(hModule, "CheckTargetDir: Path '%s' does not exist (yet)", szPathToCheck); + idxComp--; + } + + destroyTargetDirSecurityCtx(&g_TargetDirSecCtx); + } + else + logStringF(hModule, "CheckTargetDir: initTargetDirSecurityCtx failed with %Rrc\n", vrc); + + if (RT_SUCCESS(vrc)) + VBoxSetMsiProp(hModule, L"VBox_Target_Dir_Is_Valid", L"1"); + } + } + else + logStringF(hModule, "CheckTargetDir: Parsing path failed with %Rrc", vrc); + } + + RTStrFree(pszTargetDir); + } + + if (RT_FAILURE(vrc)) /* On failure (or when in doubt), mark the installation directory as invalid. */ + { + logStringF(hModule, "CheckTargetDir: Checking failed with %Rrc", vrc); + VBoxSetMsiProp(hModule, L"VBox_Target_Dir_Is_Valid", L"0"); + } + + /* Return back outcome to the MSI engine. */ + return RT_SUCCESS(vrc) ? ERROR_SUCCESS : ERROR_NO_NET_OR_BAD_PATH; +} + +/** * Runs an executable on the OS. * * @returns Windows error code. @@ -869,7 +1430,7 @@ UINT __stdcall InstallBranding(MSIHANDLE hModule) return ERROR_SUCCESS; /* Do not fail here. */ } -#ifdef VBOX_WITH_NETFLT +#if defined(VBOX_WITH_NETFLT) || defined(VBOX_WITH_NETADP) /** @todo should use some real VBox app name */ #define VBOX_NETCFG_APP_NAME L"VirtualBox Installer" @@ -1051,7 +1612,9 @@ static UINT doNetCfgInit(MSIHANDLE hModule, INetCfg **ppnc, BOOL bWrite) return uErr; } +#endif /* defined(VBOX_WITH_NETFLT) || defined(VBOX_WITH_NETADP) */ +#ifdef VBOX_WITH_NETFLT static UINT vboxNetFltQueryInfArray(MSIHANDLE hModule, OUT LPWSTR pwszPtInf, DWORD cwcPtInf, OUT LPWSTR pwszMpInf, DWORD cwcMpInf) { @@ -1082,11 +1645,8 @@ static UINT vboxNetFltQueryInfArray(MSIHANDLE hModule, OUT LPWSTR pwszPtInf, DWO return uErr; } -#endif /*VBOX_WITH_NETFLT*/ - -/*static*/ UINT _uninstallNetFlt(MSIHANDLE hModule) +static UINT _uninstallNetFlt(MSIHANDLE hModule) { -#ifdef VBOX_WITH_NETFLT INetCfg *pNetCfg; UINT uErr; @@ -1123,21 +1683,26 @@ static UINT vboxNetFltQueryInfArray(MSIHANDLE hModule, OUT LPWSTR pwszPtInf, DWO } netCfgLoggerDisable(); } -#endif /* VBOX_WITH_NETFLT */ /* Never fail the uninstall even if we did not succeed. */ return ERROR_SUCCESS; } +#endif /* VBOX_WITH_NETFLT */ UINT __stdcall UninstallNetFlt(MSIHANDLE hModule) { - (void)_uninstallNetLwf(hModule); +#ifdef VBOX_WITH_NETFLT + _uninstallNetLwf(hModule); return _uninstallNetFlt(hModule); +#else + RT_NOREF(hModule); + return ERROR_SUCCESS; +#endif } +#ifdef VBOX_WITH_NETFLT static UINT _installNetFlt(MSIHANDLE hModule) { -#ifdef VBOX_WITH_NETFLT UINT uErr; INetCfg *pNetCfg; @@ -1184,22 +1749,26 @@ static UINT _installNetFlt(MSIHANDLE hModule) } netCfgLoggerDisable(); } -#endif /* VBOX_WITH_NETFLT */ /* Never fail the install even if we did not succeed. */ return ERROR_SUCCESS; } +#endif /* VBOX_WITH_NETFLT */ UINT __stdcall InstallNetFlt(MSIHANDLE hModule) { - (void)_uninstallNetLwf(hModule); +#ifdef VBOX_WITH_NETFLT + _uninstallNetLwf(hModule); return _installNetFlt(hModule); +#else + RT_NOREF(hModule); + return ERROR_SUCCESS; +#endif } - -/*static*/ UINT _uninstallNetLwf(MSIHANDLE hModule) -{ #ifdef VBOX_WITH_NETFLT +static UINT _uninstallNetLwf(MSIHANDLE hModule) +{ INetCfg *pNetCfg; UINT uErr; @@ -1236,21 +1805,26 @@ UINT __stdcall InstallNetFlt(MSIHANDLE hModule) } netCfgLoggerDisable(); } -#endif /* VBOX_WITH_NETFLT */ /* Never fail the uninstall even if we did not succeed. */ return ERROR_SUCCESS; } +#endif /* VBOX_WITH_NETFLT */ UINT __stdcall UninstallNetLwf(MSIHANDLE hModule) { - (void)_uninstallNetFlt(hModule); +#ifdef VBOX_WITH_NETFLT + _uninstallNetFlt(hModule); return _uninstallNetLwf(hModule); +#else + RT_NOREF(hModule); + return ERROR_SUCCESS; +#endif } +#ifdef VBOX_WITH_NETFLT static UINT _installNetLwf(MSIHANDLE hModule) { -#ifdef VBOX_WITH_NETFLT UINT uErr; INetCfg *pNetCfg; @@ -1313,20 +1887,25 @@ static UINT _installNetLwf(MSIHANDLE hModule) } netCfgLoggerDisable(); } -#endif /* VBOX_WITH_NETFLT */ /* Never fail the install even if we did not succeed. */ return ERROR_SUCCESS; } +#endif /* VBOX_WITH_NETFLT */ UINT __stdcall InstallNetLwf(MSIHANDLE hModule) { - (void)_uninstallNetFlt(hModule); +#ifdef VBOX_WITH_NETFLT + _uninstallNetFlt(hModule); return _installNetLwf(hModule); +#else + RT_NOREF(hModule); + return ERROR_SUCCESS; +#endif } -#if 0 +#if 0 /** @todo r=andy Remove this? */ static BOOL RenameHostOnlyConnectionsCallback(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pContext) { WCHAR DevName[256]; @@ -1382,11 +1961,11 @@ static BOOL RenameHostOnlyConnectionsCallback(HDEVINFO hDevInfo, PSP_DEVINFO_DAT return TRUE; } -#endif +#endif /* 0 */ +#ifdef VBOX_WITH_NETADP static UINT _createHostOnlyInterface(MSIHANDLE hModule, LPCWSTR pwszId, LPCWSTR pwszInfName) { -#ifdef VBOX_WITH_NETFLT netCfgLoggerEnable(hModule); BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE); @@ -1455,12 +2034,12 @@ static UINT _createHostOnlyInterface(MSIHANDLE hModule, LPCWSTR pwszId, LPCWSTR //in fail case call CreateHostOnlyInterface logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinUpdateHostOnlyNetworkInterface failed, hr = %#x", hr); logStringF(hModule, "CreateHostOnlyInterface: calling VBoxNetCfgWinCreateHostOnlyNetworkInterface"); -#ifdef VBOXNETCFG_DELAYEDRENAME +# ifdef VBOXNETCFG_DELAYEDRENAME BSTR devId; hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsFile, NULL, &guid, &devId, NULL); -#else /* !VBOXNETCFG_DELAYEDRENAME */ +# else /* !VBOXNETCFG_DELAYEDRENAME */ hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsFile, NULL, &guid, NULL, NULL); -#endif /* !VBOXNETCFG_DELAYEDRENAME */ +# endif /* !VBOXNETCFG_DELAYEDRENAME */ logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinCreateHostOnlyNetworkInterface returns %#x", hr); if (SUCCEEDED(hr)) { @@ -1471,12 +2050,12 @@ static UINT _createHostOnlyInterface(MSIHANDLE hModule, LPCWSTR pwszId, LPCWSTR logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinEnableStaticIpConfig returns %#x", hr); if (FAILED(hr)) logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinEnableStaticIpConfig failed, error = %#x", hr); -#ifdef VBOXNETCFG_DELAYEDRENAME +# ifdef VBOXNETCFG_DELAYEDRENAME hr = VBoxNetCfgWinRenameHostOnlyConnection(&guid, devId, NULL); if (FAILED(hr)) logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinRenameHostOnlyConnection failed, error = %#x", hr); SysFreeString(devId); -#endif /* VBOXNETCFG_DELAYEDRENAME */ +# endif /* VBOXNETCFG_DELAYEDRENAME */ } else logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinCreateHostOnlyNetworkInterface failed, error = %#x", hr); @@ -1493,21 +2072,26 @@ static UINT _createHostOnlyInterface(MSIHANDLE hModule, LPCWSTR pwszId, LPCWSTR netCfgLoggerDisable(); -#endif /* VBOX_WITH_NETFLT */ - logStringF(hModule, "CreateHostOnlyInterface: Returns success (ignoring all failures)"); /* Never fail the install even if we did not succeed. */ return ERROR_SUCCESS; } +#endif /* VBOX_WITH_NETADP */ UINT __stdcall CreateHostOnlyInterface(MSIHANDLE hModule) { +#ifdef VBOX_WITH_NETADP return _createHostOnlyInterface(hModule, NETADP_ID, L"VBoxNetAdp.inf"); +#else + RT_NOREF(hModule); + return ERROR_SUCCESS; +#endif } UINT __stdcall Ndis6CreateHostOnlyInterface(MSIHANDLE hModule) { -#if 0 /* Trick for allowing the debugger to be attached. */ +#ifdef VBOX_WITH_NETADP +# if 0 /* Trick for allowing the debugger to be attached. */ for (unsigned i = 0; i < 128 && !IsDebuggerPresent(); i++) { logStringF(hModule, "Waiting for debugger to attach: windbg -p %u", GetCurrentProcessId()); @@ -1515,13 +2099,17 @@ UINT __stdcall Ndis6CreateHostOnlyInterface(MSIHANDLE hModule) } Sleep(1002); __debugbreak(); -#endif +# endif return _createHostOnlyInterface(hModule, NETADP_ID, L"VBoxNetAdp6.inf"); +#else /* !VBOX_WITH_NETADP */ + RT_NOREF(hModule); + return ERROR_SUCCESS; +#endif } +#ifdef VBOX_WITH_NETADP static UINT _removeHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszId) { -#ifdef VBOX_WITH_NETFLT netCfgLoggerEnable(hModule); logStringF(hModule, "RemoveHostOnlyInterfaces: Removing all host-only interfaces"); @@ -1545,20 +2133,25 @@ static UINT _removeHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszId) SetupSetNonInteractiveMode(fSetupModeInteractive); netCfgLoggerDisable(); -#endif /* VBOX_WITH_NETFLT */ /* Never fail the uninstall even if we did not succeed. */ return ERROR_SUCCESS; } +#endif /* VBOX_WITH_NETADP */ UINT __stdcall RemoveHostOnlyInterfaces(MSIHANDLE hModule) { +#ifdef VBOX_WITH_NETADP return _removeHostOnlyInterfaces(hModule, NETADP_ID); +#else + RT_NOREF(hModule); + return ERROR_SUCCESS; +#endif } +#ifdef VBOX_WITH_NETADP static UINT _stopHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszId) { -#ifdef VBOX_WITH_NETFLT netCfgLoggerEnable(hModule); logStringF(hModule, "StopHostOnlyInterfaces: Stopping all host-only interfaces"); @@ -1576,20 +2169,25 @@ static UINT _stopHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszId) SetupSetNonInteractiveMode(fSetupModeInteractive); netCfgLoggerDisable(); -#endif /* VBOX_WITH_NETFLT */ /* Never fail the uninstall even if we did not succeed. */ return ERROR_SUCCESS; } +#endif /* VBOX_WITH_NETADP */ UINT __stdcall StopHostOnlyInterfaces(MSIHANDLE hModule) { +#ifdef VBOX_WITH_NETADP return _stopHostOnlyInterfaces(hModule, NETADP_ID); +#else + RT_NOREF(hModule); + return ERROR_SUCCESS; +#endif } +#ifdef VBOX_WITH_NETADP static UINT _updateHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszInfName, LPCWSTR pwszId) { -#ifdef VBOX_WITH_NETFLT netCfgLoggerEnable(hModule); logStringF(hModule, "UpdateHostOnlyInterfaces: Updating all host-only interfaces"); @@ -1656,25 +2254,35 @@ static UINT _updateHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszInfName, LP SetupSetNonInteractiveMode(fSetupModeInteractive); netCfgLoggerDisable(); -#endif /* VBOX_WITH_NETFLT */ /* Never fail the update even if we did not succeed. */ return ERROR_SUCCESS; } +#endif /* VBOX_WITH_NETADP */ UINT __stdcall UpdateHostOnlyInterfaces(MSIHANDLE hModule) { +#ifdef VBOX_WITH_NETADP return _updateHostOnlyInterfaces(hModule, L"VBoxNetAdp.inf", NETADP_ID); +#else + RT_NOREF(hModule); + return ERROR_SUCCESS; +#endif } UINT __stdcall Ndis6UpdateHostOnlyInterfaces(MSIHANDLE hModule) { +#ifdef VBOX_WITH_NETADP return _updateHostOnlyInterfaces(hModule, L"VBoxNetAdp6.inf", NETADP_ID); +#else + RT_NOREF(hModule); + return ERROR_SUCCESS; +#endif } +#ifdef VBOX_WITH_NETADP static UINT _uninstallNetAdp(MSIHANDLE hModule, LPCWSTR pwszId) { -#ifdef VBOX_WITH_NETFLT INetCfg *pNetCfg; UINT uErr; @@ -1711,15 +2319,20 @@ static UINT _uninstallNetAdp(MSIHANDLE hModule, LPCWSTR pwszId) } netCfgLoggerDisable(); } -#endif /* VBOX_WITH_NETFLT */ /* Never fail the uninstall even if we did not succeed. */ return ERROR_SUCCESS; } +#endif /* VBOX_WITH_NETADP */ UINT __stdcall UninstallNetAdp(MSIHANDLE hModule) { +#ifdef VBOX_WITH_NETADP return _uninstallNetAdp(hModule, NETADP_ID); +#else + RT_NOREF(hModule); + return ERROR_SUCCESS; +#endif } static bool isTAPDevice(const WCHAR *pwszGUID) diff --git a/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.def b/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.def index 03cb5d60..3ab891c1 100644 --- a/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.def +++ b/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.def @@ -29,6 +29,7 @@ LIBRARY "VBoxInstallHelper" EXPORTS IsSerialCheckNeeded CheckSerial + CheckTargetDir IsMSCRTInstalled IsPythonInstalled IsWindows10 @@ -38,8 +39,8 @@ EXPORTS UninstallBranding InstallNetFlt UninstallNetFlt - UninstallNetAdp - InstallNetLwf + UninstallNetAdp + InstallNetLwf UninstallNetLwf UninstallTAPInstances UninstallVBoxDrv |