VirtualBox

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

Last change on this file was 104261, checked in by vboxsync, 4 weeks ago

SUP: Moved the program binary directory check to the right place, replace weird strncmp use with suplibHardenedStrCmp, and make it applicable to all hosts defining RTPATH_APP_PRIVATE_ARCH and therefore expecting the binaries to be installed in a certain location. bugref:10626

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

© 2023 Oracle
ContactPrivacy policyTerms of Use