VirtualBox

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

Revision 66858, 94.9 KB checked in by vboxsync, 5 months ago (diff)

HostDrivers/Support: fall-thru

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

www.oracle.com
ContactPrivacy policyTerms of Use