VirtualBox

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

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

SUP: typo nits.

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