VBoxExtPackHelperApp.cpp revision 1939436fa43cbf7f5cdc05a3830ed624d5fe4a6a
45e9809aff7304721fddb95654901b32195c9c7avboxsync * VirtualBox Main - Extension Pack Helper Application, usually set-uid-to-root.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * Copyright (C) 2010-2012 Oracle Corporation
45e9809aff7304721fddb95654901b32195c9c7avboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
45e9809aff7304721fddb95654901b32195c9c7avboxsync * available from http://www.virtualbox.org. This file is free software;
45e9809aff7304721fddb95654901b32195c9c7avboxsync * you can redistribute it and/or modify it under the terms of the GNU
45e9809aff7304721fddb95654901b32195c9c7avboxsync * General Public License (GPL) as published by the Free Software
45e9809aff7304721fddb95654901b32195c9c7avboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
45e9809aff7304721fddb95654901b32195c9c7avboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
45e9809aff7304721fddb95654901b32195c9c7avboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
45e9809aff7304721fddb95654901b32195c9c7avboxsync/*******************************************************************************
45e9809aff7304721fddb95654901b32195c9c7avboxsync* Header Files *
45e9809aff7304721fddb95654901b32195c9c7avboxsync*******************************************************************************/
45e9809aff7304721fddb95654901b32195c9c7avboxsync/*******************************************************************************
45e9809aff7304721fddb95654901b32195c9c7avboxsync* Defined Constants And Macros *
45e9809aff7304721fddb95654901b32195c9c7avboxsync*******************************************************************************/
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Enable elevation on Windows and Darwin. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** @name Command and option names
45e9809aff7304721fddb95654901b32195c9c7avboxsync/*******************************************************************************
45e9809aff7304721fddb95654901b32195c9c7avboxsync* Global Variables *
45e9809aff7304721fddb95654901b32195c9c7avboxsync*******************************************************************************/
45e9809aff7304721fddb95654901b32195c9c7avboxsync/* Override RTAssertShouldPanic to prevent gdb process creation. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync return true;
45e9809aff7304721fddb95654901b32195c9c7avboxsync * Handle the special standard options when these are specified after the
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @param ch The option character.
45e9809aff7304721fddb95654901b32195c9c7avboxsync RTMsgInfo(VBOX_PRODUCT " Extension Pack Helper App\n"
45e9809aff7304721fddb95654901b32195c9c7avboxsync "All rights reserved.\n"
45e9809aff7304721fddb95654901b32195c9c7avboxsync "This NOT intended for general use, please use VBoxManage instead\n"
45e9809aff7304721fddb95654901b32195c9c7avboxsync "or call the IExtPackManager API directly.\n"
45e9809aff7304721fddb95654901b32195c9c7avboxsync "Usage: %s <command> [options]\n"
45e9809aff7304721fddb95654901b32195c9c7avboxsync "Commands:\n"
45e9809aff7304721fddb95654901b32195c9c7avboxsync " install --base-dir <dir> --cert-dir <dir> --name <name> \\\n"
45e9809aff7304721fddb95654901b32195c9c7avboxsync " --tarball <tarball> --tarball-fd <fd>\n"
45e9809aff7304721fddb95654901b32195c9c7avboxsync " uninstall --base-dir <dir> --name <name>\n"
45e9809aff7304721fddb95654901b32195c9c7avboxsync " cleanup --base-dir <dir>\n"
45e9809aff7304721fddb95654901b32195c9c7avboxsync RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
45e9809aff7304721fddb95654901b32195c9c7avboxsync * Checks if the cerficiate directory is valid.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @returns true if it is valid, false if it isn't.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @param pszCertDir The certificate directory to validate.
45e9809aff7304721fddb95654901b32195c9c7avboxsyncstatic bool IsValidCertificateDir(const char *pszCertDir)
45e9809aff7304721fddb95654901b32195c9c7avboxsync * Just be darn strict for now.
45e9809aff7304721fddb95654901b32195c9c7avboxsync int rc = RTPathAppPrivateNoArch(szCorrect, sizeof(szCorrect));
45e9809aff7304721fddb95654901b32195c9c7avboxsync return false;
45e9809aff7304721fddb95654901b32195c9c7avboxsync rc = RTPathAppend(szCorrect, sizeof(szCorrect), VBOX_EXTPACK_CERT_DIR);
45e9809aff7304721fddb95654901b32195c9c7avboxsync return false;
45e9809aff7304721fddb95654901b32195c9c7avboxsync * Checks if the base directory is valid.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @returns true if it is valid, false if it isn't.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @param pszBaesDir The base directory to validate.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * Just be darn strict for now.
45e9809aff7304721fddb95654901b32195c9c7avboxsync int rc = RTPathAppPrivateArchTop(szCorrect, sizeof(szCorrect));
45e9809aff7304721fddb95654901b32195c9c7avboxsync return false;
45e9809aff7304721fddb95654901b32195c9c7avboxsync rc = RTPathAppend(szCorrect, sizeof(szCorrect), VBOX_EXTPACK_INSTALL_DIR);
45e9809aff7304721fddb95654901b32195c9c7avboxsync return false;
45e9809aff7304721fddb95654901b32195c9c7avboxsync * Cleans up a temporary extension pack directory.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * This is used by 'uninstall', 'cleanup' and in the failure path of 'install'.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @returns The program exit code.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @param pszDir The directory to clean up. The caller is
45e9809aff7304721fddb95654901b32195c9c7avboxsync * responsible for making sure this is valid.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @param fTemporary Whether this is a temporary install directory or
45e9809aff7304721fddb95654901b32195c9c7avboxsyncstatic RTEXITCODE RemoveExtPackDir(const char *pszDir, bool fTemporary)
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** @todo May have to undo 555 modes here later. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync int rc = RTDirRemoveRecursive(pszDir, RTDIRRMREC_F_CONTENT_AND_DIR);
45e9809aff7304721fddb95654901b32195c9c7avboxsync "Failed to delete the %sextension pack directory: %Rrc ('%s')",
45e9809aff7304721fddb95654901b32195c9c7avboxsync * Common uninstall worker used by both uninstall and install --replace.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @returns success or failure, message displayed on failure.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @param pszExtPackDir The extension pack directory name.
45e9809aff7304721fddb95654901b32195c9c7avboxsyncstatic RTEXITCODE CommonUninstallWorker(const char *pszExtPackDir)
45e9809aff7304721fddb95654901b32195c9c7avboxsync /* Rename the extension pack directory before deleting it to prevent new
45e9809aff7304721fddb95654901b32195c9c7avboxsync VM processes from picking it up. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync int rc = RTStrCopy(szExtPackUnInstDir, sizeof(szExtPackUnInstDir), pszExtPackDir);
45e9809aff7304721fddb95654901b32195c9c7avboxsync rc = RTStrCat(szExtPackUnInstDir, sizeof(szExtPackUnInstDir), "-_-uninst");
45e9809aff7304721fddb95654901b32195c9c7avboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to construct temporary extension pack path: %Rrc", rc);
45e9809aff7304721fddb95654901b32195c9c7avboxsync rc = RTDirRename(pszExtPackDir, szExtPackUnInstDir, RTPATHRENAME_FLAGS_NO_REPLACE);
45e9809aff7304721fddb95654901b32195c9c7avboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to rename the extension pack directory: %Rrc", rc);
45e9809aff7304721fddb95654901b32195c9c7avboxsync /* Recursively delete the directory content. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync return RemoveExtPackDir(szExtPackUnInstDir, false /*fTemporary*/);
45e9809aff7304721fddb95654901b32195c9c7avboxsync * Wrapper around VBoxExtPackOpenTarFss.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @returns success or failure, message displayed on failure.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @param hTarballFile The handle to the tarball file.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @param phTarFss Where to return the filesystem stream handle.
45e9809aff7304721fddb95654901b32195c9c7avboxsyncstatic RTEXITCODE OpenTarFss(RTFILE hTarballFile, PRTVFSFSSTREAM phTarFss)
45e9809aff7304721fddb95654901b32195c9c7avboxsync int rc = VBoxExtPackOpenTarFss(hTarballFile, szError, sizeof(szError), phTarFss, NULL);
45e9809aff7304721fddb95654901b32195c9c7avboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s", szError);
45e9809aff7304721fddb95654901b32195c9c7avboxsync * Sets the permissions of the temporary extension pack directory just before
45e9809aff7304721fddb95654901b32195c9c7avboxsync * renaming it.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * By default the temporary directory is only accessible by root, this function
45e9809aff7304721fddb95654901b32195c9c7avboxsync * will make it world readable and browseable.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @returns The program exit code.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @param pszDir The temporary extension pack directory.
45e9809aff7304721fddb95654901b32195c9c7avboxsyncstatic RTEXITCODE SetExtPackPermissions(const char *pszDir)
45e9809aff7304721fddb95654901b32195c9c7avboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to set directory permissions: %Rrc ('%s')", rc, pszDir);
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** @todo */
45e9809aff7304721fddb95654901b32195c9c7avboxsync * Wrapper around VBoxExtPackValidateMember.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @returns Program exit code, failure with message.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @param pszName The name of the directory.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @param enmType The object type.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * @param hVfsObj The VFS object.
45e9809aff7304721fddb95654901b32195c9c7avboxsyncstatic RTEXITCODE ValidateMemberOfExtPack(const char *pszName, RTVFSOBJTYPE enmType, RTVFSOBJ hVfsObj)
45e9809aff7304721fddb95654901b32195c9c7avboxsync int rc = VBoxExtPackValidateMember(pszName, enmType, hVfsObj, szError, sizeof(szError));
45e9809aff7304721fddb95654901b32195c9c7avboxsync return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s", szError);
45e9809aff7304721fddb95654901b32195c9c7avboxsync * Validates the extension pack tarball prior to unpacking.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * Operations performed:
45e9809aff7304721fddb95654901b32195c9c7avboxsync * - Hardening checks.
static RTEXITCODE ValidateUnpackedExtPack(const char *pszDir, const char *pszTarball, const char *pszExtPackName)
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Hardening check failed with %Rrc: %s", rc, ErrInfo.Core.pszMsg);
return RTEXITCODE_SUCCESS;
return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszDstDirName, rc);
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create directory '%s': %Rrc", pszDstDirName, rc);
#ifndef RT_OS_WINDOWS
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to set directory permissions on '%s': %Rrc", pszDstDirName, rc);
return RTEXITCODE_SUCCESS;
return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsIoStrmQueryInfo failed with %Rrc on '%s'", rc, pszDstFilename);
uint32_t fFlags = RTFILE_O_WRITE | RTFILE_O_DENY_ALL | RTFILE_O_CREATE | (0600 << RTFILE_O_CREATE_MODE_SHIFT);
return RTEXITCODE_SUCCESS;
RTMsgError("Failed to set the mode of '%s' to %RTfmode: %Rrc", pszDstFilename, ObjInfo.Attr.fMode, rc);
return RTEXITCODE_FAILURE;
static RTEXITCODE UnpackExtPack(RTFILE hTarballFile, const char *pszDirDst, RTMANIFEST hValidManifest,
const char *pszTarball)
int rc = RTPathAbs(pszDirDst, szDstPath, sizeof(szDstPath) - VBOX_EXTPACK_MAX_MEMBER_NAME_LENGTH - 2);
return rcExit;
char *pszName;
rc = RTManifestEqualsEx(hUnpackManifest, hValidManifest, NULL /*papszIgnoreEntries*/, NULL /*papszIgnoreAttr*/,
return rcExit;
static RTEXITCODE ValidateExtPackTarball(RTFILE hTarballFile, const char *pszExtPackName, const char *pszTarball,
return RTEXITCODE_SUCCESS;
static RTEXITCODE DoInstall2(const char *pszBaseDir, const char *pszCertDir, const char *pszTarball,
return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTFileQueryInfo failed with %Rrc on '%s'", rc, pszTarball);
RTStrPrintf(&szTmpPath[cchTmpPath], sizeof(szTmpPath) - cchTmpPath, "-_-inst-%u", (uint32_t)RTProcSelf());
if (!fReplace)
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unexpected RTPathQueryInfoEx status code %Rrc for '%s'", rc, szFinalPath);
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unexpected RTPathQueryInfoEx status code %Rrc for '%s'", rc, szFinalPath);
#ifndef RT_OS_WINDOWS
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to set directory permissions on '%s': %Rrc", pszBaseDir, rc);
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create temporary directory: %Rrc ('%s')", rc, szTmpPath);
RTEXITCODE rcExit = ValidateExtPackTarball(hTarballFile, pszName, pszTarball, pszTarballDigest, &hValidManifest);
&& fReplace
return rcExit;
bool fReplace = false;
int ch;
switch (ch)
if (pszBaseDir)
if (pszCertDir)
if (pszName)
if (pszTarball)
return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The --tarball-fd value is out of range: %#RX64", ValueUnion.u64);
fReplace = true;
if (pszTarballDigest)
if (!pszName)
if (!pszBaseDir)
if (!pszCertDir)
if (!pszTarball)
if (!pszTarballDigest)
if (!pstrMangledName)
rcExit = DoInstall2(pszBaseDir, pszCertDir, pszTarball, pszTarballDigest, hTarballFile, hTarballFileOpt,
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open the extension pack tarball: %Rrc ('%s')", rc, pszTarball);
delete pstrMangledName;
return rcExit;
int ch;
switch (ch)
if (pszBaseDir)
if (pszName)
if (!pszName)
if (!pszBaseDir)
if (!pstrMangledName)
delete pstrMangledName;
return RTEXITCODE_SUCCESS;
return rcExit;
int ch;
switch (ch)
if (pszBaseDir)
if (!pszBaseDir)
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed open the base directory: %Rrc ('%s')", rc, pszBaseDir);
bool fCandidate = false;
if ( pszMarker
if (fCandidate)
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathJoin failed with %Rrc for '%s'", rc, Entry.szName);
cCleaned++;
if (!cCleaned)
return rcExit;
#ifdef WITH_ELEVATION
static const char * const s_apszPaths[] =
#ifdef RT_OS_SOLARIS
int rc;
if (!cbRead)
if (fComplain)
static RTEXITCODE RelaunchElevatedNative(const char *pszExecPath, const char **papszArgs, int cSuArgs, int cMyArgs,
#ifdef RT_OS_WINDOWS
char *pszCmdLine;
if (pszDisplayInfoHack)
if (pszArg)
static char s_szPrompt[] = "VirtualBox needs further rights to make changes to your installation.\n\n";
NULL);
&pSocketStrm);
if (!cbRead)
char *pszCmdLine;
else if ( fHaveDisplayVar
else if (fHaveDisplayVar)
RTMsgError("Unable to locate 'pkexec', 'gksu' or 'su+xterm'. Try perform the operation using VBoxManage running as root");
RTMsgError("Unable to locate 'pkexec'. Try perform the operation using VBoxManage running as root");
&hProcess);
return rcExit;
char const **papszArgs = (char const **)RTMemTmpAllocZ((cSuArgs + cArgs + 1) * sizeof(const char *));
if (papszArgs)
return rcExit;
*pfElevated = false;
# if defined(RT_OS_WINDOWS)
return RTMsgErrorExit(RTEXITCODE_FAILURE, "OpenProcessToken failed: %u (%#x)", GetLastError(), GetLastError());
if (AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminGrpSid))
# ifdef DEBUG
# ifdef DEBUG
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "GetTokenInformation(TokenGroups,cb) failed: %u (%#x)", GetLastError(), GetLastError());
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "GetTokenInformation(TokenGroups,0) failed: %u (%#x)", GetLastError(), GetLastError());
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "AllocateAndInitializeSid failed: %u (%#x)", GetLastError(), GetLastError());
if (fIsAdmin)
DWORD dwIntegrityLevel = *GetSidSubAuthority(pSidAndAttr->Sid, *GetSidSubAuthorityCount(pSidAndAttr->Sid) - 1U);
*pfElevated = true;
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "GetTokenInformation failed: %u (%#x)", GetLastError(), GetLastError());
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "GetTokenInformation failed: %u (%#x)", GetLastError(), GetLastError());
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Membership in the Administrators group is required to perform this action");
return rcExit;
return RTEXITCODE_SUCCESS;
#ifdef WITH_ELEVATION
bool fElevated;
return rcExit;
#ifdef WITH_ELEVATION
switch (ch)
case CMD_INSTALL:
case CMD_UNINSTALL:
case CMD_CLEANUP:
#ifdef WITH_ELEVATION
if (!fElevated)
switch (ch)
case CMD_INSTALL:
case CMD_UNINSTALL:
case CMD_CLEANUP:
switch (rcExit)
case RTEXITCODE_SUCCESS:
return rcExit;
#ifdef WITH_ELEVATION
case OPT_ELEVATED:
fElevated = true;
case OPT_STDERR:
case OPT_STDOUT:
if (!pFile)
case OPT_DISP_INFO_HACK:
if (pszDisplayInfoHack)
#ifdef RT_OS_WINDOWS
extern "C" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
if (!pwszCmdLine)
char *pszCmdLine;
int cArgs;
char **papszArgs;
return rc;