VirtualBox

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

Last change on this file since 83033 was 83033, checked in by vboxsync, 5 years ago

/Config.kmk,/Makefile.kmk,Installer/darwin,FE/QT,SUPHard: Eliminated the symlinks under Resources/VirtualBoxVM.app to avoid confusing codesign and notarization. bugref:9466
Note! Best to do a clean build on mac (or at a minimum rm -Rf out/darwin.amd64/*/dist/VirtualBox.app/Contents/Frameworks/ out/darwin.amd64/*/dist/VirtualBox.app/Contents/plugins/).

  • SUPHard: Added new location SUPSECMAIN_FLAGS_LOC_OSX_HLP_APP (darwin only) for indicating a helper application and that hardened dylib and the rest of VirtualBox isn't in the executable directory.
  • SUPHard: Added a corresponding internal path ID kSupID_AppMacHelper (darwin only) for finding VirtualBoxVM for verification now that it is no longer in the main app binary directory.
  • /Config.kmk: Run install_name_tool after linking Qt binaries on darwin fix the Qt import paths (@executable_path/../Frameworks/ doesn't work for VirtualBoxVM any more).
  • /Makefile.kmk: Run install_name_tool during installation of Qt frameworks and plugins to use @rpath in non-hardened builds and the absolute paths in hardened ones (latter done by Installer/darwin/Makefile.kmk previously).
  • FE/Qt,Installer/darwin: Adjustments and path hacks.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 98.7 KB
Line 
1/* $Id: SUPR3HardenedMain.cpp 83033 2020-02-10 13:40:54Z vboxsync $ */
2/** @file
3 * VirtualBox Support Library - Hardened main().
4 */
5
6/*
7 * Copyright (C) 2006-2020 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# include <iprt/utf16.h>
449#endif
450#include <iprt/ctype.h>
451#include <iprt/string.h>
452#include <iprt/initterm.h>
453#include <iprt/param.h>
454#include <iprt/path.h>
455
456#include "SUPLibInternal.h"
457
458
459/*********************************************************************************************************************************
460* Defined Constants And Macros *
461*********************************************************************************************************************************/
462/* This mess is temporary after eliminating a define duplicated in SUPLibInternal.h. */
463#if !defined(RT_OS_OS2) && !defined(RT_OS_WINDOWS) && !defined(RT_OS_L4)
464# ifndef SUP_HARDENED_SUID
465# error "SUP_HARDENED_SUID is NOT defined?!?"
466# endif
467#else
468# ifdef SUP_HARDENED_SUID
469# error "SUP_HARDENED_SUID is defined?!?"
470# endif
471#endif
472
473/** @def SUP_HARDENED_SYM
474 * Decorate a symbol that's resolved dynamically.
475 */
476#ifdef RT_OS_OS2
477# define SUP_HARDENED_SYM(sym) "_" sym
478#else
479# define SUP_HARDENED_SYM(sym) sym
480#endif
481
482
483/*********************************************************************************************************************************
484* Structures and Typedefs *
485*********************************************************************************************************************************/
486/** @see RTR3InitEx */
487typedef DECLCALLBACK(int) FNRTR3INITEX(uint32_t iVersion, uint32_t fFlags, int cArgs,
488 char **papszArgs, const char *pszProgramPath);
489typedef FNRTR3INITEX *PFNRTR3INITEX;
490
491/** @see RTLogRelPrintf */
492typedef DECLCALLBACK(void) FNRTLOGRELPRINTF(const char *pszFormat, ...);
493typedef FNRTLOGRELPRINTF *PFNRTLOGRELPRINTF;
494
495
496/**
497 * Descriptor of an environment variable to purge.
498 */
499typedef struct SUPENVPURGEDESC
500{
501 /** Name of the environment variable to purge. */
502 const char *pszEnv;
503 /** The length of the variable name. */
504 uint8_t cchEnv;
505 /** Flag whether a failure in purging the variable leads to
506 * a fatal error resulting in an process exit. */
507 bool fPurgeErrFatal;
508} SUPENVPURGEDESC;
509/** Pointer to a environment variable purge descriptor. */
510typedef SUPENVPURGEDESC *PSUPENVPURGEDESC;
511/** Pointer to a const environment variable purge descriptor. */
512typedef const SUPENVPURGEDESC *PCSUPENVPURGEDESC;
513
514/**
515 * Descriptor of an command line argument to purge.
516 */
517typedef struct SUPARGPURGEDESC
518{
519 /** Name of the argument to purge. */
520 const char *pszArg;
521 /** The length of the argument name. */
522 uint8_t cchArg;
523 /** Flag whether the argument is followed by an extra argument
524 * which must be purged too */
525 bool fTakesValue;
526} SUPARGPURGEDESC;
527/** Pointer to a environment variable purge descriptor. */
528typedef SUPARGPURGEDESC *PSUPARGPURGEDESC;
529/** Pointer to a const environment variable purge descriptor. */
530typedef const SUPARGPURGEDESC *PCSUPARGPURGEDESC;
531
532
533/*********************************************************************************************************************************
534* Global Variables *
535*********************************************************************************************************************************/
536/** The pre-init data we pass on to SUPR3 (residing in VBoxRT). */
537static SUPPREINITDATA g_SupPreInitData;
538/** The program executable path. */
539#ifndef RT_OS_WINDOWS
540static
541#endif
542char g_szSupLibHardenedExePath[RTPATH_MAX];
543/** The application bin directory path. */
544static char g_szSupLibHardenedAppBinPath[RTPATH_MAX];
545/** The offset into g_szSupLibHardenedExePath of the executable name. */
546static size_t g_offSupLibHardenedExecName;
547/** The length of the executable name in g_szSupLibHardenedExePath. */
548static size_t g_cchSupLibHardenedExecName;
549
550/** The program name. */
551static const char *g_pszSupLibHardenedProgName;
552/** The flags passed to SUPR3HardenedMain. */
553static uint32_t g_fSupHardenedMain;
554
555#ifdef SUP_HARDENED_SUID
556/** The real UID at startup. */
557static uid_t g_uid;
558/** The real GID at startup. */
559static gid_t g_gid;
560# ifdef RT_OS_LINUX
561static uint32_t g_uCaps;
562static uint32_t g_uCapsVersion;
563# endif
564#endif
565
566/** The startup log file. */
567#ifdef RT_OS_WINDOWS
568static HANDLE g_hStartupLog = NULL;
569#else
570static int g_hStartupLog = -1;
571#endif
572/** The number of bytes we've written to the startup log. */
573static uint32_t volatile g_cbStartupLog = 0;
574
575/** The current SUPR3HardenedMain state / location. */
576SUPR3HARDENEDMAINSTATE g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED;
577AssertCompileSize(g_enmSupR3HardenedMainState, sizeof(uint32_t));
578
579#ifdef RT_OS_WINDOWS
580/** Pointer to VBoxRT's RTLogRelPrintf function so we can write errors to the
581 * release log at runtime. */
582static PFNRTLOGRELPRINTF g_pfnRTLogRelPrintf = NULL;
583/** Log volume name (for attempting volume flush). */
584static RTUTF16 g_wszStartupLogVol[16];
585#endif
586
587/** Environment variables to purge from the process because
588 * they are known to be harmful. */
589static const SUPENVPURGEDESC g_aSupEnvPurgeDescs[] =
590{
591 /* pszEnv fPurgeErrFatal */
592 /* Qt related environment variables: */
593 { RT_STR_TUPLE("QT_QPA_PLATFORM_PLUGIN_PATH"), true },
594 { RT_STR_TUPLE("QT_PLUGIN_PATH"), true },
595 /* ALSA related environment variables: */
596 { RT_STR_TUPLE("ALSA_MIXER_SIMPLE_MODULES"), true },
597 { RT_STR_TUPLE("LADSPA_PATH"), true },
598};
599
600/** Arguments to purge from the argument vector because
601 * they are known to be harmful. */
602static const SUPARGPURGEDESC g_aSupArgPurgeDescs[] =
603{
604 /* pszArg fTakesValue */
605 /* Qt related environment variables: */
606 { RT_STR_TUPLE("-platformpluginpath"), true },
607};
608
609
610/*********************************************************************************************************************************
611* Internal Functions *
612*********************************************************************************************************************************/
613#ifdef SUP_HARDENED_SUID
614static void supR3HardenedMainDropPrivileges(void);
615#endif
616static PFNSUPTRUSTEDERROR supR3HardenedMainGetTrustedError(const char *pszProgName);
617
618
619/**
620 * Safely copy one or more strings into the given buffer.
621 *
622 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
623 * @param pszDst The destionation buffer.
624 * @param cbDst The size of the destination buffer.
625 * @param ... One or more zero terminated strings, ending with
626 * a NULL.
627 */
628static int suplibHardenedStrCopyEx(char *pszDst, size_t cbDst, ...)
629{
630 int rc = VINF_SUCCESS;
631
632 if (cbDst == 0)
633 return VERR_BUFFER_OVERFLOW;
634
635 va_list va;
636 va_start(va, cbDst);
637 for (;;)
638 {
639 const char *pszSrc = va_arg(va, const char *);
640 if (!pszSrc)
641 break;
642
643 size_t cchSrc = suplibHardenedStrLen(pszSrc);
644 if (cchSrc < cbDst)
645 {
646 suplibHardenedMemCopy(pszDst, pszSrc, cchSrc);
647 pszDst += cchSrc;
648 cbDst -= cchSrc;
649 }
650 else
651 {
652 rc = VERR_BUFFER_OVERFLOW;
653 if (cbDst > 1)
654 {
655 suplibHardenedMemCopy(pszDst, pszSrc, cbDst - 1);
656 pszDst += cbDst - 1;
657 cbDst = 1;
658 }
659 }
660 *pszDst = '\0';
661 }
662 va_end(va);
663
664 return rc;
665}
666
667
668/**
669 * Exit current process in the quickest possible fashion.
670 *
671 * @param rcExit The exit code.
672 */
673DECLNORETURN(void) suplibHardenedExit(RTEXITCODE rcExit)
674{
675 for (;;)
676 {
677#ifdef RT_OS_WINDOWS
678 if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
679 ExitProcess(rcExit);
680 if (RtlExitUserProcess != NULL)
681 RtlExitUserProcess(rcExit);
682 NtTerminateProcess(NtCurrentProcess(), rcExit);
683#else
684 _Exit(rcExit);
685#endif
686 }
687}
688
689
690/**
691 * Writes a substring to standard error.
692 *
693 * @param pch The start of the substring.
694 * @param cch The length of the substring.
695 */
696static void suplibHardenedPrintStrN(const char *pch, size_t cch)
697{
698#ifdef RT_OS_WINDOWS
699 HANDLE hStdOut = NtCurrentPeb()->ProcessParameters->StandardOutput;
700 if (hStdOut != NULL)
701 {
702 if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
703 {
704 DWORD cbWritten;
705 WriteFile(hStdOut, pch, (DWORD)cch, &cbWritten, NULL);
706 }
707 /* Windows 7 and earlier uses fake handles, with the last two bits set ((hStdOut & 3) == 3). */
708 else if (NtWriteFile != NULL && ((uintptr_t)hStdOut & 3) == 0)
709 {
710 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
711 NtWriteFile(hStdOut, NULL /*Event*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/,
712 &Ios, (PVOID)pch, (ULONG)cch, NULL /*ByteOffset*/, NULL /*Key*/);
713 }
714 }
715#else
716 int res = write(2, pch, cch);
717 NOREF(res);
718#endif
719}
720
721
722/**
723 * Writes a string to standard error.
724 *
725 * @param psz The string.
726 */
727static void suplibHardenedPrintStr(const char *psz)
728{
729 suplibHardenedPrintStrN(psz, suplibHardenedStrLen(psz));
730}
731
732
733/**
734 * Writes a char to standard error.
735 *
736 * @param ch The character value to write.
737 */
738static void suplibHardenedPrintChr(char ch)
739{
740 suplibHardenedPrintStrN(&ch, 1);
741}
742
743#ifndef IPRT_NO_CRT
744
745/**
746 * Writes a decimal number to stdard error.
747 *
748 * @param uValue The value.
749 */
750static void suplibHardenedPrintDecimal(uint64_t uValue)
751{
752 char szBuf[64];
753 char *pszEnd = &szBuf[sizeof(szBuf) - 1];
754 char *psz = pszEnd;
755
756 *psz-- = '\0';
757
758 do
759 {
760 *psz-- = '0' + (uValue % 10);
761 uValue /= 10;
762 } while (uValue > 0);
763
764 psz++;
765 suplibHardenedPrintStrN(psz, pszEnd - psz);
766}
767
768
769/**
770 * Writes a hexadecimal or octal number to standard error.
771 *
772 * @param uValue The value.
773 * @param uBase The base (16 or 8).
774 * @param fFlags Format flags.
775 */
776static void suplibHardenedPrintHexOctal(uint64_t uValue, unsigned uBase, uint32_t fFlags)
777{
778 static char const s_achDigitsLower[17] = "0123456789abcdef";
779 static char const s_achDigitsUpper[17] = "0123456789ABCDEF";
780 const char *pchDigits = !(fFlags & RTSTR_F_CAPITAL) ? s_achDigitsLower : s_achDigitsUpper;
781 unsigned cShift = uBase == 16 ? 4 : 3;
782 unsigned fDigitMask = uBase == 16 ? 0xf : 7;
783 char szBuf[64];
784 char *pszEnd = &szBuf[sizeof(szBuf) - 1];
785 char *psz = pszEnd;
786
787 *psz-- = '\0';
788
789 do
790 {
791 *psz-- = pchDigits[uValue & fDigitMask];
792 uValue >>= cShift;
793 } while (uValue > 0);
794
795 if ((fFlags & RTSTR_F_SPECIAL) && uBase == 16)
796 {
797 *psz-- = !(fFlags & RTSTR_F_CAPITAL) ? 'x' : 'X';
798 *psz-- = '0';
799 }
800
801 psz++;
802 suplibHardenedPrintStrN(psz, pszEnd - psz);
803}
804
805
806/**
807 * Writes a wide character string to standard error.
808 *
809 * @param pwsz The string.
810 */
811static void suplibHardenedPrintWideStr(PCRTUTF16 pwsz)
812{
813 for (;;)
814 {
815 RTUTF16 wc = *pwsz++;
816 if (!wc)
817 return;
818 if ( (wc < 0x7f && wc >= 0x20)
819 || wc == '\n'
820 || wc == '\r')
821 suplibHardenedPrintChr((char)wc);
822 else
823 {
824 suplibHardenedPrintStrN(RT_STR_TUPLE("\\x"));
825 suplibHardenedPrintHexOctal(wc, 16, 0);
826 }
827 }
828}
829
830#else /* IPRT_NO_CRT */
831
832/** Buffer structure used by suplibHardenedOutput. */
833struct SUPLIBHARDENEDOUTPUTBUF
834{
835 size_t off;
836 char szBuf[2048];
837};
838
839/** Callback for RTStrFormatV, see FNRTSTROUTPUT. */
840static DECLCALLBACK(size_t) suplibHardenedOutput(void *pvArg, const char *pachChars, size_t cbChars)
841{
842 SUPLIBHARDENEDOUTPUTBUF *pBuf = (SUPLIBHARDENEDOUTPUTBUF *)pvArg;
843 size_t cbTodo = cbChars;
844 for (;;)
845 {
846 size_t cbSpace = sizeof(pBuf->szBuf) - pBuf->off - 1;
847
848 /* Flush the buffer? */
849 if ( cbSpace == 0
850 || (cbTodo == 0 && pBuf->off))
851 {
852 suplibHardenedPrintStrN(pBuf->szBuf, pBuf->off);
853# ifdef RT_OS_WINDOWS
854 if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
855 OutputDebugString(pBuf->szBuf);
856# endif
857 pBuf->off = 0;
858 cbSpace = sizeof(pBuf->szBuf) - 1;
859 }
860
861 /* Copy the string into the buffer. */
862 if (cbTodo == 1)
863 {
864 pBuf->szBuf[pBuf->off++] = *pachChars;
865 break;
866 }
867 if (cbSpace >= cbTodo)
868 {
869 memcpy(&pBuf->szBuf[pBuf->off], pachChars, cbTodo);
870 pBuf->off += cbTodo;
871 break;
872 }
873 memcpy(&pBuf->szBuf[pBuf->off], pachChars, cbSpace);
874 pBuf->off += cbSpace;
875 cbTodo -= cbSpace;
876 }
877 pBuf->szBuf[pBuf->off] = '\0';
878
879 return cbChars;
880}
881
882#endif /* IPRT_NO_CRT */
883
884/**
885 * Simple printf to standard error.
886 *
887 * @param pszFormat The format string.
888 * @param va Arguments to format.
889 */
890DECLHIDDEN(void) suplibHardenedPrintFV(const char *pszFormat, va_list va)
891{
892#ifdef IPRT_NO_CRT
893 /*
894 * Use buffered output here to avoid character mixing on the windows
895 * console and to enable us to use OutputDebugString.
896 */
897 SUPLIBHARDENEDOUTPUTBUF Buf;
898 Buf.off = 0;
899 Buf.szBuf[0] = '\0';
900 RTStrFormatV(suplibHardenedOutput, &Buf, NULL, NULL, pszFormat, va);
901
902#else /* !IPRT_NO_CRT */
903 /*
904 * Format loop.
905 */
906 char ch;
907 const char *pszLast = pszFormat;
908 for (;;)
909 {
910 ch = *pszFormat;
911 if (!ch)
912 break;
913 pszFormat++;
914
915 if (ch == '%')
916 {
917 /*
918 * Format argument.
919 */
920
921 /* Flush unwritten bits. */
922 if (pszLast != pszFormat - 1)
923 suplibHardenedPrintStrN(pszLast, pszFormat - pszLast - 1);
924 pszLast = pszFormat;
925 ch = *pszFormat++;
926
927 /* flags. */
928 uint32_t fFlags = 0;
929 for (;;)
930 {
931 if (ch == '#') fFlags |= RTSTR_F_SPECIAL;
932 else if (ch == '-') fFlags |= RTSTR_F_LEFT;
933 else if (ch == '+') fFlags |= RTSTR_F_PLUS;
934 else if (ch == ' ') fFlags |= RTSTR_F_BLANK;
935 else if (ch == '0') fFlags |= RTSTR_F_ZEROPAD;
936 else if (ch == '\'') fFlags |= RTSTR_F_THOUSAND_SEP;
937 else break;
938 ch = *pszFormat++;
939 }
940
941 /* Width and precision - ignored. */
942 while (RT_C_IS_DIGIT(ch))
943 ch = *pszFormat++;
944 if (ch == '*')
945 va_arg(va, int);
946 if (ch == '.')
947 {
948 do ch = *pszFormat++;
949 while (RT_C_IS_DIGIT(ch));
950 if (ch == '*')
951 va_arg(va, int);
952 }
953
954 /* Size. */
955 char chArgSize = 0;
956 switch (ch)
957 {
958 case 'z':
959 case 'L':
960 case 'j':
961 case 't':
962 chArgSize = ch;
963 ch = *pszFormat++;
964 break;
965
966 case 'l':
967 chArgSize = ch;
968 ch = *pszFormat++;
969 if (ch == 'l')
970 {
971 chArgSize = 'L';
972 ch = *pszFormat++;
973 }
974 break;
975
976 case 'h':
977 chArgSize = ch;
978 ch = *pszFormat++;
979 if (ch == 'h')
980 {
981 chArgSize = 'H';
982 ch = *pszFormat++;
983 }
984 break;
985 }
986
987 /*
988 * Do type specific formatting.
989 */
990 switch (ch)
991 {
992 case 'c':
993 ch = (char)va_arg(va, int);
994 suplibHardenedPrintChr(ch);
995 break;
996
997 case 's':
998 if (chArgSize == 'l')
999 {
1000 PCRTUTF16 pwszStr = va_arg(va, PCRTUTF16 );
1001 if (RT_VALID_PTR(pwszStr))
1002 suplibHardenedPrintWideStr(pwszStr);
1003 else
1004 suplibHardenedPrintStr("<NULL>");
1005 }
1006 else
1007 {
1008 const char *pszStr = va_arg(va, const char *);
1009 if (!RT_VALID_PTR(pszStr))
1010 pszStr = "<NULL>";
1011 suplibHardenedPrintStr(pszStr);
1012 }
1013 break;
1014
1015 case 'd':
1016 case 'i':
1017 {
1018 int64_t iValue;
1019 if (chArgSize == 'L' || chArgSize == 'j')
1020 iValue = va_arg(va, int64_t);
1021 else if (chArgSize == 'l')
1022 iValue = va_arg(va, signed long);
1023 else if (chArgSize == 'z' || chArgSize == 't')
1024 iValue = va_arg(va, intptr_t);
1025 else
1026 iValue = va_arg(va, signed int);
1027 if (iValue < 0)
1028 {
1029 suplibHardenedPrintChr('-');
1030 iValue = -iValue;
1031 }
1032 suplibHardenedPrintDecimal(iValue);
1033 break;
1034 }
1035
1036 case 'p':
1037 case 'x':
1038 case 'X':
1039 case 'u':
1040 case 'o':
1041 {
1042 unsigned uBase = 10;
1043 uint64_t uValue;
1044
1045 switch (ch)
1046 {
1047 case 'p':
1048 fFlags |= RTSTR_F_ZEROPAD; /* Note not standard behaviour (but I like it this way!) */
1049 uBase = 16;
1050 break;
1051 case 'X':
1052 fFlags |= RTSTR_F_CAPITAL;
1053 RT_FALL_THRU();
1054 case 'x':
1055 uBase = 16;
1056 break;
1057 case 'u':
1058 uBase = 10;
1059 break;
1060 case 'o':
1061 uBase = 8;
1062 break;
1063 }
1064
1065 if (ch == 'p' || chArgSize == 'z' || chArgSize == 't')
1066 uValue = va_arg(va, uintptr_t);
1067 else if (chArgSize == 'L' || chArgSize == 'j')
1068 uValue = va_arg(va, uint64_t);
1069 else if (chArgSize == 'l')
1070 uValue = va_arg(va, unsigned long);
1071 else
1072 uValue = va_arg(va, unsigned int);
1073
1074 if (uBase == 10)
1075 suplibHardenedPrintDecimal(uValue);
1076 else
1077 suplibHardenedPrintHexOctal(uValue, uBase, fFlags);
1078 break;
1079 }
1080
1081 case 'R':
1082 if (pszFormat[0] == 'r' && pszFormat[1] == 'c')
1083 {
1084 int iValue = va_arg(va, int);
1085 if (iValue < 0)
1086 {
1087 suplibHardenedPrintChr('-');
1088 iValue = -iValue;
1089 }
1090 suplibHardenedPrintDecimal(iValue);
1091 pszFormat += 2;
1092 break;
1093 }
1094 RT_FALL_THRU();
1095
1096 /*
1097 * Custom format.
1098 */
1099 default:
1100 suplibHardenedPrintStr("[bad format: ");
1101 suplibHardenedPrintStrN(pszLast, pszFormat - pszLast);
1102 suplibHardenedPrintChr(']');
1103 break;
1104 }
1105
1106 /* continue */
1107 pszLast = pszFormat;
1108 }
1109 }
1110
1111 /* Flush the last bits of the string. */
1112 if (pszLast != pszFormat)
1113 suplibHardenedPrintStrN(pszLast, pszFormat - pszLast);
1114#endif /* !IPRT_NO_CRT */
1115}
1116
1117
1118/**
1119 * Prints to standard error.
1120 *
1121 * @param pszFormat The format string.
1122 * @param ... Arguments to format.
1123 */
1124DECLHIDDEN(void) suplibHardenedPrintF(const char *pszFormat, ...)
1125{
1126 va_list va;
1127 va_start(va, pszFormat);
1128 suplibHardenedPrintFV(pszFormat, va);
1129 va_end(va);
1130}
1131
1132
1133/**
1134 * @copydoc RTPathStripFilename
1135 */
1136static void suplibHardenedPathStripFilename(char *pszPath)
1137{
1138 char *psz = pszPath;
1139 char *pszLastSep = pszPath;
1140
1141 for (;; psz++)
1142 {
1143 switch (*psz)
1144 {
1145 /* handle separators. */
1146#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
1147 case ':':
1148 pszLastSep = psz + 1;
1149 break;
1150
1151 case '\\':
1152#endif
1153 case '/':
1154 pszLastSep = psz;
1155 break;
1156
1157 /* the end */
1158 case '\0':
1159 if (pszLastSep == pszPath)
1160 *pszLastSep++ = '.';
1161 *pszLastSep = '\0';
1162 return;
1163 }
1164 }
1165 /* will never get here */
1166}
1167
1168
1169/**
1170 * @copydoc RTPathFilename
1171 */
1172DECLHIDDEN(char *) supR3HardenedPathFilename(const char *pszPath)
1173{
1174 const char *psz = pszPath;
1175 const char *pszLastComp = pszPath;
1176
1177 for (;; psz++)
1178 {
1179 switch (*psz)
1180 {
1181 /* handle separators. */
1182#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
1183 case ':':
1184 pszLastComp = psz + 1;
1185 break;
1186
1187 case '\\':
1188#endif
1189 case '/':
1190 pszLastComp = psz + 1;
1191 break;
1192
1193 /* the end */
1194 case '\0':
1195 if (*pszLastComp)
1196 return (char *)(void *)pszLastComp;
1197 return NULL;
1198 }
1199 }
1200
1201 /* will never get here */
1202}
1203
1204
1205/**
1206 * @copydoc RTPathAppPrivateNoArch
1207 */
1208DECLHIDDEN(int) supR3HardenedPathAppPrivateNoArch(char *pszPath, size_t cchPath)
1209{
1210#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE)
1211 const char *pszSrcPath = RTPATH_APP_PRIVATE;
1212 size_t cchPathPrivateNoArch = suplibHardenedStrLen(pszSrcPath);
1213 if (cchPathPrivateNoArch >= cchPath)
1214 supR3HardenedFatal("supR3HardenedPathAppPrivateNoArch: Buffer overflow, %zu >= %zu\n", cchPathPrivateNoArch, cchPath);
1215 suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathPrivateNoArch + 1);
1216 return VINF_SUCCESS;
1217
1218#else
1219 return supR3HardenedPathAppBin(pszPath, cchPath);
1220#endif
1221}
1222
1223
1224/**
1225 * @copydoc RTPathAppPrivateArch
1226 */
1227DECLHIDDEN(int) supR3HardenedPathAppPrivateArch(char *pszPath, size_t cchPath)
1228{
1229#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE_ARCH)
1230 const char *pszSrcPath = RTPATH_APP_PRIVATE_ARCH;
1231 size_t cchPathPrivateArch = suplibHardenedStrLen(pszSrcPath);
1232 if (cchPathPrivateArch >= cchPath)
1233 supR3HardenedFatal("supR3HardenedPathAppPrivateArch: Buffer overflow, %zu >= %zu\n", cchPathPrivateArch, cchPath);
1234 suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathPrivateArch + 1);
1235 return VINF_SUCCESS;
1236
1237#else
1238 return supR3HardenedPathAppBin(pszPath, cchPath);
1239#endif
1240}
1241
1242
1243/**
1244 * @copydoc RTPathSharedLibs
1245 */
1246DECLHIDDEN(int) supR3HardenedPathAppSharedLibs(char *pszPath, size_t cchPath)
1247{
1248#if !defined(RT_OS_WINDOWS) && defined(RTPATH_SHARED_LIBS)
1249 const char *pszSrcPath = RTPATH_SHARED_LIBS;
1250 size_t cchPathSharedLibs = suplibHardenedStrLen(pszSrcPath);
1251 if (cchPathSharedLibs >= cchPath)
1252 supR3HardenedFatal("supR3HardenedPathAppSharedLibs: Buffer overflow, %zu >= %zu\n", cchPathSharedLibs, cchPath);
1253 suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathSharedLibs + 1);
1254 return VINF_SUCCESS;
1255
1256#else
1257 return supR3HardenedPathAppBin(pszPath, cchPath);
1258#endif
1259}
1260
1261
1262/**
1263 * @copydoc RTPathAppDocs
1264 */
1265DECLHIDDEN(int) supR3HardenedPathAppDocs(char *pszPath, size_t cchPath)
1266{
1267#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_DOCS)
1268 const char *pszSrcPath = RTPATH_APP_DOCS;
1269 size_t cchPathAppDocs = suplibHardenedStrLen(pszSrcPath);
1270 if (cchPathAppDocs >= cchPath)
1271 supR3HardenedFatal("supR3HardenedPathAppDocs: Buffer overflow, %zu >= %zu\n", cchPathAppDocs, cchPath);
1272 suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathAppDocs + 1);
1273 return VINF_SUCCESS;
1274
1275#else
1276 return supR3HardenedPathAppBin(pszPath, cchPath);
1277#endif
1278}
1279
1280
1281/**
1282 * Returns the full path to the executable in g_szSupLibHardenedExePath.
1283 *
1284 * @returns IPRT status code.
1285 */
1286static void supR3HardenedGetFullExePath(void)
1287{
1288 /*
1289 * Get the program filename.
1290 *
1291 * Most UNIXes have no API for obtaining the executable path, but provides a symbolic
1292 * link in the proc file system that tells who was exec'ed. The bad thing about this
1293 * is that we have to use readlink, one of the weirder UNIX APIs.
1294 *
1295 * Darwin, OS/2 and Windows all have proper APIs for getting the program file name.
1296 */
1297#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_SOLARIS)
1298# ifdef RT_OS_LINUX
1299 int cchLink = readlink("/proc/self/exe", &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath) - 1);
1300
1301# elif defined(RT_OS_SOLARIS)
1302 char szFileBuf[PATH_MAX + 1];
1303 sprintf(szFileBuf, "/proc/%ld/path/a.out", (long)getpid());
1304 int cchLink = readlink(szFileBuf, &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath) - 1);
1305
1306# else /* RT_OS_FREEBSD */
1307 int aiName[4];
1308 aiName[0] = CTL_KERN;
1309 aiName[1] = KERN_PROC;
1310 aiName[2] = KERN_PROC_PATHNAME;
1311 aiName[3] = getpid();
1312
1313 size_t cbPath = sizeof(g_szSupLibHardenedExePath);
1314 if (sysctl(aiName, RT_ELEMENTS(aiName), g_szSupLibHardenedExePath, &cbPath, NULL, 0) < 0)
1315 supR3HardenedFatal("supR3HardenedExecDir: sysctl failed\n");
1316 g_szSupLibHardenedExePath[sizeof(g_szSupLibHardenedExePath) - 1] = '\0';
1317 int cchLink = suplibHardenedStrLen(g_szSupLibHardenedExePath); /* paranoid? can't we use cbPath? */
1318
1319# endif
1320 if (cchLink < 0 || cchLink == sizeof(g_szSupLibHardenedExePath) - 1)
1321 supR3HardenedFatal("supR3HardenedExecDir: couldn't read \"%s\", errno=%d cchLink=%d\n",
1322 g_szSupLibHardenedExePath, errno, cchLink);
1323 g_szSupLibHardenedExePath[cchLink] = '\0';
1324
1325#elif defined(RT_OS_OS2) || defined(RT_OS_L4)
1326 _execname(g_szSupLibHardenedExePath, sizeof(g_szSupLibHardenedExePath));
1327
1328#elif defined(RT_OS_DARWIN)
1329 const char *pszImageName = _dyld_get_image_name(0);
1330 if (!pszImageName)
1331 supR3HardenedFatal("supR3HardenedExecDir: _dyld_get_image_name(0) failed\n");
1332 size_t cchImageName = suplibHardenedStrLen(pszImageName);
1333 if (!cchImageName || cchImageName >= sizeof(g_szSupLibHardenedExePath))
1334 supR3HardenedFatal("supR3HardenedExecDir: _dyld_get_image_name(0) failed, cchImageName=%d\n", cchImageName);
1335 suplibHardenedMemCopy(g_szSupLibHardenedExePath, pszImageName, cchImageName + 1);
1336
1337#elif defined(RT_OS_WINDOWS)
1338 char *pszDst = g_szSupLibHardenedExePath;
1339 int rc = RTUtf16ToUtf8Ex(g_wszSupLibHardenedExePath, RTSTR_MAX, &pszDst, sizeof(g_szSupLibHardenedExePath), NULL);
1340 if (RT_FAILURE(rc))
1341 supR3HardenedFatal("supR3HardenedExecDir: RTUtf16ToUtf8Ex failed, rc=%Rrc\n", rc);
1342#else
1343# error needs porting.
1344#endif
1345
1346 /*
1347 * Determine the application binary directory location.
1348 */
1349 suplibHardenedStrCopy(g_szSupLibHardenedAppBinPath, g_szSupLibHardenedExePath);
1350 suplibHardenedPathStripFilename(g_szSupLibHardenedAppBinPath);
1351
1352 g_offSupLibHardenedExecName = suplibHardenedStrLen(g_szSupLibHardenedAppBinPath);
1353 while (RTPATH_IS_SEP(g_szSupLibHardenedExePath[g_offSupLibHardenedExecName]))
1354 g_offSupLibHardenedExecName++;
1355 g_cchSupLibHardenedExecName = suplibHardenedStrLen(&g_szSupLibHardenedExePath[g_offSupLibHardenedExecName]);
1356
1357 if (g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_HARDENED_MAIN_CALLED)
1358 supR3HardenedFatal("supR3HardenedExecDir: Called before SUPR3HardenedMain! (%d)\n", g_enmSupR3HardenedMainState);
1359 switch (g_fSupHardenedMain & SUPSECMAIN_FLAGS_LOC_MASK)
1360 {
1361 case SUPSECMAIN_FLAGS_LOC_APP_BIN:
1362 break;
1363 case SUPSECMAIN_FLAGS_LOC_TESTCASE:
1364 suplibHardenedPathStripFilename(g_szSupLibHardenedAppBinPath);
1365 break;
1366#ifdef RT_OS_DARWIN
1367 case SUPSECMAIN_FLAGS_LOC_OSX_HLP_APP:
1368 {
1369 /* We must ascend to the parent bundle's Contents directory then decend into its MacOS: */
1370 static const RTSTRTUPLE s_aComponentsToSkip[] =
1371 { { RT_STR_TUPLE("MacOS") }, { RT_STR_TUPLE("Contents") }, { NULL /*some.app*/, 0 }, { RT_STR_TUPLE("Resources") } };
1372 size_t cchPath = suplibHardenedStrLen(g_szSupLibHardenedAppBinPath);
1373 for (uintptr_t i = 0; i < RT_ELEMENTS(s_aComponentsToSkip); i++)
1374 {
1375 while (cchPath > 1 && g_szSupLibHardenedAppBinPath[cchPath - 1] == '/')
1376 cchPath--;
1377 size_t const cchMatch = s_aComponentsToSkip[i].cch;
1378 if (cchMatch > 0)
1379 {
1380 if ( cchPath >= cchMatch + sizeof("VirtualBox.app/Contents")
1381 && g_szSupLibHardenedAppBinPath[cchPath - cchMatch - 1] == '/'
1382 && suplibHardenedMemComp(&g_szSupLibHardenedAppBinPath[cchPath - cchMatch],
1383 s_aComponentsToSkip[i].psz, cchMatch) == 0)
1384 cchPath -= cchMatch;
1385 else
1386 supR3HardenedFatal("supR3HardenedExecDir: Bad helper app path (tail component #%u '%s'): %s\n",
1387 i, s_aComponentsToSkip[i].psz, g_szSupLibHardenedAppBinPath);
1388 }
1389 else if ( cchPath > g_cchSupLibHardenedExecName + sizeof("VirtualBox.app/Contents/Resources/.app")
1390 && suplibHardenedMemComp(&g_szSupLibHardenedAppBinPath[cchPath - 4], ".app", 4) == 0
1391 && suplibHardenedMemComp(&g_szSupLibHardenedAppBinPath[cchPath - 4 - g_cchSupLibHardenedExecName],
1392 &g_szSupLibHardenedExePath[g_offSupLibHardenedExecName],
1393 g_cchSupLibHardenedExecName) == 0)
1394 cchPath -= g_cchSupLibHardenedExecName + 4;
1395 else
1396 supR3HardenedFatal("supR3HardenedExecDir: Bad helper app path (tail component #%u '%s.app'): %s\n",
1397 i, &g_szSupLibHardenedExePath[g_offSupLibHardenedExecName], g_szSupLibHardenedAppBinPath);
1398 }
1399 suplibHardenedMemCopy(&g_szSupLibHardenedAppBinPath[cchPath], "MacOS", sizeof("MacOS"));
1400 break;
1401 }
1402#endif /* RT_OS_DARWIN */
1403 default:
1404 supR3HardenedFatal("supR3HardenedExecDir: Unknown program binary location: %#x\n", g_fSupHardenedMain);
1405 }
1406}
1407
1408
1409#ifdef RT_OS_LINUX
1410/**
1411 * Checks if we can read /proc/self/exe.
1412 *
1413 * This is used on linux to see if we have to call init
1414 * with program path or not.
1415 *
1416 * @returns true / false.
1417 */
1418static bool supR3HardenedMainIsProcSelfExeAccssible(void)
1419{
1420 char szPath[RTPATH_MAX];
1421 int cchLink = readlink("/proc/self/exe", szPath, sizeof(szPath));
1422 return cchLink != -1;
1423}
1424#endif /* RT_OS_LINUX */
1425
1426
1427
1428/**
1429 * @copydoc RTPathExecDir
1430 * @remarks not quite like RTPathExecDir actually...
1431 */
1432DECLHIDDEN(int) supR3HardenedPathAppBin(char *pszPath, size_t cchPath)
1433{
1434 /*
1435 * Lazy init (probably not required).
1436 */
1437 if (!g_szSupLibHardenedAppBinPath[0])
1438 supR3HardenedGetFullExePath();
1439
1440 /*
1441 * Calc the length and check if there is space before copying.
1442 */
1443 size_t cch = suplibHardenedStrLen(g_szSupLibHardenedAppBinPath) + 1;
1444 if (cch <= cchPath)
1445 {
1446 suplibHardenedMemCopy(pszPath, g_szSupLibHardenedAppBinPath, cch + 1);
1447 return VINF_SUCCESS;
1448 }
1449
1450 supR3HardenedFatal("supR3HardenedPathAppBin: Buffer too small (%u < %u)\n", cchPath, cch);
1451 /* not reached */
1452}
1453
1454
1455#ifdef RT_OS_WINDOWS
1456extern "C" uint32_t g_uNtVerCombined;
1457#endif
1458
1459DECLHIDDEN(void) supR3HardenedOpenLog(int *pcArgs, char **papszArgs)
1460{
1461 static const char s_szLogOption[] = "--sup-hardening-log=";
1462
1463 /*
1464 * Scan the argument vector.
1465 */
1466 int cArgs = *pcArgs;
1467 for (int iArg = 1; iArg < cArgs; iArg++)
1468 if (strncmp(papszArgs[iArg], s_szLogOption, sizeof(s_szLogOption) - 1) == 0)
1469 {
1470#ifdef RT_OS_WINDOWS
1471 const char *pszLogFile = &papszArgs[iArg][sizeof(s_szLogOption) - 1];
1472#endif
1473
1474 /*
1475 * Drop the argument from the vector (has trailing NULL entry).
1476 */
1477 memmove(&papszArgs[iArg], &papszArgs[iArg + 1], (cArgs - iArg) * sizeof(papszArgs[0]));
1478 *pcArgs -= 1;
1479 cArgs -= 1;
1480
1481 /*
1482 * Open the log file, unless we've already opened one.
1483 * First argument takes precedence
1484 */
1485#ifdef RT_OS_WINDOWS
1486 if (g_hStartupLog == NULL)
1487 {
1488 int rc = RTNtPathOpen(pszLogFile,
1489 GENERIC_WRITE | SYNCHRONIZE,
1490 FILE_ATTRIBUTE_NORMAL,
1491 FILE_SHARE_READ | FILE_SHARE_WRITE,
1492 FILE_OPEN_IF,
1493 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1494 OBJ_CASE_INSENSITIVE,
1495 &g_hStartupLog,
1496 NULL);
1497 if (RT_SUCCESS(rc))
1498 {
1499 SUP_DPRINTF(("Log file opened: " VBOX_VERSION_STRING "r%u g_hStartupLog=%p g_uNtVerCombined=%#x\n",
1500 VBOX_SVN_REV, g_hStartupLog, g_uNtVerCombined));
1501
1502 /*
1503 * If the path contains a drive volume, save it so we can
1504 * use it to flush the volume containing the log file.
1505 */
1506 if (RT_C_IS_ALPHA(pszLogFile[0]) && pszLogFile[1] == ':')
1507 {
1508 RTUtf16CopyAscii(g_wszStartupLogVol, RT_ELEMENTS(g_wszStartupLogVol), "\\??\\");
1509 g_wszStartupLogVol[sizeof("\\??\\") - 1] = RT_C_TO_UPPER(pszLogFile[0]);
1510 g_wszStartupLogVol[sizeof("\\??\\") + 0] = ':';
1511 g_wszStartupLogVol[sizeof("\\??\\") + 1] = '\0';
1512 }
1513 }
1514 else
1515 g_hStartupLog = NULL;
1516 }
1517#else
1518 /* Just some mumbo jumbo to shut up the compiler. */
1519 g_hStartupLog -= 1;
1520 g_cbStartupLog += 1;
1521 //g_hStartupLog = open()
1522#endif
1523 }
1524}
1525
1526
1527DECLHIDDEN(void) supR3HardenedLogV(const char *pszFormat, va_list va)
1528{
1529#ifdef RT_OS_WINDOWS
1530 if ( g_hStartupLog != NULL
1531 && g_cbStartupLog < 16*_1M)
1532 {
1533 char szBuf[5120];
1534 PCLIENT_ID pSelfId = &((PTEB)NtCurrentTeb())->ClientId;
1535 size_t cchPrefix = RTStrPrintf(szBuf, sizeof(szBuf), "%x.%x: ", pSelfId->UniqueProcess, pSelfId->UniqueThread);
1536 size_t cch = RTStrPrintfV(&szBuf[cchPrefix], sizeof(szBuf) - cchPrefix, pszFormat, va) + cchPrefix;
1537
1538 if ((size_t)cch >= sizeof(szBuf))
1539 cch = sizeof(szBuf) - 1;
1540
1541 if (!cch || szBuf[cch - 1] != '\n')
1542 szBuf[cch++] = '\n';
1543
1544 ASMAtomicAddU32(&g_cbStartupLog, (uint32_t)cch);
1545
1546 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1547 LARGE_INTEGER Offset;
1548 Offset.QuadPart = -1; /* Write to end of file. */
1549 NtWriteFile(g_hStartupLog, NULL /*Event*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/,
1550 &Ios, szBuf, (ULONG)cch, &Offset, NULL /*Key*/);
1551 }
1552#else
1553 RT_NOREF(pszFormat, va);
1554 /* later */
1555#endif
1556}
1557
1558
1559DECLHIDDEN(void) supR3HardenedLog(const char *pszFormat, ...)
1560{
1561 va_list va;
1562 va_start(va, pszFormat);
1563 supR3HardenedLogV(pszFormat, va);
1564 va_end(va);
1565}
1566
1567
1568DECLHIDDEN(void) supR3HardenedLogFlush(void)
1569{
1570#ifdef RT_OS_WINDOWS
1571 if ( g_hStartupLog != NULL
1572 && g_cbStartupLog < 16*_1M)
1573 {
1574 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1575 NTSTATUS rcNt = NtFlushBuffersFile(g_hStartupLog, &Ios);
1576
1577 /*
1578 * Try flush the volume containing the log file too.
1579 */
1580 if (g_wszStartupLogVol[0])
1581 {
1582 HANDLE hLogVol = RTNT_INVALID_HANDLE_VALUE;
1583 UNICODE_STRING NtName;
1584 NtName.Buffer = g_wszStartupLogVol;
1585 NtName.Length = (USHORT)(RTUtf16Len(g_wszStartupLogVol) * sizeof(RTUTF16));
1586 NtName.MaximumLength = NtName.Length + 1;
1587 OBJECT_ATTRIBUTES ObjAttr;
1588 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1589 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
1590 rcNt = NtCreateFile(&hLogVol,
1591 GENERIC_WRITE | GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
1592 &ObjAttr,
1593 &Ios,
1594 NULL /* Allocation Size*/,
1595 0 /*FileAttributes*/,
1596 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1597 FILE_OPEN,
1598 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1599 NULL /*EaBuffer*/,
1600 0 /*EaLength*/);
1601 if (NT_SUCCESS(rcNt))
1602 rcNt = Ios.Status;
1603 if (NT_SUCCESS(rcNt))
1604 {
1605 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
1606 rcNt = NtFlushBuffersFile(hLogVol, &Ios);
1607 NtClose(hLogVol);
1608 }
1609 else
1610 {
1611 /* This may have sideeffects similar to what we want... */
1612 hLogVol = RTNT_INVALID_HANDLE_VALUE;
1613 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
1614 rcNt = NtCreateFile(&hLogVol,
1615 GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
1616 &ObjAttr,
1617 &Ios,
1618 NULL /* Allocation Size*/,
1619 0 /*FileAttributes*/,
1620 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1621 FILE_OPEN,
1622 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1623 NULL /*EaBuffer*/,
1624 0 /*EaLength*/);
1625 if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
1626 NtClose(hLogVol);
1627 }
1628 }
1629 }
1630#else
1631 /* later */
1632#endif
1633}
1634
1635
1636/**
1637 * Prints the message prefix.
1638 */
1639static void suplibHardenedPrintPrefix(void)
1640{
1641 if (g_pszSupLibHardenedProgName)
1642 suplibHardenedPrintStr(g_pszSupLibHardenedProgName);
1643 suplibHardenedPrintStr(": ");
1644}
1645
1646
1647DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatalMsgV(const char *pszWhere, SUPINITOP enmWhat, int rc,
1648 const char *pszMsgFmt, va_list va)
1649{
1650 /*
1651 * First to the log.
1652 */
1653 supR3HardenedLog("Error %d in %s! (enmWhat=%d)\n", rc, pszWhere, enmWhat);
1654 va_list vaCopy;
1655 va_copy(vaCopy, va);
1656 supR3HardenedLogV(pszMsgFmt, vaCopy);
1657 va_end(vaCopy);
1658
1659#ifdef RT_OS_WINDOWS
1660 /*
1661 * The release log.
1662 */
1663 if (g_pfnRTLogRelPrintf)
1664 {
1665 va_copy(vaCopy, va);
1666 g_pfnRTLogRelPrintf("supR3HardenedFatalMsgV: %s enmWhat=%d rc=%Rrc (%#x)\n", pszWhere, enmWhat, rc);
1667 g_pfnRTLogRelPrintf("supR3HardenedFatalMsgV: %N\n", pszMsgFmt, &vaCopy);
1668 va_end(vaCopy);
1669 }
1670#endif
1671
1672 /*
1673 * Then to the console.
1674 */
1675 suplibHardenedPrintPrefix();
1676 suplibHardenedPrintF("Error %d in %s!\n", rc, pszWhere);
1677
1678 suplibHardenedPrintPrefix();
1679 va_copy(vaCopy, va);
1680 suplibHardenedPrintFV(pszMsgFmt, vaCopy);
1681 va_end(vaCopy);
1682 suplibHardenedPrintChr('\n');
1683
1684 switch (enmWhat)
1685 {
1686 case kSupInitOp_Driver:
1687 suplibHardenedPrintChr('\n');
1688 suplibHardenedPrintPrefix();
1689 suplibHardenedPrintStr("Tip! Make sure the kernel module is loaded. It may also help to reinstall VirtualBox.\n");
1690 break;
1691
1692 case kSupInitOp_Misc:
1693 case kSupInitOp_IPRT:
1694 case kSupInitOp_Integrity:
1695 case kSupInitOp_RootCheck:
1696 suplibHardenedPrintChr('\n');
1697 suplibHardenedPrintPrefix();
1698 suplibHardenedPrintStr("Tip! It may help to reinstall VirtualBox.\n");
1699 break;
1700
1701 default:
1702 /* no hints here */
1703 break;
1704 }
1705
1706 /*
1707 * Finally, TrustedError if appropriate.
1708 */
1709 if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
1710 {
1711#ifdef SUP_HARDENED_SUID
1712 /* Drop any root privileges we might be holding, this won't return
1713 if it fails but end up calling supR3HardenedFatal[V]. */
1714 supR3HardenedMainDropPrivileges();
1715#endif
1716 /* Close the driver, if we succeeded opening it. Both because
1717 TrustedError may be untrustworthy and because the driver deosn't
1718 like us if we fork(). @bugref{8838} */
1719 suplibOsTerm(&g_SupPreInitData.Data);
1720
1721 /*
1722 * Now try resolve and call the TrustedError entry point if we can find it.
1723 * Note! Loader involved, so we must guard against loader hooks calling us.
1724 */
1725 static volatile bool s_fRecursive = false;
1726 if (!s_fRecursive)
1727 {
1728 s_fRecursive = true;
1729
1730 PFNSUPTRUSTEDERROR pfnTrustedError = supR3HardenedMainGetTrustedError(g_pszSupLibHardenedProgName);
1731 if (pfnTrustedError)
1732 {
1733 /* We'll fork before we make the call because that way the session management
1734 in main will see us exiting immediately (if it's involved with us) and possibly
1735 get an error back to the API / user. */
1736#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
1737 int pid = fork();
1738 if (pid <= 0)
1739#endif
1740 {
1741 pfnTrustedError(pszWhere, enmWhat, rc, pszMsgFmt, va);
1742 }
1743 }
1744
1745 s_fRecursive = false;
1746 }
1747 }
1748#if defined(RT_OS_WINDOWS)
1749 /*
1750 * Report the error to the parent if this happens during early VM init.
1751 */
1752 else if ( g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED
1753 && g_enmSupR3HardenedMainState != SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED)
1754 supR3HardenedWinReportErrorToParent(pszWhere, enmWhat, rc, pszMsgFmt, va);
1755#endif
1756
1757 /*
1758 * Quit
1759 */
1760 suplibHardenedExit(RTEXITCODE_FAILURE);
1761}
1762
1763
1764DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatalMsg(const char *pszWhere, SUPINITOP enmWhat, int rc,
1765 const char *pszMsgFmt, ...)
1766{
1767 va_list va;
1768 va_start(va, pszMsgFmt);
1769 supR3HardenedFatalMsgV(pszWhere, enmWhat, rc, pszMsgFmt, va);
1770 /* not reached */
1771}
1772
1773
1774DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatalV(const char *pszFormat, va_list va)
1775{
1776 supR3HardenedLog("Fatal error:\n");
1777 va_list vaCopy;
1778 va_copy(vaCopy, va);
1779 supR3HardenedLogV(pszFormat, vaCopy);
1780 va_end(vaCopy);
1781
1782#if defined(RT_OS_WINDOWS)
1783 /*
1784 * Report the error to the parent if this happens during early VM init.
1785 */
1786 if ( g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED
1787 && g_enmSupR3HardenedMainState != SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED)
1788 supR3HardenedWinReportErrorToParent(NULL, kSupInitOp_Invalid, VERR_INTERNAL_ERROR, pszFormat, va);
1789 else
1790#endif
1791 {
1792#ifdef RT_OS_WINDOWS
1793 if (g_pfnRTLogRelPrintf)
1794 {
1795 va_copy(vaCopy, va);
1796 g_pfnRTLogRelPrintf("supR3HardenedFatalV: %N", pszFormat, &vaCopy);
1797 va_end(vaCopy);
1798 }
1799#endif
1800
1801 suplibHardenedPrintPrefix();
1802 suplibHardenedPrintFV(pszFormat, va);
1803 }
1804
1805 suplibHardenedExit(RTEXITCODE_FAILURE);
1806}
1807
1808
1809DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatal(const char *pszFormat, ...)
1810{
1811 va_list va;
1812 va_start(va, pszFormat);
1813 supR3HardenedFatalV(pszFormat, va);
1814 /* not reached */
1815}
1816
1817
1818DECLHIDDEN(int) supR3HardenedErrorV(int rc, bool fFatal, const char *pszFormat, va_list va)
1819{
1820 if (fFatal)
1821 supR3HardenedFatalV(pszFormat, va);
1822
1823 supR3HardenedLog("Error (rc=%d):\n", rc);
1824 va_list vaCopy;
1825 va_copy(vaCopy, va);
1826 supR3HardenedLogV(pszFormat, vaCopy);
1827 va_end(vaCopy);
1828
1829#ifdef RT_OS_WINDOWS
1830 if (g_pfnRTLogRelPrintf)
1831 {
1832 va_copy(vaCopy, va);
1833 g_pfnRTLogRelPrintf("supR3HardenedErrorV: %N", pszFormat, &vaCopy);
1834 va_end(vaCopy);
1835 }
1836#endif
1837
1838 suplibHardenedPrintPrefix();
1839 suplibHardenedPrintFV(pszFormat, va);
1840
1841 return rc;
1842}
1843
1844
1845DECLHIDDEN(int) supR3HardenedError(int rc, bool fFatal, const char *pszFormat, ...)
1846{
1847 va_list va;
1848 va_start(va, pszFormat);
1849 supR3HardenedErrorV(rc, fFatal, pszFormat, va);
1850 va_end(va);
1851 return rc;
1852}
1853
1854
1855
1856/**
1857 * Attempts to open /dev/vboxdrv (or equvivalent).
1858 *
1859 * @remarks This function will not return on failure.
1860 */
1861DECLHIDDEN(void) supR3HardenedMainOpenDevice(void)
1862{
1863 RTERRINFOSTATIC ErrInfo;
1864 SUPINITOP enmWhat = kSupInitOp_Driver;
1865 int rc = suplibOsInit(&g_SupPreInitData.Data, false /*fPreInit*/, true /*fUnrestricted*/,
1866 &enmWhat, RTErrInfoInitStatic(&ErrInfo));
1867 if (RT_SUCCESS(rc))
1868 return;
1869
1870 if (RTErrInfoIsSet(&ErrInfo.Core))
1871 supR3HardenedFatalMsg("suplibOsInit", enmWhat, rc, "%s", ErrInfo.szMsg);
1872
1873 switch (rc)
1874 {
1875 /** @todo better messages! */
1876 case VERR_VM_DRIVER_NOT_INSTALLED:
1877 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Kernel driver not installed");
1878 case VERR_VM_DRIVER_NOT_ACCESSIBLE:
1879 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Kernel driver not accessible");
1880 case VERR_VM_DRIVER_LOAD_ERROR:
1881 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_VM_DRIVER_LOAD_ERROR");
1882 case VERR_VM_DRIVER_OPEN_ERROR:
1883 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_VM_DRIVER_OPEN_ERROR");
1884 case VERR_VM_DRIVER_VERSION_MISMATCH:
1885 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Kernel driver version mismatch");
1886 case VERR_ACCESS_DENIED:
1887 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_ACCESS_DENIED");
1888 case VERR_NO_MEMORY:
1889 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Kernel memory allocation/mapping failed");
1890 case VERR_SUPDRV_HARDENING_EVIL_HANDLE:
1891 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Integrity, rc, "VERR_SUPDRV_HARDENING_EVIL_HANDLE");
1892 case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_0:
1893 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Integrity, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_0");
1894 case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_1:
1895 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Integrity, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_1");
1896 case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_2:
1897 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Integrity, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_2");
1898 default:
1899 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Unknown rc=%d (%Rrc)", rc, rc);
1900 }
1901}
1902
1903
1904#ifdef SUP_HARDENED_SUID
1905
1906/**
1907 * Grabs extra non-root capabilities / privileges that we might require.
1908 *
1909 * This is currently only used for being able to do ICMP from the NAT engine
1910 * and for being able to raise thread scheduling priority
1911 *
1912 * @note We still have root privileges at the time of this call.
1913 */
1914static void supR3HardenedMainGrabCapabilites(void)
1915{
1916# if defined(RT_OS_LINUX)
1917 /*
1918 * We are about to drop all our privileges. Remove all capabilities but
1919 * keep the cap_net_raw capability for ICMP sockets for the NAT stack,
1920 * also keep cap_sys_nice capability for priority tweaking.
1921 */
1922 if (g_uCaps != 0)
1923 {
1924# ifdef USE_LIB_PCAP
1925 /* XXX cap_net_bind_service */
1926 if (!cap_set_proc(cap_from_text("all-eip cap_net_raw+ep cap_sys_nice+ep")))
1927 prctl(PR_SET_KEEPCAPS, 1 /*keep=*/, 0, 0, 0);
1928 prctl(PR_SET_DUMPABLE, 1 /*dump*/, 0, 0, 0);
1929# else
1930 cap_user_header_t hdr = (cap_user_header_t)alloca(sizeof(*hdr));
1931 cap_user_data_t cap = (cap_user_data_t)alloca(2 /*_LINUX_CAPABILITY_U32S_3*/ * sizeof(*cap));
1932 memset(hdr, 0, sizeof(*hdr));
1933 capget(hdr, NULL);
1934 if ( hdr->version != 0x19980330 /* _LINUX_CAPABILITY_VERSION_1, _LINUX_CAPABILITY_U32S_1 = 1 */
1935 && hdr->version != 0x20071026 /* _LINUX_CAPABILITY_VERSION_2, _LINUX_CAPABILITY_U32S_2 = 2 */
1936 && hdr->version != 0x20080522 /* _LINUX_CAPABILITY_VERSION_3, _LINUX_CAPABILITY_U32S_3 = 2 */)
1937 hdr->version = _LINUX_CAPABILITY_VERSION;
1938 g_uCapsVersion = hdr->version;
1939 memset(cap, 0, 2 /* _LINUX_CAPABILITY_U32S_3 */ * sizeof(*cap));
1940 cap->effective = g_uCaps;
1941 cap->permitted = g_uCaps;
1942 if (!capset(hdr, cap))
1943 prctl(PR_SET_KEEPCAPS, 1 /*keep*/, 0, 0, 0);
1944 prctl(PR_SET_DUMPABLE, 1 /*dump*/, 0, 0, 0);
1945# endif /* !USE_LIB_PCAP */
1946 }
1947
1948# elif defined(RT_OS_SOLARIS)
1949 /*
1950 * Add net_icmpaccess privilege to effective privileges and limit
1951 * permitted privileges before completely dropping root privileges.
1952 * This requires dropping root privileges temporarily to get the normal
1953 * user's privileges.
1954 */
1955 seteuid(g_uid);
1956 priv_set_t *pPrivEffective = priv_allocset();
1957 priv_set_t *pPrivNew = priv_allocset();
1958 if (pPrivEffective && pPrivNew)
1959 {
1960 int rc = getppriv(PRIV_EFFECTIVE, pPrivEffective);
1961 seteuid(0);
1962 if (!rc)
1963 {
1964 priv_copyset(pPrivEffective, pPrivNew);
1965 rc = priv_addset(pPrivNew, PRIV_NET_ICMPACCESS);
1966 if (!rc)
1967 {
1968 /* Order is important, as one can't set a privilege which is
1969 * not in the permitted privilege set. */
1970 rc = setppriv(PRIV_SET, PRIV_EFFECTIVE, pPrivNew);
1971 if (rc)
1972 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set effective privilege set.\n");
1973 rc = setppriv(PRIV_SET, PRIV_PERMITTED, pPrivNew);
1974 if (rc)
1975 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set permitted privilege set.\n");
1976 }
1977 else
1978 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to add NET_ICMPACCESS privilege.\n");
1979 }
1980 }
1981 else
1982 {
1983 /* for memory allocation failures just continue */
1984 seteuid(0);
1985 }
1986
1987 if (pPrivEffective)
1988 priv_freeset(pPrivEffective);
1989 if (pPrivNew)
1990 priv_freeset(pPrivNew);
1991# endif
1992}
1993
1994/*
1995 * Look at the environment for some special options.
1996 */
1997static void supR3GrabOptions(void)
1998{
1999# ifdef RT_OS_LINUX
2000 g_uCaps = 0;
2001
2002 /*
2003 * Do _not_ perform any capability-related system calls for root processes
2004 * (leaving g_uCaps at 0).
2005 * (Hint: getuid gets the real user id, not the effective.)
2006 */
2007 if (getuid() != 0)
2008 {
2009 /*
2010 * CAP_NET_RAW.
2011 * Default: enabled.
2012 * Can be disabled with 'export VBOX_HARD_CAP_NET_RAW=0'.
2013 */
2014 const char *pszOpt = getenv("VBOX_HARD_CAP_NET_RAW");
2015 if ( !pszOpt
2016 || memcmp(pszOpt, "0", sizeof("0")) != 0)
2017 g_uCaps = CAP_TO_MASK(CAP_NET_RAW);
2018
2019 /*
2020 * CAP_NET_BIND_SERVICE.
2021 * Default: disabled.
2022 * Can be enabled with 'export VBOX_HARD_CAP_NET_BIND_SERVICE=1'.
2023 */
2024 pszOpt = getenv("VBOX_HARD_CAP_NET_BIND_SERVICE");
2025 if ( pszOpt
2026 && memcmp(pszOpt, "0", sizeof("0")) != 0)
2027 g_uCaps |= CAP_TO_MASK(CAP_NET_BIND_SERVICE);
2028
2029 /*
2030 * CAP_SYS_NICE.
2031 * Default: enabled.
2032 * Can be disabled with 'export VBOX_HARD_CAP_SYS_NICE=0'.
2033 */
2034 pszOpt = getenv("VBOX_HARD_CAP_SYS_NICE");
2035 if ( !pszOpt
2036 || memcmp(pszOpt, "0", sizeof("0")) != 0)
2037 g_uCaps |= CAP_TO_MASK(CAP_SYS_NICE);
2038 }
2039# endif
2040}
2041
2042/**
2043 * Drop any root privileges we might be holding.
2044 */
2045static void supR3HardenedMainDropPrivileges(void)
2046{
2047 /*
2048 * Try use setre[ug]id since this will clear the save uid/gid and thus
2049 * leave fewer traces behind that libs like GTK+ may pick up.
2050 */
2051 uid_t euid, ruid, suid;
2052 gid_t egid, rgid, sgid;
2053# if defined(RT_OS_DARWIN)
2054 /* The really great thing here is that setreuid isn't available on
2055 OS X 10.4, libc emulates it. While 10.4 have a slightly different and
2056 non-standard setuid implementation compared to 10.5, the following
2057 works the same way with both version since we're super user (10.5 req).
2058 The following will set all three variants of the group and user IDs. */
2059 setgid(g_gid);
2060 setuid(g_uid);
2061 euid = geteuid();
2062 ruid = suid = getuid();
2063 egid = getegid();
2064 rgid = sgid = getgid();
2065
2066# elif defined(RT_OS_SOLARIS)
2067 /* Solaris doesn't have setresuid, but the setreuid interface is BSD
2068 compatible and will set the saved uid to euid when we pass it a ruid
2069 that isn't -1 (which we do). */
2070 setregid(g_gid, g_gid);
2071 setreuid(g_uid, g_uid);
2072 euid = geteuid();
2073 ruid = suid = getuid();
2074 egid = getegid();
2075 rgid = sgid = getgid();
2076
2077# else
2078 /* This is the preferred one, full control no questions about semantics.
2079 PORTME: If this isn't work, try join one of two other gangs above. */
2080 int res = setresgid(g_gid, g_gid, g_gid);
2081 NOREF(res);
2082 res = setresuid(g_uid, g_uid, g_uid);
2083 NOREF(res);
2084 if (getresuid(&ruid, &euid, &suid) != 0)
2085 {
2086 euid = geteuid();
2087 ruid = suid = getuid();
2088 }
2089 if (getresgid(&rgid, &egid, &sgid) != 0)
2090 {
2091 egid = getegid();
2092 rgid = sgid = getgid();
2093 }
2094# endif
2095
2096
2097 /* Check that it worked out all right. */
2098 if ( euid != g_uid
2099 || ruid != g_uid
2100 || suid != g_uid
2101 || egid != g_gid
2102 || rgid != g_gid
2103 || sgid != g_gid)
2104 supR3HardenedFatal("SUPR3HardenedMain: failed to drop root privileges!"
2105 " (euid=%d ruid=%d suid=%d egid=%d rgid=%d sgid=%d; wanted uid=%d and gid=%d)\n",
2106 euid, ruid, suid, egid, rgid, sgid, g_uid, g_gid);
2107
2108# if RT_OS_LINUX
2109 /*
2110 * Re-enable the cap_net_raw and cap_sys_nice capabilities which were disabled during setresuid.
2111 */
2112 if (g_uCaps != 0)
2113 {
2114# ifdef USE_LIB_PCAP
2115 /** @todo Warn if that does not work? */
2116 /* XXX cap_net_bind_service */
2117 cap_set_proc(cap_from_text("cap_net_raw+ep cap_sys_nice+ep"));
2118# else
2119 cap_user_header_t hdr = (cap_user_header_t)alloca(sizeof(*hdr));
2120 cap_user_data_t cap = (cap_user_data_t)alloca(2 /* _LINUX_CAPABILITY_U32S_3 */ * sizeof(*cap));
2121 memset(hdr, 0, sizeof(*hdr));
2122 hdr->version = g_uCapsVersion;
2123 memset(cap, 0, 2 /* _LINUX_CAPABILITY_U32S_3 */ * sizeof(*cap));
2124 cap->effective = g_uCaps;
2125 cap->permitted = g_uCaps;
2126 /** @todo Warn if that does not work? */
2127 capset(hdr, cap);
2128# endif /* !USE_LIB_PCAP */
2129 }
2130# endif
2131}
2132
2133#endif /* SUP_HARDENED_SUID */
2134
2135/**
2136 * Purge the process environment from any environment vairable which can lead
2137 * to loading untrusted binaries compromising the process address space.
2138 *
2139 * @param envp The initial environment vector. (Can be NULL.)
2140 */
2141static void supR3HardenedMainPurgeEnvironment(char **envp)
2142{
2143 for (unsigned i = 0; i < RT_ELEMENTS(g_aSupEnvPurgeDescs); i++)
2144 {
2145 /*
2146 * Update the initial environment vector, just in case someone actually cares about it.
2147 */
2148 if (envp)
2149 {
2150 const char * const pszEnv = g_aSupEnvPurgeDescs[i].pszEnv;
2151 size_t const cchEnv = g_aSupEnvPurgeDescs[i].cchEnv;
2152 unsigned iSrc = 0;
2153 unsigned iDst = 0;
2154 char *pszTmp;
2155
2156 while ((pszTmp = envp[iSrc]) != NULL)
2157 {
2158 if ( memcmp(pszTmp, pszEnv, cchEnv) != 0
2159 || (pszTmp[cchEnv] != '=' && pszTmp[cchEnv] != '\0'))
2160 {
2161 if (iDst != iSrc)
2162 envp[iDst] = pszTmp;
2163 iDst++;
2164 }
2165 else
2166 SUP_DPRINTF(("supR3HardenedMainPurgeEnvironment: dropping envp[%d]=%s\n", iSrc, pszTmp));
2167 iSrc++;
2168 }
2169
2170 if (iDst != iSrc)
2171 while (iDst <= iSrc)
2172 envp[iDst++] = NULL;
2173 }
2174
2175 /*
2176 * Remove from the process environment if present.
2177 */
2178#ifndef RT_OS_WINDOWS
2179 const char *pszTmp = getenv(g_aSupEnvPurgeDescs[i].pszEnv);
2180 if (pszTmp != NULL)
2181 {
2182 if (unsetenv((char *)g_aSupEnvPurgeDescs[i].pszEnv) == 0)
2183 SUP_DPRINTF(("supR3HardenedMainPurgeEnvironment: dropped %s\n", pszTmp));
2184 else
2185 if (g_aSupEnvPurgeDescs[i].fPurgeErrFatal)
2186 supR3HardenedFatal("SUPR3HardenedMain: failed to purge %s environment variable! (errno=%d %s)\n",
2187 g_aSupEnvPurgeDescs[i].pszEnv, errno, strerror(errno));
2188 else
2189 SUP_DPRINTF(("supR3HardenedMainPurgeEnvironment: dropping %s failed! errno=%d\n", pszTmp, errno));
2190 }
2191#else
2192 /** @todo Call NT API to do the same. */
2193#endif
2194 }
2195}
2196
2197
2198/**
2199 * Returns the argument purge descriptor of the given argument if available.
2200 *
2201 * @retval 0 if it should not be purged.
2202 * @retval 1 if it only the current argument should be purged.
2203 * @retval 2 if the argument and the following (if present) should be purged.
2204 * @param pszArg The argument to look for.
2205 */
2206static unsigned supR3HardenedMainShouldPurgeArg(const char *pszArg)
2207{
2208 for (unsigned i = 0; i < RT_ELEMENTS(g_aSupArgPurgeDescs); i++)
2209 {
2210 size_t const cchPurge = g_aSupArgPurgeDescs[i].cchArg;
2211 if (!memcmp(pszArg, g_aSupArgPurgeDescs[i].pszArg, cchPurge))
2212 {
2213 if (pszArg[cchPurge] == '\0')
2214 return 1 + g_aSupArgPurgeDescs[i].fTakesValue;
2215 if ( g_aSupArgPurgeDescs[i].fTakesValue
2216 && (pszArg[cchPurge] == ':' || pszArg[cchPurge] == '='))
2217 return 1;
2218 }
2219 }
2220
2221 return 0;
2222}
2223
2224
2225/**
2226 * Purges any command line arguments considered harmful.
2227 *
2228 * @returns nothing.
2229 * @param cArgsOrig The original number of arguments.
2230 * @param papszArgsOrig The original argument vector.
2231 * @param pcArgsNew Where to store the new number of arguments on success.
2232 * @param ppapszArgsNew Where to store the pointer to the purged argument vector.
2233 */
2234static void supR3HardenedMainPurgeArgs(int cArgsOrig, char **papszArgsOrig, int *pcArgsNew, char ***ppapszArgsNew)
2235{
2236 int iDst = 0;
2237#ifdef RT_OS_WINDOWS
2238 char **papszArgsNew = papszArgsOrig; /* We allocated this, no need to allocate again. */
2239#else
2240 char **papszArgsNew = (char **)malloc((cArgsOrig + 1) * sizeof(char *));
2241#endif
2242 if (papszArgsNew)
2243 {
2244 for (int iSrc = 0; iSrc < cArgsOrig; iSrc++)
2245 {
2246 unsigned cPurgedArgs = supR3HardenedMainShouldPurgeArg(papszArgsOrig[iSrc]);
2247 if (!cPurgedArgs)
2248 papszArgsNew[iDst++] = papszArgsOrig[iSrc];
2249 else
2250 iSrc += cPurgedArgs - 1;
2251 }
2252
2253 papszArgsNew[iDst] = NULL; /* The array is NULL terminated, just like envp. */
2254 }
2255 else
2256 supR3HardenedFatal("SUPR3HardenedMain: failed to allocate memory for purged command line!\n");
2257 *pcArgsNew = iDst;
2258 *ppapszArgsNew = papszArgsNew;
2259
2260#ifdef RT_OS_WINDOWS
2261 /** @todo Update command line pointers in PEB, wont really work without it. */
2262#endif
2263}
2264
2265
2266/**
2267 * Loads the VBoxRT DLL/SO/DYLIB, hands it the open driver,
2268 * and calls RTR3InitEx.
2269 *
2270 * @param fFlags The SUPR3HardenedMain fFlags argument, passed to supR3PreInit.
2271 *
2272 * @remarks VBoxRT contains both IPRT and SUPR3.
2273 * @remarks This function will not return on failure.
2274 */
2275static void supR3HardenedMainInitRuntime(uint32_t fFlags)
2276{
2277 /*
2278 * Construct the name.
2279 */
2280 char szPath[RTPATH_MAX];
2281 supR3HardenedPathAppSharedLibs(szPath, sizeof(szPath) - sizeof("/VBoxRT" SUPLIB_DLL_SUFF));
2282 suplibHardenedStrCat(szPath, "/VBoxRT" SUPLIB_DLL_SUFF);
2283
2284 /*
2285 * Open it and resolve the symbols.
2286 */
2287#if defined(RT_OS_WINDOWS)
2288 HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/, g_fSupHardenedMain);
2289 if (!hMod)
2290 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_MODULE_NOT_FOUND,
2291 "LoadLibrary \"%s\" failed (rc=%d)",
2292 szPath, RtlGetLastWin32Error());
2293 PFNRTR3INITEX pfnRTInitEx = (PFNRTR3INITEX)GetProcAddress(hMod, SUP_HARDENED_SYM("RTR3InitEx"));
2294 if (!pfnRTInitEx)
2295 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
2296 "Entrypoint \"RTR3InitEx\" not found in \"%s\" (rc=%d)",
2297 szPath, RtlGetLastWin32Error());
2298
2299 PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)GetProcAddress(hMod, SUP_HARDENED_SYM("supR3PreInit"));
2300 if (!pfnSUPPreInit)
2301 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
2302 "Entrypoint \"supR3PreInit\" not found in \"%s\" (rc=%d)",
2303 szPath, RtlGetLastWin32Error());
2304
2305 g_pfnRTLogRelPrintf = (PFNRTLOGRELPRINTF)GetProcAddress(hMod, SUP_HARDENED_SYM("RTLogRelPrintf"));
2306 Assert(g_pfnRTLogRelPrintf); /* Not fatal in non-strict builds. */
2307
2308#else
2309 /* the dlopen crowd */
2310 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
2311 if (!pvMod)
2312 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_MODULE_NOT_FOUND,
2313 "dlopen(\"%s\",) failed: %s",
2314 szPath, dlerror());
2315 PFNRTR3INITEX pfnRTInitEx = (PFNRTR3INITEX)(uintptr_t)dlsym(pvMod, SUP_HARDENED_SYM("RTR3InitEx"));
2316 if (!pfnRTInitEx)
2317 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
2318 "Entrypoint \"RTR3InitEx\" not found in \"%s\"!\ndlerror: %s",
2319 szPath, dlerror());
2320 PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)(uintptr_t)dlsym(pvMod, SUP_HARDENED_SYM("supR3PreInit"));
2321 if (!pfnSUPPreInit)
2322 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
2323 "Entrypoint \"supR3PreInit\" not found in \"%s\"!\ndlerror: %s",
2324 szPath, dlerror());
2325#endif
2326
2327 /*
2328 * Make the calls.
2329 */
2330 supR3HardenedGetPreInitData(&g_SupPreInitData);
2331 int rc = pfnSUPPreInit(&g_SupPreInitData, fFlags);
2332 if (RT_FAILURE(rc))
2333 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, rc,
2334 "supR3PreInit failed with rc=%d", rc);
2335 const char *pszExePath = NULL;
2336#ifdef RT_OS_LINUX
2337 if (!supR3HardenedMainIsProcSelfExeAccssible())
2338 pszExePath = g_szSupLibHardenedExePath;
2339#endif
2340 rc = pfnRTInitEx(RTR3INIT_VER_1,
2341 fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV ? 0 : RTR3INIT_FLAGS_SUPLIB,
2342 0 /*cArgs*/, NULL /*papszArgs*/, pszExePath);
2343 if (RT_FAILURE(rc))
2344 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, rc,
2345 "RTR3InitEx failed with rc=%d", rc);
2346
2347#if defined(RT_OS_WINDOWS)
2348 /*
2349 * Windows: Create thread that terminates the process when the parent stub
2350 * process terminates (VBoxNetDHCP, Ctrl-C, etc).
2351 */
2352 if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
2353 supR3HardenedWinCreateParentWatcherThread(hMod);
2354#endif
2355}
2356
2357
2358/**
2359 * Construct the path to the DLL/SO/DYLIB containing the actual program.
2360 *
2361 * @returns VBox status code.
2362 * @param pszProgName The program name.
2363 * @param fMainFlags The flags passed to SUPR3HardenedMain.
2364 * @param pszPath The output buffer.
2365 * @param cbPath The size of the output buffer, in bytes. Must be at
2366 * least 128 bytes!
2367 */
2368static int supR3HardenedMainGetTrustedLib(const char *pszProgName, uint32_t fMainFlags, char *pszPath, size_t cbPath)
2369{
2370 supR3HardenedPathAppPrivateArch(pszPath, sizeof(cbPath) - 10);
2371 const char *pszSubDirSlash;
2372 switch (g_fSupHardenedMain & SUPSECMAIN_FLAGS_LOC_MASK)
2373 {
2374 case SUPSECMAIN_FLAGS_LOC_APP_BIN:
2375#ifdef RT_OS_DARWIN
2376 case SUPSECMAIN_FLAGS_LOC_OSX_HLP_APP:
2377#endif
2378 pszSubDirSlash = "/";
2379 break;
2380 case SUPSECMAIN_FLAGS_LOC_TESTCASE:
2381 pszSubDirSlash = "/testcase/";
2382 break;
2383 default:
2384 pszSubDirSlash = "/";
2385 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Unknown program binary location: %#x\n", g_fSupHardenedMain);
2386 }
2387#ifdef RT_OS_DARWIN
2388 if (fMainFlags & SUPSECMAIN_FLAGS_OSX_VM_APP)
2389 pszProgName = "VirtualBox";
2390#else
2391 RT_NOREF1(fMainFlags);
2392#endif
2393 size_t cch = suplibHardenedStrLen(pszPath);
2394 return suplibHardenedStrCopyEx(&pszPath[cch], cbPath - cch, pszSubDirSlash, pszProgName, SUPLIB_DLL_SUFF, NULL);
2395}
2396
2397
2398/**
2399 * Loads the DLL/SO/DYLIB containing the actual program and
2400 * resolves the TrustedError symbol.
2401 *
2402 * This is very similar to supR3HardenedMainGetTrustedMain().
2403 *
2404 * @returns Pointer to the trusted error symbol if it is exported, NULL
2405 * and no error messages otherwise.
2406 * @param pszProgName The program name.
2407 */
2408static PFNSUPTRUSTEDERROR supR3HardenedMainGetTrustedError(const char *pszProgName)
2409{
2410 /*
2411 * Don't bother if the main() function didn't advertise any TrustedError
2412 * export. It's both a waste of time and may trigger additional problems,
2413 * confusing or obscuring the original issue.
2414 */
2415 if (!(g_fSupHardenedMain & SUPSECMAIN_FLAGS_TRUSTED_ERROR))
2416 return NULL;
2417
2418 /*
2419 * Construct the name.
2420 */
2421 char szPath[RTPATH_MAX];
2422 supR3HardenedMainGetTrustedLib(pszProgName, g_fSupHardenedMain, szPath, sizeof(szPath));
2423
2424 /*
2425 * Open it and resolve the symbol.
2426 */
2427#if defined(RT_OS_WINDOWS)
2428 supR3HardenedWinEnableThreadCreation();
2429 HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/, 0 /*fMainFlags*/);
2430 if (!hMod)
2431 return NULL;
2432 FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedError"));
2433 if (!pfn)
2434 return NULL;
2435 return (PFNSUPTRUSTEDERROR)pfn;
2436
2437#else
2438 /* the dlopen crowd */
2439 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
2440 if (!pvMod)
2441 return NULL;
2442 void *pvSym = dlsym(pvMod, SUP_HARDENED_SYM("TrustedError"));
2443 if (!pvSym)
2444 return NULL;
2445 return (PFNSUPTRUSTEDERROR)(uintptr_t)pvSym;
2446#endif
2447}
2448
2449
2450/**
2451 * Loads the DLL/SO/DYLIB containing the actual program and
2452 * resolves the TrustedMain symbol.
2453 *
2454 * @returns Pointer to the trusted main of the actual program.
2455 * @param pszProgName The program name.
2456 * @param fMainFlags The flags passed to SUPR3HardenedMain.
2457 * @remarks This function will not return on failure.
2458 */
2459static PFNSUPTRUSTEDMAIN supR3HardenedMainGetTrustedMain(const char *pszProgName, uint32_t fMainFlags)
2460{
2461 /*
2462 * Construct the name.
2463 */
2464 char szPath[RTPATH_MAX];
2465 supR3HardenedMainGetTrustedLib(pszProgName, fMainFlags, szPath, sizeof(szPath));
2466
2467 /*
2468 * Open it and resolve the symbol.
2469 */
2470#if defined(RT_OS_WINDOWS)
2471 HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/, 0 /*fMainFlags*/);
2472 if (!hMod)
2473 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: LoadLibrary \"%s\" failed, rc=%d\n",
2474 szPath, RtlGetLastWin32Error());
2475 FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedMain"));
2476 if (!pfn)
2477 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\" (rc=%d)\n",
2478 szPath, RtlGetLastWin32Error());
2479 return (PFNSUPTRUSTEDMAIN)pfn;
2480
2481#else
2482 /* the dlopen crowd */
2483 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
2484 if (!pvMod)
2485 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: dlopen(\"%s\",) failed: %s\n",
2486 szPath, dlerror());
2487 void *pvSym = dlsym(pvMod, SUP_HARDENED_SYM("TrustedMain"));
2488 if (!pvSym)
2489 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\"!\ndlerror: %s\n",
2490 szPath, dlerror());
2491 return (PFNSUPTRUSTEDMAIN)(uintptr_t)pvSym;
2492#endif
2493}
2494
2495
2496/**
2497 * Secure main.
2498 *
2499 * This is used for the set-user-ID-on-execute binaries on unixy systems
2500 * and when using the open-vboxdrv-via-root-service setup on Windows.
2501 *
2502 * This function will perform the integrity checks of the VirtualBox
2503 * installation, open the support driver, open the root service (later),
2504 * and load the DLL corresponding to \a pszProgName and execute its main
2505 * function.
2506 *
2507 * @returns Return code appropriate for main().
2508 *
2509 * @param pszProgName The program name. This will be used to figure out which
2510 * DLL/SO/DYLIB to load and execute.
2511 * @param fFlags Flags.
2512 * @param argc The argument count.
2513 * @param argv The argument vector.
2514 * @param envp The environment vector.
2515 */
2516DECLHIDDEN(int) SUPR3HardenedMain(const char *pszProgName, uint32_t fFlags, int argc, char **argv, char **envp)
2517{
2518 SUP_DPRINTF(("SUPR3HardenedMain: pszProgName=%s fFlags=%#x\n", pszProgName, fFlags));
2519 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_HARDENED_MAIN_CALLED;
2520
2521 /*
2522 * Note! At this point there is no IPRT, so we will have to stick
2523 * to basic CRT functions that everyone agree upon.
2524 */
2525 g_pszSupLibHardenedProgName = pszProgName;
2526 g_fSupHardenedMain = fFlags;
2527 g_SupPreInitData.u32Magic = SUPPREINITDATA_MAGIC;
2528 g_SupPreInitData.u32EndMagic = SUPPREINITDATA_MAGIC;
2529#ifdef RT_OS_WINDOWS
2530 if (!g_fSupEarlyProcessInit)
2531#endif
2532 g_SupPreInitData.Data.hDevice = SUP_HDEVICE_NIL;
2533
2534 /*
2535 * Determine the full exe path as we'll be needing it for the verify all
2536 * call(s) below. (We have to do this early on Linux because we * *might*
2537 * not be able to access /proc/self/exe after the seteuid call.)
2538 */
2539 supR3HardenedGetFullExePath();
2540#ifdef RT_OS_WINDOWS
2541 supR3HardenedWinInitAppBin(fFlags);
2542#endif
2543
2544#ifdef SUP_HARDENED_SUID
2545 /*
2546 * Grab any options from the environment.
2547 */
2548 supR3GrabOptions();
2549
2550 /*
2551 * Check that we're root, if we aren't then the installation is butchered.
2552 */
2553 g_uid = getuid();
2554 g_gid = getgid();
2555 if (geteuid() != 0 /* root */)
2556 supR3HardenedFatalMsg("SUPR3HardenedMain", kSupInitOp_RootCheck, VERR_PERMISSION_DENIED,
2557 "Effective UID is not root (euid=%d egid=%d uid=%d gid=%d)",
2558 geteuid(), getegid(), g_uid, g_gid);
2559#endif /* SUP_HARDENED_SUID */
2560
2561#ifdef RT_OS_WINDOWS
2562 /*
2563 * Windows: First respawn. On Windows we will respawn the process twice to establish
2564 * something we can put some kind of reliable trust in. The first respawning aims
2565 * at dropping compatibility layers and process "security" solutions.
2566 */
2567 if ( !g_fSupEarlyProcessInit
2568 && !(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)
2569 && supR3HardenedWinIsReSpawnNeeded(1 /*iWhich*/, argc, argv))
2570 {
2571 SUP_DPRINTF(("SUPR3HardenedMain: Respawn #1\n"));
2572 supR3HardenedWinInit(SUPSECMAIN_FLAGS_DONT_OPEN_DEV | SUPSECMAIN_FLAGS_FIRST_PROCESS, false /*fAvastKludge*/);
2573 supR3HardenedVerifyAll(true /* fFatal */, pszProgName, g_szSupLibHardenedExePath, fFlags);
2574 return supR3HardenedWinReSpawn(1 /*iWhich*/);
2575 }
2576
2577 /*
2578 * Windows: Initialize the image verification global data so we can verify the
2579 * signature of the process image and hook the core of the DLL loader API so we
2580 * can check the signature of all DLLs mapped into the process. (Already done
2581 * by early VM process init.)
2582 */
2583 if (!g_fSupEarlyProcessInit)
2584 supR3HardenedWinInit(fFlags, true /*fAvastKludge*/);
2585#endif /* RT_OS_WINDOWS */
2586
2587 /*
2588 * Validate the installation.
2589 */
2590 supR3HardenedVerifyAll(true /* fFatal */, pszProgName, g_szSupLibHardenedExePath, fFlags);
2591
2592 /*
2593 * The next steps are only taken if we actually need to access the support
2594 * driver. (Already done by early process init.)
2595 */
2596 if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
2597 {
2598#ifdef RT_OS_WINDOWS
2599 /*
2600 * Windows: Must have done early process init if we get here.
2601 */
2602 if (!g_fSupEarlyProcessInit)
2603 supR3HardenedFatalMsg("SUPR3HardenedMain", kSupInitOp_Integrity, VERR_WRONG_ORDER,
2604 "Early process init was somehow skipped.");
2605
2606 /*
2607 * Windows: The second respawn. This time we make a special arrangement
2608 * with vboxdrv to monitor access to the new process from its inception.
2609 */
2610 if (supR3HardenedWinIsReSpawnNeeded(2 /* iWhich*/, argc, argv))
2611 {
2612 SUP_DPRINTF(("SUPR3HardenedMain: Respawn #2\n"));
2613 return supR3HardenedWinReSpawn(2 /* iWhich*/);
2614 }
2615 SUP_DPRINTF(("SUPR3HardenedMain: Final process, opening VBoxDrv...\n"));
2616 supR3HardenedWinFlushLoaderCache();
2617
2618#else
2619 /*
2620 * Open the vboxdrv device.
2621 */
2622 supR3HardenedMainOpenDevice();
2623#endif /* !RT_OS_WINDOWS */
2624 }
2625
2626#ifdef RT_OS_WINDOWS
2627 /*
2628 * Windows: Enable the use of windows APIs to verify images at load time.
2629 */
2630 supR3HardenedWinEnableThreadCreation();
2631 supR3HardenedWinFlushLoaderCache();
2632 supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation(g_pszSupLibHardenedProgName);
2633 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_VERIFY_TRUST_READY;
2634#else /* !RT_OS_WINDOWS */
2635# ifndef RT_OS_FREEBSD /** @todo portme */
2636 /*
2637 * Posix: Hook the load library interface interface.
2638 */
2639 supR3HardenedPosixInit();
2640# endif
2641#endif /* !RT_OS_WINDOWS */
2642
2643#ifdef SUP_HARDENED_SUID
2644 /*
2645 * Grab additional capabilities / privileges.
2646 */
2647 supR3HardenedMainGrabCapabilites();
2648
2649 /*
2650 * Drop any root privileges we might be holding (won't return on failure)
2651 */
2652 supR3HardenedMainDropPrivileges();
2653#endif
2654
2655 /*
2656 * Purge any environment variables and command line arguments considered harmful.
2657 */
2658 /** @todo May need to move this to a much earlier stage on windows. */
2659 supR3HardenedMainPurgeEnvironment(envp);
2660 supR3HardenedMainPurgeArgs(argc, argv, &argc, &argv);
2661
2662 /*
2663 * Load the IPRT, hand the SUPLib part the open driver and
2664 * call RTR3InitEx.
2665 */
2666 SUP_DPRINTF(("SUPR3HardenedMain: Load Runtime...\n"));
2667 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_INIT_RUNTIME;
2668 supR3HardenedMainInitRuntime(fFlags);
2669#ifdef RT_OS_WINDOWS
2670 supR3HardenedWinModifyDllSearchPath(fFlags, g_szSupLibHardenedAppBinPath);
2671#endif
2672
2673 /*
2674 * Load the DLL/SO/DYLIB containing the actual program
2675 * and pass control to it.
2676 */
2677 SUP_DPRINTF(("SUPR3HardenedMain: Load TrustedMain...\n"));
2678 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_GET_TRUSTED_MAIN;
2679 PFNSUPTRUSTEDMAIN pfnTrustedMain = supR3HardenedMainGetTrustedMain(pszProgName, fFlags);
2680
2681 SUP_DPRINTF(("SUPR3HardenedMain: Calling TrustedMain (%p)...\n", pfnTrustedMain));
2682 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_CALLED_TRUSTED_MAIN;
2683 return pfnTrustedMain(argc, argv, envp);
2684}
2685
Note: See TracBrowser for help on using the repository browser.

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