VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp@ 58363

Last change on this file since 58363 was 58363, checked in by vboxsync, 9 years ago

SUPR3HardenedMain.cpp: Started on a @page about the code, still a lot of work left on windows.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 80.1 KB
Line 
1/* $Id: SUPR3HardenedMain.cpp 58363 2015-10-22 00:23:07Z vboxsync $ */
2/** @file
3 * VirtualBox Support Library - Hardened main().
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27/** @page pg_hardening VirtualBox VM Process Hardening
28 *
29 * The VM process hardening is to prevent malicious software from using
30 * VirtualBox as a vehicle to obtain kernel level access.
31 *
32 * The VirtualBox VMM requires supervisor (kernel) level access to the CPU. For
33 * both practical and historical reasons part of the VMM is still implemented in
34 * ring-3 and has a rich interface to the kernel part. While the device
35 * emulations can be run all in ring-3, we have performance optimizations that
36 * loads device emulation code into ring-0 and our special raw-mode execution
37 * context (non-VT-x/AMD-V mode) for handling frequent operations. These share
38 * data between all three context (ring-3, ring-0 and raw-mode). All this poses
39 * a rather broad attack surface, which the hardening protects.
40 *
41 * The hardening primarily focuses on restricting access to the support driver,
42 * VBoxDrv or vboxdrv depending on the OS, as it is ultimately the link and
43 * instigator of the communication between ring-3 and the ring-0 and raw-mode
44 * contexts. A secondary focus is to make sure malicious code cannot be loaded
45 * and executed in the VM process. Exactly how we go about this depends a lot
46 * on the host OS.
47 *
48 *
49 * @section sec_hardening_unix Hardening on UNIX-like OSes
50 *
51 * On UNIX-like systems (Solaris, Linux, darwin, freebsd, ...) only allow root
52 * to get full unrestricted access to the support driver. The device node
53 * corresponding to unrestricted access is own by root and has a 0600 access
54 * mode (i.e. only accessible to the owner, root). In addition to this file
55 * system level restriction, the support driver also checks that the effective
56 * user ID (EUID) is root when it is being opened.
57 *
58 * The VM processes temporarily assume root privileges using the set-uid-bit on
59 * the executable with root as owner. In fact, all the executable files, shared
60 * objects and the other files and directories we install are owned by root and
61 * the wheel (or equivalent gid = 0) group.
62 *
63 * The executable with the set-uid-to-root-bit set is a stub binary that has no
64 * unnecessary library dependencies (only libc) and simply calls
65 * #SUPR3HardenedMain. SUPR3HardenedMain does the following:
66 *
67 * -# Validate installation (supR3HardenedVerifyAll):
68 * - Check that the executable file of the process is one of the known
69 * VirtualBox executables.
70 * - Check that all mandatory files are present.
71 * - Check that all installed files and directories (both optional and
72 * mandatory ones) are owned by root:wheel and are not writable by
73 * anyone except root.
74 * - Check that all the parent directories, all the way up to the root
75 * if possible, only permits root (or system admin) to change them -
76 * in order to exclude directory renaming races.
77 * - On systems where it is possible, we may also valiadate signatures.
78 *
79 * -# Open a file descriptor for the support device driver
80 * (supR3HardenedMainOpenDevice).
81 *
82 * -# Grab ICMP capabilites, if needed (supR3HardenedMainGrabCapabilites).
83 *
84 * -# Correctly drop the root privileges (supR3HardenedMainDropPrivileges).
85 *
86 * -# Load the VBoxRT dynamic link library and hand over the file
87 * descriptor to the SUPLib code in it (supR3HardenedMainInitRuntime).
88 *
89 * -# Load a dynamic library containing the VM frontend code and run it
90 * (tail of SUPR3HardenedMain). The set-uid-to-root stub executable is
91 * paired with a dynamic link library which export one TrustedMain
92 * entrypoint (see FNSUPTRUSTEDMAIN) that we call.
93 *
94 * In case of error reporting, the library may also export a
95 * TrustedError function (FNSUPTRUSTEDERROR).
96 *
97 * That a process was started with a set-uid-to-root-bit applied is something
98 * that sticks with the process even if after dropping the root privileges and
99 * becoming the original user. The dynamic linkers take special care when
100 * dealing with processes of this kind to not allow the user to load arbitrary
101 * code into a root process and causing a privilege escalation issue. This is
102 * of course exactly the kind of behavior we're looking for.
103 *
104 * In addition to what the dynamic linker does for us, we will not directly
105 * call either RTLdrLoad or dlopen to load dynamic link libraries into the
106 * process. Instead we will call SUPR3HardenedLdrLoad,
107 * SUPR3HardenedLdrLoadAppPriv or SUPR3HardenedLdrLoadPlugIn to do the loading.
108 * These functions will perform the validations on the file being loaded as
109 * SUPR3HardenedMain did in its validation step. So, anything we load must be
110 * installed owned by root:wheel, the directory we load it from must also be
111 * owned by root:wheel and now allow for renaming the file. Similar ownership
112 * restricts applies to all the parent directories (except on darwin).
113 *
114 * So, we leave the responsibility of not installing malicious software on the
115 * root user on UNIX-like systems. Which is fair enough, in our opinion.
116 *
117 *
118 * @section sec_hardening_win Hardening on Windows
119 *
120 * On Windows things are a lot more complicated, unforunately. This is mainly
121 * because on windows you cannot trust the Administrators users. Some or the
122 * blame for this is that Windows is a decentant/replacement for a set of single
123 * user systems: DOS, Windows 1.0-3.11 Windows 95-ME, and OS/2. Users of NT
124 * 3.51 and later was inclied to want to always run it with full
125 * root/administrator privileges like they had done on the predecessors, while
126 * Microsoft made doing some very simple and didn't help with the alternative.
127 * Bad idea, security wise, which is good for the security software industry.
128 * For this reason using a set-uid-to-root approach is pointless, even if
129 * windows had one, which is doesn't.
130 *
131 * So, in order to protect access to the support driver and protect the
132 * VM process while it's running we have to do a lot more work. A keystone in
133 * the defences is code signing. The short version is this:
134 *
135 * - Minimal stub executable, signed with the same certificate as the
136 * kernel driver.
137 *
138 * - The stub executable respawns itself twice, hooking the NTDLL init
139 * routine to perform protection tasks as early as possible. The parent
140 * stub helps keep in the child clean for verification as does the
141 * support driver.
142 *
143 * - In order to protect against loading unwanted code into the process,
144 * the stub processes installs DLL load hooks with NTDLL as well as
145 * directly intercepting the LdrLoadDll and NtCreateSection APIs.
146 *
147 * - The support driver will verify all but the initial process very
148 * thoroughly before allowing them protection and in the final case full
149 * unrestricted access.
150 *
151 * What makes our life REALLY difficult on Windows is this 3rd party "security"
152 * software which is more or less required to keep a Windows system safe for
153 * normal users and all corporate IT departments righly insists on installing.
154 * After the kernel patching clampdown in Vista, AV software have to do a lot
155 * more mucking about in user mode to get their job (kind of) done. So, it
156 * common practice to patch a lot of NTDLL, KERNEL32, the executable import
157 * table, load extra DLLS into the process, allocate executable memory in the
158 * process and worse. The BIG problem with all this is that it is
159 * indistiguishable from what malicious software would be doing in order to
160 * intercept process acctivity (network sniffing, maybe password snooping) or
161 * gain a level of kernel access via the the support driver.
162 *
163 * We share the stub executable approach with the UNIX-like systems, so there's
164 * the SUPR3HardenedMain and a paried DLL with TrustedMain and TrustedError.
165 * However, the stub executable is pushed a bit further here.
166 * - It has no CRT (libc) because we don't need one and we need full
167 * control over the code in the stub.
168 * - It does not statically import anything to avoid having a import table
169 * that can be patched or extended to either intercept our calls or load
170 * additional DLLs.
171 * - System calls normally going thru NTDLL are done directly because there
172 * is so much software out there which wants to patch known NTDLL entry
173 * points to control our software (either for good or malicious reasons).
174 *
175 * The initial stub process is not really to be trusted, though we try our best
176 * to limit potential harm (user mode debugger checks, disable thread creation).
177 * So, when it enters SUPR3HardenedMain we only call supR3HardenedVerifyAll to
178 * verify the installation (known executables and dlls, checking their code
179 * signing signatures, keeping them all open to deny deletion and replacing) and
180 * does a respawn via supR3HardenedWinReSpawn.
181 *
182 * The second stub process will be created in suspended state (the thread hasn't
183 * executed a single instruction) and with less generous ACLs associated with it
184 * (skin deep protection only). In order for SUPR3TrustedMain to figure it's
185 * the second stub process, the zero'th command line argument has been replaced
186 * by a known magic string (UUID). Now, before the process starts executing,
187 * the parent will patch the LdrInitializeThunk entrypoint in NTDLL to call
188 * supR3HardenedEarlyProcessInit via supR3HardenedEarlyProcessInitThunk. The
189 * parent will also plant some synchronization stuff via SUPR3WINPROCPARAMS
190 * (NTDLL location, inherited event handles and associated ping-pong equipment).
191 *
192 * The LdrInitializeThunk entrypoint of NTDLL is where the kernel sets up
193 * process execution to start executing (via a user alert, so not subject to
194 * SetThreadContext). LdrInitializeThunk performs process, NTDLL and
195 * sub-system client (kernel32) initialization. A lot of "protection" software
196 * uses triggers in this initialization sequence (like the KERNEL32.DLL load
197 * event), so we avoid quite a bit problems by getting our stuff done early on.
198 *
199 * However, there is also those that uses events that triggers immediately when
200 * the process is created or/and starts executing the first instruction, we have
201 * a well know process state we can restore. The first thing that
202 * supR3HardenedEarlyProcessInit does is to signal the parent to do perform a
203 * child purification to exorcise potentially evil influences.
204 *
205 * What the parent does during the purification is very similar to what the
206 * kernel driver will do later on when verifying the second stub and the VM
207 * processes, except that instead of failing when encountering an issue it will
208 * take corrective actions:
209 * - Executable memory regions not belonging to a DLL mapping will be
210 * attempted freed, and we'll only fail if we cann evict it.
211 * - All pages in the executable images in the process (should be just the
212 * stub executable and NTDLL) will be compared to the pristine fixed-up
213 * copy prepared by the IPRT PE loader code, restoring any bytes which
214 * appears differently in the child. (g_ProcParams (SUPR3WINPROCPARAMS)
215 * is exempted, LdrInitializeThunk is set to call NtTerminateThread.)
216 * - Unwanted DLLs will be unloaded (we have a set of DLLs we like).
217 *
218 * Before signalling the second stub process that it has been purified and shoud
219 * get on with it, the parent will close all handles with unrestricted access to
220 * the process and thread so that the initial stub process no longer can
221 * influence the child in any really harmful way. (The caller of CreateProcess
222 * usually receives handles with unrestricted access to the child process and
223 * main thread. These could in theory be used with DuplicateHandle or
224 * WriteProcessMemory to get at the VM process if we're not careful.)
225 *
226 * supR3HardenedEarlyProcessInit will continue with opening the log file
227 * (require command line parsing). It will continue to initialize a bunch of
228 * globals, syscalls and trustworthy/harmless NTDLL imports.
229 * supR3HardenedWinInit is then called to setup image verification, that is:
230 * - Hook (insert jump instruction) the NtCreateSection entrypoint in NTDLL
231 * so we can check all executable mappings before they're created and can
232 * be mapped.
233 * - Hook (ditto) the LdrLoadDll entrypoint in NTDLL so we can prevalidate
234 * all images that gets loaded the normal way (partly because the
235 * NtCreateSection context is restrictive because the NTDLL loader lock
236 * is usally held, which prevents us from safely calling WinVerityTrust).
237 * The image/dll verification hooks are at this point able to verify DLLs
238 * containing code signing signatures, and will restrict the locations from
239 * which DLLs will be loaded. When SUPR3HardenedMain gets going later one, they
240 * will start insiting on everything having valid signatures in the DLL or in an
241 * installer catalog file.
242 *
243 * The function also irrevocably disables debug notifications related to teh
244 * current thread, just to make attaching a debugging that much more difficult.
245 *
246 * Now, the second stub process will open the so called stub device, that is a
247 * special support driver device node that tells the support driver to:
248 * - Protect the process against the OpenProcess and OpenThread attack
249 * vectors by stripping risky access rights.
250 * - Check that the process isn't being debugged.
251 * - Check that the process contains exactly one thread.
252 * - Check that the process doesn't have any unknown DLLs loaded into it.
253 * - Check that the process doesn't have any executable memory (other that
254 * DLL sections) in it.
255 * - Check that the process executable is a known VBox executable which may
256 * access the support driver.
257 * - Check that the process executable is signed with the same code signing
258 * certificate as the driver and that the on disk image is valid
259 * according to its embedded signature.
260 * - Check all the signature of all DLLs in the process (NTDLL) if they are
261 * signed, and only accept unsigned ones in versions where they are known
262 * not to be signed.
263 *
264 *
265 *
266 */
267
268
269/*********************************************************************************************************************************
270* Header Files *
271*********************************************************************************************************************************/
272#if defined(RT_OS_OS2)
273# define INCL_BASE
274# define INCL_ERRORS
275# include <os2.h>
276# include <stdio.h>
277# include <stdlib.h>
278# include <dlfcn.h>
279# include <unistd.h>
280
281#elif RT_OS_WINDOWS
282# include <iprt/nt/nt-and-windows.h>
283
284#else /* UNIXes */
285# include <iprt/types.h> /* stdint fun on darwin. */
286
287# include <stdio.h>
288# include <stdlib.h>
289# include <dlfcn.h>
290# include <limits.h>
291# include <errno.h>
292# include <unistd.h>
293# include <sys/stat.h>
294# include <sys/time.h>
295# include <sys/types.h>
296# if defined(RT_OS_LINUX)
297# undef USE_LIB_PCAP /* don't depend on libcap as we had to depend on either
298 libcap1 or libcap2 */
299
300# undef _POSIX_SOURCE
301# include <linux/types.h> /* sys/capabilities from uek-headers require this */
302# include <sys/capability.h>
303# include <sys/prctl.h>
304# ifndef CAP_TO_MASK
305# define CAP_TO_MASK(cap) RT_BIT(cap)
306# endif
307# elif defined(RT_OS_FREEBSD)
308# include <sys/param.h>
309# include <sys/sysctl.h>
310# elif defined(RT_OS_SOLARIS)
311# include <priv.h>
312# endif
313# include <pwd.h>
314# ifdef RT_OS_DARWIN
315# include <mach-o/dyld.h>
316# endif
317
318#endif
319
320#include <VBox/sup.h>
321#include <VBox/err.h>
322#ifdef RT_OS_WINDOWS
323# include <VBox/version.h>
324#endif
325#include <iprt/ctype.h>
326#include <iprt/string.h>
327#include <iprt/initterm.h>
328#include <iprt/param.h>
329
330#include "SUPLibInternal.h"
331
332
333/*********************************************************************************************************************************
334* Defined Constants And Macros *
335*********************************************************************************************************************************/
336/** @def SUP_HARDENED_SUID
337 * Whether we're employing set-user-ID-on-execute in the hardening.
338 */
339#if !defined(RT_OS_OS2) && !defined(RT_OS_WINDOWS) && !defined(RT_OS_L4)
340# define SUP_HARDENED_SUID
341#else
342# undef SUP_HARDENED_SUID
343#endif
344
345/** @def SUP_HARDENED_SYM
346 * Decorate a symbol that's resolved dynamically.
347 */
348#ifdef RT_OS_OS2
349# define SUP_HARDENED_SYM(sym) "_" sym
350#else
351# define SUP_HARDENED_SYM(sym) sym
352#endif
353
354
355/*********************************************************************************************************************************
356* Structures and Typedefs *
357*********************************************************************************************************************************/
358/** @see RTR3InitEx */
359typedef DECLCALLBACK(int) FNRTR3INITEX(uint32_t iVersion, uint32_t fFlags, int cArgs,
360 char **papszArgs, const char *pszProgramPath);
361typedef FNRTR3INITEX *PFNRTR3INITEX;
362
363/** @see RTLogRelPrintf */
364typedef DECLCALLBACK(void) FNRTLOGRELPRINTF(const char *pszFormat, ...);
365typedef FNRTLOGRELPRINTF *PFNRTLOGRELPRINTF;
366
367
368/*********************************************************************************************************************************
369* Global Variables *
370*********************************************************************************************************************************/
371/** The pre-init data we pass on to SUPR3 (residing in VBoxRT). */
372static SUPPREINITDATA g_SupPreInitData;
373/** The program executable path. */
374#ifndef RT_OS_WINDOWS
375static
376#endif
377char g_szSupLibHardenedExePath[RTPATH_MAX];
378/** The application bin directory path. */
379static char g_szSupLibHardenedAppBinPath[RTPATH_MAX];
380
381/** The program name. */
382static const char *g_pszSupLibHardenedProgName;
383/** The flags passed to SUPR3HardenedMain. */
384static uint32_t g_fSupHardenedMain;
385
386#ifdef SUP_HARDENED_SUID
387/** The real UID at startup. */
388static uid_t g_uid;
389/** The real GID at startup. */
390static gid_t g_gid;
391# ifdef RT_OS_LINUX
392static uint32_t g_uCaps;
393# endif
394#endif
395
396/** The startup log file. */
397#ifdef RT_OS_WINDOWS
398static HANDLE g_hStartupLog = NULL;
399#else
400static int g_hStartupLog = -1;
401#endif
402/** The number of bytes we've written to the startup log. */
403static uint32_t volatile g_cbStartupLog = 0;
404
405/** The current SUPR3HardenedMain state / location. */
406SUPR3HARDENEDMAINSTATE g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED;
407AssertCompileSize(g_enmSupR3HardenedMainState, sizeof(uint32_t));
408
409#ifdef RT_OS_WINDOWS
410/** Pointer to VBoxRT's RTLogRelPrintf function so we can write errors to the
411 * release log at runtime. */
412static PFNRTLOGRELPRINTF g_pfnRTLogRelPrintf = NULL;
413/** Log volume name (for attempting volume flush). */
414static RTUTF16 g_wszStartupLogVol[16];
415#endif
416
417
418/*********************************************************************************************************************************
419* Internal Functions *
420*********************************************************************************************************************************/
421#ifdef SUP_HARDENED_SUID
422static void supR3HardenedMainDropPrivileges(void);
423#endif
424static PFNSUPTRUSTEDERROR supR3HardenedMainGetTrustedError(const char *pszProgName);
425
426
427/**
428 * Safely copy one or more strings into the given buffer.
429 *
430 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
431 * @param pszDst The destionation buffer.
432 * @param cbDst The size of the destination buffer.
433 * @param ... One or more zero terminated strings, ending with
434 * a NULL.
435 */
436static int suplibHardenedStrCopyEx(char *pszDst, size_t cbDst, ...)
437{
438 int rc = VINF_SUCCESS;
439
440 if (cbDst == 0)
441 return VERR_BUFFER_OVERFLOW;
442
443 va_list va;
444 va_start(va, cbDst);
445 for (;;)
446 {
447 const char *pszSrc = va_arg(va, const char *);
448 if (!pszSrc)
449 break;
450
451 size_t cchSrc = suplibHardenedStrLen(pszSrc);
452 if (cchSrc < cbDst)
453 {
454 suplibHardenedMemCopy(pszDst, pszSrc, cchSrc);
455 pszDst += cchSrc;
456 cbDst -= cchSrc;
457 }
458 else
459 {
460 rc = VERR_BUFFER_OVERFLOW;
461 if (cbDst > 1)
462 {
463 suplibHardenedMemCopy(pszDst, pszSrc, cbDst - 1);
464 pszDst += cbDst - 1;
465 cbDst = 1;
466 }
467 }
468 *pszDst = '\0';
469 }
470 va_end(va);
471
472 return rc;
473}
474
475
476/**
477 * Exit current process in the quickest possible fashion.
478 *
479 * @param rcExit The exit code.
480 */
481DECLNORETURN(void) suplibHardenedExit(RTEXITCODE rcExit)
482{
483 for (;;)
484 {
485#ifdef RT_OS_WINDOWS
486 if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
487 ExitProcess(rcExit);
488 if (RtlExitUserProcess != NULL)
489 RtlExitUserProcess(rcExit);
490 NtTerminateProcess(NtCurrentProcess(), rcExit);
491#else
492 _Exit(rcExit);
493#endif
494 }
495}
496
497
498/**
499 * Writes a substring to standard error.
500 *
501 * @param pch The start of the substring.
502 * @param cch The length of the substring.
503 */
504static void suplibHardenedPrintStrN(const char *pch, size_t cch)
505{
506#ifdef RT_OS_WINDOWS
507 HANDLE hStdOut = NtCurrentPeb()->ProcessParameters->StandardOutput;
508 if (hStdOut != NULL)
509 {
510 if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
511 {
512 DWORD cbWritten;
513 WriteFile(hStdOut, pch, (DWORD)cch, &cbWritten, NULL);
514 }
515 /* Windows 7 and earlier uses fake handles, with the last two bits set ((hStdOut & 3) == 3). */
516 else if (NtWriteFile != NULL && ((uintptr_t)hStdOut & 3) == 0)
517 {
518 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
519 NtWriteFile(hStdOut, NULL /*Event*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/,
520 &Ios, (PVOID)pch, (ULONG)cch, NULL /*ByteOffset*/, NULL /*Key*/);
521 }
522 }
523#else
524 (void)write(2, pch, cch);
525#endif
526}
527
528
529/**
530 * Writes a string to standard error.
531 *
532 * @param psz The string.
533 */
534static void suplibHardenedPrintStr(const char *psz)
535{
536 suplibHardenedPrintStrN(psz, suplibHardenedStrLen(psz));
537}
538
539
540/**
541 * Writes a char to standard error.
542 *
543 * @param ch The character value to write.
544 */
545static void suplibHardenedPrintChr(char ch)
546{
547 suplibHardenedPrintStrN(&ch, 1);
548}
549
550
551/**
552 * Writes a decimal number to stdard error.
553 *
554 * @param uValue The value.
555 */
556static void suplibHardenedPrintDecimal(uint64_t uValue)
557{
558 char szBuf[64];
559 char *pszEnd = &szBuf[sizeof(szBuf) - 1];
560 char *psz = pszEnd;
561
562 *psz-- = '\0';
563
564 do
565 {
566 *psz-- = '0' + (uValue % 10);
567 uValue /= 10;
568 } while (uValue > 0);
569
570 psz++;
571 suplibHardenedPrintStrN(psz, pszEnd - psz);
572}
573
574
575/**
576 * Writes a hexadecimal or octal number to standard error.
577 *
578 * @param uValue The value.
579 * @param uBase The base (16 or 8).
580 * @param fFlags Format flags.
581 */
582static void suplibHardenedPrintHexOctal(uint64_t uValue, unsigned uBase, uint32_t fFlags)
583{
584 static char const s_achDigitsLower[17] = "0123456789abcdef";
585 static char const s_achDigitsUpper[17] = "0123456789ABCDEF";
586 const char *pchDigits = !(fFlags & RTSTR_F_CAPITAL) ? s_achDigitsLower : s_achDigitsUpper;
587 unsigned cShift = uBase == 16 ? 4 : 3;
588 unsigned fDigitMask = uBase == 16 ? 0xf : 7;
589 char szBuf[64];
590 char *pszEnd = &szBuf[sizeof(szBuf) - 1];
591 char *psz = pszEnd;
592
593 *psz-- = '\0';
594
595 do
596 {
597 *psz-- = pchDigits[uValue & fDigitMask];
598 uValue >>= cShift;
599 } while (uValue > 0);
600
601 if ((fFlags & RTSTR_F_SPECIAL) && uBase == 16)
602 {
603 *psz-- = !(fFlags & RTSTR_F_CAPITAL) ? 'x' : 'X';
604 *psz-- = '0';
605 }
606
607 psz++;
608 suplibHardenedPrintStrN(psz, pszEnd - psz);
609}
610
611
612/**
613 * Writes a wide character string to standard error.
614 *
615 * @param pwsz The string.
616 */
617static void suplibHardenedPrintWideStr(PCRTUTF16 pwsz)
618{
619 for (;;)
620 {
621 RTUTF16 wc = *pwsz++;
622 if (!wc)
623 return;
624 if ( (wc < 0x7f && wc >= 0x20)
625 || wc == '\n'
626 || wc == '\r')
627 suplibHardenedPrintChr((char)wc);
628 else
629 {
630 suplibHardenedPrintStrN(RT_STR_TUPLE("\\x"));
631 suplibHardenedPrintHexOctal(wc, 16, 0);
632 }
633 }
634}
635
636#ifdef IPRT_NO_CRT
637
638/** Buffer structure used by suplibHardenedOutput. */
639struct SUPLIBHARDENEDOUTPUTBUF
640{
641 size_t off;
642 char szBuf[2048];
643};
644
645/** Callback for RTStrFormatV, see FNRTSTROUTPUT. */
646static DECLCALLBACK(size_t) suplibHardenedOutput(void *pvArg, const char *pachChars, size_t cbChars)
647{
648 SUPLIBHARDENEDOUTPUTBUF *pBuf = (SUPLIBHARDENEDOUTPUTBUF *)pvArg;
649 size_t cbTodo = cbChars;
650 for (;;)
651 {
652 size_t cbSpace = sizeof(pBuf->szBuf) - pBuf->off - 1;
653
654 /* Flush the buffer? */
655 if ( cbSpace == 0
656 || (cbTodo == 0 && pBuf->off))
657 {
658 suplibHardenedPrintStrN(pBuf->szBuf, pBuf->off);
659# ifdef RT_OS_WINDOWS
660 if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
661 OutputDebugString(pBuf->szBuf);
662# endif
663 pBuf->off = 0;
664 cbSpace = sizeof(pBuf->szBuf) - 1;
665 }
666
667 /* Copy the string into the buffer. */
668 if (cbTodo == 1)
669 {
670 pBuf->szBuf[pBuf->off++] = *pachChars;
671 break;
672 }
673 if (cbSpace >= cbTodo)
674 {
675 memcpy(&pBuf->szBuf[pBuf->off], pachChars, cbTodo);
676 pBuf->off += cbTodo;
677 break;
678 }
679 memcpy(&pBuf->szBuf[pBuf->off], pachChars, cbSpace);
680 pBuf->off += cbSpace;
681 cbTodo -= cbSpace;
682 }
683 pBuf->szBuf[pBuf->off] = '\0';
684
685 return cbChars;
686}
687
688#endif /* IPRT_NO_CRT */
689
690/**
691 * Simple printf to standard error.
692 *
693 * @param pszFormat The format string.
694 * @param va Arguments to format.
695 */
696DECLHIDDEN(void) suplibHardenedPrintFV(const char *pszFormat, va_list va)
697{
698#ifdef IPRT_NO_CRT
699 /*
700 * Use buffered output here to avoid character mixing on the windows
701 * console and to enable us to use OutputDebugString.
702 */
703 SUPLIBHARDENEDOUTPUTBUF Buf;
704 Buf.off = 0;
705 Buf.szBuf[0] = '\0';
706 RTStrFormatV(suplibHardenedOutput, &Buf, NULL, NULL, pszFormat, va);
707
708#else /* !IPRT_NO_CRT */
709 /*
710 * Format loop.
711 */
712 char ch;
713 const char *pszLast = pszFormat;
714 for (;;)
715 {
716 ch = *pszFormat;
717 if (!ch)
718 break;
719 pszFormat++;
720
721 if (ch == '%')
722 {
723 /*
724 * Format argument.
725 */
726
727 /* Flush unwritten bits. */
728 if (pszLast != pszFormat - 1)
729 suplibHardenedPrintStrN(pszLast, pszFormat - pszLast - 1);
730 pszLast = pszFormat;
731 ch = *pszFormat++;
732
733 /* flags. */
734 uint32_t fFlags = 0;
735 for (;;)
736 {
737 if (ch == '#') fFlags |= RTSTR_F_SPECIAL;
738 else if (ch == '-') fFlags |= RTSTR_F_LEFT;
739 else if (ch == '+') fFlags |= RTSTR_F_PLUS;
740 else if (ch == ' ') fFlags |= RTSTR_F_BLANK;
741 else if (ch == '0') fFlags |= RTSTR_F_ZEROPAD;
742 else if (ch == '\'') fFlags |= RTSTR_F_THOUSAND_SEP;
743 else break;
744 ch = *pszFormat++;
745 }
746
747 /* Width and precision - ignored. */
748 while (RT_C_IS_DIGIT(ch))
749 ch = *pszFormat++;
750 if (ch == '*')
751 va_arg(va, int);
752 if (ch == '.')
753 {
754 do ch = *pszFormat++;
755 while (RT_C_IS_DIGIT(ch));
756 if (ch == '*')
757 va_arg(va, int);
758 }
759
760 /* Size. */
761 char chArgSize = 0;
762 switch (ch)
763 {
764 case 'z':
765 case 'L':
766 case 'j':
767 case 't':
768 chArgSize = ch;
769 ch = *pszFormat++;
770 break;
771
772 case 'l':
773 chArgSize = ch;
774 ch = *pszFormat++;
775 if (ch == 'l')
776 {
777 chArgSize = 'L';
778 ch = *pszFormat++;
779 }
780 break;
781
782 case 'h':
783 chArgSize = ch;
784 ch = *pszFormat++;
785 if (ch == 'h')
786 {
787 chArgSize = 'H';
788 ch = *pszFormat++;
789 }
790 break;
791 }
792
793 /*
794 * Do type specific formatting.
795 */
796 switch (ch)
797 {
798 case 'c':
799 ch = (char)va_arg(va, int);
800 suplibHardenedPrintChr(ch);
801 break;
802
803 case 's':
804 if (chArgSize == 'l')
805 {
806 PCRTUTF16 pwszStr = va_arg(va, PCRTUTF16 );
807 if (RT_VALID_PTR(pwszStr))
808 suplibHardenedPrintWideStr(pwszStr);
809 else
810 suplibHardenedPrintStr("<NULL>");
811 }
812 else
813 {
814 const char *pszStr = va_arg(va, const char *);
815 if (!RT_VALID_PTR(pszStr))
816 pszStr = "<NULL>";
817 suplibHardenedPrintStr(pszStr);
818 }
819 break;
820
821 case 'd':
822 case 'i':
823 {
824 int64_t iValue;
825 if (chArgSize == 'L' || chArgSize == 'j')
826 iValue = va_arg(va, int64_t);
827 else if (chArgSize == 'l')
828 iValue = va_arg(va, signed long);
829 else if (chArgSize == 'z' || chArgSize == 't')
830 iValue = va_arg(va, intptr_t);
831 else
832 iValue = va_arg(va, signed int);
833 if (iValue < 0)
834 {
835 suplibHardenedPrintChr('-');
836 iValue = -iValue;
837 }
838 suplibHardenedPrintDecimal(iValue);
839 break;
840 }
841
842 case 'p':
843 case 'x':
844 case 'X':
845 case 'u':
846 case 'o':
847 {
848 unsigned uBase = 10;
849 uint64_t uValue;
850
851 switch (ch)
852 {
853 case 'p':
854 fFlags |= RTSTR_F_ZEROPAD; /* Note not standard behaviour (but I like it this way!) */
855 uBase = 16;
856 break;
857 case 'X':
858 fFlags |= RTSTR_F_CAPITAL;
859 case 'x':
860 uBase = 16;
861 break;
862 case 'u':
863 uBase = 10;
864 break;
865 case 'o':
866 uBase = 8;
867 break;
868 }
869
870 if (ch == 'p' || chArgSize == 'z' || chArgSize == 't')
871 uValue = va_arg(va, uintptr_t);
872 else if (chArgSize == 'L' || chArgSize == 'j')
873 uValue = va_arg(va, uint64_t);
874 else if (chArgSize == 'l')
875 uValue = va_arg(va, unsigned long);
876 else
877 uValue = va_arg(va, unsigned int);
878
879 if (uBase == 10)
880 suplibHardenedPrintDecimal(uValue);
881 else
882 suplibHardenedPrintHexOctal(uValue, uBase, fFlags);
883 break;
884 }
885
886 case 'R':
887 if (pszFormat[0] == 'r' && pszFormat[1] == 'c')
888 {
889 int iValue = va_arg(va, int);
890 if (iValue < 0)
891 {
892 suplibHardenedPrintChr('-');
893 iValue = -iValue;
894 }
895 suplibHardenedPrintDecimal(iValue);
896 pszFormat += 2;
897 break;
898 }
899 /* fall thru */
900
901 /*
902 * Custom format.
903 */
904 default:
905 suplibHardenedPrintStr("[bad format: ");
906 suplibHardenedPrintStrN(pszLast, pszFormat - pszLast);
907 suplibHardenedPrintChr(']');
908 break;
909 }
910
911 /* continue */
912 pszLast = pszFormat;
913 }
914 }
915
916 /* Flush the last bits of the string. */
917 if (pszLast != pszFormat)
918 suplibHardenedPrintStrN(pszLast, pszFormat - pszLast);
919#endif /* !IPRT_NO_CRT */
920}
921
922
923/**
924 * Prints to standard error.
925 *
926 * @param pszFormat The format string.
927 * @param ... Arguments to format.
928 */
929DECLHIDDEN(void) suplibHardenedPrintF(const char *pszFormat, ...)
930{
931 va_list va;
932 va_start(va, pszFormat);
933 suplibHardenedPrintFV(pszFormat, va);
934 va_end(va);
935}
936
937
938/**
939 * @copydoc RTPathStripFilename
940 */
941static void suplibHardenedPathStripFilename(char *pszPath)
942{
943 char *psz = pszPath;
944 char *pszLastSep = pszPath;
945
946 for (;; psz++)
947 {
948 switch (*psz)
949 {
950 /* handle separators. */
951#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
952 case ':':
953 pszLastSep = psz + 1;
954 break;
955
956 case '\\':
957#endif
958 case '/':
959 pszLastSep = psz;
960 break;
961
962 /* the end */
963 case '\0':
964 if (pszLastSep == pszPath)
965 *pszLastSep++ = '.';
966 *pszLastSep = '\0';
967 return;
968 }
969 }
970 /* will never get here */
971}
972
973
974/**
975 * @copydoc RTPathFilename
976 */
977DECLHIDDEN(char *) supR3HardenedPathFilename(const char *pszPath)
978{
979 const char *psz = pszPath;
980 const char *pszLastComp = pszPath;
981
982 for (;; psz++)
983 {
984 switch (*psz)
985 {
986 /* handle separators. */
987#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
988 case ':':
989 pszLastComp = psz + 1;
990 break;
991
992 case '\\':
993#endif
994 case '/':
995 pszLastComp = psz + 1;
996 break;
997
998 /* the end */
999 case '\0':
1000 if (*pszLastComp)
1001 return (char *)(void *)pszLastComp;
1002 return NULL;
1003 }
1004 }
1005
1006 /* will never get here */
1007 return NULL;
1008}
1009
1010
1011/**
1012 * @copydoc RTPathAppPrivateNoArch
1013 */
1014DECLHIDDEN(int) supR3HardenedPathAppPrivateNoArch(char *pszPath, size_t cchPath)
1015{
1016#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE)
1017 const char *pszSrcPath = RTPATH_APP_PRIVATE;
1018 size_t cchPathPrivateNoArch = suplibHardenedStrLen(pszSrcPath);
1019 if (cchPathPrivateNoArch >= cchPath)
1020 supR3HardenedFatal("supR3HardenedPathAppPrivateNoArch: Buffer overflow, %zu >= %zu\n", cchPathPrivateNoArch, cchPath);
1021 suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathPrivateNoArch + 1);
1022 return VINF_SUCCESS;
1023
1024#else
1025 return supR3HardenedPathAppBin(pszPath, cchPath);
1026#endif
1027}
1028
1029
1030/**
1031 * @copydoc RTPathAppPrivateArch
1032 */
1033DECLHIDDEN(int) supR3HardenedPathAppPrivateArch(char *pszPath, size_t cchPath)
1034{
1035#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE_ARCH)
1036 const char *pszSrcPath = RTPATH_APP_PRIVATE_ARCH;
1037 size_t cchPathPrivateArch = suplibHardenedStrLen(pszSrcPath);
1038 if (cchPathPrivateArch >= cchPath)
1039 supR3HardenedFatal("supR3HardenedPathAppPrivateArch: Buffer overflow, %zu >= %zu\n", cchPathPrivateArch, cchPath);
1040 suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathPrivateArch + 1);
1041 return VINF_SUCCESS;
1042
1043#else
1044 return supR3HardenedPathAppBin(pszPath, cchPath);
1045#endif
1046}
1047
1048
1049/**
1050 * @copydoc RTPathSharedLibs
1051 */
1052DECLHIDDEN(int) supR3HardenedPathAppSharedLibs(char *pszPath, size_t cchPath)
1053{
1054#if !defined(RT_OS_WINDOWS) && defined(RTPATH_SHARED_LIBS)
1055 const char *pszSrcPath = RTPATH_SHARED_LIBS;
1056 size_t cchPathSharedLibs = suplibHardenedStrLen(pszSrcPath);
1057 if (cchPathSharedLibs >= cchPath)
1058 supR3HardenedFatal("supR3HardenedPathAppSharedLibs: Buffer overflow, %zu >= %zu\n", cchPathSharedLibs, cchPath);
1059 suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathSharedLibs + 1);
1060 return VINF_SUCCESS;
1061
1062#else
1063 return supR3HardenedPathAppBin(pszPath, cchPath);
1064#endif
1065}
1066
1067
1068/**
1069 * @copydoc RTPathAppDocs
1070 */
1071DECLHIDDEN(int) supR3HardenedPathAppDocs(char *pszPath, size_t cchPath)
1072{
1073#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_DOCS)
1074 const char *pszSrcPath = RTPATH_APP_DOCS;
1075 size_t cchPathAppDocs = suplibHardenedStrLen(pszSrcPath);
1076 if (cchPathAppDocs >= cchPath)
1077 supR3HardenedFatal("supR3HardenedPathAppDocs: Buffer overflow, %zu >= %zu\n", cchPathAppDocs, cchPath);
1078 suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathAppDocs + 1);
1079 return VINF_SUCCESS;
1080
1081#else
1082 return supR3HardenedPathAppBin(pszPath, cchPath);
1083#endif
1084}
1085
1086
1087/**
1088 * Returns the full path to the executable in g_szSupLibHardenedExePath.
1089 *
1090 * @returns IPRT status code.
1091 */
1092static void supR3HardenedGetFullExePath(void)
1093{
1094 /*
1095 * Get the program filename.
1096 *
1097 * Most UNIXes have no API for obtaining the executable path, but provides a symbolic
1098 * link in the proc file system that tells who was exec'ed. The bad thing about this
1099 * is that we have to use readlink, one of the weirder UNIX APIs.
1100 *
1101 * Darwin, OS/2 and Windows all have proper APIs for getting the program file name.
1102 */
1103#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_SOLARIS)
1104# ifdef RT_OS_LINUX
1105 int cchLink = readlink("/proc/self/exe", &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath) - 1);
1106
1107# elif defined(RT_OS_SOLARIS)
1108 char szFileBuf[PATH_MAX + 1];
1109 sprintf(szFileBuf, "/proc/%ld/path/a.out", (long)getpid());
1110 int cchLink = readlink(szFileBuf, &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath) - 1);
1111
1112# else /* RT_OS_FREEBSD */
1113 int aiName[4];
1114 aiName[0] = CTL_KERN;
1115 aiName[1] = KERN_PROC;
1116 aiName[2] = KERN_PROC_PATHNAME;
1117 aiName[3] = getpid();
1118
1119 size_t cbPath = sizeof(g_szSupLibHardenedExePath);
1120 if (sysctl(aiName, RT_ELEMENTS(aiName), g_szSupLibHardenedExePath, &cbPath, NULL, 0) < 0)
1121 supR3HardenedFatal("supR3HardenedExecDir: sysctl failed\n");
1122 g_szSupLibHardenedExePath[sizeof(g_szSupLibHardenedExePath) - 1] = '\0';
1123 int cchLink = suplibHardenedStrLen(g_szSupLibHardenedExePath); /* paranoid? can't we use cbPath? */
1124
1125# endif
1126 if (cchLink < 0 || cchLink == sizeof(g_szSupLibHardenedExePath) - 1)
1127 supR3HardenedFatal("supR3HardenedExecDir: couldn't read \"%s\", errno=%d cchLink=%d\n",
1128 g_szSupLibHardenedExePath, errno, cchLink);
1129 g_szSupLibHardenedExePath[cchLink] = '\0';
1130
1131#elif defined(RT_OS_OS2) || defined(RT_OS_L4)
1132 _execname(g_szSupLibHardenedExePath, sizeof(g_szSupLibHardenedExePath));
1133
1134#elif defined(RT_OS_DARWIN)
1135 const char *pszImageName = _dyld_get_image_name(0);
1136 if (!pszImageName)
1137 supR3HardenedFatal("supR3HardenedExecDir: _dyld_get_image_name(0) failed\n");
1138 size_t cchImageName = suplibHardenedStrLen(pszImageName);
1139 if (!cchImageName || cchImageName >= sizeof(g_szSupLibHardenedExePath))
1140 supR3HardenedFatal("supR3HardenedExecDir: _dyld_get_image_name(0) failed, cchImageName=%d\n", cchImageName);
1141 suplibHardenedMemCopy(g_szSupLibHardenedExePath, pszImageName, cchImageName + 1);
1142
1143#elif defined(RT_OS_WINDOWS)
1144 char *pszDst = g_szSupLibHardenedExePath;
1145 int rc = RTUtf16ToUtf8Ex(g_wszSupLibHardenedExePath, RTSTR_MAX, &pszDst, sizeof(g_szSupLibHardenedExePath), NULL);
1146 if (RT_FAILURE(rc))
1147 supR3HardenedFatal("supR3HardenedExecDir: RTUtf16ToUtf8Ex failed, rc=%Rrc\n", rc);
1148#else
1149# error needs porting.
1150#endif
1151
1152 /*
1153 * Determine the application binary directory location.
1154 */
1155 suplibHardenedStrCopy(g_szSupLibHardenedAppBinPath, g_szSupLibHardenedExePath);
1156 suplibHardenedPathStripFilename(g_szSupLibHardenedAppBinPath);
1157
1158 if (g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_HARDENED_MAIN_CALLED)
1159 supR3HardenedFatal("supR3HardenedExecDir: Called before SUPR3HardenedMain! (%d)\n", g_enmSupR3HardenedMainState);
1160 switch (g_fSupHardenedMain & SUPSECMAIN_FLAGS_LOC_MASK)
1161 {
1162 case SUPSECMAIN_FLAGS_LOC_APP_BIN:
1163 break;
1164 case SUPSECMAIN_FLAGS_LOC_TESTCASE:
1165 suplibHardenedPathStripFilename(g_szSupLibHardenedAppBinPath);
1166 break;
1167 default:
1168 supR3HardenedFatal("supR3HardenedExecDir: Unknown program binary location: %#x\n", g_fSupHardenedMain);
1169 }
1170}
1171
1172
1173#ifdef RT_OS_LINUX
1174/**
1175 * Checks if we can read /proc/self/exe.
1176 *
1177 * This is used on linux to see if we have to call init
1178 * with program path or not.
1179 *
1180 * @returns true / false.
1181 */
1182static bool supR3HardenedMainIsProcSelfExeAccssible(void)
1183{
1184 char szPath[RTPATH_MAX];
1185 int cchLink = readlink("/proc/self/exe", szPath, sizeof(szPath));
1186 return cchLink != -1;
1187}
1188#endif /* RT_OS_LINUX */
1189
1190
1191
1192/**
1193 * @copydoc RTPathExecDir
1194 * @remarks not quite like RTPathExecDir actually...
1195 */
1196DECLHIDDEN(int) supR3HardenedPathAppBin(char *pszPath, size_t cchPath)
1197{
1198 /*
1199 * Lazy init (probably not required).
1200 */
1201 if (!g_szSupLibHardenedAppBinPath[0])
1202 supR3HardenedGetFullExePath();
1203
1204 /*
1205 * Calc the length and check if there is space before copying.
1206 */
1207 size_t cch = suplibHardenedStrLen(g_szSupLibHardenedAppBinPath) + 1;
1208 if (cch <= cchPath)
1209 {
1210 suplibHardenedMemCopy(pszPath, g_szSupLibHardenedAppBinPath, cch + 1);
1211 return VINF_SUCCESS;
1212 }
1213
1214 supR3HardenedFatal("supR3HardenedPathAppBin: Buffer too small (%u < %u)\n", cchPath, cch);
1215 return VERR_BUFFER_OVERFLOW;
1216}
1217
1218
1219#ifdef RT_OS_WINDOWS
1220extern "C" uint32_t g_uNtVerCombined;
1221#endif
1222
1223DECLHIDDEN(void) supR3HardenedOpenLog(int *pcArgs, char **papszArgs)
1224{
1225 static const char s_szLogOption[] = "--sup-hardening-log=";
1226
1227 /*
1228 * Scan the argument vector.
1229 */
1230 int cArgs = *pcArgs;
1231 for (int iArg = 1; iArg < cArgs; iArg++)
1232 if (strncmp(papszArgs[iArg], s_szLogOption, sizeof(s_szLogOption) - 1) == 0)
1233 {
1234 const char *pszLogFile = &papszArgs[iArg][sizeof(s_szLogOption) - 1];
1235
1236 /*
1237 * Drop the argument from the vector (has trailing NULL entry).
1238 */
1239 memmove(&papszArgs[iArg], &papszArgs[iArg + 1], (cArgs - iArg) * sizeof(papszArgs[0]));
1240 *pcArgs -= 1;
1241 cArgs -= 1;
1242
1243 /*
1244 * Open the log file, unless we've already opened one.
1245 * First argument takes precedence
1246 */
1247#ifdef RT_OS_WINDOWS
1248 if (g_hStartupLog == NULL)
1249 {
1250 int rc = RTNtPathOpen(pszLogFile,
1251 GENERIC_WRITE | SYNCHRONIZE,
1252 FILE_ATTRIBUTE_NORMAL,
1253 FILE_SHARE_READ | FILE_SHARE_WRITE,
1254 FILE_OPEN_IF,
1255 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1256 OBJ_CASE_INSENSITIVE,
1257 &g_hStartupLog,
1258 NULL);
1259 if (RT_SUCCESS(rc))
1260 {
1261 SUP_DPRINTF(("Log file opened: " VBOX_VERSION_STRING "r%u g_hStartupLog=%p g_uNtVerCombined=%#x\n",
1262 VBOX_SVN_REV, g_hStartupLog, g_uNtVerCombined));
1263
1264 /*
1265 * If the path contains a drive volume, save it so we can
1266 * use it to flush the volume containing the log file.
1267 */
1268 if (RT_C_IS_ALPHA(pszLogFile[0]) && pszLogFile[1] == ':')
1269 {
1270 RTUtf16CopyAscii(g_wszStartupLogVol, RT_ELEMENTS(g_wszStartupLogVol), "\\??\\");
1271 g_wszStartupLogVol[sizeof("\\??\\") - 1] = RT_C_TO_UPPER(pszLogFile[0]);
1272 g_wszStartupLogVol[sizeof("\\??\\") + 0] = ':';
1273 g_wszStartupLogVol[sizeof("\\??\\") + 1] = '\0';
1274 }
1275 }
1276 else
1277 g_hStartupLog = NULL;
1278 }
1279#else
1280 //g_hStartupLog = open()
1281#endif
1282 }
1283}
1284
1285
1286DECLHIDDEN(void) supR3HardenedLogV(const char *pszFormat, va_list va)
1287{
1288#ifdef RT_OS_WINDOWS
1289 if ( g_hStartupLog != NULL
1290 && g_cbStartupLog < 16*_1M)
1291 {
1292 char szBuf[5120];
1293 PCLIENT_ID pSelfId = &((PTEB)NtCurrentTeb())->ClientId;
1294 size_t cchPrefix = RTStrPrintf(szBuf, sizeof(szBuf), "%x.%x: ", pSelfId->UniqueProcess, pSelfId->UniqueThread);
1295 size_t cch = RTStrPrintfV(&szBuf[cchPrefix], sizeof(szBuf) - cchPrefix, pszFormat, va) + cchPrefix;
1296
1297 if ((size_t)cch >= sizeof(szBuf))
1298 cch = sizeof(szBuf) - 1;
1299
1300 if (!cch || szBuf[cch - 1] != '\n')
1301 szBuf[cch++] = '\n';
1302
1303 ASMAtomicAddU32(&g_cbStartupLog, (uint32_t)cch);
1304
1305 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1306 LARGE_INTEGER Offset;
1307 Offset.QuadPart = -1; /* Write to end of file. */
1308 NtWriteFile(g_hStartupLog, NULL /*Event*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/,
1309 &Ios, szBuf, (ULONG)cch, &Offset, NULL /*Key*/);
1310 }
1311#else
1312 /* later */
1313#endif
1314}
1315
1316
1317DECLHIDDEN(void) supR3HardenedLog(const char *pszFormat, ...)
1318{
1319 va_list va;
1320 va_start(va, pszFormat);
1321 supR3HardenedLogV(pszFormat, va);
1322 va_end(va);
1323}
1324
1325
1326DECLHIDDEN(void) supR3HardenedLogFlush(void)
1327{
1328#ifdef RT_OS_WINDOWS
1329 if ( g_hStartupLog != NULL
1330 && g_cbStartupLog < 16*_1M)
1331 {
1332 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1333 NTSTATUS rcNt = NtFlushBuffersFile(g_hStartupLog, &Ios);
1334
1335 /*
1336 * Try flush the volume containing the log file too.
1337 */
1338 if (g_wszStartupLogVol[0])
1339 {
1340 HANDLE hLogVol = RTNT_INVALID_HANDLE_VALUE;
1341 UNICODE_STRING NtName;
1342 NtName.Buffer = g_wszStartupLogVol;
1343 NtName.Length = (USHORT)(RTUtf16Len(g_wszStartupLogVol) * sizeof(RTUTF16));
1344 NtName.MaximumLength = NtName.Length + 1;
1345 OBJECT_ATTRIBUTES ObjAttr;
1346 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1347 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
1348 rcNt = NtCreateFile(&hLogVol,
1349 GENERIC_WRITE | GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
1350 &ObjAttr,
1351 &Ios,
1352 NULL /* Allocation Size*/,
1353 0 /*FileAttributes*/,
1354 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1355 FILE_OPEN,
1356 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1357 NULL /*EaBuffer*/,
1358 0 /*EaLength*/);
1359 if (NT_SUCCESS(rcNt))
1360 rcNt = Ios.Status;
1361 if (NT_SUCCESS(rcNt))
1362 {
1363 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
1364 rcNt = NtFlushBuffersFile(hLogVol, &Ios);
1365 NtClose(hLogVol);
1366 }
1367 else
1368 {
1369 /* This may have sideeffects similar to what we want... */
1370 hLogVol = RTNT_INVALID_HANDLE_VALUE;
1371 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
1372 rcNt = NtCreateFile(&hLogVol,
1373 GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
1374 &ObjAttr,
1375 &Ios,
1376 NULL /* Allocation Size*/,
1377 0 /*FileAttributes*/,
1378 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1379 FILE_OPEN,
1380 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1381 NULL /*EaBuffer*/,
1382 0 /*EaLength*/);
1383 if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
1384 NtClose(hLogVol);
1385 }
1386 }
1387 }
1388#else
1389 /* later */
1390#endif
1391}
1392
1393
1394/**
1395 * Prints the message prefix.
1396 */
1397static void suplibHardenedPrintPrefix(void)
1398{
1399 if (g_pszSupLibHardenedProgName)
1400 suplibHardenedPrintStr(g_pszSupLibHardenedProgName);
1401 suplibHardenedPrintStr(": ");
1402}
1403
1404
1405DECLHIDDEN(void) supR3HardenedFatalMsgV(const char *pszWhere, SUPINITOP enmWhat, int rc, const char *pszMsgFmt, va_list va)
1406{
1407 /*
1408 * First to the log.
1409 */
1410 supR3HardenedLog("Error %d in %s! (enmWhat=%d)\n", rc, pszWhere, enmWhat);
1411 va_list vaCopy;
1412 va_copy(vaCopy, va);
1413 supR3HardenedLogV(pszMsgFmt, vaCopy);
1414 va_end(vaCopy);
1415
1416#ifdef RT_OS_WINDOWS
1417 /*
1418 * The release log.
1419 */
1420 if (g_pfnRTLogRelPrintf)
1421 {
1422 va_copy(vaCopy, va);
1423 g_pfnRTLogRelPrintf("supR3HardenedFatalMsgV: %s enmWhat=%d rc=%Rrc (%#x)\n", pszWhere, enmWhat, rc);
1424 g_pfnRTLogRelPrintf("supR3HardenedFatalMsgV: %N\n", pszMsgFmt, &vaCopy);
1425 va_end(vaCopy);
1426 }
1427#endif
1428
1429 /*
1430 * Then to the console.
1431 */
1432 suplibHardenedPrintPrefix();
1433 suplibHardenedPrintF("Error %d in %s!\n", rc, pszWhere);
1434
1435 suplibHardenedPrintPrefix();
1436 va_copy(vaCopy, va);
1437 suplibHardenedPrintFV(pszMsgFmt, vaCopy);
1438 va_end(vaCopy);
1439 suplibHardenedPrintChr('\n');
1440
1441 switch (enmWhat)
1442 {
1443 case kSupInitOp_Driver:
1444 suplibHardenedPrintChr('\n');
1445 suplibHardenedPrintPrefix();
1446 suplibHardenedPrintStr("Tip! Make sure the kernel module is loaded. It may also help to reinstall VirtualBox.\n");
1447 break;
1448
1449 case kSupInitOp_Misc:
1450 case kSupInitOp_IPRT:
1451 case kSupInitOp_Integrity:
1452 case kSupInitOp_RootCheck:
1453 suplibHardenedPrintChr('\n');
1454 suplibHardenedPrintPrefix();
1455 suplibHardenedPrintStr("Tip! It may help to reinstall VirtualBox.\n");
1456 break;
1457
1458 default:
1459 /* no hints here */
1460 break;
1461 }
1462
1463 /*
1464 * Finally, TrustedError if appropriate.
1465 */
1466 if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
1467 {
1468#ifdef SUP_HARDENED_SUID
1469 /*
1470 * Drop any root privileges we might be holding, this won't return
1471 * if it fails but end up calling supR3HardenedFatal[V].
1472 */
1473 supR3HardenedMainDropPrivileges();
1474#endif
1475
1476 /*
1477 * Now try resolve and call the TrustedError entry point if we can
1478 * find it. We'll fork before we attempt this because that way the
1479 * session management in main will see us exiting immediately (if
1480 * it's involved with us).
1481 */
1482#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
1483 int pid = fork();
1484 if (pid <= 0)
1485#endif
1486 {
1487 static volatile bool s_fRecursive = false; /* Loader hooks may cause recursion. */
1488 if (!s_fRecursive)
1489 {
1490 s_fRecursive = true;
1491
1492 PFNSUPTRUSTEDERROR pfnTrustedError = supR3HardenedMainGetTrustedError(g_pszSupLibHardenedProgName);
1493 if (pfnTrustedError)
1494 pfnTrustedError(pszWhere, enmWhat, rc, pszMsgFmt, va);
1495
1496 s_fRecursive = false;
1497 }
1498 }
1499 }
1500#if defined(RT_OS_WINDOWS)
1501 /*
1502 * Report the error to the parent if this happens during early VM init.
1503 */
1504 else if ( g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED
1505 && g_enmSupR3HardenedMainState != SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED)
1506 supR3HardenedWinReportErrorToParent(pszWhere, enmWhat, rc, pszMsgFmt, va);
1507#endif
1508
1509 /*
1510 * Quit
1511 */
1512 suplibHardenedExit(RTEXITCODE_FAILURE);
1513}
1514
1515
1516DECLHIDDEN(void) supR3HardenedFatalMsg(const char *pszWhere, SUPINITOP enmWhat, int rc, const char *pszMsgFmt, ...)
1517{
1518 va_list va;
1519 va_start(va, pszMsgFmt);
1520 supR3HardenedFatalMsgV(pszWhere, enmWhat, rc, pszMsgFmt, va);
1521 va_end(va);
1522}
1523
1524
1525DECLHIDDEN(void) supR3HardenedFatalV(const char *pszFormat, va_list va)
1526{
1527 supR3HardenedLog("Fatal error:\n");
1528 va_list vaCopy;
1529 va_copy(vaCopy, va);
1530 supR3HardenedLogV(pszFormat, vaCopy);
1531 va_end(vaCopy);
1532
1533#if defined(RT_OS_WINDOWS)
1534 /*
1535 * Report the error to the parent if this happens during early VM init.
1536 */
1537 if ( g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED
1538 && g_enmSupR3HardenedMainState != SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED)
1539 supR3HardenedWinReportErrorToParent(NULL, kSupInitOp_Invalid, VERR_INTERNAL_ERROR, pszFormat, va);
1540 else
1541#endif
1542 {
1543#ifdef RT_OS_WINDOWS
1544 if (g_pfnRTLogRelPrintf)
1545 {
1546 va_copy(vaCopy, va);
1547 g_pfnRTLogRelPrintf("supR3HardenedFatalV: %N", pszFormat, &vaCopy);
1548 va_end(vaCopy);
1549 }
1550#endif
1551
1552 suplibHardenedPrintPrefix();
1553 suplibHardenedPrintFV(pszFormat, va);
1554 }
1555
1556 suplibHardenedExit(RTEXITCODE_FAILURE);
1557}
1558
1559
1560DECLHIDDEN(void) supR3HardenedFatal(const char *pszFormat, ...)
1561{
1562 va_list va;
1563 va_start(va, pszFormat);
1564 supR3HardenedFatalV(pszFormat, va);
1565 va_end(va);
1566}
1567
1568
1569DECLHIDDEN(int) supR3HardenedErrorV(int rc, bool fFatal, const char *pszFormat, va_list va)
1570{
1571 if (fFatal)
1572 supR3HardenedFatalV(pszFormat, va);
1573
1574 supR3HardenedLog("Error (rc=%d):\n", rc);
1575 va_list vaCopy;
1576 va_copy(vaCopy, va);
1577 supR3HardenedLogV(pszFormat, vaCopy);
1578 va_end(vaCopy);
1579
1580#ifdef RT_OS_WINDOWS
1581 if (g_pfnRTLogRelPrintf)
1582 {
1583 va_copy(vaCopy, va);
1584 g_pfnRTLogRelPrintf("supR3HardenedErrorV: %N", pszFormat, &vaCopy);
1585 va_end(vaCopy);
1586 }
1587#endif
1588
1589 suplibHardenedPrintPrefix();
1590 suplibHardenedPrintFV(pszFormat, va);
1591
1592 return rc;
1593}
1594
1595
1596DECLHIDDEN(int) supR3HardenedError(int rc, bool fFatal, const char *pszFormat, ...)
1597{
1598 va_list va;
1599 va_start(va, pszFormat);
1600 supR3HardenedErrorV(rc, fFatal, pszFormat, va);
1601 va_end(va);
1602 return rc;
1603}
1604
1605
1606
1607/**
1608 * Attempts to open /dev/vboxdrv (or equvivalent).
1609 *
1610 * @remarks This function will not return on failure.
1611 */
1612DECLHIDDEN(void) supR3HardenedMainOpenDevice(void)
1613{
1614 RTERRINFOSTATIC ErrInfo;
1615 SUPINITOP enmWhat = kSupInitOp_Driver;
1616 int rc = suplibOsInit(&g_SupPreInitData.Data, false /*fPreInit*/, true /*fUnrestricted*/,
1617 &enmWhat, RTErrInfoInitStatic(&ErrInfo));
1618 if (RT_SUCCESS(rc))
1619 return;
1620
1621 if (RTErrInfoIsSet(&ErrInfo.Core))
1622 supR3HardenedFatalMsg("suplibOsInit", enmWhat, rc, "%s", ErrInfo.szMsg);
1623
1624 switch (rc)
1625 {
1626 /** @todo better messages! */
1627 case VERR_VM_DRIVER_NOT_INSTALLED:
1628 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Kernel driver not installed");
1629 case VERR_VM_DRIVER_NOT_ACCESSIBLE:
1630 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Kernel driver not accessible");
1631 case VERR_VM_DRIVER_LOAD_ERROR:
1632 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_VM_DRIVER_LOAD_ERROR");
1633 case VERR_VM_DRIVER_OPEN_ERROR:
1634 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_VM_DRIVER_OPEN_ERROR");
1635 case VERR_VM_DRIVER_VERSION_MISMATCH:
1636 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Kernel driver version mismatch");
1637 case VERR_ACCESS_DENIED:
1638 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_ACCESS_DENIED");
1639 case VERR_NO_MEMORY:
1640 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Kernel memory allocation/mapping failed");
1641 case VERR_SUPDRV_HARDENING_EVIL_HANDLE:
1642 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Integrity, rc, "VERR_SUPDRV_HARDENING_EVIL_HANDLE");
1643 case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_0:
1644 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Integrity, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_0");
1645 case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_1:
1646 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Integrity, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_1");
1647 case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_2:
1648 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Integrity, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_2");
1649 default:
1650 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Unknown rc=%d (%Rrc)", rc, rc);
1651 }
1652}
1653
1654
1655#ifdef SUP_HARDENED_SUID
1656
1657/**
1658 * Grabs extra non-root capabilities / privileges that we might require.
1659 *
1660 * This is currently only used for being able to do ICMP from the NAT engine.
1661 *
1662 * @note We still have root privileges at the time of this call.
1663 */
1664static void supR3HardenedMainGrabCapabilites(void)
1665{
1666# if defined(RT_OS_LINUX)
1667 /*
1668 * We are about to drop all our privileges. Remove all capabilities but
1669 * keep the cap_net_raw capability for ICMP sockets for the NAT stack.
1670 */
1671 if (g_uCaps != 0)
1672 {
1673# ifdef USE_LIB_PCAP
1674 /* XXX cap_net_bind_service */
1675 if (!cap_set_proc(cap_from_text("all-eip cap_net_raw+ep")))
1676 prctl(PR_SET_KEEPCAPS, 1 /*keep=*/, 0, 0, 0);
1677 prctl(PR_SET_DUMPABLE, 1 /*dump*/, 0, 0, 0);
1678# else
1679 cap_user_header_t hdr = (cap_user_header_t)alloca(sizeof(*hdr));
1680 cap_user_data_t cap = (cap_user_data_t)alloca(sizeof(*cap));
1681 memset(hdr, 0, sizeof(*hdr));
1682 hdr->version = _LINUX_CAPABILITY_VERSION;
1683 memset(cap, 0, sizeof(*cap));
1684 cap->effective = g_uCaps;
1685 cap->permitted = g_uCaps;
1686 if (!capset(hdr, cap))
1687 prctl(PR_SET_KEEPCAPS, 1 /*keep*/, 0, 0, 0);
1688 prctl(PR_SET_DUMPABLE, 1 /*dump*/, 0, 0, 0);
1689# endif /* !USE_LIB_PCAP */
1690 }
1691
1692# elif defined(RT_OS_SOLARIS)
1693 /*
1694 * Add net_icmpaccess privilege to effective privileges and limit
1695 * permitted privileges before completely dropping root privileges.
1696 * This requires dropping root privileges temporarily to get the normal
1697 * user's privileges.
1698 */
1699 seteuid(g_uid);
1700 priv_set_t *pPrivEffective = priv_allocset();
1701 priv_set_t *pPrivNew = priv_allocset();
1702 if (pPrivEffective && pPrivNew)
1703 {
1704 int rc = getppriv(PRIV_EFFECTIVE, pPrivEffective);
1705 seteuid(0);
1706 if (!rc)
1707 {
1708 priv_copyset(pPrivEffective, pPrivNew);
1709 rc = priv_addset(pPrivNew, PRIV_NET_ICMPACCESS);
1710 if (!rc)
1711 {
1712 /* Order is important, as one can't set a privilege which is
1713 * not in the permitted privilege set. */
1714 rc = setppriv(PRIV_SET, PRIV_EFFECTIVE, pPrivNew);
1715 if (rc)
1716 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set effective privilege set.\n");
1717 rc = setppriv(PRIV_SET, PRIV_PERMITTED, pPrivNew);
1718 if (rc)
1719 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set permitted privilege set.\n");
1720 }
1721 else
1722 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to add NET_ICMPACCESS privilege.\n");
1723 }
1724 }
1725 else
1726 {
1727 /* for memory allocation failures just continue */
1728 seteuid(0);
1729 }
1730
1731 if (pPrivEffective)
1732 priv_freeset(pPrivEffective);
1733 if (pPrivNew)
1734 priv_freeset(pPrivNew);
1735# endif
1736}
1737
1738/*
1739 * Look at the environment for some special options.
1740 */
1741static void supR3GrabOptions(void)
1742{
1743 const char *pszOpt;
1744
1745# ifdef RT_OS_LINUX
1746 g_uCaps = 0;
1747
1748 /*
1749 * Do _not_ perform any capability-related system calls for root processes
1750 * (leaving g_uCaps at 0).
1751 * (Hint: getuid gets the real user id, not the effective.)
1752 */
1753 if (getuid() != 0)
1754 {
1755 /*
1756 * CAP_NET_RAW.
1757 * Default: enabled.
1758 * Can be disabled with 'export VBOX_HARD_CAP_NET_RAW=0'.
1759 */
1760 pszOpt = getenv("VBOX_HARD_CAP_NET_RAW");
1761 if ( !pszOpt
1762 || memcmp(pszOpt, "0", sizeof("0")) != 0)
1763 g_uCaps = CAP_TO_MASK(CAP_NET_RAW);
1764
1765 /*
1766 * CAP_NET_BIND_SERVICE.
1767 * Default: disabled.
1768 * Can be enabled with 'export VBOX_HARD_CAP_NET_BIND_SERVICE=1'.
1769 */
1770 pszOpt = getenv("VBOX_HARD_CAP_NET_BIND_SERVICE");
1771 if ( pszOpt
1772 && memcmp(pszOpt, "0", sizeof("0")) != 0)
1773 g_uCaps |= CAP_TO_MASK(CAP_NET_BIND_SERVICE);
1774 }
1775# endif
1776}
1777
1778/**
1779 * Drop any root privileges we might be holding.
1780 */
1781static void supR3HardenedMainDropPrivileges(void)
1782{
1783 /*
1784 * Try use setre[ug]id since this will clear the save uid/gid and thus
1785 * leave fewer traces behind that libs like GTK+ may pick up.
1786 */
1787 uid_t euid, ruid, suid;
1788 gid_t egid, rgid, sgid;
1789# if defined(RT_OS_DARWIN)
1790 /* The really great thing here is that setreuid isn't available on
1791 OS X 10.4, libc emulates it. While 10.4 have a slightly different and
1792 non-standard setuid implementation compared to 10.5, the following
1793 works the same way with both version since we're super user (10.5 req).
1794 The following will set all three variants of the group and user IDs. */
1795 setgid(g_gid);
1796 setuid(g_uid);
1797 euid = geteuid();
1798 ruid = suid = getuid();
1799 egid = getegid();
1800 rgid = sgid = getgid();
1801
1802# elif defined(RT_OS_SOLARIS)
1803 /* Solaris doesn't have setresuid, but the setreuid interface is BSD
1804 compatible and will set the saved uid to euid when we pass it a ruid
1805 that isn't -1 (which we do). */
1806 setregid(g_gid, g_gid);
1807 setreuid(g_uid, g_uid);
1808 euid = geteuid();
1809 ruid = suid = getuid();
1810 egid = getegid();
1811 rgid = sgid = getgid();
1812
1813# else
1814 /* This is the preferred one, full control no questions about semantics.
1815 PORTME: If this isn't work, try join one of two other gangs above. */
1816 setresgid(g_gid, g_gid, g_gid);
1817 setresuid(g_uid, g_uid, g_uid);
1818 if (getresuid(&ruid, &euid, &suid) != 0)
1819 {
1820 euid = geteuid();
1821 ruid = suid = getuid();
1822 }
1823 if (getresgid(&rgid, &egid, &sgid) != 0)
1824 {
1825 egid = getegid();
1826 rgid = sgid = getgid();
1827 }
1828# endif
1829
1830
1831 /* Check that it worked out all right. */
1832 if ( euid != g_uid
1833 || ruid != g_uid
1834 || suid != g_uid
1835 || egid != g_gid
1836 || rgid != g_gid
1837 || sgid != g_gid)
1838 supR3HardenedFatal("SUPR3HardenedMain: failed to drop root privileges!"
1839 " (euid=%d ruid=%d suid=%d egid=%d rgid=%d sgid=%d; wanted uid=%d and gid=%d)\n",
1840 euid, ruid, suid, egid, rgid, sgid, g_uid, g_gid);
1841
1842# if RT_OS_LINUX
1843 /*
1844 * Re-enable the cap_net_raw capability which was disabled during setresuid.
1845 */
1846 if (g_uCaps != 0)
1847 {
1848# ifdef USE_LIB_PCAP
1849 /** @todo Warn if that does not work? */
1850 /* XXX cap_net_bind_service */
1851 cap_set_proc(cap_from_text("cap_net_raw+ep"));
1852# else
1853 cap_user_header_t hdr = (cap_user_header_t)alloca(sizeof(*hdr));
1854 cap_user_data_t cap = (cap_user_data_t)alloca(sizeof(*cap));
1855 memset(hdr, 0, sizeof(*hdr));
1856 hdr->version = _LINUX_CAPABILITY_VERSION;
1857 memset(cap, 0, sizeof(*cap));
1858 cap->effective = g_uCaps;
1859 cap->permitted = g_uCaps;
1860 /** @todo Warn if that does not work? */
1861 capset(hdr, cap);
1862# endif /* !USE_LIB_PCAP */
1863 }
1864# endif
1865}
1866
1867#endif /* SUP_HARDENED_SUID */
1868
1869/**
1870 * Loads the VBoxRT DLL/SO/DYLIB, hands it the open driver,
1871 * and calls RTR3InitEx.
1872 *
1873 * @param fFlags The SUPR3HardenedMain fFlags argument, passed to supR3PreInit.
1874 *
1875 * @remarks VBoxRT contains both IPRT and SUPR3.
1876 * @remarks This function will not return on failure.
1877 */
1878static void supR3HardenedMainInitRuntime(uint32_t fFlags)
1879{
1880 /*
1881 * Construct the name.
1882 */
1883 char szPath[RTPATH_MAX];
1884 supR3HardenedPathAppSharedLibs(szPath, sizeof(szPath) - sizeof("/VBoxRT" SUPLIB_DLL_SUFF));
1885 suplibHardenedStrCat(szPath, "/VBoxRT" SUPLIB_DLL_SUFF);
1886
1887 /*
1888 * Open it and resolve the symbols.
1889 */
1890#if defined(RT_OS_WINDOWS)
1891 HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/, g_fSupHardenedMain);
1892 if (!hMod)
1893 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_MODULE_NOT_FOUND,
1894 "LoadLibrary \"%s\" failed (rc=%d)",
1895 szPath, RtlGetLastWin32Error());
1896 PFNRTR3INITEX pfnRTInitEx = (PFNRTR3INITEX)GetProcAddress(hMod, SUP_HARDENED_SYM("RTR3InitEx"));
1897 if (!pfnRTInitEx)
1898 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
1899 "Entrypoint \"RTR3InitEx\" not found in \"%s\" (rc=%d)",
1900 szPath, RtlGetLastWin32Error());
1901
1902 PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)GetProcAddress(hMod, SUP_HARDENED_SYM("supR3PreInit"));
1903 if (!pfnSUPPreInit)
1904 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
1905 "Entrypoint \"supR3PreInit\" not found in \"%s\" (rc=%d)",
1906 szPath, RtlGetLastWin32Error());
1907
1908 g_pfnRTLogRelPrintf = (PFNRTLOGRELPRINTF)GetProcAddress(hMod, SUP_HARDENED_SYM("RTLogRelPrintf"));
1909 Assert(g_pfnRTLogRelPrintf); /* Not fatal in non-strict builds. */
1910
1911#else
1912 /* the dlopen crowd */
1913 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
1914 if (!pvMod)
1915 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_MODULE_NOT_FOUND,
1916 "dlopen(\"%s\",) failed: %s",
1917 szPath, dlerror());
1918 PFNRTR3INITEX pfnRTInitEx = (PFNRTR3INITEX)(uintptr_t)dlsym(pvMod, SUP_HARDENED_SYM("RTR3InitEx"));
1919 if (!pfnRTInitEx)
1920 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
1921 "Entrypoint \"RTR3InitEx\" not found in \"%s\"!\ndlerror: %s",
1922 szPath, dlerror());
1923 PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)(uintptr_t)dlsym(pvMod, SUP_HARDENED_SYM("supR3PreInit"));
1924 if (!pfnSUPPreInit)
1925 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
1926 "Entrypoint \"supR3PreInit\" not found in \"%s\"!\ndlerror: %s",
1927 szPath, dlerror());
1928#endif
1929
1930 /*
1931 * Make the calls.
1932 */
1933 supR3HardenedGetPreInitData(&g_SupPreInitData);
1934 int rc = pfnSUPPreInit(&g_SupPreInitData, fFlags);
1935 if (RT_FAILURE(rc))
1936 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, rc,
1937 "supR3PreInit failed with rc=%d", rc);
1938 const char *pszExePath = NULL;
1939#ifdef RT_OS_LINUX
1940 if (!supR3HardenedMainIsProcSelfExeAccssible())
1941 pszExePath = g_szSupLibHardenedExePath;
1942#endif
1943 rc = pfnRTInitEx(RTR3INIT_VER_1,
1944 fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV ? 0 : RTR3INIT_FLAGS_SUPLIB,
1945 0 /*cArgs*/, NULL /*papszArgs*/, pszExePath);
1946 if (RT_FAILURE(rc))
1947 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, rc,
1948 "RTR3InitEx failed with rc=%d", rc);
1949
1950#if defined(RT_OS_WINDOWS)
1951 /*
1952 * Windows: Create thread that terminates the process when the parent stub
1953 * process terminates (VBoxNetDHCP, Ctrl-C, etc).
1954 */
1955 if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
1956 supR3HardenedWinCreateParentWatcherThread(hMod);
1957#endif
1958}
1959
1960
1961/**
1962 * Construct the path to the DLL/SO/DYLIB containing the actual program.
1963 *
1964 * @returns VBox status code.
1965 * @param pszProgName The program name.
1966 * @param fMainFlags The flags passed to SUPR3HardenedMain.
1967 * @param pszPath The output buffer.
1968 * @param cbPath The size of the output buffer, in bytes. Must be at
1969 * least 128 bytes!
1970 */
1971static int supR3HardenedMainGetTrustedLib(const char *pszProgName, uint32_t fMainFlags, char *pszPath, size_t cbPath)
1972{
1973 supR3HardenedPathAppPrivateArch(pszPath, sizeof(cbPath) - 10);
1974 const char *pszSubDirSlash;
1975 switch (g_fSupHardenedMain & SUPSECMAIN_FLAGS_LOC_MASK)
1976 {
1977 case SUPSECMAIN_FLAGS_LOC_APP_BIN:
1978 pszSubDirSlash = "/";
1979 break;
1980 case SUPSECMAIN_FLAGS_LOC_TESTCASE:
1981 pszSubDirSlash = "/testcase/";
1982 break;
1983 default:
1984 pszSubDirSlash = "/";
1985 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Unknown program binary location: %#x\n", g_fSupHardenedMain);
1986 }
1987#ifdef RT_OS_DARWIN
1988 if (fMainFlags & SUPSECMAIN_FLAGS_OSX_VM_APP)
1989 pszProgName = "VirtualBox";
1990#endif
1991 size_t cch = suplibHardenedStrLen(pszPath);
1992 return suplibHardenedStrCopyEx(&pszPath[cch], cbPath - cch, pszSubDirSlash, pszProgName, SUPLIB_DLL_SUFF, NULL);
1993}
1994
1995
1996/**
1997 * Loads the DLL/SO/DYLIB containing the actual program and
1998 * resolves the TrustedError symbol.
1999 *
2000 * This is very similar to supR3HardenedMainGetTrustedMain().
2001 *
2002 * @returns Pointer to the trusted error symbol if it is exported, NULL
2003 * and no error messages otherwise.
2004 * @param pszProgName The program name.
2005 */
2006static PFNSUPTRUSTEDERROR supR3HardenedMainGetTrustedError(const char *pszProgName)
2007{
2008 /*
2009 * Don't bother if the main() function didn't advertise any TrustedError
2010 * export. It's both a waste of time and may trigger additional problems,
2011 * confusing or obscuring the original issue.
2012 */
2013 if (!(g_fSupHardenedMain & SUPSECMAIN_FLAGS_TRUSTED_ERROR))
2014 return NULL;
2015
2016 /*
2017 * Construct the name.
2018 */
2019 char szPath[RTPATH_MAX];
2020 supR3HardenedMainGetTrustedLib(pszProgName, g_fSupHardenedMain, szPath, sizeof(szPath));
2021
2022 /*
2023 * Open it and resolve the symbol.
2024 */
2025#if defined(RT_OS_WINDOWS)
2026 supR3HardenedWinEnableThreadCreation();
2027 HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/, 0 /*fMainFlags*/);
2028 if (!hMod)
2029 return NULL;
2030 FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedError"));
2031 if (!pfn)
2032 return NULL;
2033 return (PFNSUPTRUSTEDERROR)pfn;
2034
2035#else
2036 /* the dlopen crowd */
2037 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
2038 if (!pvMod)
2039 return NULL;
2040 void *pvSym = dlsym(pvMod, SUP_HARDENED_SYM("TrustedError"));
2041 if (!pvSym)
2042 return NULL;
2043 return (PFNSUPTRUSTEDERROR)(uintptr_t)pvSym;
2044#endif
2045}
2046
2047
2048/**
2049 * Loads the DLL/SO/DYLIB containing the actual program and
2050 * resolves the TrustedMain symbol.
2051 *
2052 * @returns Pointer to the trusted main of the actual program.
2053 * @param pszProgName The program name.
2054 * @param fMainFlags The flags passed to SUPR3HardenedMain.
2055 * @remarks This function will not return on failure.
2056 */
2057static PFNSUPTRUSTEDMAIN supR3HardenedMainGetTrustedMain(const char *pszProgName, uint32_t fMainFlags)
2058{
2059 /*
2060 * Construct the name.
2061 */
2062 char szPath[RTPATH_MAX];
2063 supR3HardenedMainGetTrustedLib(pszProgName, fMainFlags, szPath, sizeof(szPath));
2064
2065 /*
2066 * Open it and resolve the symbol.
2067 */
2068#if defined(RT_OS_WINDOWS)
2069 HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/, 0 /*fMainFlags*/);
2070 if (!hMod)
2071 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: LoadLibrary \"%s\" failed, rc=%d\n",
2072 szPath, RtlGetLastWin32Error());
2073 FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedMain"));
2074 if (!pfn)
2075 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\" (rc=%d)\n",
2076 szPath, RtlGetLastWin32Error());
2077 return (PFNSUPTRUSTEDMAIN)pfn;
2078
2079#else
2080 /* the dlopen crowd */
2081 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
2082 if (!pvMod)
2083 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: dlopen(\"%s\",) failed: %s\n",
2084 szPath, dlerror());
2085 void *pvSym = dlsym(pvMod, SUP_HARDENED_SYM("TrustedMain"));
2086 if (!pvSym)
2087 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\"!\ndlerror: %s\n",
2088 szPath, dlerror());
2089 return (PFNSUPTRUSTEDMAIN)(uintptr_t)pvSym;
2090#endif
2091}
2092
2093
2094/**
2095 * Secure main.
2096 *
2097 * This is used for the set-user-ID-on-execute binaries on unixy systems
2098 * and when using the open-vboxdrv-via-root-service setup on Windows.
2099 *
2100 * This function will perform the integrity checks of the VirtualBox
2101 * installation, open the support driver, open the root service (later),
2102 * and load the DLL corresponding to \a pszProgName and execute its main
2103 * function.
2104 *
2105 * @returns Return code appropriate for main().
2106 *
2107 * @param pszProgName The program name. This will be used to figure out which
2108 * DLL/SO/DYLIB to load and execute.
2109 * @param fFlags Flags.
2110 * @param argc The argument count.
2111 * @param argv The argument vector.
2112 * @param envp The environment vector.
2113 */
2114DECLHIDDEN(int) SUPR3HardenedMain(const char *pszProgName, uint32_t fFlags, int argc, char **argv, char **envp)
2115{
2116 SUP_DPRINTF(("SUPR3HardenedMain: pszProgName=%s fFlags=%#x\n", pszProgName, fFlags));
2117 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_HARDENED_MAIN_CALLED;
2118
2119 /*
2120 * Note! At this point there is no IPRT, so we will have to stick
2121 * to basic CRT functions that everyone agree upon.
2122 */
2123 g_pszSupLibHardenedProgName = pszProgName;
2124 g_fSupHardenedMain = fFlags;
2125 g_SupPreInitData.u32Magic = SUPPREINITDATA_MAGIC;
2126 g_SupPreInitData.u32EndMagic = SUPPREINITDATA_MAGIC;
2127#ifdef RT_OS_WINDOWS
2128 if (!g_fSupEarlyProcessInit)
2129#endif
2130 g_SupPreInitData.Data.hDevice = SUP_HDEVICE_NIL;
2131
2132 /*
2133 * Determine the full exe path as we'll be needing it for the verify all
2134 * call(s) below. (We have to do this early on Linux because we * *might*
2135 * not be able to access /proc/self/exe after the seteuid call.)
2136 */
2137 supR3HardenedGetFullExePath();
2138#ifdef RT_OS_WINDOWS
2139 supR3HardenedWinInitAppBin(fFlags);
2140#endif
2141
2142#ifdef SUP_HARDENED_SUID
2143 /*
2144 * Grab any options from the environment.
2145 */
2146 supR3GrabOptions();
2147
2148 /*
2149 * Check that we're root, if we aren't then the installation is butchered.
2150 */
2151 g_uid = getuid();
2152 g_gid = getgid();
2153 if (geteuid() != 0 /* root */)
2154 supR3HardenedFatalMsg("SUPR3HardenedMain", kSupInitOp_RootCheck, VERR_PERMISSION_DENIED,
2155 "Effective UID is not root (euid=%d egid=%d uid=%d gid=%d)",
2156 geteuid(), getegid(), g_uid, g_gid);
2157#endif /* SUP_HARDENED_SUID */
2158
2159#ifdef RT_OS_WINDOWS
2160 /*
2161 * Windows: First respawn. On Windows we will respawn the process twice to establish
2162 * something we can put some kind of reliable trust in. The first respawning aims
2163 * at dropping compatibility layers and process "security" solutions.
2164 */
2165 if ( !g_fSupEarlyProcessInit
2166 && !(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)
2167 && supR3HardenedWinIsReSpawnNeeded(1 /*iWhich*/, argc, argv))
2168 {
2169 SUP_DPRINTF(("SUPR3HardenedMain: Respawn #1\n"));
2170 supR3HardenedWinInit(SUPSECMAIN_FLAGS_DONT_OPEN_DEV, false /*fAvastKludge*/);
2171 supR3HardenedVerifyAll(true /* fFatal */, pszProgName, g_szSupLibHardenedExePath, fFlags);
2172 return supR3HardenedWinReSpawn(1 /*iWhich*/);
2173 }
2174
2175 /*
2176 * Windows: Initialize the image verification global data so we can verify the
2177 * signature of the process image and hook the core of the DLL loader API so we
2178 * can check the signature of all DLLs mapped into the process. (Already done
2179 * by early VM process init.)
2180 */
2181 if (!g_fSupEarlyProcessInit)
2182 supR3HardenedWinInit(fFlags, true /*fAvastKludge*/);
2183#endif /* RT_OS_WINDOWS */
2184
2185 /*
2186 * Validate the installation.
2187 */
2188 supR3HardenedVerifyAll(true /* fFatal */, pszProgName, g_szSupLibHardenedExePath, fFlags);
2189
2190 /*
2191 * The next steps are only taken if we actually need to access the support
2192 * driver. (Already done by early process init.)
2193 */
2194 if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
2195 {
2196#ifdef RT_OS_WINDOWS
2197 /*
2198 * Windows: Must have done early process init if we get here.
2199 */
2200 if (!g_fSupEarlyProcessInit)
2201 supR3HardenedFatalMsg("SUPR3HardenedMain", kSupInitOp_Integrity, VERR_WRONG_ORDER,
2202 "Early process init was somehow skipped.");
2203
2204 /*
2205 * Windows: The second respawn. This time we make a special arrangement
2206 * with vboxdrv to monitor access to the new process from its inception.
2207 */
2208 if (supR3HardenedWinIsReSpawnNeeded(2 /* iWhich*/, argc, argv))
2209 {
2210 SUP_DPRINTF(("SUPR3HardenedMain: Respawn #2\n"));
2211 return supR3HardenedWinReSpawn(2 /* iWhich*/);
2212 }
2213 SUP_DPRINTF(("SUPR3HardenedMain: Final process, opening VBoxDrv...\n"));
2214 supR3HardenedWinFlushLoaderCache();
2215
2216#else
2217 /*
2218 * Open the vboxdrv device.
2219 */
2220 supR3HardenedMainOpenDevice();
2221#endif /* !RT_OS_WINDOWS */
2222 }
2223
2224#ifdef RT_OS_WINDOWS
2225 /*
2226 * Windows: Enable the use of windows APIs to verify images at load time.
2227 */
2228 supR3HardenedWinEnableThreadCreation();
2229 supR3HardenedWinFlushLoaderCache();
2230 supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation(g_pszSupLibHardenedProgName);
2231 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_VERIFY_TRUST_READY;
2232#endif
2233
2234#ifdef SUP_HARDENED_SUID
2235 /*
2236 * Grab additional capabilities / privileges.
2237 */
2238 supR3HardenedMainGrabCapabilites();
2239
2240 /*
2241 * Drop any root privileges we might be holding (won't return on failure)
2242 */
2243 supR3HardenedMainDropPrivileges();
2244#endif
2245
2246 /*
2247 * Load the IPRT, hand the SUPLib part the open driver and
2248 * call RTR3InitEx.
2249 */
2250 SUP_DPRINTF(("SUPR3HardenedMain: Load Runtime...\n"));
2251 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_INIT_RUNTIME;
2252 supR3HardenedMainInitRuntime(fFlags);
2253#ifdef RT_OS_WINDOWS
2254 supR3HardenedWinModifyDllSearchPath(fFlags, g_szSupLibHardenedAppBinPath);
2255#endif
2256
2257 /*
2258 * Load the DLL/SO/DYLIB containing the actual program
2259 * and pass control to it.
2260 */
2261 SUP_DPRINTF(("SUPR3HardenedMain: Load TrustedMain...\n"));
2262 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_GET_TRUSTED_MAIN;
2263 PFNSUPTRUSTEDMAIN pfnTrustedMain = supR3HardenedMainGetTrustedMain(pszProgName, fFlags);
2264
2265 SUP_DPRINTF(("SUPR3HardenedMain: Calling TrustedMain (%p)...\n", pfnTrustedMain));
2266 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_CALLED_TRUSTED_MAIN;
2267 return pfnTrustedMain(argc, argv, envp);
2268}
2269
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette