VBoxExtPackHelperApp.cpp revision 64cebe9ba3fe3787672cf522c22eb763a8a6dad4
/* $Id$ */
/** @file
* VirtualBox Main - Extension Pack Helper Application, usually set-uid-to-root.
*/
/*
* Copyright (C) 2010-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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 "../include/ExtPackUtil.h"
#include <iprt/buildconfig.h>
#include <iprt/initterm.h>
#include <iprt/manifest.h>
#ifdef RT_OS_WINDOWS
# define _WIN32_WINNT 0x0501
# include <Objbase.h> /* CoInitializeEx */
# include <Windows.h> /* ShellExecuteEx, ++ */
# ifdef DEBUG
# include <Sddl.h>
# endif
#endif
#ifdef RT_OS_DARWIN
# include <Security/Authorization.h>
# include <Security/AuthorizationTags.h>
# include <CoreFoundation/CoreFoundation.h>
#endif
#if !defined(RT_OS_OS2)
# include <stdio.h>
# include <errno.h>
# if !defined(RT_OS_WINDOWS)
# include <unistd.h> /* geteuid */
# endif
#endif
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** Enable elevation on Windows and Darwin. */
#if !defined(RT_OS_OS2) || defined(DOXYGEN_RUNNING)
# define WITH_ELEVATION
#endif
/** @name Command and option names
* @{ */
#define CMD_INSTALL 1000
#define CMD_UNINSTALL 1001
#define CMD_CLEANUP 1002
#ifdef WITH_ELEVATION
# define OPT_ELEVATED 1090
# define OPT_STDOUT 1091
# define OPT_STDERR 1092
#endif
#define OPT_DISP_INFO_HACK 1093
/** @} */
/*******************************************************************************
* Global Variables *
*******************************************************************************/
#ifdef RT_OS_WINDOWS
static HINSTANCE g_hInstance;
#endif
#ifdef IN_RT_R3
/* Override RTAssertShouldPanic to prevent gdb process creation. */
RTDECL(bool) RTAssertShouldPanic(void)
{
return true;
}
#endif
/**
* Handle the special standard options when these are specified after the
* command.
*
* @param ch The option character.
*/
{
switch (ch)
{
case 'h':
{
"All rights reserved.\n"
"\n"
"This NOT intended for general use, please use VBoxManage instead\n"
"or call the IExtPackManager API directly.\n"
"\n"
"Usage: %s <command> [options]\n"
"Commands:\n"
" install --base-dir <dir> --cert-dir <dir> --name <name> \\\n"
" --tarball <tarball> --tarball-fd <fd>\n"
" uninstall --base-dir <dir> --name <name>\n"
" cleanup --base-dir <dir>\n"
, RTProcShortName());
return RTEXITCODE_SUCCESS;
}
case 'V':
return RTEXITCODE_SUCCESS;
default:
}
}
/**
* Checks if the cerficiate directory is valid.
*
* @returns true if it is valid, false if it isn't.
* @param pszCertDir The certificate directory to validate.
*/
static bool IsValidCertificateDir(const char *pszCertDir)
{
/*
* Just be darn strict for now.
*/
char szCorrect[RTPATH_MAX];
if (RT_FAILURE(rc))
return false;
if (RT_FAILURE(rc))
return false;
}
/**
* Checks if the base directory is valid.
*
* @returns true if it is valid, false if it isn't.
* @param pszBaesDir The base directory to validate.
*/
static bool IsValidBaseDir(const char *pszBaseDir)
{
/*
* Just be darn strict for now.
*/
char szCorrect[RTPATH_MAX];
if (RT_FAILURE(rc))
return false;
if (RT_FAILURE(rc))
return false;
}
/**
* Cleans up a temporary extension pack directory.
*
* This is used by 'uninstall', 'cleanup' and in the failure path of 'install'.
*
* @returns The program exit code.
* @param pszDir The directory to clean up. The caller is
* responsible for making sure this is valid.
* @param fTemporary Whether this is a temporary install directory or
* not.
*/
{
/** @todo May have to undo 555 modes here later. */
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE,
"Failed to delete the %sextension pack directory: %Rrc ('%s')",
return RTEXITCODE_SUCCESS;
}
/**
* Common uninstall worker used by both uninstall and install --replace.
*
* @returns success or failure, message displayed on failure.
* @param pszExtPackDir The extension pack directory name.
*/
{
/* Rename the extension pack directory before deleting it to prevent new
VM processes from picking it up. */
char szExtPackUnInstDir[RTPATH_MAX];
if (RT_SUCCESS(rc))
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to construct temporary extension pack path: %Rrc", rc);
if (rc == VERR_ALREADY_EXISTS)
{
/* Automatic cleanup and try again. It's in theory possible that we're
racing another cleanup operation here, so just ignore errors and try
again. (There is no installation race due to the exclusive temporary
installation directory.) */
}
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE,
"Failed to rename the extension pack directory: %Rrc\n"
"If the problem persists, try running the command: VBoxManage extpack cleanup", rc);
/* Recursively delete the directory content. */
}
/**
* Wrapper around VBoxExtPackOpenTarFss.
*
* @returns success or failure, message displayed on failure.
* @param hTarballFile The handle to the tarball file.
* @param phTarFss Where to return the filesystem stream handle.
*/
{
char szError[8192];
if (RT_FAILURE(rc))
{
}
return RTEXITCODE_SUCCESS;
}
/**
* Sets the permissions of the temporary extension pack directory just before
* renaming it.
*
* By default the temporary directory is only accessible by root, this function
* will make it world readable and browseable.
*
* @returns The program exit code.
* @param pszDir The temporary extension pack directory.
*/
{
RTMsgInfo("Setting permissions...");
#if !defined(RT_OS_WINDOWS)
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to set directory permissions: %Rrc ('%s')", rc, pszDir);
#else
/** @todo TrustedInstaller? */
#endif
return RTEXITCODE_SUCCESS;
}
/**
* Wrapper around VBoxExtPackValidateMember.
*
* @returns Program exit code, failure with message.
* @param pszName The name of the directory.
* @param enmType The object type.
* @param hVfsObj The VFS object.
*/
static RTEXITCODE ValidateMemberOfExtPack(const char *pszName, RTVFSOBJTYPE enmType, RTVFSOBJ hVfsObj)
{
char szError[8192];
if (RT_FAILURE(rc))
{
}
return RTEXITCODE_SUCCESS;
}
/**
* Validates the extension pack tarball prior to unpacking.
*
* Operations performed:
* - Hardening checks.
*
* @returns The program exit code.
* @param pszDir The directory where the extension pack has been
* unpacked.
* @param pszExtPackName The expected extension pack name.
* @param pszTarball The name of the tarball in case we have to
* complain about something.
*/
static RTEXITCODE ValidateUnpackedExtPack(const char *pszDir, const char *pszTarball, const char *pszExtPackName)
{
RTMsgInfo("Validating unpacked extension pack...");
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Hardening check failed with %Rrc: %s", rc, ErrInfo.Core.pszMsg);
return RTEXITCODE_SUCCESS;
}
/**
* Unpacks a directory from an extension pack tarball.
*
* @returns Program exit code, failure with message.
* @param pszDstDirName The name of the unpacked directory.
* @param hVfsObj The source object for the directory.
*/
{
/*
* Get the mode mask before creating the directory.
*/
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszDstDirName, rc);
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create directory '%s': %Rrc", pszDstDirName, rc);
#ifndef RT_OS_WINDOWS
/*
* Because of umask, we have to apply the mode again.
*/
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to set directory permissions on '%s': %Rrc", pszDstDirName, rc);
#else
/** @todo Ownership tricks on windows? */
#endif
return RTEXITCODE_SUCCESS;
}
/**
* Unpacks a file from an extension pack tarball.
*
* @returns Program exit code, failure with message.
* @param pszName The name in the tarball.
* @param pszDstFilename The name of the unpacked file.
* @param hVfsIosSrc The source stream for the file.
* @param hUnpackManifest The manifest to add the file digest to.
*/
{
/*
* Query the object info, we'll need it for buffer sizing as well as
* setting the file mode.
*/
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsIoStrmQueryInfo failed with %Rrc on '%s'", rc, pszDstFilename);
/*
* Create the file.
*/
uint32_t fFlags = RTFILE_O_WRITE | RTFILE_O_DENY_ALL | RTFILE_O_CREATE | (0600 << RTFILE_O_CREATE_MODE_SHIFT);
if (RT_FAILURE(rc))
/*
* Create a I/O stream for the destination file, stack a manifest entry
* creator on top of it.
*/
if (RT_SUCCESS(rc))
{
false /*fReadOrWrite*/, &hVfsIosDst);
if (RT_SUCCESS(rc))
{
/*
* Pump the data thru.
*/
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
/*
* Set the mode mask.
*/
/** @todo Windows needs to do more here, I think. */
if (RT_SUCCESS(rc))
{
return RTEXITCODE_SUCCESS;
}
RTMsgError("Failed to set the mode of '%s' to %RTfmode: %Rrc", pszDstFilename, ObjInfo.Attr.fMode, rc);
}
else
}
else
}
else
}
else
return RTEXITCODE_FAILURE;
}
/**
* Unpacks the extension pack into the specified directory.
*
* This will apply ownership and permission changes to all the content, the
* exception is @a pszDirDst which will be handled by SetExtPackPermissions.
*
* @returns The program exit code.
* @param hTarballFile The tarball to unpack.
* @param pszDirDst Where to unpack it.
* @param hValidManifest The manifest we've validated.
* @param pszTarball The name of the tarball in case we have to
* complain about something.
*/
static RTEXITCODE UnpackExtPack(RTFILE hTarballFile, const char *pszDirDst, RTMANIFEST hValidManifest,
const char *pszTarball)
{
/*
* Set up the destination path.
*/
char szDstPath[RTPATH_MAX];
int rc = RTPathAbs(pszDirDst, szDstPath, sizeof(szDstPath) - VBOX_EXTPACK_MAX_MEMBER_NAME_LENGTH - 2);
if (RT_FAILURE(rc))
/*
* Open the tar.gz filesystem stream and set up an manifest in-memory file.
*/
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
if (RT_SUCCESS(rc))
{
/*
* Process the tarball (would be nice to move this to a function).
*/
for (;;)
{
/*
* Get the next stream object.
*/
char *pszName;
if (RT_FAILURE(rc))
{
break;
}
/*
* Check the type & name validity then unpack it.
*/
if (rcExit == RTEXITCODE_SUCCESS)
{
if (RT_SUCCESS(rc))
{
if ( enmType == RTVFSOBJTYPE_FILE
|| enmType == RTVFSOBJTYPE_IO_STREAM)
{
}
}
else
}
/*
* Clean up and break out on failure.
*/
if (rcExit != RTEXITCODE_SUCCESS)
break;
}
/*
* Check that what we just extracted matches the already verified
* manifest.
*/
if (rcExit == RTEXITCODE_SUCCESS)
{
char szError[RTPATH_MAX];
rc = RTManifestEqualsEx(hUnpackManifest, hValidManifest, NULL /*papszIgnoreEntries*/, NULL /*papszIgnoreAttr*/,
if (RT_SUCCESS(rc))
else
}
#if 0
#endif
}
return rcExit;
}
/**
* Wrapper around VBoxExtPackValidateTarball.
*
* @returns The program exit code.
* @param hTarballFile The handle to open the @a pszTarball file.
* @param pszExtPackName The name of the extension pack name.
* @param pszTarball The name of the tarball in case we have to
* complain about something.
* @param pszTarballDigest The SHA-256 digest of the tarball.
* @param phValidManifest Where to return the handle to fully validated
* the manifest for the extension pack. This
* includes all files.
*/
static RTEXITCODE ValidateExtPackTarball(RTFILE hTarballFile, const char *pszExtPackName, const char *pszTarball,
{
char szError[8192];
if (RT_FAILURE(rc))
{
}
return RTEXITCODE_SUCCESS;
}
/**
* The 2nd part of the installation process.
*
* @returns The program exit code.
* @param pszBaseDir The base directory.
* @param pszCertDir The certificat directory.
* @param pszTarball The tarball name.
* @param pszTarballDigest The SHA-256 digest of the tarball. Empty string
* if no digest available.
* @param hTarballFile The handle to open the @a pszTarball file.
* @param hTarballFileOpt The tarball file handle (optional).
* @param pszName The extension pack name.
* @param pszMangledName The mangled extension pack name.
* @param fReplace Whether to replace any existing ext pack.
*/
static RTEXITCODE DoInstall2(const char *pszBaseDir, const char *pszCertDir, const char *pszTarball,
{
/*
* Do some basic validation of the tarball file.
*/
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTFileQueryInfo failed with %Rrc on '%s'", rc, pszTarball);
if (hTarballFileOpt != NIL_RTFILE)
{
if (RT_FAILURE(rc))
}
/*
* Construct the paths to the two directories we'll be using.
*/
char szFinalPath[RTPATH_MAX];
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE,
"Failed to construct the path to the final extension pack directory: %Rrc", rc);
char szTmpPath[RTPATH_MAX];
if (RT_SUCCESS(rc))
{
RTStrPrintf(&szTmpPath[cchTmpPath], sizeof(szTmpPath) - cchTmpPath, "-_-inst-%u", (uint32_t)RTProcSelf());
}
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE,
"Failed to construct the path to the temporary extension pack directory: %Rrc", rc);
/*
* Check that they don't exist at this point in time, unless fReplace=true.
*/
{
if (!fReplace)
return RTMsgErrorExit(RTEXITCODE_FAILURE,
"The extension pack is already installed. You must uninstall the old one first.");
}
else if (RT_SUCCESS(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE,
"Found non-directory file system object where the extension pack would be installed ('%s')",
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);
/*
* Create the temporary directory and prepare the extension pack within it.
* If all checks out correctly, rename it to the final directory.
*/
#ifndef RT_OS_WINDOWS
/*
* Because of umask, we have to apply the mode again.
*/
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to set directory permissions on '%s': %Rrc", pszBaseDir, rc);
#else
/** @todo Ownership tricks on windows? */
#endif
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create temporary directory: %Rrc ('%s')", rc, szTmpPath);
RTEXITCODE rcExit = ValidateExtPackTarball(hTarballFile, pszName, pszTarball, pszTarballDigest, &hValidManifest);
if (rcExit == RTEXITCODE_SUCCESS)
if (rcExit == RTEXITCODE_SUCCESS)
if (rcExit == RTEXITCODE_SUCCESS)
if (rcExit == RTEXITCODE_SUCCESS)
{
if ( RT_FAILURE(rc)
&& fReplace
&& RTDirExists(szFinalPath))
{
/* Automatic uninstall if --replace was given. */
if (rcExit == RTEXITCODE_SUCCESS)
}
if (RT_SUCCESS(rc))
else if (rcExit == RTEXITCODE_SUCCESS)
"Failed to rename the temporary directory to the final one: %Rrc ('%s' -> '%s')",
}
/*
* Clean up the temporary directory on failure.
*/
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
}
/**
* Implements the 'install' command.
*
* @returns The program exit code.
* @param argc The number of program arguments.
* @param argv The program arguments.
*/
{
/*
* Parse the parameters.
*
* Note! The --base-dir and --cert-dir are only for checking that the
* caller and this help applications have the same idea of where
* things are. Likewise, the --name is for verifying assumptions
* the caller made about the name. The optional --tarball-fd option
* is just for easing the paranoia on the user side.
*/
static const RTGETOPTDEF s_aOptions[] =
{
};
if (RT_FAILURE(rc))
const char *pszBaseDir = NULL;
const char *pszCertDir = NULL;
const char *pszTarball = NULL;
const char *pszTarballDigest = NULL;
bool fReplace = false;
int ch;
{
switch (ch)
{
case 'b':
if (pszBaseDir)
if (!IsValidBaseDir(pszBaseDir))
break;
case 'c':
if (pszCertDir)
if (!IsValidCertificateDir(pszCertDir))
break;
case 'n':
if (pszName)
if (!VBoxExtPackIsValidName(pszName))
break;
case 't':
if (pszTarball)
break;
case 'd':
{
if (hTarballFileOpt != NIL_RTFILE)
return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The --tarball-fd value is out of range: %#RX64", ValueUnion.u64);
if (RT_FAILURE(rc))
break;
}
case 'r':
fReplace = true;
break;
case 's':
{
if (pszTarballDigest)
if (RT_FAILURE(rc))
break;
}
case 'h':
case 'V':
return DoStandardOption(ch);
default:
}
}
if (!pszName)
if (!pszBaseDir)
if (!pszCertDir)
if (!pszTarball)
if (!pszTarballDigest)
/*
* Ok, down to business.
*/
if (!pstrMangledName)
if (RT_SUCCESS(rc))
{
rcExit = DoInstall2(pszBaseDir, pszCertDir, pszTarball, pszTarballDigest, hTarballFile, hTarballFileOpt,
}
else
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open the extension pack tarball: %Rrc ('%s')", rc, pszTarball);
delete pstrMangledName;
return rcExit;
}
/**
* Implements the 'uninstall' command.
*
* @returns The program exit code.
* @param argc The number of program arguments.
* @param argv The program arguments.
*/
{
/*
* Parse the parameters.
*
* Note! The --base-dir is only for checking that the caller and this help
* applications have the same idea of where things are.
*/
static const RTGETOPTDEF s_aOptions[] =
{
};
if (RT_FAILURE(rc))
const char *pszBaseDir = NULL;
int ch;
{
switch (ch)
{
case 'b':
if (pszBaseDir)
if (!IsValidBaseDir(pszBaseDir))
break;
case 'n':
if (pszName)
if (!VBoxExtPackIsValidName(pszName))
break;
case 'h':
case 'V':
return DoStandardOption(ch);
default:
}
}
if (!pszName)
if (!pszBaseDir)
/*
* Mangle the name so we can construct the directory names.
*/
if (!pstrMangledName)
delete pstrMangledName;
/*
* Ok, down to business.
*/
/* Check that it exists. */
char szExtPackDir[RTPATH_MAX];
if (RT_FAILURE(rc))
if (!RTDirExists(szExtPackDir))
{
RTMsgInfo("Extension pack not installed. Nothing to do.");
return RTEXITCODE_SUCCESS;
}
if (rcExit == RTEXITCODE_SUCCESS)
return rcExit;
}
/**
* Implements the 'cleanup' command.
*
* @returns The program exit code.
* @param argc The number of program arguments.
* @param argv The program arguments.
*/
{
/*
* Parse the parameters.
*
* Note! The --base-dir is only for checking that the caller and this help
* applications have the same idea of where things are.
*/
static const RTGETOPTDEF s_aOptions[] =
{
};
if (RT_FAILURE(rc))
const char *pszBaseDir = NULL;
int ch;
{
switch (ch)
{
case 'b':
if (pszBaseDir)
if (!IsValidBaseDir(pszBaseDir))
break;
case 'h':
case 'V':
return DoStandardOption(ch);
default:
}
}
if (!pszBaseDir)
/*
* Ok, down to business.
*/
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed open the base directory: %Rrc ('%s')", rc, pszBaseDir);
for (;;)
{
if (RT_FAILURE(rc))
{
if (rc != VERR_NO_MORE_FILES)
break;
}
/*
* naming scheme are candidates for cleaning.
*/
{
bool fCandidate = false;
if ( pszMarker
if (fCandidate)
{
/*
* Recursive delete, safe.
*/
char szPath[RTPATH_MAX];
if (RT_SUCCESS(rc))
{
if (rcExit2 == RTEXITCODE_SUCCESS)
else if (rcExit == RTEXITCODE_SUCCESS)
}
else
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathJoin failed with %Rrc for '%s'", rc, Entry.szName);
cCleaned++;
}
}
}
if (!cCleaned)
RTMsgInfo("Nothing to clean.");
return rcExit;
}
#ifdef WITH_ELEVATION
#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_DARWIN)
/**
* Looks in standard locations for a suitable exec tool.
*
* @returns true if found, false if not.
* @param pszPath Where to store the path to the tool on
* successs.
* @param cbPath The size of the buffer @a pszPath points to.
* @param pszName The name of the tool we're looking for.
*/
{
static const char * const s_apszPaths[] =
{
"/bin",
"/sbin",
#ifdef RT_OS_SOLARIS
#endif
};
for (unsigned i = 0; i < RT_ELEMENTS(s_apszPaths); i++)
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
return true;
}
}
}
return false;
}
#endif
/**
* Copies the content of a file to a stream.
*
* @param hSrc The source file.
* @param pDst The destination stream.
* @param fComplain Whether to complain about errors (i.e. is this
* stderr, if not keep the trap shut because it
* may be missing when running under VBoxSVC.)
*/
{
int rc;
for (;;)
{
char abBuf[0x1000];
if (RT_FAILURE(rc))
{
break;
}
if (!cbRead)
break;
if (RT_FAILURE(rc))
{
if (fComplain)
break;
}
}
}
/**
* Relaunches ourselves as a elevated process using platform specific facilities.
*
* @returns Program exit code.
* @param pszExecPath The executable path.
* @param papszArgs The arguments.
* @param cSuArgs The number of argument entries reserved for the
* 'su' like programs at the start of papszArgs.
* @param cMyArgs The number of arguments following @a cSuArgs.
* @param iCmd The command that is being executed. (For
* selecting messages.)
* @param pszDisplayInfoHack Display information hack. Platform specific++.
*/
static RTEXITCODE RelaunchElevatedNative(const char *pszExecPath, const char **papszArgs, int cSuArgs, int cMyArgs,
int iCmd, const char *pszDisplayInfoHack)
{
#ifdef RT_OS_WINDOWS
if (RT_SUCCESS(rc))
{
char *pszCmdLine;
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
/* Apply display hacks. */
if (pszDisplayInfoHack)
{
if (pszArg)
{
if (RT_SUCCESS(rc))
{
}
}
}
{
}
if (ShellExecuteExW(&Info))
{
{
/*
* Wait for the process, make sure the deal with messages.
*/
for (;;)
{
if (dwRc == WAIT_OBJECT_0)
break;
if ( dwRc != WAIT_TIMEOUT
{
break;
}
{
}
}
{
else
}
}
else
RTMsgError("ShellExecuteExW return INVALID_HANDLE_VALUE as Info.hProcess");
}
else
}
}
}
else
#elif defined(RT_OS_DARWIN)
char szIconName[RTPATH_MAX];
if (RT_SUCCESS(rc))
if (RT_FAILURE(rc))
if (orc == errAuthorizationSuccess)
{
/*
* Preautorize the privileged execution of ourselves.
*/
static char s_szPrompt[] = "VirtualBox needs further rights to make changes to your installation.\n\n";
{
};
NULL);
if (orc == errAuthorizationSuccess)
{
/*
* Execute with extra permissions
*/
&pSocketStrm);
if (orc == errAuthorizationSuccess)
{
/*
* Read the output of the tool, the read will fail when it quits.
*/
for (;;)
{
char achBuf[1024];
if (!cbRead)
break;
}
}
else
}
else if (orc == errAuthorizationCanceled)
RTMsgError("Authorization canceled by the user");
else
}
else
#else
/*
* Several of the alternatives below will require a command line.
*/
char *pszCmdLine;
if (RT_FAILURE(rc))
/*
* Look for various standard stuff for executing a program as root.
*
* N.B. When adding new arguments, please make 100% sure RelaunchElevated
* allocates enough array entries.
*
* TODO: Feel free to contribute code for using PolicyKit directly.
*/
char szExecTool[260];
char szXterm[260];
/*
*/
{
? "VirtualBox extension pack installer"
: iCmd == CMD_UNINSTALL
? "VirtualBox extension pack uninstaller"
: "VirtualBox extension pack maintainer";
}
/*
* gksu is our favorite as it is very well integrated.
*/
{
#if 0 /* older gksu does not grok --description nor '--' and multiple args. */
? "VirtualBox extension pack installer"
: iCmd == CMD_UNINSTALL
? "VirtualBox extension pack uninstaller"
: "VirtualBox extension pack maintainer";
#else
#endif
}
/*
* pkexec may work for ssh console sessions as well if the right agents
* are installed. However it is very generic and does not allow for any
* custom messages. Thus it comes after gksu.
*/
{
}
/*
* The ultimate fallback is running 'su -' within an xterm. We use the
* title of the xterm to tell what is going on.
*/
else if ( fHaveDisplayVar
{
? "VirtualBox extension pack installer - su"
: iCmd == CMD_UNINSTALL
? "VirtualBox extension pack uninstaller - su"
: "VirtualBox extension pack maintainer - su";
}
else if (fHaveDisplayVar)
RTMsgError("Unable to locate 'pkexec', 'gksu' or 'su+xterm'. Try perform the operation using VBoxManage running as root");
else
RTMsgError("Unable to locate 'pkexec'. Try perform the operation using VBoxManage running as root");
{
AssertRelease(iSuArg >= 0);
/*
* Argument list constructed, execute it and wait for the exec
* program to complete.
*/
&hProcess);
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
else
}
else
}
else
}
#endif
return rcExit;
}
/**
* Relaunches ourselves as a elevated process using platform specific facilities.
*
* @returns Program exit code.
* @param argc The number of arguments.
* @param argv The arguments.
* @param iCmd The command that is being executed.
* @param pszDisplayInfoHack Display information hack. Platform specific++.
*/
{
/*
* We need the executable name later, so get it now when it's easy to quit.
*/
char szExecPath[RTPATH_MAX];
/*
* Create a couple of temporary files for stderr and stdout.
*/
if (RT_FAILURE(rc))
if (RT_FAILURE(rc))
if (RT_FAILURE(rc))
char szStdOut[RTPATH_MAX];
char szStdErr[RTPATH_MAX];
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
{
| (0600 << RTFILE_O_CREATE_MODE_SHIFT));
if (RT_SUCCESS(rc))
{
| (0600 << RTFILE_O_CREATE_MODE_SHIFT));
if (RT_SUCCESS(rc))
{
/*
* list. Note that darwin skips the --stdout bit, so don't
* change the order here.
*/
int const cSuArgs = 12;
char const **papszArgs = (char const **)RTMemTmpAllocZ((cSuArgs + cArgs + 1) * sizeof(const char *));
if (papszArgs)
{
/*
* Do the platform specific process execution (waiting included).
*/
/*
* Copy the standard files to our standard handles.
*/
}
}
}
}
return rcExit;
}
/**
* Checks if the process is elevated or not.
*
* @returns RTEXITCODE_SUCCESS if preconditions are fine,
* otherwise error message + RTEXITCODE_FAILURE.
* @param pfElevated Where to store the elevation indicator.
*/
{
*pfElevated = false;
# if defined(RT_OS_WINDOWS)
/** @todo This should probably check if UAC is diabled and if we are
* Administrator first. Also needs to check for Vista+ first, probably.
*/
return RTMsgErrorExit(RTEXITCODE_FAILURE, "OpenProcessToken failed: %u (%#x)", GetLastError(), GetLastError());
/*
* Check if we're member of the Administrators group. If we aren't, there
* is no way to elevate ourselves to system admin.
* N.B. CheckTokenMembership does not do the job here (due to attributes?).
*/
if (AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminGrpSid))
{
# ifdef DEBUG
char *pszAdminGrpSid = NULL;
# endif
&& GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
{
{
# ifdef DEBUG
# endif
{
/* That it's listed is enough I think, ignore attributes. */
break;
}
}
}
else
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "GetTokenInformation(TokenGroups,cb) failed: %u (%#x)", GetLastError(), GetLastError());
}
else
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "GetTokenInformation(TokenGroups,0) failed: %u (%#x)", GetLastError(), GetLastError());
}
else
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "AllocateAndInitializeSid failed: %u (%#x)", GetLastError(), GetLastError());
if (fIsAdmin)
{
/*
* Check the integrity level (Vista / UAC).
*/
# define MY_SECURITY_MANDATORY_HIGH_RID 0x00003000L
&& GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
{
DWORD dwIntegrityLevel = *GetSidSubAuthority(pSidAndAttr->Sid, *GetSidSubAuthorityCount(pSidAndAttr->Sid) - 1U);
*pfElevated = true;
}
else
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "GetTokenInformation failed: %u (%#x)", GetLastError(), GetLastError());
}
else if ( GetLastError() == ERROR_INVALID_PARAMETER
|| GetLastError() == ERROR_NOT_SUPPORTED)
*pfElevated = true; /* Older Windows version. */
else
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "GetTokenInformation failed: %u (%#x)", GetLastError(), GetLastError());
}
else
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Membership in the Administrators group is required to perform this action");
return rcExit;
# else
/*
* On Unixy systems, we check if the executable and the current user is
* the same. This heuristic works fine for both hardened and development
* builds.
*/
char szExecPath[RTPATH_MAX];
if (RT_FAILURE(rc))
return RTEXITCODE_SUCCESS;
# endif
}
#endif /* WITH_ELEVATION */
{
/*
* Initialize IPRT and check that we're correctly installed.
*/
#ifdef RT_OS_WINDOWS
int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_UTF8_ARGV); /* WinMain gives us UTF-8, see below. */
#else
#endif
if (RT_FAILURE(rc))
return RTMsgInitFailure(rc);
if (RT_FAILURE(rc))
/*
* Elevation check.
*/
const char *pszDisplayInfoHack = NULL;
#ifdef WITH_ELEVATION
bool fElevated;
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
#endif
/*
* Parse the top level arguments until we find a command.
*/
static const RTGETOPTDEF s_aOptions[] =
{
#ifdef WITH_ELEVATION
#endif
};
if (RT_FAILURE(rc))
for (;;)
{
switch (ch)
{
case 0:
case CMD_INSTALL:
case CMD_UNINSTALL:
case CMD_CLEANUP:
{
#ifdef WITH_ELEVATION
if (!fElevated)
#endif
switch (ch)
{
case CMD_INSTALL:
break;
case CMD_UNINSTALL:
break;
case CMD_CLEANUP:
break;
default:
}
/*
* Standard error should end with rcExit=RTEXITCODE_SUCCESS on
* success since the exit code may otherwise get lost in the
* process elevation fun.
*/
switch (rcExit)
{
case RTEXITCODE_SUCCESS:
break;
default:
break;
}
return rcExit;
}
#ifdef WITH_ELEVATION
case OPT_ELEVATED:
fElevated = true;
break;
case OPT_STDERR:
case OPT_STDOUT:
{
if (!pFile)
{
}
break;
}
#endif
case OPT_DISP_INFO_HACK:
if (pszDisplayInfoHack)
break;
case 'h':
case 'V':
return DoStandardOption(ch);
default:
}
/* not currently reached */
}
/* not reached */
}
#ifdef RT_OS_WINDOWS
extern "C" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
int rc = RTR3InitExeNoArguments(0);
if (RT_FAILURE(rc))
return RTMsgInitFailure(rc);
if (!pwszCmdLine)
char *pszCmdLine;
if (RT_FAILURE(rc))
int cArgs;
char **papszArgs;
if (RT_SUCCESS(rc))
{
}
else
return rc;
}
#endif