SUPR3HardenedMain.cpp revision 1c2c968fd241148110002d75b2c0fdeddc211e14
/* $Id$ */
/** @file
* VirtualBox Support Library - Hardened main().
*/
/*
* Copyright (C) 2006-2008 Sun Microsystems, Inc.
*
* 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.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#if defined(RT_OS_OS2)
# define INCL_BASE
# define INCL_ERRORS
# include <os2.h>
# include <stdio.h>
# include <Windows.h>
# include <stdio.h>
#else /* UNIXes */
# include <stdio.h>
# include <stdlib.h>
# include <dlfcn.h>
# include <limits.h>
# include <errno.h>
# include <unistd.h>
# include <stdio.h>
# include <pwd.h>
# ifdef RT_OS_DARWIN
# endif
#endif
#include "SUPLibInternal.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** @def SUP_HARDENED_SUID
* Whether we're employing set-user-ID-on-execute in the hardening.
*/
# define SUP_HARDENED_SUID
#else
#endif
/** @def SUP_HARDENED_SYM
* Decorate a symbol that's resolved dynamically.
*/
#ifdef RT_OS_OS2
#else
#endif
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
typedef FNTRUSTEDMAIN *PFNTRUSTEDMAIN;
typedef FNRTR3INIT *PFNRTR3INIT;
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** The pre-init data we pass on to SUPR3 (residing in VBoxRT). */
static SUPPREINITDATA g_SupPreInitData;
/** The program path. */
static char g_szSupLibHardenedProgramPath[RTPATH_MAX];
/** The program name. */
static const char *g_szSupLibHardenedProgName;
/**
* @copydoc RTPathStripFilename.
*/
static void suplibHardenedPathStripFilename(char *pszPath)
{
char *pszLastSep = pszPath;
for (;; psz++)
{
switch (*psz)
{
/* handle separators. */
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
case ':':
break;
case '\\':
#endif
case '/':
pszLastSep = psz;
break;
/* the end */
case '\0':
if (pszLastSep == pszPath)
*pszLastSep++ = '.';
*pszLastSep = '\0';
return;
}
}
/* will never get here */
}
/**
* @copydoc RTPathFilename
*/
{
const char *pszLastComp = pszPath;
for (;; psz++)
{
switch (*psz)
{
/* handle separators. */
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
case ':':
break;
case '\\':
#endif
case '/':
break;
/* the end */
case '\0':
if (*pszLastComp)
return (char *)(void *)pszLastComp;
return NULL;
}
}
/* will never get here */
return NULL;
}
/**
* @copydoc RTPathAppPrivateNoArch
*/
{
#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE)
const char *pszSrcPath = RTPATH_APP_PRIVATE;
if (cchPathPrivateNoArch >= cchPath)
supR3HardenedFatal("supR3HardenedPathAppPrivateNoArch: Buffer overflow, %lu >= %lu\n",
(unsigned long)cchPathPrivateNoArch, (unsigned long)cchPath);
return VINF_SUCCESS;
#else
#endif
}
/**
* @copydoc RTPathAppPrivateArch
*/
{
#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE_ARCH)
const char *pszSrcPath = RTPATH_APP_PRIVATE_ARCH;
if (cchPathPrivateArch >= cchPath)
supR3HardenedFatal("supR3HardenedPathAppPrivateArch: Buffer overflow, %lu >= %lu\n",
(unsigned long)cchPathPrivateArch, (unsigned long)cchPath);
return VINF_SUCCESS;
#else
#endif
}
/**
* @copydoc RTPathSharedLibs
*/
{
#if !defined(RT_OS_WINDOWS) && defined(RTPATH_SHARED_LIBS)
const char *pszSrcPath = RTPATH_SHARED_LIBS;
if (cchPathSharedLibs >= cchPath)
supR3HardenedFatal("supR3HardenedPathSharedLibs: Buffer overflow, %lu >= %lu\n",
(unsigned long)cchPathSharedLibs, (unsigned long)cchPath);
return VINF_SUCCESS;
#else
#endif
}
/**
* @copydoc RTPathAppDocs
*/
{
#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_DOCS)
const char *pszSrcPath = RTPATH_APP_DOCS;
if (cchPathAppDocs >= cchPath)
supR3HardenedFatal("supR3HardenedPathAppDocs: Buffer overflow, %lu >= %lu\n",
(unsigned long)cchPathAppDocs, (unsigned long)cchPath);
return VINF_SUCCESS;
#else
#endif
}
/**
* @copydoc RTPathProgram
*/
{
/*
* First time only.
*/
if (!g_szSupLibHardenedProgramPath[0])
{
/*
* Get the program filename.
*
* Most UNIXes have no API for obtaining the executable path, but provides a symbolic
* link in the proc file system that tells who was exec'ed. The bad thing about this
* is that we have to use readlink, one of the weirder UNIX APIs.
*
* Darwin, OS/2 and Windows all have proper APIs for getting the program file name.
*/
# ifdef RT_OS_LINUX
int cchLink = readlink("/proc/self/exe", &g_szSupLibHardenedProgramPath[0], sizeof(g_szSupLibHardenedProgramPath) - 1);
# elif defined(RT_OS_SOLARIS)
int cchLink = readlink(szFileBuf, &g_szSupLibHardenedProgramPath[0], sizeof(g_szSupLibHardenedProgramPath) - 1);
# else /* RT_OS_FREEBSD: */
int cchLink = readlink("/proc/curproc/file", &g_szSupLibHardenedProgramPath[0], sizeof(g_szSupLibHardenedProgramPath) - 1);
# endif
supR3HardenedFatal("supR3HardenedPathProgram: couldn't read \"%s\", errno=%d cchLink=%d\n",
#elif defined(RT_OS_DARWIN)
const char *pszImageName = _dyld_get_image_name(0);
if (!pszImageName)
supR3HardenedFatal("supR3HardenedPathProgram: _dyld_get_image_name(0) failed\n");
supR3HardenedFatal("supR3HardenedPathProgram: _dyld_get_image_name(0) failed, cchImageName=%d\n", cchImageName);
#elif defined(RT_OS_WINDOWS)
if (!GetModuleFileName(hExe, &g_szSupLibHardenedProgramPath[0], sizeof(g_szSupLibHardenedProgramPath)))
#else
#endif
/*
* Strip off the filename part (RTPathStripFilename()).
*/
}
/*
* Calc the length and check if there is space before copying.
*/
{
return VINF_SUCCESS;
}
return VERR_BUFFER_OVERFLOW;
}
{
for (;;)
#ifdef _MSC_VER
exit(1);
#else
_Exit(1);
#endif
}
{
}
{
if (fFatal)
return rc;
}
{
return rc;
}
/**
* Wrapper around snprintf which will throw a fatal error on buffer overflow.
*
* @returns Number of chars in the result string.
* @param pszDst The destination buffer.
* @param cchDst The size of the buffer.
* @param pszFormat The format string.
* @param ... Format arguments.
*/
{
#ifdef _MSC_VER
#else
#endif
return cch;
}
/**
*
* @remarks This function will not return on failure.
*/
static void supR3HardenedMainOpenDevice(void)
{
if (RT_SUCCESS(rc))
return;
switch (rc)
{
supR3HardenedFatal("supR3HardenedMainOpenDevice: VERR_VM_DRIVER_NOT_INSTALLED\n");
supR3HardenedFatal("supR3HardenedMainOpenDevice: VERR_VM_DRIVER_NOT_ACCESSIBLE\n");
supR3HardenedFatal("supR3HardenedMainOpenDevice: VERR_VM_DRIVER_LOAD_ERROR\n");
supR3HardenedFatal("supR3HardenedMainOpenDevice: VERR_VM_DRIVER_OPEN_ERROR\n");
supR3HardenedFatal("supR3HardenedMainOpenDevice: VERR_VM_DRIVER_VERSION_MISMATCH\n");
case VERR_ACCESS_DENIED:
supR3HardenedFatal("supR3HardenedMainOpenDevice: VERR_ACCESS_DENIED\n");
default:
}
}
/**
* and calls RTR3Init.
*
* @param fFlags The SUPR3HardenedMain fFlags argument, passed to supR3PreInit.
*
* @remarks VBoxRT contains both IPRT and SUPR3.
* @remarks This function will not return on failure.
*/
{
/*
* Construct the name.
*/
char szPath[RTPATH_MAX];
/*
* Open it and resolve the symbols.
*/
#if defined(RT_OS_WINDOWS)
/** @todo consider using LOAD_WITH_ALTERED_SEARCH_PATH here! */
if (!hMod)
supR3HardenedFatal("supR3HardenedMainGetTrustedMain: LoadLibraryEx(\"%s\",,) failed, rc=%d\n",
szPath, GetLastError());
if (!pfnRTInit)
supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"RTR3Init\" not found in \"%s\" (rc=%d)\n",
szPath, GetLastError());
PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)GetProcAddress(hMod, SUP_HARDENED_SYM("supR3PreInit"));
if (!pfnSUPPreInit)
supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"supR3PreInit\" not found in \"%s\" (rc=%d)\n",
szPath, GetLastError());
#else
/* the dlopen crowd */
if (!pvMod)
supR3HardenedFatal("supR3HardenedMainGetTrustedMain: dlopen(\"%s\",) failed: %s\n",
if (!pfnRTInit)
supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"RTR3Init\" not found in \"%s\"!\ndlerror: %s\n",
PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)(uintptr_t)dlsym(pvMod, SUP_HARDENED_SYM("supR3PreInit"));
if (!pfnSUPPreInit)
supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"supR3PreInit\" not found in \"%s\"!\ndlerror: %s\n",
#endif
/*
* Make the calls.
*/
if (RT_FAILURE(rc))
if (RT_FAILURE(rc))
}
/**
* resolves the TrustedMain symbol.
*
* @returns Pointer to the trusted main of the actual program.
* @param pszProgName The program name.
* @remarks This function will not return on failure.
*/
{
/*
* Construct the name.
*/
char szPath[RTPATH_MAX];
/*
* Open it and resolve the symbol.
*/
#if defined(RT_OS_WINDOWS)
/** @todo consider using LOAD_WITH_ALTERED_SEARCH_PATH here! */
if (!hMod)
supR3HardenedFatal("supR3HardenedMainGetTrustedMain: LoadLibraryEx(\"%s\",,) failed, rc=%d\n",
szPath, GetLastError());
if (!pfn)
supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\" (rc=%d)\n",
szPath, GetLastError());
return (PFNTRUSTEDMAIN)pfn;
#else
/* the dlopen crowd */
if (!pvMod)
supR3HardenedFatal("supR3HardenedMainGetTrustedMain: dlopen(\"%s\",) failed: %s\n",
if (!pvSym)
supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\"!\ndlerror: %s\n",
#endif
}
/**
* Secure main.
*
* This is used for the set-user-ID-on-execute binaries on unixy systems
* and when using the open-vboxdrv-via-root-service setup on Windows.
*
* This function will perform the integrity checks of the VirtualBox
* installation, open the support driver, open the root service (later),
* and load the DLL corresponding to \a pszProgName and execute its main
* function.
*
* @returns Return code appropriate for main().
*
* @param pszProgName The program name. This will be used to figure out which
* @param fFlags Flags.
* @param argc The argument count.
* @param argv The argument vector.
* @param envp The environment vector.
*/
DECLHIDDEN(int) SUPR3HardenedMain(const char *pszProgName, uint32_t fFlags, int argc, char **argv, char **envp)
{
/*
* Note! At this point there is no IPRT, so we will have to stick
* to basic CRT functions that everyone agree upon.
*/
#ifdef SUP_HARDENED_SUID
/*
* Check that we're root, if we aren't then the installation is butchered.
*/
if (geteuid() != 0 /* root */)
supR3HardenedFatal("SUPR3HardenedMain: effective uid is not root (euid=%d egid=%d uid=%d gid=%d)\n",
#endif
/*
* Validate the installation.
*/
/*
* Open the vboxdrv device.
*/
if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
/*
* Open the root service connection.
*/
//if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_SVC))
//supR3HardenedMainOpenService(&g_SupPreInitData, true /* fFatal */);
#ifdef SUP_HARDENED_SUID
/*
* Drop any root privileges we might be holding.
*/
supR3HardenedFatal("SUPR3HardenedMain: failed to drop root privileges! (euid=%d egid=%d; wanted %d and %d)\n",
#endif
/*
* Load the IPRT, hand the SUPLib part the open driver and
* call RTR3Init.
*/
/*
* and pass control to it.
*/
}