diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
commit | f8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch) | |
tree | 26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Installer/win/Stub | |
parent | Initial commit. (diff) | |
download | virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip |
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Installer/win/Stub')
-rw-r--r-- | src/VBox/Installer/win/Stub/Makefile.kmk | 144 | ||||
-rw-r--r-- | src/VBox/Installer/win/Stub/VBoxStub.cpp | 1197 | ||||
-rw-r--r-- | src/VBox/Installer/win/Stub/VBoxStub.h | 30 | ||||
-rw-r--r-- | src/VBox/Installer/win/Stub/VBoxStub.manifest | 22 | ||||
-rw-r--r-- | src/VBox/Installer/win/Stub/VBoxStub.rc | 56 | ||||
-rw-r--r-- | src/VBox/Installer/win/Stub/VBoxStubCertUtil.cpp | 142 | ||||
-rw-r--r-- | src/VBox/Installer/win/Stub/VBoxStubCertUtil.h | 27 | ||||
-rw-r--r-- | src/VBox/Installer/win/Stub/crypt32.def | 25 | ||||
-rw-r--r-- | src/VBox/Installer/win/Stub/msi.def | 23 | ||||
-rw-r--r-- | src/VBox/Installer/win/Stub/resource.h | 32 | ||||
-rw-r--r-- | src/VBox/Installer/win/Stub/shell32.def | 22 | ||||
-rw-r--r-- | src/VBox/Installer/win/Stub/user32.def | 23 | ||||
-rw-r--r-- | src/VBox/Installer/win/Stub/ws2_32.def | 22 |
13 files changed, 1765 insertions, 0 deletions
diff --git a/src/VBox/Installer/win/Stub/Makefile.kmk b/src/VBox/Installer/win/Stub/Makefile.kmk new file mode 100644 index 00000000..5a221381 --- /dev/null +++ b/src/VBox/Installer/win/Stub/Makefile.kmk @@ -0,0 +1,144 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the stub installer. +# + +# +# Copyright (C) 2009-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# + +SUB_DEPTH = ../../../../.. +include $(KBUILD_PATH)/subheader.kmk + +# This has to be 32-bit, so don't include it in the 64-bit build. +if "$(KBUILD_TARGET_ARCH)" == "x86" + + TEMPLATE_VBOXSTUB = Drop the signing, we will sign it later. + TEMPLATE_VBOXSTUB_EXTENDS = VBOXR3STATIC + TEMPLATE_VBOXSTUB_POST_CMDS = $(NO_SUCH_VARIABLE) + + PROGRAMS += VBoxStub + VBoxStub_TEMPLATE= VBOXSTUB + VBoxStub_DEFS = _WIN32_WINNT=0x0501 IN_RT_R3 + + VBoxStub_SOURCES = \ + VBoxStub.cpp \ + VBoxStub.rc + + VBoxStub_SDKS += \ + VBOX_NTDLL + VBoxStub_LIBS = \ + $(VBOX_LIB_RUNTIME_STATIC) \ + $(PATH_TOOL_$(VBOX_VCC_TOOL)_LIB)/delayimp.lib + VBoxStub_LDFLAGS = \ + -DelayLoad:comctl32.dll + VBoxStub_POST_CMDS = $(VBOX_CHECK_IMPORTS) --image $(out) ntdll.dll kernel32.dll + VBoxStub_LNK_DEPS = $(VBOX_CHECK_IMPORTS) + + VBoxStub.cpp_DEFS += VBOX_SVN_REV=$(VBOX_SVN_REV) + VBoxStub.cpp_DEPS = $(VBOX_SVN_REV_KMK) + + # If signing mode is enabled, then add the possibility to install the code + # signing certificates (public key only) automatically in /silent mode. + ifdef VBOX_SIGNING_MODE + + VBoxStub_SOURCES += VBoxStubCertUtil.cpp + VBoxStub_CLEAN += $(VBoxStub_0_OUTDIR)/VBoxStubPublicCert.h + VBoxStub.cpp_DEPS += $(VBoxStub_0_OUTDIR)/VBoxStubPublicCert.h + VBoxStub.cpp_INCS += $(VBoxStub_0_OUTDIR) + VBoxStub.cpp_DEFS += VBOX_WITH_CODE_SIGNING + + $$(VBoxStub_0_OUTDIR)/VBoxStubPublicCert.h: | $$(dir $$@) $(VBOX_RTSIGNTOOL) $(PATH_STAGE_SYS)/VBoxDrv.sys + $(RM) -f -- "$@" "$@.cer0" "$@.cer1" "$@.cer2" "$@.array" + + $(VBOX_RTSIGNTOOL) extract-exe-signer-cert --signature-index 0 --exe "$(PATH_STAGE_SYS)/VBoxDrv.sys" --output "$@.cer0" --der + $(VBOX_BIN2C) -ascii --append VBoxStubTrustedCert0 "$@.cer0" $@ + $(APPEND) "$@.array" " { g_abVBoxStubTrustedCert0, sizeof(g_abVBoxStubTrustedCert0) }, " + if defined(VBOX_CERTIFICATE_SHA2_SUBJECT_NAME) && (!defined(VBOX_WITH_CORP_CODE_SIGNING) || "$(VBOX_WITH_CORP_CODE_SIGNING)" != "all") + $(VBOX_RTSIGNTOOL) extract-exe-signer-cert --signature-index 1 --exe "$(PATH_STAGE_SYS)/VBoxDrv.sys" --output "$@.cer1" --der + $(VBOX_BIN2C) -ascii --append VBoxStubTrustedCert1 "$@.cer1" $@ + $(APPEND) "$@.array" " { g_abVBoxStubTrustedCert1, sizeof(g_abVBoxStubTrustedCert1) }, " + endif + if defined(VBOX_WITH_CORP_CODE_SIGNING) && "$(VBOX_WITH_CORP_CODE_SIGNING)" != "all" && "$(VBOX_SIGNING_MODE)" == "release" + $(VBOX_RTSIGNTOOL) extract-exe-signer-cert --signature-index 1 --exe "$(VBOX_RTSIGNTOOL)" --output "$@.cer2" --der + $(VBOX_BIN2C) -ascii --append VBoxStubTrustedCert2 "$@.cer2" $@ + $(APPEND) "$@.array" " { g_abVBoxStubTrustedCert2, sizeof(g_abVBoxStubTrustedCert2) }, " + endif + $(APPEND) -n "$@" \ + "" \ + "struct { uint8_t const *pab; uint32_t cb; }" "g_aVBoxStubTrustedCerts[] = " \ + "{" + $(SED) --append "$@" -e "" "$@.array" + $(APPEND) -n "$@" \ + "};" + $(RM) -f -- "$@.cer0" "$@.cer1" "$@.cer2" "$@.array" + + VBoxStubPublicCert.h:: $$(VBoxStub_0_OUTDIR)/VBoxStubPublicCert.h + + endif + + # The icon location is configurable. + VBoxStub.rc_INCS += $(VBoxStub_0_OUTDIR) + VBoxStub.rc_DEPS += \ + $(VBoxStub_0_OUTDIR)/VBoxStub-icon.rc \ + $(VBoxStub_0_OUTDIR)/VBoxStub-manifest.rc + VBoxStub.rc_CLEAN = \ + $(VBoxStub_0_OUTDIR)/VBoxStub-icon.rc \ + $(VBoxStub_0_OUTDIR)/VBoxStub-manifest.rc + + # Icon include file. + $$(VBoxStub_0_OUTDIR)/VBoxStub-icon.rc: $(VBOX_WINDOWS_ICON_FILE) $(MAKEFILE_CURRENT) | $$(dir $$@) + $(APPEND) -t $@ 'IDI_VIRTUALBOX ICON DISCARDABLE "$(subst /,\\,$(VBOX_WINDOWS_ICON_FILE))"' + + # Manifest. + VBOX_STUB_MANIFEST_FILE := $(PATH_SUB_CURRENT)/VBoxStub.manifest + $$(VBoxStub_0_OUTDIR)/VBoxStub-manifest.rc: $(VBOX_STUB_MANIFEST_FILE) $(MAKEFILE_CURRENT) | $$(dir $$@) + $(APPEND) -t $@ 'APP_MANIFEST RT_MANIFEST "$(subst /,\\,$(VBOX_STUB_MANIFEST_FILE))"' + + # Dynamic import no. 1: MSI.DLL + VBoxStub_SOURCES += $(VBoxStub_0_OUTDIR)/MsiLazyLoad.asm + $$(VBoxStub_0_OUTDIR)/MsiLazyLoad.asm: $(PATH_SUB_CURRENT)/msi.def $(VBOX_DEF_2_LAZY_LOAD) | $$(dir $$@) + $(call MSG_TOOL,VBoxDef2LazyLoad,VBoxStub,$(filter %.def, $^),$@) + $(QUIET)$(RM) -f -- "$@" + $(VBOX_DEF_2_LAZY_LOAD) --system --library MSI.DLL --output "$@" $(filter %.def, $^) + + # Dynamic import no. 2: CRYPTO32.DLL + VBoxStub_SOURCES += $(VBoxStub_0_OUTDIR)/Crypt32LazyLoad.asm + $$(VBoxStub_0_OUTDIR)/Crypt32LazyLoad.asm: $(PATH_SUB_CURRENT)/crypt32.def $(VBOX_DEF_2_LAZY_LOAD) | $$(dir $$@) + $(call MSG_TOOL,VBoxDef2LazyLoad,VBoxStub,$(filter %.def, $^),$@) + $(QUIET)$(RM) -f -- "$@" + $(VBOX_DEF_2_LAZY_LOAD) --system --library CRYPT32.DLL --output "$@" $(filter %.def, $^) + + # Dynamic import no. 3: WS2_32.DLL + VBoxStub_SOURCES += $(VBoxStub_0_OUTDIR)/Ws232LazyLoad.asm + $$(VBoxStub_0_OUTDIR)/Ws232LazyLoad.asm: $(PATH_SUB_CURRENT)/ws2_32.def $(VBOX_DEF_2_LAZY_LOAD) | $$(dir $$@) + $(call MSG_TOOL,VBoxDef2LazyLoad,VBoxStub,$(filter %.def, $^),$@) + $(QUIET)$(RM) -f -- "$@" + $(VBOX_DEF_2_LAZY_LOAD) --system --library WS2_32.DLL --output "$@" $(filter %.def, $^) + + # Dynamic import no. 4: USER32.DLL + VBoxStub_SOURCES += $(VBoxStub_0_OUTDIR)/User32LazyLoad.asm + $$(VBoxStub_0_OUTDIR)/User32LazyLoad.asm: $(PATH_SUB_CURRENT)/user32.def $(VBOX_DEF_2_LAZY_LOAD) | $$(dir $$@) + $(call MSG_TOOL,VBoxDef2LazyLoad,VBoxStub,$(filter %.def, $^),$@) + $(QUIET)$(RM) -f -- "$@" + $(VBOX_DEF_2_LAZY_LOAD) --system --library USER32.DLL --output "$@" $(filter %.def, $^) + + # Dynamic import no. 5: SHELL32.DLL + VBoxStub_SOURCES += $(VBoxStub_0_OUTDIR)/Shell32LazyLoad.asm + $$(VBoxStub_0_OUTDIR)/Shell32LazyLoad.asm: $(PATH_SUB_CURRENT)/shell32.def $(VBOX_DEF_2_LAZY_LOAD) | $$(dir $$@) + $(call MSG_TOOL,VBoxDef2LazyLoad,VBoxStub,$(filter %.def, $^),$@) + $(QUIET)$(RM) -f -- "$@" + $(VBOX_DEF_2_LAZY_LOAD) --system --library SHELL32.DLL --output "$@" $(filter %.def, $^) + + +endif # x86 only + +include $(FILE_KBUILD_SUB_FOOTER) diff --git a/src/VBox/Installer/win/Stub/VBoxStub.cpp b/src/VBox/Installer/win/Stub/VBoxStub.cpp new file mode 100644 index 00000000..32647468 --- /dev/null +++ b/src/VBox/Installer/win/Stub/VBoxStub.cpp @@ -0,0 +1,1197 @@ +/* $Id: VBoxStub.cpp $ */ +/** @file + * VBoxStub - VirtualBox's Windows installer stub. + */ + +/* + * Copyright (C) 2010-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0501 +# undef _WIN32_WINNT +# define _WIN32_WINNT 0x0501 /* AttachConsole() / FreeConsole(). */ +#endif + +#include <iprt/win/windows.h> +#include <commctrl.h> +#include <fcntl.h> +#include <io.h> +#include <lmerr.h> +#include <msiquery.h> +#include <iprt/win/objbase.h> + +#include <iprt/win/shlobj.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strsafe.h> + +#include <VBox/version.h> + +#include <iprt/assert.h> +#include <iprt/dir.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/getopt.h> +#include <iprt/initterm.h> +#include <iprt/list.h> +#include <iprt/mem.h> +#include <iprt/message.h> +#include <iprt/param.h> +#include <iprt/path.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/thread.h> +#include <iprt/utf16.h> + +#include "VBoxStub.h" +#include "../StubBld/VBoxStubBld.h" +#include "resource.h" + +#ifdef VBOX_WITH_CODE_SIGNING +# include "VBoxStubCertUtil.h" +# include "VBoxStubPublicCert.h" +#endif + +#ifndef TARGET_NT4 +/* Use an own console window if run in verbose mode. */ +# define VBOX_STUB_WITH_OWN_CONSOLE +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define MY_UNICODE_SUB(str) L ##str +#define MY_UNICODE(str) MY_UNICODE_SUB(str) + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Cleanup record. + */ +typedef struct STUBCLEANUPREC +{ + /** List entry. */ + RTLISTNODE ListEntry; + /** True if file, false if directory. */ + bool fFile; + /** The path to the file or directory to clean up. */ + char szPath[1]; +} STUBCLEANUPREC; +/** Pointer to a cleanup record. */ +typedef STUBCLEANUPREC *PSTUBCLEANUPREC; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Whether it's a silent or interactive GUI driven install. */ +static bool g_fSilent = false; +/** List of temporary files. */ +static RTLISTANCHOR g_TmpFiles; +/** Verbosity flag. */ +static int g_iVerbosity = 0; + + + +/** + * Shows an error message box with a printf() style formatted string. + * + * @returns RTEXITCODE_FAILURE + * @param pszFmt Printf-style format string to show in the message box body. + * + */ +static RTEXITCODE ShowError(const char *pszFmt, ...) +{ + char *pszMsg; + va_list va; + + va_start(va, pszFmt); + if (RTStrAPrintfV(&pszMsg, pszFmt, va)) + { + if (g_fSilent) + RTMsgError("%s", pszMsg); + else + { + PRTUTF16 pwszMsg; + int rc = RTStrToUtf16(pszMsg, &pwszMsg); + if (RT_SUCCESS(rc)) + { + MessageBoxW(GetDesktopWindow(), pwszMsg, MY_UNICODE(VBOX_STUB_TITLE), MB_ICONERROR); + RTUtf16Free(pwszMsg); + } + else + MessageBoxA(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONERROR); + } + RTStrFree(pszMsg); + } + else /* Should never happen! */ + AssertMsgFailed(("Failed to format error text of format string: %s!\n", pszFmt)); + va_end(va); + return RTEXITCODE_FAILURE; +} + + +/** + * Shows a message box with a printf() style formatted string. + * + * @param uType Type of the message box (see MSDN). + * @param pszFmt Printf-style format string to show in the message box body. + * + */ +static void ShowInfo(const char *pszFmt, ...) +{ + char *pszMsg; + va_list va; + va_start(va, pszFmt); + int rc = RTStrAPrintfV(&pszMsg, pszFmt, va); + va_end(va); + if (rc >= 0) + { + if (g_fSilent) + RTPrintf("%s\n", pszMsg); + else + { + PRTUTF16 pwszMsg; + int rc = RTStrToUtf16(pszMsg, &pwszMsg); + if (RT_SUCCESS(rc)) + { + MessageBoxW(GetDesktopWindow(), pwszMsg, MY_UNICODE(VBOX_STUB_TITLE), MB_ICONINFORMATION); + RTUtf16Free(pwszMsg); + } + else + MessageBoxA(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONINFORMATION); + } + } + else /* Should never happen! */ + AssertMsgFailed(("Failed to format error text of format string: %s!\n", pszFmt)); + RTStrFree(pszMsg); +} + + +/** + * Finds the specified in the resource section of the executable. + * + * @returns IPRT status code. + * + * @param pszDataName Name of resource to read. + * @param ppvResource Where to return the pointer to the data. + * @param pdwSize Where to return the size of the data (if found). + * Optional. + */ +static int FindData(const char *pszDataName, PVOID *ppvResource, DWORD *pdwSize) +{ + AssertReturn(pszDataName, VERR_INVALID_PARAMETER); + HINSTANCE hInst = NULL; /* indicates the executable image */ + + /* Find our resource. */ + PRTUTF16 pwszDataName; + int rc = RTStrToUtf16(pszDataName, &pwszDataName); + AssertRCReturn(rc, rc); + HRSRC hRsrc = FindResourceExW(hInst, + (LPWSTR)RT_RCDATA, + pwszDataName, + MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); + RTUtf16Free(pwszDataName); + AssertReturn(hRsrc, VERR_IO_GEN_FAILURE); + + /* Get resource size. */ + DWORD cb = SizeofResource(hInst, hRsrc); + AssertReturn(cb > 0, VERR_NO_DATA); + if (pdwSize) + *pdwSize = cb; + + /* Get pointer to resource. */ + HGLOBAL hData = LoadResource(hInst, hRsrc); + AssertReturn(hData, VERR_IO_GEN_FAILURE); + + /* Lock resource. */ + *ppvResource = LockResource(hData); + AssertReturn(*ppvResource, VERR_IO_GEN_FAILURE); + return VINF_SUCCESS; +} + + +/** + * Finds the header for the given package. + * + * @returns Pointer to the package header on success. On failure NULL is + * returned after ShowError has been invoked. + * @param iPackage The package number. + */ +static PVBOXSTUBPKG FindPackageHeader(unsigned iPackage) +{ + char szHeaderName[32]; + RTStrPrintf(szHeaderName, sizeof(szHeaderName), "HDR_%02d", iPackage); + + PVBOXSTUBPKG pPackage; + int rc = FindData(szHeaderName, (PVOID *)&pPackage, NULL); + if (RT_FAILURE(rc)) + { + ShowError("Internal error: Could not find package header #%u: %Rrc", iPackage, rc); + return NULL; + } + + /** @todo validate it. */ + return pPackage; +} + + + +/** + * Constructs a full temporary file path from the given parameters. + * + * @returns iprt status code. + * + * @param pszTempPath The pure path to use for construction. + * @param pszTargetFileName The pure file name to use for construction. + * @param ppszTempFile Pointer to the constructed string. Must be freed + * using RTStrFree(). + */ +static int GetTempFileAlloc(const char *pszTempPath, + const char *pszTargetFileName, + char **ppszTempFile) +{ + if (RTStrAPrintf(ppszTempFile, "%s\\%s", pszTempPath, pszTargetFileName) >= 0) + return VINF_SUCCESS; + return VERR_NO_STR_MEMORY; +} + + +/** + * Extracts a built-in resource to disk. + * + * @returns iprt status code. + * + * @param pszResourceName The resource name to extract. + * @param pszTempFile The full file path + name to extract the resource to. + * + */ +static int ExtractFile(const char *pszResourceName, + const char *pszTempFile) +{ +#if 0 /* Another example of how unnecessarily complicated things get with + do-break-while-false and you end up with buggy code using uninitialized + variables. */ + int rc; + RTFILE fh; + BOOL bCreatedFile = FALSE; + + do + { + AssertMsgBreak(pszResourceName, ("Resource pointer invalid!\n")); /* rc is not initialized here, we'll return garbage. */ + AssertMsgBreak(pszTempFile, ("Temp file pointer invalid!")); /* Ditto. */ + + /* Read the data of the built-in resource. */ + PVOID pvData = NULL; + DWORD dwDataSize = 0; + rc = FindData(pszResourceName, &pvData, &dwDataSize); + AssertMsgRCBreak(rc, ("Could not read resource data!\n")); + + /* Create new (and replace an old) file. */ + rc = RTFileOpen(&fh, pszTempFile, + RTFILE_O_CREATE_REPLACE + | RTFILE_O_WRITE + | RTFILE_O_DENY_NOT_DELETE + | RTFILE_O_DENY_WRITE); + AssertMsgRCBreak(rc, ("Could not open file for writing!\n")); + bCreatedFile = TRUE; + + /* Write contents to new file. */ + size_t cbWritten = 0; + rc = RTFileWrite(fh, pvData, dwDataSize, &cbWritten); + AssertMsgRCBreak(rc, ("Could not open file for writing!\n")); + AssertMsgBreak(dwDataSize == cbWritten, ("File was not extracted completely! Disk full?\n")); + + } while (0); + + if (RTFileIsValid(fh)) /* fh is unused uninitalized (MSC agrees) */ + RTFileClose(fh); + + if (RT_FAILURE(rc)) + { + if (bCreatedFile) + RTFileDelete(pszTempFile); + } + +#else /* This is exactly the same as above, except no bug and better assertion + message. Note only the return-success statment is indented, indicating + that the whole do-break-while-false approach was totally unnecessary. */ + + AssertPtrReturn(pszResourceName, VERR_INVALID_POINTER); + AssertPtrReturn(pszTempFile, VERR_INVALID_POINTER); + + /* Read the data of the built-in resource. */ + PVOID pvData = NULL; + DWORD dwDataSize = 0; + int rc = FindData(pszResourceName, &pvData, &dwDataSize); + AssertMsgRCReturn(rc, ("Could not read resource data: %Rrc\n", rc), rc); + + /* Create new (and replace an old) file. */ + RTFILE hFile; + rc = RTFileOpen(&hFile, pszTempFile, + RTFILE_O_CREATE_REPLACE + | RTFILE_O_WRITE + | RTFILE_O_DENY_NOT_DELETE + | RTFILE_O_DENY_WRITE); + AssertMsgRCReturn(rc, ("Could not open '%s' for writing: %Rrc\n", pszTempFile, rc), rc); + + /* Write contents to new file. */ + size_t cbWritten = 0; + rc = RTFileWrite(hFile, pvData, dwDataSize, &cbWritten); + AssertMsgStmt(cbWritten == dwDataSize || RT_FAILURE_NP(rc), ("%#zx vs %#x\n", cbWritten, dwDataSize), rc = VERR_WRITE_ERROR); + + int rc2 = RTFileClose(hFile); + AssertRC(rc2); + + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + + RTFileDelete(pszTempFile); + +#endif + return rc; +} + + +/** + * Extracts a built-in resource to disk. + * + * @returns iprt status code. + * + * @param pPackage Pointer to a VBOXSTUBPKG struct that contains the resource. + * @param pszTempFile The full file path + name to extract the resource to. + */ +static int Extract(const PVBOXSTUBPKG pPackage, + const char *pszTempFile) +{ + return ExtractFile(pPackage->szResourceName, pszTempFile); +} + + +/** + * Detects whether we're running on a 32- or 64-bit platform and returns the result. + * + * @returns TRUE if we're running on a 64-bit OS, FALSE if not. + */ +static BOOL IsWow64(void) +{ + BOOL fIsWow64 = TRUE; + fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process"); + if (NULL != fnIsWow64Process) + { + if (!fnIsWow64Process(GetCurrentProcess(), &fIsWow64)) + { + /* Error in retrieving process type - assume that we're running on 32bit. */ + return FALSE; + } + } + return fIsWow64; +} + + +/** + * Decides whether we need a specified package to handle or not. + * + * @returns @c true if we need to handle the specified package, @c false if not. + * + * @param pPackage Pointer to a VBOXSTUBPKG struct that contains the resource. + */ +static bool PackageIsNeeded(PVBOXSTUBPKG pPackage) +{ + if (pPackage->byArch == VBOXSTUBPKGARCH_ALL) + return true; + VBOXSTUBPKGARCH enmArch = IsWow64() ? VBOXSTUBPKGARCH_AMD64 : VBOXSTUBPKGARCH_X86; + return pPackage->byArch == enmArch; +} + + +/** + * Adds a cleanup record. + * + * @returns Fully complained boolean success indicator. + * @param pszPath The path to the file or directory to clean up. + * @param fFile @c true if file, @c false if directory. + */ +static bool AddCleanupRec(const char *pszPath, bool fFile) +{ + size_t cchPath = strlen(pszPath); Assert(cchPath > 0); + PSTUBCLEANUPREC pRec = (PSTUBCLEANUPREC)RTMemAlloc(RT_UOFFSETOF_DYN(STUBCLEANUPREC, szPath[cchPath + 1])); + if (!pRec) + { + ShowError("Out of memory!"); + return false; + } + pRec->fFile = fFile; + memcpy(pRec->szPath, pszPath, cchPath + 1); + + RTListPrepend(&g_TmpFiles, &pRec->ListEntry); + return true; +} + + +/** + * Cleans up all the extracted files and optionally removes the package + * directory. + * + * @param pszPkgDir The package directory, NULL if it shouldn't be + * removed. + */ +static void CleanUp(const char *pszPkgDir) +{ + for (int i = 0; i < 5; i++) + { + int rc; + bool fFinalTry = i == 4; + + PSTUBCLEANUPREC pCur, pNext; + RTListForEachSafe(&g_TmpFiles, pCur, pNext, STUBCLEANUPREC, ListEntry) + { + if (pCur->fFile) + rc = RTFileDelete(pCur->szPath); + else + { + rc = RTDirRemoveRecursive(pCur->szPath, RTDIRRMREC_F_CONTENT_AND_DIR); + if (rc == VERR_DIR_NOT_EMPTY && fFinalTry) + rc = VINF_SUCCESS; + } + if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND) + rc = VINF_SUCCESS; + if (RT_SUCCESS(rc)) + { + RTListNodeRemove(&pCur->ListEntry); + RTMemFree(pCur); + } + else if (fFinalTry) + { + if (pCur->fFile) + ShowError("Failed to delete temporary file '%s': %Rrc", pCur->szPath, rc); + else + ShowError("Failed to delete temporary directory '%s': %Rrc", pCur->szPath, rc); + } + } + + if (RTListIsEmpty(&g_TmpFiles) || fFinalTry) + { + if (!pszPkgDir) + return; + rc = RTDirRemove(pszPkgDir); + if (RT_SUCCESS(rc) || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND || fFinalTry) + return; + } + + /* Delay a little and try again. */ + RTThreadSleep(i == 0 ? 100 : 3000); + } +} + + +/** + * Processes an MSI package. + * + * @returns Fully complained exit code. + * @param pszMsi The path to the MSI to process. + * @param pszMsiArgs Any additional installer (MSI) argument + * @param fLogging Whether to enable installer logging. + */ +static RTEXITCODE ProcessMsiPackage(const char *pszMsi, const char *pszMsiArgs, bool fLogging) +{ + int rc; + + /* + * Set UI level. + */ + INSTALLUILEVEL enmDesiredUiLevel = g_fSilent ? INSTALLUILEVEL_NONE : INSTALLUILEVEL_FULL; + INSTALLUILEVEL enmRet = MsiSetInternalUI(enmDesiredUiLevel, NULL); + if (enmRet == INSTALLUILEVEL_NOCHANGE /* means error */) + return ShowError("Internal error: MsiSetInternalUI failed."); + + /* + * Enable logging? + */ + if (fLogging) + { + char szLogFile[RTPATH_MAX]; + rc = RTStrCopy(szLogFile, sizeof(szLogFile), pszMsi); + if (RT_SUCCESS(rc)) + { + RTPathStripFilename(szLogFile); + rc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxInstallLog.txt"); + } + if (RT_FAILURE(rc)) + return ShowError("Internal error: Filename path too long."); + + PRTUTF16 pwszLogFile; + rc = RTStrToUtf16(szLogFile, &pwszLogFile); + if (RT_FAILURE(rc)) + return ShowError("RTStrToUtf16 failed on '%s': %Rrc", szLogFile, rc); + + UINT uLogLevel = MsiEnableLogW(INSTALLLOGMODE_VERBOSE, + pwszLogFile, + INSTALLLOGATTRIBUTES_FLUSHEACHLINE); + RTUtf16Free(pwszLogFile); + if (uLogLevel != ERROR_SUCCESS) + return ShowError("MsiEnableLogW failed"); + } + + /* + * Initialize the common controls (extended version). This is necessary to + * run the actual .MSI installers with the new fancy visual control + * styles (XP+). Also, an integrated manifest is required. + */ + INITCOMMONCONTROLSEX ccEx; + ccEx.dwSize = sizeof(INITCOMMONCONTROLSEX); + ccEx.dwICC = ICC_LINK_CLASS | ICC_LISTVIEW_CLASSES | ICC_PAGESCROLLER_CLASS | + ICC_PROGRESS_CLASS | ICC_STANDARD_CLASSES | ICC_TAB_CLASSES | ICC_TREEVIEW_CLASSES | + ICC_UPDOWN_CLASS | ICC_USEREX_CLASSES | ICC_WIN95_CLASSES; + InitCommonControlsEx(&ccEx); /* Ignore failure. */ + + /* + * Convert both strings to UTF-16 and start the installation. + */ + PRTUTF16 pwszMsi; + rc = RTStrToUtf16(pszMsi, &pwszMsi); + if (RT_FAILURE(rc)) + return ShowError("RTStrToUtf16 failed on '%s': %Rrc", pszMsi, rc); + PRTUTF16 pwszMsiArgs; + rc = RTStrToUtf16(pszMsiArgs, &pwszMsiArgs); + if (RT_FAILURE(rc)) + { + RTUtf16Free(pwszMsi); + return ShowError("RTStrToUtf16 failed on '%s': %Rrc", pszMsi, rc); + } + + UINT uStatus = MsiInstallProductW(pwszMsi, pwszMsiArgs); + RTUtf16Free(pwszMsi); + RTUtf16Free(pwszMsiArgs); + + if (uStatus == ERROR_SUCCESS) + return RTEXITCODE_SUCCESS; + if (uStatus == ERROR_SUCCESS_REBOOT_REQUIRED) + return RTEXITCODE_SUCCESS; /* we currently don't indicate this */ + + /* + * Installation failed. Figure out what to say. + */ + switch (uStatus) + { + case ERROR_INSTALL_USEREXIT: + /* Don't say anything? */ + break; + + case ERROR_INSTALL_PACKAGE_VERSION: + ShowError("This installation package cannot be installed by the Windows Installer service.\n" + "You must install a Windows service pack that contains a newer version of the Windows Installer service."); + break; + + case ERROR_INSTALL_PLATFORM_UNSUPPORTED: + ShowError("This installation package is not supported on this platform."); + break; + + default: + { + /* + * Try get windows to format the message. + */ + DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_IGNORE_INSERTS + | FORMAT_MESSAGE_FROM_SYSTEM; + HMODULE hModule = NULL; + if (uStatus >= NERR_BASE && uStatus <= MAX_NERR) + { + hModule = LoadLibraryExW(L"netmsg.dll", + NULL, + LOAD_LIBRARY_AS_DATAFILE); + if (hModule != NULL) + dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE; + } + + PWSTR pwszMsg; + if (FormatMessageW(dwFormatFlags, + hModule, /* If NULL, load system stuff. */ + uStatus, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (PWSTR)&pwszMsg, + 0, + NULL) > 0) + { + ShowError("Installation failed! Error: %ls", pwszMsg); + LocalFree(pwszMsg); + } + else /* If text lookup failed, show at least the error number. */ + ShowError("Installation failed! Error: %u", uStatus); + + if (hModule) + FreeLibrary(hModule); + break; + } + } + + return RTEXITCODE_FAILURE; +} + + +/** + * Processes a package. + * + * @returns Fully complained exit code. + * @param iPackage The package number. + * @param pszPkgDir The package directory (aka extraction dir). + * @param pszMsiArgs Any additional installer (MSI) argument + * @param fLogging Whether to enable installer logging. + */ +static RTEXITCODE ProcessPackage(unsigned iPackage, const char *pszPkgDir, const char *pszMsiArgs, bool fLogging) +{ + /* + * Get the package header and check if it's needed. + */ + PVBOXSTUBPKG pPackage = FindPackageHeader(iPackage); + if (pPackage == NULL) + return RTEXITCODE_FAILURE; + + if (!PackageIsNeeded(pPackage)) + return RTEXITCODE_SUCCESS; + + /* + * Deal with the file based on it's extension. + */ + char szPkgFile[RTPATH_MAX]; + int rc = RTPathJoin(szPkgFile, sizeof(szPkgFile), pszPkgDir, pPackage->szFileName); + if (RT_FAILURE(rc)) + return ShowError("Internal error: RTPathJoin failed: %Rrc", rc); + RTPathChangeToDosSlashes(szPkgFile, true /* Force conversion. */); /* paranoia */ + + RTEXITCODE rcExit; + const char *pszSuff = RTPathSuffix(szPkgFile); + if (RTStrICmp(pszSuff, ".msi") == 0) + rcExit = ProcessMsiPackage(szPkgFile, pszMsiArgs, fLogging); + else if (RTStrICmp(pszSuff, ".cab") == 0) + rcExit = RTEXITCODE_SUCCESS; /* Ignore .cab files, they're generally referenced by other files. */ + else + rcExit = ShowError("Internal error: Do not know how to handle file '%s'.", pPackage->szFileName); + + return rcExit; +} + + +#ifdef VBOX_WITH_CODE_SIGNING +/** + * Install the public certificate into TrustedPublishers so the installer won't + * prompt the user during silent installs. + * + * @returns Fully complained exit code. + */ +static RTEXITCODE InstallCertificates(void) +{ + for (uint32_t i = 0; i < RT_ELEMENTS(g_aVBoxStubTrustedCerts); i++) + { + if (!addCertToStore(CERT_SYSTEM_STORE_LOCAL_MACHINE, + "TrustedPublisher", + g_aVBoxStubTrustedCerts[i].pab, + g_aVBoxStubTrustedCerts[i].cb)) + return ShowError("Failed to construct install certificate."); + } + return RTEXITCODE_SUCCESS; +} +#endif /* VBOX_WITH_CODE_SIGNING */ + + +/** + * Copies the "<exepath>.custom" directory to the extraction path if it exists. + * + * This is used by the MSI packages from the resource section. + * + * @returns Fully complained exit code. + * @param pszDstDir The destination directory. + */ +static RTEXITCODE CopyCustomDir(const char *pszDstDir) +{ + char szSrcDir[RTPATH_MAX]; + int rc = RTPathExecDir(szSrcDir, sizeof(szSrcDir)); + if (RT_SUCCESS(rc)) + rc = RTPathAppend(szSrcDir, sizeof(szSrcDir), ".custom"); + if (RT_FAILURE(rc)) + return ShowError("Failed to construct '.custom' dir path: %Rrc", rc); + + if (RTDirExists(szSrcDir)) + { + /* + * Use SHFileOperation w/ FO_COPY to do the job. This API requires an + * extra zero at the end of both source and destination paths. + */ + size_t cwc; + RTUTF16 wszSrcDir[RTPATH_MAX + 1]; + PRTUTF16 pwszSrcDir = wszSrcDir; + rc = RTStrToUtf16Ex(szSrcDir, RTSTR_MAX, &pwszSrcDir, RTPATH_MAX, &cwc); + if (RT_FAILURE(rc)) + return ShowError("RTStrToUtf16Ex failed on '%s': %Rrc", szSrcDir, rc); + wszSrcDir[cwc] = '\0'; + + RTUTF16 wszDstDir[RTPATH_MAX + 1]; + PRTUTF16 pwszDstDir = wszSrcDir; + rc = RTStrToUtf16Ex(pszDstDir, RTSTR_MAX, &pwszDstDir, RTPATH_MAX, &cwc); + if (RT_FAILURE(rc)) + return ShowError("RTStrToUtf16Ex failed on '%s': %Rrc", pszDstDir, rc); + wszDstDir[cwc] = '\0'; + + SHFILEOPSTRUCTW FileOp; + RT_ZERO(FileOp); /* paranoia */ + FileOp.hwnd = NULL; + FileOp.wFunc = FO_COPY; + FileOp.pFrom = wszSrcDir; + FileOp.pTo = wszDstDir; + FileOp.fFlags = FOF_SILENT + | FOF_NOCONFIRMATION + | FOF_NOCONFIRMMKDIR + | FOF_NOERRORUI; + FileOp.fAnyOperationsAborted = FALSE; + FileOp.hNameMappings = NULL; + FileOp.lpszProgressTitle = NULL; + + rc = SHFileOperationW(&FileOp); + if (rc != 0) /* Not a Win32 status code! */ + return ShowError("Copying the '.custom' dir failed: %#x", rc); + + /* + * Add a cleanup record for recursively deleting the destination + * .custom directory. We should actually add this prior to calling + * SHFileOperationW since it may partially succeed... + */ + char *pszDstSubDir = RTPathJoinA(pszDstDir, ".custom"); + if (!pszDstSubDir) + return ShowError("Out of memory!"); + bool fRc = AddCleanupRec(pszDstSubDir, false /*fFile*/); + RTStrFree(pszDstSubDir); + if (!fRc) + return RTEXITCODE_FAILURE; + } + + return RTEXITCODE_SUCCESS; +} + + +static RTEXITCODE ExtractFiles(unsigned cPackages, const char *pszDstDir, bool fExtractOnly, bool *pfCreatedExtractDir) +{ + int rc; + + /* + * Make sure the directory exists. + */ + *pfCreatedExtractDir = false; + if (!RTDirExists(pszDstDir)) + { + rc = RTDirCreate(pszDstDir, 0700, 0); + if (RT_FAILURE(rc)) + return ShowError("Failed to create extraction path '%s': %Rrc", pszDstDir, rc); + *pfCreatedExtractDir = true; + } + + /* + * Extract files. + */ + for (unsigned k = 0; k < cPackages; k++) + { + PVBOXSTUBPKG pPackage = FindPackageHeader(k); + if (!pPackage) + return RTEXITCODE_FAILURE; /* Done complaining already. */ + + if (fExtractOnly || PackageIsNeeded(pPackage)) + { + char szDstFile[RTPATH_MAX]; + rc = RTPathJoin(szDstFile, sizeof(szDstFile), pszDstDir, pPackage->szFileName); + if (RT_FAILURE(rc)) + return ShowError("Internal error: RTPathJoin failed: %Rrc", rc); + + rc = Extract(pPackage, szDstFile); + if (RT_FAILURE(rc)) + return ShowError("Error extracting package #%u: %Rrc", k, rc); + + if (!fExtractOnly && !AddCleanupRec(szDstFile, true /*fFile*/)) + { + RTFileDelete(szDstFile); + return RTEXITCODE_FAILURE; + } + } + } + + return RTEXITCODE_SUCCESS; +} + + +int WINAPI WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + char *lpCmdLine, + int nCmdShow) +{ + RT_NOREF(hInstance, hPrevInstance, lpCmdLine, nCmdShow); + char **argv = __argv; + int argc = __argc; + + /* + * Init IPRT. This is _always_ the very first thing we do. + */ + int vrc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_STANDALONE_APP); + if (RT_FAILURE(vrc)) + return RTMsgInitFailure(vrc); + + /* + * Check if we're already running and jump out if so. + * + * Note! Do not use a global namespace ("Global\\") for mutex name here, + * will blow up NT4 compatibility! + */ + HANDLE hMutexAppRunning = CreateMutex(NULL, FALSE, "VBoxStubInstaller"); + if ( hMutexAppRunning != NULL + && GetLastError() == ERROR_ALREADY_EXISTS) + { + /* Close the mutex for this application instance. */ + CloseHandle(hMutexAppRunning); + hMutexAppRunning = NULL; + return RTEXITCODE_FAILURE; + } + + /* + * Parse arguments. + */ + + /* Parameter variables. */ + bool fExtractOnly = false; + bool fEnableLogging = false; +#ifdef VBOX_WITH_CODE_SIGNING + bool fEnableSilentCert = true; +#endif + char szExtractPath[RTPATH_MAX] = {0}; + char szMSIArgs[_4K] = {0}; + + /* Parameter definitions. */ + static const RTGETOPTDEF s_aOptions[] = + { + /** @todo Replace short parameters with enums since they're not + * used (and not documented to the public). */ + { "--extract", 'x', RTGETOPT_REQ_NOTHING }, + { "-extract", 'x', RTGETOPT_REQ_NOTHING }, + { "/extract", 'x', RTGETOPT_REQ_NOTHING }, + { "--silent", 's', RTGETOPT_REQ_NOTHING }, + { "-silent", 's', RTGETOPT_REQ_NOTHING }, + { "/silent", 's', RTGETOPT_REQ_NOTHING }, +#ifdef VBOX_WITH_CODE_SIGNING + { "--no-silent-cert", 'c', RTGETOPT_REQ_NOTHING }, + { "-no-silent-cert", 'c', RTGETOPT_REQ_NOTHING }, + { "/no-silent-cert", 'c', RTGETOPT_REQ_NOTHING }, +#endif + { "--logging", 'l', RTGETOPT_REQ_NOTHING }, + { "-logging", 'l', RTGETOPT_REQ_NOTHING }, + { "/logging", 'l', RTGETOPT_REQ_NOTHING }, + { "--path", 'p', RTGETOPT_REQ_STRING }, + { "-path", 'p', RTGETOPT_REQ_STRING }, + { "/path", 'p', RTGETOPT_REQ_STRING }, + { "--msiparams", 'm', RTGETOPT_REQ_STRING }, + { "-msiparams", 'm', RTGETOPT_REQ_STRING }, + { "--reinstall", 'f', RTGETOPT_REQ_NOTHING }, + { "-reinstall", 'f', RTGETOPT_REQ_NOTHING }, + { "/reinstall", 'f', RTGETOPT_REQ_NOTHING }, + { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, + { "-verbose", 'v', RTGETOPT_REQ_NOTHING }, + { "/verbose", 'v', RTGETOPT_REQ_NOTHING }, + { "--version", 'V', RTGETOPT_REQ_NOTHING }, + { "-version", 'V', RTGETOPT_REQ_NOTHING }, + { "/version", 'V', RTGETOPT_REQ_NOTHING }, + { "-v", 'V', RTGETOPT_REQ_NOTHING }, + { "--help", 'h', RTGETOPT_REQ_NOTHING }, + { "-help", 'h', RTGETOPT_REQ_NOTHING }, + { "/help", 'h', RTGETOPT_REQ_NOTHING }, + { "/?", 'h', RTGETOPT_REQ_NOTHING }, + }; + + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + + /* Parse the parameters. */ + int ch; + bool fExitEarly = false; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0); + while ( (ch = RTGetOpt(&GetState, &ValueUnion)) + && rcExit == RTEXITCODE_SUCCESS + && !fExitEarly) + { + switch (ch) + { + case 'f': /* Force re-installation. */ + if (szMSIArgs[0]) + vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), " "); + if (RT_SUCCESS(vrc)) + vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), + "REINSTALLMODE=vomus REINSTALL=ALL"); + if (RT_FAILURE(vrc)) + rcExit = ShowError("MSI parameters are too long."); + break; + + case 'x': + fExtractOnly = true; + break; + + case 's': + g_fSilent = true; + break; + +#ifdef VBOX_WITH_CODE_SIGNING + case 'c': + fEnableSilentCert = false; + break; +#endif + case 'l': + fEnableLogging = true; + break; + + case 'p': + vrc = RTStrCopy(szExtractPath, sizeof(szExtractPath), ValueUnion.psz); + if (RT_FAILURE(vrc)) + rcExit = ShowError("Extraction path is too long."); + break; + + case 'm': + if (szMSIArgs[0]) + vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), " "); + if (RT_SUCCESS(vrc)) + vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), ValueUnion.psz); + if (RT_FAILURE(vrc)) + rcExit = ShowError("MSI parameters are too long."); + break; + + case 'V': + ShowInfo("Version: %d.%d.%d.%d", + VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, + VBOX_SVN_REV); + fExitEarly = true; + break; + + case 'v': + g_iVerbosity++; + break; + + case 'h': + ShowInfo("-- %s v%d.%d.%d.%d --\n" + "\n" + "Command Line Parameters:\n\n" + "--extract - Extract file contents to temporary directory\n" + "--help - Print this help and exit\n" + "--logging - Enables installer logging\n" + "--msiparams <parameters> - Specifies extra parameters for the MSI installers\n" + "--no-silent-cert - Do not install VirtualBox Certificate automatically when --silent option is specified\n" + "--path - Sets the path of the extraction directory\n" + "--reinstall - Forces VirtualBox to get re-installed\n" + "--silent - Enables silent mode installation\n" + "--version - Print version number and exit\n\n" + "Examples:\n" + "%s --msiparams INSTALLDIR=C:\\VBox\n" + "%s --extract -path C:\\VBox", + VBOX_STUB_TITLE, VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV, + argv[0], argv[0]); + fExitEarly = true; + break; + + case VINF_GETOPT_NOT_OPTION: + /* Are (optional) MSI parameters specified and this is the last + * parameter? Append everything to the MSI parameter list then. */ + if (szMSIArgs[0]) + { + vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), " "); + if (RT_SUCCESS(vrc)) + vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), ValueUnion.psz); + if (RT_FAILURE(vrc)) + rcExit = ShowError("MSI parameters are too long."); + continue; + } + /* Fall through is intentional. */ + + default: + if (g_fSilent) + rcExit = RTGetOptPrintError(ch, &ValueUnion); + if (ch == VERR_GETOPT_UNKNOWN_OPTION) + rcExit = ShowError("Unknown option \"%s\"\n" + "Please refer to the command line help by specifying \"/?\"\n" + "to get more information.", ValueUnion.psz); + else + rcExit = ShowError("Parameter parsing error: %Rrc\n" + "Please refer to the command line help by specifying \"/?\"\n" + "to get more information.", ch); + break; + } + } + + /* Check if we can bail out early. */ + if (fExitEarly) + return rcExit; + + if (rcExit != RTEXITCODE_SUCCESS) + vrc = VERR_PARSE_ERROR; + +/** @todo + * + * Split the remainder up in functions and simplify the code flow!! + * + * */ + +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0501 +# ifdef VBOX_STUB_WITH_OWN_CONSOLE /* Use an own console window if run in debug mode. */ + if ( RT_SUCCESS(vrc) + && g_iVerbosity) + { + if (!AllocConsole()) + { + DWORD dwErr = GetLastError(); + ShowError("Unable to allocate console, error = %ld\n", + dwErr); + + /* Close the mutex for this application instance. */ + CloseHandle(hMutexAppRunning); + hMutexAppRunning = NULL; + return RTEXITCODE_FAILURE; + } + + freopen("CONOUT$", "w", stdout); + setvbuf(stdout, NULL, _IONBF, 0); + + freopen("CONOUT$", "w", stderr); + } +# endif /* VBOX_STUB_WITH_OWN_CONSOLE */ +#endif + + if ( RT_SUCCESS(vrc) + && g_iVerbosity) + { + RTPrintf("Silent installation : %RTbool\n", g_fSilent); + RTPrintf("Logging enabled : %RTbool\n", fEnableLogging); +#ifdef VBOX_WITH_CODE_SIGNING + RTPrintf("Certificate installation : %RTbool\n", fEnableSilentCert); +#endif + RTPrintf("Additional MSI parameters: %s\n", + szMSIArgs[0] ? szMSIArgs : "<None>"); + } + + /* + * 32-bit is not officially supported any more. + */ + if ( !fExtractOnly + && !g_fSilent + && !IsWow64()) + { + rcExit = ShowError("32-bit Windows hosts are not supported by this VirtualBox release."); + vrc = VERR_NOT_SUPPORTED; + } + + if (RT_SUCCESS(vrc)) + { + /* + * Determine the extration path if not given by the user, and gather some + * other bits we'll be needing later. + */ + if (szExtractPath[0] == '\0') + { + vrc = RTPathTemp(szExtractPath, sizeof(szExtractPath)); + if (RT_SUCCESS(vrc)) + vrc = RTPathAppend(szExtractPath, sizeof(szExtractPath), "VirtualBox"); + if (RT_FAILURE(vrc)) + ShowError("Failed to determine extraction path (%Rrc)", vrc); + + } + else + { + /** @todo should check if there is a .custom subdirectory there or not. */ + } + RTPathChangeToDosSlashes(szExtractPath, + true /* Force conversion. */); /* MSI requirement. */ + } + + /* Read our manifest. */ + if (RT_SUCCESS(vrc)) + { + PVBOXSTUBPKGHEADER pHeader; + vrc = FindData("MANIFEST", (PVOID *)&pHeader, NULL); + if (RT_SUCCESS(vrc)) + { + /** @todo If we could, we should validate the header. Only the magic isn't + * commonly defined, nor the version number... */ + + RTListInit(&g_TmpFiles); + + /* + * Up to this point, we haven't done anything that requires any cleanup. + * From here on, we do everything in function so we can counter clean up. + */ + bool fCreatedExtractDir; + rcExit = ExtractFiles(pHeader->byCntPkgs, szExtractPath, + fExtractOnly, &fCreatedExtractDir); + if (rcExit == RTEXITCODE_SUCCESS) + { + if (fExtractOnly) + ShowInfo("Files were extracted to: %s", szExtractPath); + else + { + rcExit = CopyCustomDir(szExtractPath); +#ifdef VBOX_WITH_CODE_SIGNING + if (rcExit == RTEXITCODE_SUCCESS && fEnableSilentCert && g_fSilent) + rcExit = InstallCertificates(); +#endif + unsigned iPackage = 0; + while ( iPackage < pHeader->byCntPkgs + && rcExit == RTEXITCODE_SUCCESS) + { + rcExit = ProcessPackage(iPackage, szExtractPath, + szMSIArgs, fEnableLogging); + iPackage++; + } + + /* Don't fail if cleanup fail. At least for now. */ + CleanUp( !fEnableLogging + && fCreatedExtractDir ? szExtractPath : NULL); + } + } + + /* Free any left behind cleanup records (not strictly needed). */ + PSTUBCLEANUPREC pCur, pNext; + RTListForEachSafe(&g_TmpFiles, pCur, pNext, STUBCLEANUPREC, ListEntry) + { + RTListNodeRemove(&pCur->ListEntry); + RTMemFree(pCur); + } + } + else + rcExit = ShowError("Internal package error: Manifest not found (%Rrc)", vrc); + } + +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0501 +# ifdef VBOX_STUB_WITH_OWN_CONSOLE + if (g_iVerbosity) + FreeConsole(); +# endif /* VBOX_STUB_WITH_OWN_CONSOLE */ +#endif + + /* + * Release instance mutex. + */ + if (hMutexAppRunning != NULL) + { + CloseHandle(hMutexAppRunning); + hMutexAppRunning = NULL; + } + + return rcExit; +} + diff --git a/src/VBox/Installer/win/Stub/VBoxStub.h b/src/VBox/Installer/win/Stub/VBoxStub.h new file mode 100644 index 00000000..1676ec60 --- /dev/null +++ b/src/VBox/Installer/win/Stub/VBoxStub.h @@ -0,0 +1,30 @@ +/* $Id: VBoxStub.h $ */ +/** @file + * VBoxStub - VirtualBox's Windows installer stub. + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#ifndef VBOX_INCLUDED_SRC_Stub_VBoxStub_h +#define VBOX_INCLUDED_SRC_Stub_VBoxStub_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#define VBOX_STUB_TITLE "VirtualBox Installer" + +typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); +LPFN_ISWOW64PROCESS fnIsWow64Process; + +#endif /* !VBOX_INCLUDED_SRC_Stub_VBoxStub_h */ + diff --git a/src/VBox/Installer/win/Stub/VBoxStub.manifest b/src/VBox/Installer/win/Stub/VBoxStub.manifest new file mode 100644 index 00000000..f2076fee --- /dev/null +++ b/src/VBox/Installer/win/Stub/VBoxStub.manifest @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> +<assemblyIdentity + version="1.0.0.0" + processorArchitecture="*" + name="VBoxStub.exe" + type="win32" +/> +<description>VirtualBox Windows Installer</description> +<dependency> + <dependentAssembly> + <assemblyIdentity + type="win32" + name="Microsoft.Windows.Common-Controls" + version="6.0.0.0" + processorArchitecture="*" + publicKeyToken="6595b64144ccf1df" + language="*" + /> + </dependentAssembly> +</dependency> +</assembly> diff --git a/src/VBox/Installer/win/Stub/VBoxStub.rc b/src/VBox/Installer/win/Stub/VBoxStub.rc new file mode 100644 index 00000000..ce0a9db7 --- /dev/null +++ b/src/VBox/Installer/win/Stub/VBoxStub.rc @@ -0,0 +1,56 @@ +/* $Id: VBoxStub.rc $*/ +/** @file + * Resource file for the Windows install stub program. + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <windows.h> +#include <VBox/version.h> +#include "resource.h" + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "VirtualBox\0" + VALUE "InternalName", "VirtualBox\0" + VALUE "OriginalFilename", "VirtualBox.exe\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#include "VBoxStub-icon.rc" +#include "VBoxStub-manifest.rc" + diff --git a/src/VBox/Installer/win/Stub/VBoxStubCertUtil.cpp b/src/VBox/Installer/win/Stub/VBoxStubCertUtil.cpp new file mode 100644 index 00000000..cfaefa93 --- /dev/null +++ b/src/VBox/Installer/win/Stub/VBoxStubCertUtil.cpp @@ -0,0 +1,142 @@ +/* $Id: VBoxStubCertUtil.cpp $ */ +/** @file + * VBoxStub - VirtualBox's Windows installer stub (certificate manipulations). + * + * NOTE: The content of this file is partly + * grabbed from src/VBox/Additions/WINNT/tools/VBoxCertUtil.cpp + */ + +/* + * Copyright (C) 2012-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/win/windows.h> +#include <Wincrypt.h> + +#include <iprt/errcore.h> +#include <iprt/message.h> +#include <iprt/string.h> +#include <iprt/utf16.h> + + +/** + * Reads a certificate from a (const char []) buffer, returning a context + * or a the handle to a temporary memory store. + * + * @returns true on success, false on failure (error message written). + * @param kpCertBuf The pointer to the buffer containing the + * certificates. + * @param cbCertBuf Size of @param kpCertBuf in bytes. + * @param ppOutCtx Where to return the handle to the temporary + * memory store. + */ +static bool readCertBuf(const unsigned char kpCertBuf[], DWORD cbCertBuf, PCCERT_CONTEXT *ppOutCtx) +{ + *ppOutCtx = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + (PBYTE)kpCertBuf, cbCertBuf); + if (*ppOutCtx) + return true; + + return false; +} + +/** + * Opens a certificate store. + * + * @returns true on success, false on failure (error message written). + * @param dwDst The destination, like + * CERT_SYSTEM_STORE_LOCAL_MACHINE or + * CERT_SYSTEM_STORE_CURRENT_USER. + * @param pszStoreNm The store name. + */ +static HCERTSTORE openCertStore(DWORD dwDst, const char *pszStoreNm) +{ + HCERTSTORE hStore = NULL; + PRTUTF16 pwszStoreNm; + int rc = RTStrToUtf16(pszStoreNm, &pwszStoreNm); + if (RT_SUCCESS(rc)) + { + /* + * Make sure CERT_STORE_OPEN_EXISTING_FLAG is not set. This causes Windows XP + * to return ACCESS_DENIED when installing TrustedPublisher certificates via + * CertAddCertificateContextToStore() if the TrustedPublisher store never has + * been used (through certmgr.exe and friends) yet. + * + * According to MSDN, if neither CERT_STORE_OPEN_EXISTING_FLAG nor + * CERT_STORE_CREATE_NEW_FLAG is set, the store will be either opened or + * created accordingly. + */ + dwDst &= ~CERT_STORE_OPEN_EXISTING_FLAG; + + hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, + PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, + NULL /* hCryptProv = default */, + dwDst, + pwszStoreNm); + + RTUtf16Free(pwszStoreNm); + } + return hStore; +} + +/** + * Adds a certificate to a store. + * + * @returns true on success, false on failure (error message written). + * @param dwDst The destination, like + * CERT_SYSTEM_STORE_LOCAL_MACHINE or + * CERT_SYSTEM_STORE_CURRENT_USER. + * @param pszStoreNm The store name. + * @param kpCertBuf Buffer that contains a certificate + * @param cbCertBuf Size of @param kpCertBuf in bytes + */ +bool addCertToStore(DWORD dwDst, const char *pszStoreNm, const unsigned char kpCertBuf[], DWORD cbCertBuf) +{ + /* + * Get certificate from buffer. + */ + PCCERT_CONTEXT pSrcCtx = NULL; + bool fRc = false; + + if (!readCertBuf(kpCertBuf, cbCertBuf, &pSrcCtx)) + { + RTMsgError("Unable to get certificate context: %d", GetLastError()); + return fRc; + } + + /* + * Open the certificates store. + */ + HCERTSTORE hDstStore = openCertStore(dwDst, pszStoreNm); + if (hDstStore) + { + /* + * Finally, add certificate to store + */ + if (CertAddCertificateContextToStore(hDstStore, pSrcCtx, CERT_STORE_ADD_REPLACE_EXISTING, NULL)) + fRc = true; + else + RTMsgError("Unable to install certificate: %d", GetLastError()); + + CertCloseStore(hDstStore, CERT_CLOSE_STORE_CHECK_FLAG); + } + else + RTMsgError("Unable to open certificates store: %d", GetLastError()); + + /* Release resources */ + CertFreeCertificateContext(pSrcCtx); + + return fRc; +} diff --git a/src/VBox/Installer/win/Stub/VBoxStubCertUtil.h b/src/VBox/Installer/win/Stub/VBoxStubCertUtil.h new file mode 100644 index 00000000..197c5865 --- /dev/null +++ b/src/VBox/Installer/win/Stub/VBoxStubCertUtil.h @@ -0,0 +1,27 @@ +/* $Id: VBoxStubCertUtil.h $ */ +/** @file + * VBoxStub - VirtualBox's Windows installer stub (certificate manipulations). + */ + +/* + * Copyright (C) 2012-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#ifndef VBOX_INCLUDED_SRC_Stub_VBoxStubCertUtil_h +#define VBOX_INCLUDED_SRC_Stub_VBoxStubCertUtil_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +extern bool addCertToStore(DWORD dwDst, const char *pszStoreNm, const unsigned char kpCertBuf[], DWORD cbCertBuf); + +#endif /* !VBOX_INCLUDED_SRC_Stub_VBoxStubCertUtil_h */ + diff --git a/src/VBox/Installer/win/Stub/crypt32.def b/src/VBox/Installer/win/Stub/crypt32.def new file mode 100644 index 00000000..c65806f3 --- /dev/null +++ b/src/VBox/Installer/win/Stub/crypt32.def @@ -0,0 +1,25 @@ +; $Id: crypt32.def $ +;; @file +; Definitions for CRYPT32.DLL lazy import library. +; + +; +; Copyright (C) 2009-2019 Oracle Corporation +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; General Public License (GPL) as published by the Free Software +; Foundation, in version 2 as it comes in the "COPYING" file of the +; VirtualBox OSE distribution. VirtualBox OSE is distributed in the +; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +; + +LIBRARY CRYPT32.DLL +EXPORTS + _CertCreateCertificateContext@12 + _CertOpenStore@20 + _CertFreeCertificateContext@4 + _CertAddCertificateContextToStore@16 + _CertCloseStore@8 + diff --git a/src/VBox/Installer/win/Stub/msi.def b/src/VBox/Installer/win/Stub/msi.def new file mode 100644 index 00000000..64b661a5 --- /dev/null +++ b/src/VBox/Installer/win/Stub/msi.def @@ -0,0 +1,23 @@ +; $Id: msi.def $ +;; @file +; Definitions for MSI.DLL lazy import library. +; + +; +; Copyright (C) 2009-2019 Oracle Corporation +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; General Public License (GPL) as published by the Free Software +; Foundation, in version 2 as it comes in the "COPYING" file of the +; VirtualBox OSE distribution. VirtualBox OSE is distributed in the +; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +; + +LIBRARY MSI.DLL +EXPORTS + _MsiInstallProductW@8 + _MsiEnableLogW@12 + _MsiSetInternalUI@8 + diff --git a/src/VBox/Installer/win/Stub/resource.h b/src/VBox/Installer/win/Stub/resource.h new file mode 100644 index 00000000..0a201ff9 --- /dev/null +++ b/src/VBox/Installer/win/Stub/resource.h @@ -0,0 +1,32 @@ +/* $Id: resource.h $ */ +/** @file + * VBoxStub - resource header file. + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#ifndef VBOX_INCLUDED_SRC_Stub_resource_h +#define VBOX_INCLUDED_SRC_Stub_resource_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#define IDI_VIRTUALBOX 101 + +#ifndef RT_MANIFEST +# define RT_MANIFEST 24 +#endif +#define APP_MANIFEST 1 + +#endif /* !VBOX_INCLUDED_SRC_Stub_resource_h */ + diff --git a/src/VBox/Installer/win/Stub/shell32.def b/src/VBox/Installer/win/Stub/shell32.def new file mode 100644 index 00000000..b888a760 --- /dev/null +++ b/src/VBox/Installer/win/Stub/shell32.def @@ -0,0 +1,22 @@ +; $Id: shell32.def $ +;; @file +; Definitions for SHELL32.DLL lazy import library. +; + +; +; Copyright (C) 2009-2019 Oracle Corporation +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; General Public License (GPL) as published by the Free Software +; Foundation, in version 2 as it comes in the "COPYING" file of the +; VirtualBox OSE distribution. VirtualBox OSE is distributed in the +; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +; + +LIBRARY SHELL32.DLL +EXPORTS + _CommandLineToArgvW@8 + _SHFileOperationW@4 + diff --git a/src/VBox/Installer/win/Stub/user32.def b/src/VBox/Installer/win/Stub/user32.def new file mode 100644 index 00000000..6850543e --- /dev/null +++ b/src/VBox/Installer/win/Stub/user32.def @@ -0,0 +1,23 @@ +; $Id: user32.def $ +;; @file +; Definitions for USER32.DLL lazy import library. +; + +; +; Copyright (C) 2009-2019 Oracle Corporation +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; General Public License (GPL) as published by the Free Software +; Foundation, in version 2 as it comes in the "COPYING" file of the +; VirtualBox OSE distribution. VirtualBox OSE is distributed in the +; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +; + +LIBRARY USER32.DLL +EXPORTS + _MessageBoxW@16 + _MessageBoxA@16 + _GetDesktopWindow@0 + diff --git a/src/VBox/Installer/win/Stub/ws2_32.def b/src/VBox/Installer/win/Stub/ws2_32.def new file mode 100644 index 00000000..09b434fc --- /dev/null +++ b/src/VBox/Installer/win/Stub/ws2_32.def @@ -0,0 +1,22 @@ +; $Id: ws2_32.def $ +;; @file +; Definitions for WS2_32.DLL lazy import library. +; + +; +; Copyright (C) 2009-2019 Oracle Corporation +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; General Public License (GPL) as published by the Free Software +; Foundation, in version 2 as it comes in the "COPYING" file of the +; VirtualBox OSE distribution. VirtualBox OSE is distributed in the +; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +; + +LIBRARY WS2_32.DLL +EXPORTS + _WSASetLastError@4 + _WSAGetLastError@0 + |