VBoxExtPackHelperApp.cpp revision 230bd8589bba39933ac5ec21482d6186d675e604
/* $Id$ */
/** @file
* VirtualBox Main - Extension Pack Helper Application, usually set-uid-to-root.
*/
/*
* Copyright (C) 2010-2011 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 (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to rename the extension pack directory: %Rrc", 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 */
#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 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 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);
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;
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 'h':
case 'V':
return DoStandardOption(ch);
default:
}
}
if (!pszName)
if (!pszBaseDir)
if (!pszCertDir)
if (!pszTarball)
/*
* Ok, down to business.
*/
if (!pstrMangledName)
if (RT_SUCCESS(rc))
{
}
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.
*/
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