VirtualBox

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

Last change on this file since 99220 was 98103, checked in by vboxsync, 17 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 99.9 KB
Line 
1/* $Id: SUPR3HardenedMain.cpp 98103 2023-01-17 14:15:46Z 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
1179/**
1180 * @copydoc RTPathFilename
1181 */
1182DECLHIDDEN(char *) supR3HardenedPathFilename(const char *pszPath)
1183{
1184 const char *psz = pszPath;
1185 const char *pszLastComp = pszPath;
1186
1187 for (;; psz++)
1188 {
1189 switch (*psz)
1190 {
1191 /* handle separators. */
1192#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
1193 case ':':
1194 pszLastComp = psz + 1;
1195 break;
1196
1197 case '\\':
1198#endif
1199 case '/':
1200 pszLastComp = psz + 1;
1201 break;
1202
1203 /* the end */
1204 case '\0':
1205 if (*pszLastComp)
1206 return (char *)(void *)pszLastComp;
1207 return NULL;
1208 }
1209 }
1210
1211 /* will never get here */
1212}
1213
1214
1215/**
1216 * @copydoc RTPathAppPrivateNoArch
1217 */
1218DECLHIDDEN(int) supR3HardenedPathAppPrivateNoArch(char *pszPath, size_t cchPath)
1219{
1220#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE)
1221 const char *pszSrcPath = RTPATH_APP_PRIVATE;
1222 size_t cchPathPrivateNoArch = suplibHardenedStrLen(pszSrcPath);
1223 if (cchPathPrivateNoArch >= cchPath)
1224 supR3HardenedFatal("supR3HardenedPathAppPrivateNoArch: Buffer overflow, %zu >= %zu\n", cchPathPrivateNoArch, cchPath);
1225 suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathPrivateNoArch + 1);
1226 return VINF_SUCCESS;
1227
1228#else
1229 return supR3HardenedPathAppBin(pszPath, cchPath);
1230#endif
1231}
1232
1233
1234/**
1235 * @copydoc RTPathAppPrivateArch
1236 */
1237DECLHIDDEN(int) supR3HardenedPathAppPrivateArch(char *pszPath, size_t cchPath)
1238{
1239#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE_ARCH)
1240 const char *pszSrcPath = RTPATH_APP_PRIVATE_ARCH;
1241 size_t cchPathPrivateArch = suplibHardenedStrLen(pszSrcPath);
1242 if (cchPathPrivateArch >= cchPath)
1243 supR3HardenedFatal("supR3HardenedPathAppPrivateArch: Buffer overflow, %zu >= %zu\n", cchPathPrivateArch, cchPath);
1244 suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathPrivateArch + 1);
1245 return VINF_SUCCESS;
1246
1247#else
1248 return supR3HardenedPathAppBin(pszPath, cchPath);
1249#endif
1250}
1251
1252
1253/**
1254 * @copydoc RTPathSharedLibs
1255 */
1256DECLHIDDEN(int) supR3HardenedPathAppSharedLibs(char *pszPath, size_t cchPath)
1257{
1258#if !defined(RT_OS_WINDOWS) && defined(RTPATH_SHARED_LIBS)
1259 const char *pszSrcPath = RTPATH_SHARED_LIBS;
1260 size_t cchPathSharedLibs = suplibHardenedStrLen(pszSrcPath);
1261 if (cchPathSharedLibs >= cchPath)
1262 supR3HardenedFatal("supR3HardenedPathAppSharedLibs: Buffer overflow, %zu >= %zu\n", cchPathSharedLibs, cchPath);
1263 suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathSharedLibs + 1);
1264 return VINF_SUCCESS;
1265
1266#else
1267 return supR3HardenedPathAppBin(pszPath, cchPath);
1268#endif
1269}
1270
1271
1272/**
1273 * @copydoc RTPathAppDocs
1274 */
1275DECLHIDDEN(int) supR3HardenedPathAppDocs(char *pszPath, size_t cchPath)
1276{
1277#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_DOCS)
1278 const char *pszSrcPath = RTPATH_APP_DOCS;
1279 size_t cchPathAppDocs = suplibHardenedStrLen(pszSrcPath);
1280 if (cchPathAppDocs >= cchPath)
1281 supR3HardenedFatal("supR3HardenedPathAppDocs: Buffer overflow, %zu >= %zu\n", cchPathAppDocs, cchPath);
1282 suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathAppDocs + 1);
1283 return VINF_SUCCESS;
1284
1285#else
1286 return supR3HardenedPathAppBin(pszPath, cchPath);
1287#endif
1288}
1289
1290
1291/**
1292 * Returns the full path to the executable in g_szSupLibHardenedExePath.
1293 *
1294 * @returns IPRT status code.
1295 */
1296static void supR3HardenedGetFullExePath(void)
1297{
1298 /*
1299 * Get the program filename.
1300 *
1301 * Most UNIXes have no API for obtaining the executable path, but provides a symbolic
1302 * link in the proc file system that tells who was exec'ed. The bad thing about this
1303 * is that we have to use readlink, one of the weirder UNIX APIs.
1304 *
1305 * Darwin, OS/2 and Windows all have proper APIs for getting the program file name.
1306 */
1307#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_SOLARIS)
1308# ifdef RT_OS_LINUX
1309 int cchLink = readlink("/proc/self/exe", &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath) - 1);
1310
1311# elif defined(RT_OS_SOLARIS)
1312 char szFileBuf[PATH_MAX + 1];
1313 sprintf(szFileBuf, "/proc/%ld/path/a.out", (long)getpid());
1314 int cchLink = readlink(szFileBuf, &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath) - 1);
1315
1316# else /* RT_OS_FREEBSD */
1317 int aiName[4];
1318 aiName[0] = CTL_KERN;
1319 aiName[1] = KERN_PROC;
1320 aiName[2] = KERN_PROC_PATHNAME;
1321 aiName[3] = getpid();
1322
1323 size_t cbPath = sizeof(g_szSupLibHardenedExePath);
1324 if (sysctl(aiName, RT_ELEMENTS(aiName), g_szSupLibHardenedExePath, &cbPath, NULL, 0) < 0)
1325 supR3HardenedFatal("supR3HardenedExecDir: sysctl failed\n");
1326 g_szSupLibHardenedExePath[sizeof(g_szSupLibHardenedExePath) - 1] = '\0';
1327 int cchLink = suplibHardenedStrLen(g_szSupLibHardenedExePath); /* paranoid? can't we use cbPath? */
1328
1329# endif
1330 if (cchLink < 0 || cchLink == sizeof(g_szSupLibHardenedExePath) - 1)
1331 supR3HardenedFatal("supR3HardenedExecDir: couldn't read \"%s\", errno=%d cchLink=%d\n",
1332 g_szSupLibHardenedExePath, errno, cchLink);
1333 g_szSupLibHardenedExePath[cchLink] = '\0';
1334
1335#elif defined(RT_OS_OS2) || defined(RT_OS_L4)
1336 _execname(g_szSupLibHardenedExePath, sizeof(g_szSupLibHardenedExePath));
1337
1338#elif defined(RT_OS_DARWIN)
1339 const char *pszImageName = _dyld_get_image_name(0);
1340 if (!pszImageName)
1341 supR3HardenedFatal("supR3HardenedExecDir: _dyld_get_image_name(0) failed\n");
1342 size_t cchImageName = suplibHardenedStrLen(pszImageName);
1343 if (!cchImageName || cchImageName >= sizeof(g_szSupLibHardenedExePath))
1344 supR3HardenedFatal("supR3HardenedExecDir: _dyld_get_image_name(0) failed, cchImageName=%d\n", cchImageName);
1345 suplibHardenedMemCopy(g_szSupLibHardenedExePath, pszImageName, cchImageName + 1);
1346 /** @todo abspath the string or this won't work:
1347 * cd /Applications/VirtualBox.app/Contents/Resources/VirtualBoxVM.app/Contents/MacOS/ && ./VirtualBoxVM --startvm name */
1348
1349#elif defined(RT_OS_WINDOWS)
1350 char *pszDst = g_szSupLibHardenedExePath;
1351 int rc = RTUtf16ToUtf8Ex(g_wszSupLibHardenedExePath, RTSTR_MAX, &pszDst, sizeof(g_szSupLibHardenedExePath), NULL);
1352 if (RT_FAILURE(rc))
1353 supR3HardenedFatal("supR3HardenedExecDir: RTUtf16ToUtf8Ex failed, rc=%Rrc\n", rc);
1354#else
1355# error needs porting.
1356#endif
1357
1358 /*
1359 * Determine the application binary directory location.
1360 */
1361 suplibHardenedStrCopy(g_szSupLibHardenedAppBinPath, g_szSupLibHardenedExePath);
1362 suplibHardenedPathStripFilename(g_szSupLibHardenedAppBinPath);
1363
1364 g_offSupLibHardenedExecName = suplibHardenedStrLen(g_szSupLibHardenedAppBinPath);
1365 while (RTPATH_IS_SEP(g_szSupLibHardenedExePath[g_offSupLibHardenedExecName]))
1366 g_offSupLibHardenedExecName++;
1367 g_cchSupLibHardenedExecName = suplibHardenedStrLen(&g_szSupLibHardenedExePath[g_offSupLibHardenedExecName]);
1368
1369 if (g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_HARDENED_MAIN_CALLED)
1370 supR3HardenedFatal("supR3HardenedExecDir: Called before SUPR3HardenedMain! (%d)\n", g_enmSupR3HardenedMainState);
1371 switch (g_fSupHardenedMain & SUPSECMAIN_FLAGS_LOC_MASK)
1372 {
1373 case SUPSECMAIN_FLAGS_LOC_APP_BIN:
1374 break;
1375 case SUPSECMAIN_FLAGS_LOC_TESTCASE:
1376 suplibHardenedPathStripFilename(g_szSupLibHardenedAppBinPath);
1377 break;
1378#ifdef RT_OS_DARWIN
1379 case SUPSECMAIN_FLAGS_LOC_OSX_HLP_APP:
1380 {
1381 /* We must ascend to the parent bundle's Contents directory then decend into its MacOS: */
1382 static const RTSTRTUPLE s_aComponentsToSkip[] =
1383 { { RT_STR_TUPLE("MacOS") }, { RT_STR_TUPLE("Contents") }, { NULL /*some.app*/, 0 }, { RT_STR_TUPLE("Resources") } };
1384 size_t cchPath = suplibHardenedStrLen(g_szSupLibHardenedAppBinPath);
1385 for (uintptr_t i = 0; i < RT_ELEMENTS(s_aComponentsToSkip); i++)
1386 {
1387 while (cchPath > 1 && g_szSupLibHardenedAppBinPath[cchPath - 1] == '/')
1388 cchPath--;
1389 size_t const cchMatch = s_aComponentsToSkip[i].cch;
1390 if (cchMatch > 0)
1391 {
1392 if ( cchPath >= cchMatch + sizeof("VirtualBox.app/Contents")
1393 && g_szSupLibHardenedAppBinPath[cchPath - cchMatch - 1] == '/'
1394 && suplibHardenedMemComp(&g_szSupLibHardenedAppBinPath[cchPath - cchMatch],
1395 s_aComponentsToSkip[i].psz, cchMatch) == 0)
1396 cchPath -= cchMatch;
1397 else
1398 supR3HardenedFatal("supR3HardenedExecDir: Bad helper app path (tail component #%u '%s'): %s\n",
1399 i, s_aComponentsToSkip[i].psz, g_szSupLibHardenedAppBinPath);
1400 }
1401 else if ( cchPath > g_cchSupLibHardenedExecName + sizeof("VirtualBox.app/Contents/Resources/.app")
1402 && suplibHardenedMemComp(&g_szSupLibHardenedAppBinPath[cchPath - 4], ".app", 4) == 0
1403 && suplibHardenedMemComp(&g_szSupLibHardenedAppBinPath[cchPath - 4 - g_cchSupLibHardenedExecName],
1404 &g_szSupLibHardenedExePath[g_offSupLibHardenedExecName],
1405 g_cchSupLibHardenedExecName) == 0)
1406 cchPath -= g_cchSupLibHardenedExecName + 4;
1407 else
1408 supR3HardenedFatal("supR3HardenedExecDir: Bad helper app path (tail component #%u '%s.app'): %s\n",
1409 i, &g_szSupLibHardenedExePath[g_offSupLibHardenedExecName], g_szSupLibHardenedAppBinPath);
1410 }
1411 suplibHardenedMemCopy(&g_szSupLibHardenedAppBinPath[cchPath], "MacOS", sizeof("MacOS"));
1412 break;
1413 }
1414#endif /* RT_OS_DARWIN */
1415 default:
1416 supR3HardenedFatal("supR3HardenedExecDir: Unknown program binary location: %#x\n", g_fSupHardenedMain);
1417 }
1418}
1419
1420
1421#ifdef RT_OS_LINUX
1422/**
1423 * Checks if we can read /proc/self/exe.
1424 *
1425 * This is used on linux to see if we have to call init
1426 * with program path or not.
1427 *
1428 * @returns true / false.
1429 */
1430static bool supR3HardenedMainIsProcSelfExeAccssible(void)
1431{
1432 char szPath[RTPATH_MAX];
1433 int cchLink = readlink("/proc/self/exe", szPath, sizeof(szPath));
1434 return cchLink != -1;
1435}
1436#endif /* RT_OS_LINUX */
1437
1438
1439
1440/**
1441 * @remarks not quite like RTPathExecDir actually...
1442 */
1443DECLHIDDEN(int) supR3HardenedPathAppBin(char *pszPath, size_t cchPath)
1444{
1445 /*
1446 * Lazy init (probably not required).
1447 */
1448 if (!g_szSupLibHardenedAppBinPath[0])
1449 supR3HardenedGetFullExePath();
1450
1451 /*
1452 * Calc the length and check if there is space before copying.
1453 */
1454 size_t cch = suplibHardenedStrLen(g_szSupLibHardenedAppBinPath) + 1;
1455 if (cch <= cchPath)
1456 {
1457 suplibHardenedMemCopy(pszPath, g_szSupLibHardenedAppBinPath, cch + 1);
1458 return VINF_SUCCESS;
1459 }
1460
1461 supR3HardenedFatal("supR3HardenedPathAppBin: Buffer too small (%u < %u)\n", cchPath, cch);
1462 /* not reached */
1463}
1464
1465
1466#ifdef RT_OS_WINDOWS
1467extern "C" uint32_t g_uNtVerCombined;
1468#endif
1469
1470DECLHIDDEN(void) supR3HardenedOpenLog(int *pcArgs, char **papszArgs)
1471{
1472 static const char s_szLogOption[] = "--sup-hardening-log=";
1473
1474 /*
1475 * Scan the argument vector.
1476 */
1477 int cArgs = *pcArgs;
1478 for (int iArg = 1; iArg < cArgs; iArg++)
1479 if (strncmp(papszArgs[iArg], s_szLogOption, sizeof(s_szLogOption) - 1) == 0)
1480 {
1481#ifdef RT_OS_WINDOWS
1482 const char *pszLogFile = &papszArgs[iArg][sizeof(s_szLogOption) - 1];
1483#endif
1484
1485 /*
1486 * Drop the argument from the vector (has trailing NULL entry).
1487 */
1488// memmove(&papszArgs[iArg], &papszArgs[iArg + 1], (cArgs - iArg) * sizeof(papszArgs[0]));
1489 *pcArgs -= 1;
1490 cArgs -= 1;
1491
1492 /*
1493 * Open the log file, unless we've already opened one.
1494 * First argument takes precedence
1495 */
1496#ifdef RT_OS_WINDOWS
1497 if (g_hStartupLog == NULL)
1498 {
1499 int rc = RTNtPathOpen(pszLogFile,
1500 GENERIC_WRITE | SYNCHRONIZE,
1501 FILE_ATTRIBUTE_NORMAL,
1502 FILE_SHARE_READ | FILE_SHARE_WRITE,
1503 FILE_OPEN_IF,
1504 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1505 OBJ_CASE_INSENSITIVE,
1506 &g_hStartupLog,
1507 NULL);
1508 if (RT_SUCCESS(rc))
1509 {
1510// SUP_DPRINTF(("Log file opened: " VBOX_VERSION_STRING "r%u g_hStartupLog=%p g_uNtVerCombined=%#x\n",
1511// VBOX_SVN_REV, g_hStartupLog, g_uNtVerCombined));
1512
1513 /*
1514 * If the path contains a drive volume, save it so we can
1515 * use it to flush the volume containing the log file.
1516 */
1517 if (RT_C_IS_ALPHA(pszLogFile[0]) && pszLogFile[1] == ':')
1518 {
1519// RTUtf16CopyAscii(g_wszStartupLogVol, RT_ELEMENTS(g_wszStartupLogVol), "\\??\\");
1520 g_wszStartupLogVol[sizeof("\\??\\") - 1] = RT_C_TO_UPPER(pszLogFile[0]);
1521 g_wszStartupLogVol[sizeof("\\??\\") + 0] = ':';
1522 g_wszStartupLogVol[sizeof("\\??\\") + 1] = '\0';
1523 }
1524 }
1525 else
1526 g_hStartupLog = NULL;
1527 }
1528#else
1529 /* Just some mumbo jumbo to shut up the compiler. */
1530 g_hStartupLog -= 1;
1531 g_cbStartupLog += 1;
1532 //g_hStartupLog = open()
1533#endif
1534 }
1535}
1536
1537
1538DECLHIDDEN(void) supR3HardenedLogV(const char *pszFormat, va_list va)
1539{
1540#ifdef RT_OS_WINDOWS
1541 if ( g_hStartupLog != NULL
1542 && g_cbStartupLog < 16*_1M)
1543 {
1544 char szBuf[5120];
1545 PCLIENT_ID pSelfId = &((PTEB)NtCurrentTeb())->ClientId;
1546 size_t cchPrefix = RTStrPrintf(szBuf, sizeof(szBuf), "%x.%x: ", pSelfId->UniqueProcess, pSelfId->UniqueThread);
1547 size_t cch = RTStrPrintfV(&szBuf[cchPrefix], sizeof(szBuf) - cchPrefix, pszFormat, va) + cchPrefix;
1548
1549 if ((size_t)cch >= sizeof(szBuf))
1550 cch = sizeof(szBuf) - 1;
1551
1552 if (!cch || szBuf[cch - 1] != '\n')
1553 szBuf[cch++] = '\n';
1554
1555 ASMAtomicAddU32(&g_cbStartupLog, (uint32_t)cch);
1556
1557 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1558 LARGE_INTEGER Offset;
1559 Offset.QuadPart = -1; /* Write to end of file. */
1560 NtWriteFile(g_hStartupLog, NULL /*Event*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/,
1561 &Ios, szBuf, (ULONG)cch, &Offset, NULL /*Key*/);
1562 }
1563#else
1564 RT_NOREF(pszFormat, va);
1565 /* later */
1566#endif
1567}
1568
1569
1570DECLHIDDEN(void) supR3HardenedLog(const char *pszFormat, ...)
1571{
1572 va_list va;
1573 va_start(va, pszFormat);
1574 supR3HardenedLogV(pszFormat, va);
1575 va_end(va);
1576}
1577
1578
1579DECLHIDDEN(void) supR3HardenedLogFlush(void)
1580{
1581#ifdef RT_OS_WINDOWS
1582 if ( g_hStartupLog != NULL
1583 && g_cbStartupLog < 16*_1M)
1584 {
1585 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1586 NTSTATUS rcNt = NtFlushBuffersFile(g_hStartupLog, &Ios);
1587
1588 /*
1589 * Try flush the volume containing the log file too.
1590 */
1591 if (g_wszStartupLogVol[0])
1592 {
1593 HANDLE hLogVol = RTNT_INVALID_HANDLE_VALUE;
1594 UNICODE_STRING NtName;
1595 NtName.Buffer = g_wszStartupLogVol;
1596 NtName.Length = (USHORT)(RTUtf16Len(g_wszStartupLogVol) * sizeof(RTUTF16));
1597 NtName.MaximumLength = NtName.Length + 1;
1598 OBJECT_ATTRIBUTES ObjAttr;
1599 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
1600 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
1601 rcNt = NtCreateFile(&hLogVol,
1602 GENERIC_WRITE | GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
1603 &ObjAttr,
1604 &Ios,
1605 NULL /* Allocation Size*/,
1606 0 /*FileAttributes*/,
1607 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1608 FILE_OPEN,
1609 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1610 NULL /*EaBuffer*/,
1611 0 /*EaLength*/);
1612 if (NT_SUCCESS(rcNt))
1613 rcNt = Ios.Status;
1614 if (NT_SUCCESS(rcNt))
1615 {
1616 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
1617 rcNt = NtFlushBuffersFile(hLogVol, &Ios);
1618 NtClose(hLogVol);
1619 }
1620 else
1621 {
1622 /* This may have sideeffects similar to what we want... */
1623 hLogVol = RTNT_INVALID_HANDLE_VALUE;
1624 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
1625 rcNt = NtCreateFile(&hLogVol,
1626 GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
1627 &ObjAttr,
1628 &Ios,
1629 NULL /* Allocation Size*/,
1630 0 /*FileAttributes*/,
1631 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1632 FILE_OPEN,
1633 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
1634 NULL /*EaBuffer*/,
1635 0 /*EaLength*/);
1636 if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
1637 NtClose(hLogVol);
1638 }
1639 }
1640 }
1641#else
1642 /* later */
1643#endif
1644}
1645
1646
1647/**
1648 * Prints the message prefix.
1649 */
1650static void suplibHardenedPrintPrefix(void)
1651{
1652 if (g_pszSupLibHardenedProgName)
1653 suplibHardenedPrintStr(g_pszSupLibHardenedProgName);
1654 suplibHardenedPrintStr(": ");
1655}
1656
1657
1658DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatalMsgV(const char *pszWhere, SUPINITOP enmWhat, int rc,
1659 const char *pszMsgFmt, va_list va)
1660{
1661 /*
1662 * First to the log.
1663 */
1664 supR3HardenedLog("Error %d in %s! (enmWhat=%d)\n", rc, pszWhere, enmWhat);
1665 va_list vaCopy;
1666 va_copy(vaCopy, va);
1667 supR3HardenedLogV(pszMsgFmt, vaCopy);
1668 va_end(vaCopy);
1669
1670#ifdef RT_OS_WINDOWS
1671 /*
1672 * The release log.
1673 */
1674 if (g_pfnRTLogRelPrintf)
1675 {
1676 va_copy(vaCopy, va);
1677 g_pfnRTLogRelPrintf("supR3HardenedFatalMsgV: %s enmWhat=%d rc=%Rrc (%#x)\n", pszWhere, enmWhat, rc);
1678 g_pfnRTLogRelPrintf("supR3HardenedFatalMsgV: %N\n", pszMsgFmt, &vaCopy);
1679 va_end(vaCopy);
1680 }
1681#endif
1682
1683 /*
1684 * Then to the console.
1685 */
1686 suplibHardenedPrintPrefix();
1687 suplibHardenedPrintF("Error %d in %s!\n", rc, pszWhere);
1688
1689 suplibHardenedPrintPrefix();
1690 va_copy(vaCopy, va);
1691 suplibHardenedPrintFV(pszMsgFmt, vaCopy);
1692 va_end(vaCopy);
1693 suplibHardenedPrintChr('\n');
1694
1695 switch (enmWhat)
1696 {
1697 case kSupInitOp_Driver:
1698 suplibHardenedPrintChr('\n');
1699 suplibHardenedPrintPrefix();
1700 suplibHardenedPrintStr("Tip! Make sure the kernel module is loaded. It may also help to reinstall VirtualBox.\n");
1701 break;
1702
1703 case kSupInitOp_Misc:
1704 case kSupInitOp_IPRT:
1705 case kSupInitOp_Integrity:
1706 case kSupInitOp_RootCheck:
1707 suplibHardenedPrintChr('\n');
1708 suplibHardenedPrintPrefix();
1709 suplibHardenedPrintStr("Tip! It may help to reinstall VirtualBox.\n");
1710 break;
1711
1712 default:
1713 /* no hints here */
1714 break;
1715 }
1716
1717 /*
1718 * Finally, TrustedError if appropriate.
1719 */
1720 if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
1721 {
1722#ifdef SUP_HARDENED_SUID
1723 /* Drop any root privileges we might be holding, this won't return
1724 if it fails but end up calling supR3HardenedFatal[V]. */
1725 supR3HardenedMainDropPrivileges();
1726#endif
1727 /* Close the driver, if we succeeded opening it. Both because
1728 TrustedError may be untrustworthy and because the driver deosn't
1729 like us if we fork(). @bugref{8838} */
1730 suplibOsTerm(&g_SupPreInitData.Data);
1731
1732 /*
1733 * Now try resolve and call the TrustedError entry point if we can find it.
1734 * Note! Loader involved, so we must guard against loader hooks calling us.
1735 */
1736 static volatile bool s_fRecursive = false;
1737 if (!s_fRecursive)
1738 {
1739 s_fRecursive = true;
1740
1741 PFNSUPTRUSTEDERROR pfnTrustedError = supR3HardenedMainGetTrustedError(g_pszSupLibHardenedProgName);
1742 if (pfnTrustedError)
1743 {
1744 /* We'll fork before we make the call because that way the session management
1745 in main will see us exiting immediately (if it's involved with us) and possibly
1746 get an error back to the API / user. */
1747#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2) && /* @bugref{10170}: */ !defined(RT_OS_DARWIN)
1748 int pid = fork();
1749 if (pid <= 0)
1750#endif
1751 {
1752 pfnTrustedError(pszWhere, enmWhat, rc, pszMsgFmt, va);
1753 }
1754 }
1755
1756 s_fRecursive = false;
1757 }
1758 }
1759#if defined(RT_OS_WINDOWS)
1760 /*
1761 * Report the error to the parent if this happens during early VM init.
1762 */
1763 else if ( g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED
1764 && g_enmSupR3HardenedMainState != SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED)
1765 supR3HardenedWinReportErrorToParent(pszWhere, enmWhat, rc, pszMsgFmt, va);
1766#endif
1767
1768 /*
1769 * Quit
1770 */
1771 suplibHardenedExit(RTEXITCODE_FAILURE);
1772}
1773
1774
1775DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatalMsg(const char *pszWhere, SUPINITOP enmWhat, int rc,
1776 const char *pszMsgFmt, ...)
1777{
1778 va_list va;
1779 va_start(va, pszMsgFmt);
1780 supR3HardenedFatalMsgV(pszWhere, enmWhat, rc, pszMsgFmt, va);
1781 /* not reached */
1782}
1783
1784
1785DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatalV(const char *pszFormat, va_list va)
1786{
1787 supR3HardenedLog("Fatal error:\n");
1788 va_list vaCopy;
1789 va_copy(vaCopy, va);
1790 supR3HardenedLogV(pszFormat, vaCopy);
1791 va_end(vaCopy);
1792
1793#if defined(RT_OS_WINDOWS)
1794 /*
1795 * Report the error to the parent if this happens during early VM init.
1796 */
1797 if ( g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED
1798 && g_enmSupR3HardenedMainState != SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED)
1799 supR3HardenedWinReportErrorToParent(NULL, kSupInitOp_Invalid, VERR_INTERNAL_ERROR, pszFormat, va);
1800 else
1801#endif
1802 {
1803#ifdef RT_OS_WINDOWS
1804 if (g_pfnRTLogRelPrintf)
1805 {
1806 va_copy(vaCopy, va);
1807 g_pfnRTLogRelPrintf("supR3HardenedFatalV: %N", pszFormat, &vaCopy);
1808 va_end(vaCopy);
1809 }
1810#endif
1811
1812 suplibHardenedPrintPrefix();
1813 suplibHardenedPrintFV(pszFormat, va);
1814 }
1815
1816 suplibHardenedExit(RTEXITCODE_FAILURE);
1817}
1818
1819
1820DECL_NO_RETURN(DECLHIDDEN(void)) supR3HardenedFatal(const char *pszFormat, ...)
1821{
1822 va_list va;
1823 va_start(va, pszFormat);
1824 supR3HardenedFatalV(pszFormat, va);
1825 /* not reached */
1826}
1827
1828
1829DECLHIDDEN(int) supR3HardenedErrorV(int rc, bool fFatal, const char *pszFormat, va_list va)
1830{
1831 if (fFatal)
1832 supR3HardenedFatalV(pszFormat, va);
1833
1834 supR3HardenedLog("Error (rc=%d):\n", rc);
1835 va_list vaCopy;
1836 va_copy(vaCopy, va);
1837 supR3HardenedLogV(pszFormat, vaCopy);
1838 va_end(vaCopy);
1839
1840#ifdef RT_OS_WINDOWS
1841 if (g_pfnRTLogRelPrintf)
1842 {
1843 va_copy(vaCopy, va);
1844 g_pfnRTLogRelPrintf("supR3HardenedErrorV: %N", pszFormat, &vaCopy);
1845 va_end(vaCopy);
1846 }
1847#endif
1848
1849 suplibHardenedPrintPrefix();
1850 suplibHardenedPrintFV(pszFormat, va);
1851
1852 return rc;
1853}
1854
1855
1856DECLHIDDEN(int) supR3HardenedError(int rc, bool fFatal, const char *pszFormat, ...)
1857{
1858 va_list va;
1859 va_start(va, pszFormat);
1860 supR3HardenedErrorV(rc, fFatal, pszFormat, va);
1861 va_end(va);
1862 return rc;
1863}
1864
1865
1866
1867/**
1868 * Attempts to open /dev/vboxdrv (or equvivalent).
1869 *
1870 * @remarks This function will not return on failure.
1871 */
1872DECLHIDDEN(void) supR3HardenedMainOpenDevice(void)
1873{
1874 RTERRINFOSTATIC ErrInfo;
1875 SUPINITOP enmWhat = kSupInitOp_Driver;
1876 uint32_t fFlags = SUPR3INIT_F_UNRESTRICTED;
1877 if (g_fSupHardenedMain & SUPSECMAIN_FLAGS_DRIVERLESS)
1878 fFlags |= SUPR3INIT_F_DRIVERLESS;
1879 if (g_fSupHardenedMain & SUPSECMAIN_FLAGS_DRIVERLESS_IEM_ALLOWED)
1880 fFlags |= SUPR3INIT_F_DRIVERLESS_IEM_ALLOWED;
1881#ifdef VBOX_WITH_DRIVERLESS_NEM_FALLBACK
1882 if (g_fSupHardenedMain & SUPSECMAIN_FLAGS_DRIVERLESS_NEM_FALLBACK)
1883 fFlags |= SUPR3INIT_F_DRIVERLESS_NEM_FALLBACK;
1884#endif
1885 int rc = suplibOsInit(&g_SupPreInitData.Data, false /*fPreInit*/, fFlags, &enmWhat, RTErrInfoInitStatic(&ErrInfo));
1886 if (RT_SUCCESS(rc))
1887 return;
1888
1889 if (RTErrInfoIsSet(&ErrInfo.Core))
1890 supR3HardenedFatalMsg("suplibOsInit", enmWhat, rc, "%s", ErrInfo.szMsg);
1891
1892 switch (rc)
1893 {
1894 /** @todo better messages! */
1895 case VERR_VM_DRIVER_NOT_INSTALLED:
1896 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Kernel driver not installed");
1897 case VERR_VM_DRIVER_NOT_ACCESSIBLE:
1898 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Kernel driver not accessible");
1899 case VERR_VM_DRIVER_LOAD_ERROR:
1900 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_VM_DRIVER_LOAD_ERROR");
1901 case VERR_VM_DRIVER_OPEN_ERROR:
1902 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_VM_DRIVER_OPEN_ERROR");
1903 case VERR_VM_DRIVER_VERSION_MISMATCH:
1904 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Kernel driver version mismatch");
1905 case VERR_ACCESS_DENIED:
1906 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_ACCESS_DENIED");
1907 case VERR_NO_MEMORY:
1908 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Kernel memory allocation/mapping failed");
1909 case VERR_SUPDRV_HARDENING_EVIL_HANDLE:
1910 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Integrity, rc, "VERR_SUPDRV_HARDENING_EVIL_HANDLE");
1911 case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_0:
1912 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Integrity, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_0");
1913 case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_1:
1914 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Integrity, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_1");
1915 case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_2:
1916 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Integrity, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_2");
1917 default:
1918 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "Unknown rc=%d (%Rrc)", rc, rc);
1919 }
1920}
1921
1922
1923#ifdef SUP_HARDENED_SUID
1924
1925/**
1926 * Grabs extra non-root capabilities / privileges that we might require.
1927 *
1928 * This is currently only used for being able to do ICMP from the NAT engine
1929 * and for being able to raise thread scheduling priority
1930 *
1931 * @note We still have root privileges at the time of this call.
1932 */
1933static void supR3HardenedMainGrabCapabilites(void)
1934{
1935# if defined(RT_OS_LINUX)
1936 /*
1937 * We are about to drop all our privileges. Remove all capabilities but
1938 * keep the cap_net_raw capability for ICMP sockets for the NAT stack,
1939 * also keep cap_sys_nice capability for priority tweaking.
1940 */
1941 if (g_uCaps != 0)
1942 {
1943# ifdef USE_LIB_PCAP
1944 /* XXX cap_net_bind_service */
1945 if (!cap_set_proc(cap_from_text("all-eip cap_net_raw+ep cap_sys_nice+ep")))
1946 prctl(PR_SET_KEEPCAPS, 1 /*keep=*/, 0, 0, 0);
1947 prctl(PR_SET_DUMPABLE, 1 /*dump*/, 0, 0, 0);
1948# else
1949 cap_user_header_t hdr = (cap_user_header_t)alloca(sizeof(*hdr));
1950 cap_user_data_t cap = (cap_user_data_t)alloca(2 /*_LINUX_CAPABILITY_U32S_3*/ * sizeof(*cap));
1951 memset(hdr, 0, sizeof(*hdr));
1952 capget(hdr, NULL);
1953 if ( hdr->version != 0x19980330 /* _LINUX_CAPABILITY_VERSION_1, _LINUX_CAPABILITY_U32S_1 = 1 */
1954 && hdr->version != 0x20071026 /* _LINUX_CAPABILITY_VERSION_2, _LINUX_CAPABILITY_U32S_2 = 2 */
1955 && hdr->version != 0x20080522 /* _LINUX_CAPABILITY_VERSION_3, _LINUX_CAPABILITY_U32S_3 = 2 */)
1956 hdr->version = _LINUX_CAPABILITY_VERSION;
1957 g_uCapsVersion = hdr->version;
1958 memset(cap, 0, 2 /* _LINUX_CAPABILITY_U32S_3 */ * sizeof(*cap));
1959 cap->effective = g_uCaps;
1960 cap->permitted = g_uCaps;
1961 if (!capset(hdr, cap))
1962 prctl(PR_SET_KEEPCAPS, 1 /*keep*/, 0, 0, 0);
1963 prctl(PR_SET_DUMPABLE, 1 /*dump*/, 0, 0, 0);
1964# endif /* !USE_LIB_PCAP */
1965 }
1966
1967# elif defined(RT_OS_SOLARIS)
1968 /*
1969 * Add net_icmpaccess privilege to effective privileges and limit
1970 * permitted privileges before completely dropping root privileges.
1971 * This requires dropping root privileges temporarily to get the normal
1972 * user's privileges.
1973 */
1974 seteuid(g_uid);
1975 priv_set_t *pPrivEffective = priv_allocset();
1976 priv_set_t *pPrivNew = priv_allocset();
1977 if (pPrivEffective && pPrivNew)
1978 {
1979 int rc = getppriv(PRIV_EFFECTIVE, pPrivEffective);
1980 seteuid(0);
1981 if (!rc)
1982 {
1983 priv_copyset(pPrivEffective, pPrivNew);
1984 rc = priv_addset(pPrivNew, PRIV_NET_ICMPACCESS);
1985 if (!rc)
1986 {
1987 /* Order is important, as one can't set a privilege which is
1988 * not in the permitted privilege set. */
1989 rc = setppriv(PRIV_SET, PRIV_EFFECTIVE, pPrivNew);
1990 if (rc)
1991 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set effective privilege set.\n");
1992 rc = setppriv(PRIV_SET, PRIV_PERMITTED, pPrivNew);
1993 if (rc)
1994 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set permitted privilege set.\n");
1995 }
1996 else
1997 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to add NET_ICMPACCESS privilege.\n");
1998 }
1999 }
2000 else
2001 {
2002 /* for memory allocation failures just continue */
2003 seteuid(0);
2004 }
2005
2006 if (pPrivEffective)
2007 priv_freeset(pPrivEffective);
2008 if (pPrivNew)
2009 priv_freeset(pPrivNew);
2010# endif
2011}
2012
2013/*
2014 * Look at the environment for some special options.
2015 */
2016static void supR3GrabOptions(void)
2017{
2018# ifdef RT_OS_LINUX
2019 g_uCaps = 0;
2020
2021 /*
2022 * Do _not_ perform any capability-related system calls for root processes
2023 * (leaving g_uCaps at 0).
2024 * (Hint: getuid gets the real user id, not the effective.)
2025 */
2026 if (getuid() != 0)
2027 {
2028 /*
2029 * CAP_NET_RAW.
2030 * Default: enabled.
2031 * Can be disabled with 'export VBOX_HARD_CAP_NET_RAW=0'.
2032 */
2033 const char *pszOpt = getenv("VBOX_HARD_CAP_NET_RAW");
2034 if ( !pszOpt
2035 || memcmp(pszOpt, "0", sizeof("0")) != 0)
2036 g_uCaps = CAP_TO_MASK(CAP_NET_RAW);
2037
2038 /*
2039 * CAP_NET_BIND_SERVICE.
2040 * Default: disabled.
2041 * Can be enabled with 'export VBOX_HARD_CAP_NET_BIND_SERVICE=1'.
2042 */
2043 pszOpt = getenv("VBOX_HARD_CAP_NET_BIND_SERVICE");
2044 if ( pszOpt
2045 && memcmp(pszOpt, "0", sizeof("0")) != 0)
2046 g_uCaps |= CAP_TO_MASK(CAP_NET_BIND_SERVICE);
2047
2048 /*
2049 * CAP_SYS_NICE.
2050 * Default: enabled.
2051 * Can be disabled with 'export VBOX_HARD_CAP_SYS_NICE=0'.
2052 */
2053 pszOpt = getenv("VBOX_HARD_CAP_SYS_NICE");
2054 if ( !pszOpt
2055 || memcmp(pszOpt, "0", sizeof("0")) != 0)
2056 g_uCaps |= CAP_TO_MASK(CAP_SYS_NICE);
2057 }
2058# endif
2059}
2060
2061/**
2062 * Drop any root privileges we might be holding.
2063 */
2064static void supR3HardenedMainDropPrivileges(void)
2065{
2066 /*
2067 * Try use setre[ug]id since this will clear the save uid/gid and thus
2068 * leave fewer traces behind that libs like GTK+ may pick up.
2069 */
2070 uid_t euid, ruid, suid;
2071 gid_t egid, rgid, sgid;
2072# if defined(RT_OS_DARWIN)
2073 /* The really great thing here is that setreuid isn't available on
2074 OS X 10.4, libc emulates it. While 10.4 have a slightly different and
2075 non-standard setuid implementation compared to 10.5, the following
2076 works the same way with both version since we're super user (10.5 req).
2077 The following will set all three variants of the group and user IDs. */
2078 setgid(g_gid);
2079 setuid(g_uid);
2080 euid = geteuid();
2081 ruid = suid = getuid();
2082 egid = getegid();
2083 rgid = sgid = getgid();
2084
2085# elif defined(RT_OS_SOLARIS)
2086 /* Solaris doesn't have setresuid, but the setreuid interface is BSD
2087 compatible and will set the saved uid to euid when we pass it a ruid
2088 that isn't -1 (which we do). */
2089 setregid(g_gid, g_gid);
2090 setreuid(g_uid, g_uid);
2091 euid = geteuid();
2092 ruid = suid = getuid();
2093 egid = getegid();
2094 rgid = sgid = getgid();
2095
2096# else
2097 /* This is the preferred one, full control no questions about semantics.
2098 PORTME: If this isn't work, try join one of two other gangs above. */
2099 int res = setresgid(g_gid, g_gid, g_gid);
2100 NOREF(res);
2101 res = setresuid(g_uid, g_uid, g_uid);
2102 NOREF(res);
2103 if (getresuid(&ruid, &euid, &suid) != 0)
2104 {
2105 euid = geteuid();
2106 ruid = suid = getuid();
2107 }
2108 if (getresgid(&rgid, &egid, &sgid) != 0)
2109 {
2110 egid = getegid();
2111 rgid = sgid = getgid();
2112 }
2113# endif
2114
2115
2116 /* Check that it worked out all right. */
2117 if ( euid != g_uid
2118 || ruid != g_uid
2119 || suid != g_uid
2120 || egid != g_gid
2121 || rgid != g_gid
2122 || sgid != g_gid)
2123 supR3HardenedFatal("SUPR3HardenedMain: failed to drop root privileges!"
2124 " (euid=%d ruid=%d suid=%d egid=%d rgid=%d sgid=%d; wanted uid=%d and gid=%d)\n",
2125 euid, ruid, suid, egid, rgid, sgid, g_uid, g_gid);
2126
2127# if RT_OS_LINUX
2128 /*
2129 * Re-enable the cap_net_raw and cap_sys_nice capabilities which were disabled during setresuid.
2130 */
2131 if (g_uCaps != 0)
2132 {
2133# ifdef USE_LIB_PCAP
2134 /** @todo Warn if that does not work? */
2135 /* XXX cap_net_bind_service */
2136 cap_set_proc(cap_from_text("cap_net_raw+ep cap_sys_nice+ep"));
2137# else
2138 cap_user_header_t hdr = (cap_user_header_t)alloca(sizeof(*hdr));
2139 cap_user_data_t cap = (cap_user_data_t)alloca(2 /* _LINUX_CAPABILITY_U32S_3 */ * sizeof(*cap));
2140 memset(hdr, 0, sizeof(*hdr));
2141 hdr->version = g_uCapsVersion;
2142 memset(cap, 0, 2 /* _LINUX_CAPABILITY_U32S_3 */ * sizeof(*cap));
2143 cap->effective = g_uCaps;
2144 cap->permitted = g_uCaps;
2145 /** @todo Warn if that does not work? */
2146 capset(hdr, cap);
2147# endif /* !USE_LIB_PCAP */
2148 }
2149# endif
2150}
2151
2152#endif /* SUP_HARDENED_SUID */
2153
2154/**
2155 * Purge the process environment from any environment vairable which can lead
2156 * to loading untrusted binaries compromising the process address space.
2157 *
2158 * @param envp The initial environment vector. (Can be NULL.)
2159 */
2160static void supR3HardenedMainPurgeEnvironment(char **envp)
2161{
2162 for (unsigned i = 0; i < RT_ELEMENTS(g_aSupEnvPurgeDescs); i++)
2163 {
2164 /*
2165 * Update the initial environment vector, just in case someone actually cares about it.
2166 */
2167 if (envp)
2168 {
2169 const char * const pszEnv = g_aSupEnvPurgeDescs[i].pszEnv;
2170 size_t const cchEnv = g_aSupEnvPurgeDescs[i].cchEnv;
2171 unsigned iSrc = 0;
2172 unsigned iDst = 0;
2173 char *pszTmp;
2174
2175 while ((pszTmp = envp[iSrc]) != NULL)
2176 {
2177 if ( memcmp(pszTmp, pszEnv, cchEnv) != 0
2178 || (pszTmp[cchEnv] != '=' && pszTmp[cchEnv] != '\0'))
2179 {
2180 if (iDst != iSrc)
2181 envp[iDst] = pszTmp;
2182 iDst++;
2183 }
2184 else
2185 SUP_DPRINTF(("supR3HardenedMainPurgeEnvironment: dropping envp[%d]=%s\n", iSrc, pszTmp));
2186 iSrc++;
2187 }
2188
2189 if (iDst != iSrc)
2190 while (iDst <= iSrc)
2191 envp[iDst++] = NULL;
2192 }
2193
2194 /*
2195 * Remove from the process environment if present.
2196 */
2197#ifndef RT_OS_WINDOWS
2198 const char *pszTmp = getenv(g_aSupEnvPurgeDescs[i].pszEnv);
2199 if (pszTmp != NULL)
2200 {
2201 if (unsetenv((char *)g_aSupEnvPurgeDescs[i].pszEnv) == 0)
2202 SUP_DPRINTF(("supR3HardenedMainPurgeEnvironment: dropped %s\n", pszTmp));
2203 else
2204 if (g_aSupEnvPurgeDescs[i].fPurgeErrFatal)
2205 supR3HardenedFatal("SUPR3HardenedMain: failed to purge %s environment variable! (errno=%d %s)\n",
2206 g_aSupEnvPurgeDescs[i].pszEnv, errno, strerror(errno));
2207 else
2208 SUP_DPRINTF(("supR3HardenedMainPurgeEnvironment: dropping %s failed! errno=%d\n", pszTmp, errno));
2209 }
2210#else
2211 /** @todo Call NT API to do the same. */
2212#endif
2213 }
2214}
2215
2216
2217/**
2218 * Returns the argument purge descriptor of the given argument if available.
2219 *
2220 * @retval 0 if it should not be purged.
2221 * @retval 1 if it only the current argument should be purged.
2222 * @retval 2 if the argument and the following (if present) should be purged.
2223 * @param pszArg The argument to look for.
2224 */
2225static unsigned supR3HardenedMainShouldPurgeArg(const char *pszArg)
2226{
2227 for (unsigned i = 0; i < RT_ELEMENTS(g_aSupArgPurgeDescs); i++)
2228 {
2229 size_t const cchPurge = g_aSupArgPurgeDescs[i].cchArg;
2230 if (!memcmp(pszArg, g_aSupArgPurgeDescs[i].pszArg, cchPurge))
2231 {
2232 if (pszArg[cchPurge] == '\0')
2233 return 1 + g_aSupArgPurgeDescs[i].fTakesValue;
2234 if ( g_aSupArgPurgeDescs[i].fTakesValue
2235 && (pszArg[cchPurge] == ':' || pszArg[cchPurge] == '='))
2236 return 1;
2237 }
2238 }
2239
2240 return 0;
2241}
2242
2243
2244/**
2245 * Purges any command line arguments considered harmful.
2246 *
2247 * @returns nothing.
2248 * @param cArgsOrig The original number of arguments.
2249 * @param papszArgsOrig The original argument vector.
2250 * @param pcArgsNew Where to store the new number of arguments on success.
2251 * @param ppapszArgsNew Where to store the pointer to the purged argument vector.
2252 */
2253static void supR3HardenedMainPurgeArgs(int cArgsOrig, char **papszArgsOrig, int *pcArgsNew, char ***ppapszArgsNew)
2254{
2255 int iDst = 0;
2256#ifdef RT_OS_WINDOWS
2257 char **papszArgsNew = papszArgsOrig; /* We allocated this, no need to allocate again. */
2258#else
2259 char **papszArgsNew = (char **)malloc((cArgsOrig + 1) * sizeof(char *));
2260#endif
2261 if (papszArgsNew)
2262 {
2263 for (int iSrc = 0; iSrc < cArgsOrig; iSrc++)
2264 {
2265 unsigned cPurgedArgs = supR3HardenedMainShouldPurgeArg(papszArgsOrig[iSrc]);
2266 if (!cPurgedArgs)
2267 papszArgsNew[iDst++] = papszArgsOrig[iSrc];
2268 else
2269 iSrc += cPurgedArgs - 1;
2270 }
2271
2272 papszArgsNew[iDst] = NULL; /* The array is NULL terminated, just like envp. */
2273 }
2274 else
2275 supR3HardenedFatal("SUPR3HardenedMain: failed to allocate memory for purged command line!\n");
2276 *pcArgsNew = iDst;
2277 *ppapszArgsNew = papszArgsNew;
2278
2279#ifdef RT_OS_WINDOWS
2280 /** @todo Update command line pointers in PEB, wont really work without it. */
2281#endif
2282}
2283
2284
2285/**
2286 * Loads the VBoxRT DLL/SO/DYLIB, hands it the open driver,
2287 * and calls RTR3InitEx.
2288 *
2289 * @param fFlags The SUPR3HardenedMain fFlags argument, passed to supR3PreInit.
2290 *
2291 * @remarks VBoxRT contains both IPRT and SUPR3.
2292 * @remarks This function will not return on failure.
2293 */
2294static void supR3HardenedMainInitRuntime(uint32_t fFlags)
2295{
2296 /*
2297 * Construct the name.
2298 */
2299 char szPath[RTPATH_MAX];
2300 supR3HardenedPathAppSharedLibs(szPath, sizeof(szPath) - sizeof("/VBoxRT" SUPLIB_DLL_SUFF));
2301 suplibHardenedStrCat(szPath, "/VBoxRT" SUPLIB_DLL_SUFF);
2302
2303 /*
2304 * Open it and resolve the symbols.
2305 */
2306#if defined(RT_OS_WINDOWS)
2307 HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/, g_fSupHardenedMain);
2308 if (!hMod)
2309 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_MODULE_NOT_FOUND,
2310 "LoadLibrary \"%s\" failed (rc=%d)",
2311 szPath, RtlGetLastWin32Error());
2312 PFNRTR3INITEX pfnRTInitEx = (PFNRTR3INITEX)GetProcAddress(hMod, SUP_HARDENED_SYM("RTR3InitEx"));
2313 if (!pfnRTInitEx)
2314 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
2315 "Entrypoint \"RTR3InitEx\" not found in \"%s\" (rc=%d)",
2316 szPath, RtlGetLastWin32Error());
2317
2318 PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)GetProcAddress(hMod, SUP_HARDENED_SYM("supR3PreInit"));
2319 if (!pfnSUPPreInit)
2320 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
2321 "Entrypoint \"supR3PreInit\" not found in \"%s\" (rc=%d)",
2322 szPath, RtlGetLastWin32Error());
2323
2324 g_pfnRTLogRelPrintf = (PFNRTLOGRELPRINTF)GetProcAddress(hMod, SUP_HARDENED_SYM("RTLogRelPrintf"));
2325 Assert(g_pfnRTLogRelPrintf); /* Not fatal in non-strict builds. */
2326
2327#else
2328 /* the dlopen crowd */
2329 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
2330 if (!pvMod)
2331 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_MODULE_NOT_FOUND,
2332 "dlopen(\"%s\",) failed: %s",
2333 szPath, dlerror());
2334 PFNRTR3INITEX pfnRTInitEx = (PFNRTR3INITEX)(uintptr_t)dlsym(pvMod, SUP_HARDENED_SYM("RTR3InitEx"));
2335 if (!pfnRTInitEx)
2336 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
2337 "Entrypoint \"RTR3InitEx\" not found in \"%s\"!\ndlerror: %s",
2338 szPath, dlerror());
2339 PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)(uintptr_t)dlsym(pvMod, SUP_HARDENED_SYM("supR3PreInit"));
2340 if (!pfnSUPPreInit)
2341 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
2342 "Entrypoint \"supR3PreInit\" not found in \"%s\"!\ndlerror: %s",
2343 szPath, dlerror());
2344#endif
2345
2346 /*
2347 * Make the calls.
2348 */
2349 supR3HardenedGetPreInitData(&g_SupPreInitData);
2350 int rc = pfnSUPPreInit(&g_SupPreInitData, fFlags);
2351 if (RT_FAILURE(rc))
2352 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, rc,
2353 "supR3PreInit failed with rc=%d", rc);
2354
2355 /* Get the executable path for the IPRT init on linux if /proc/self/exe isn't accessible. */
2356 const char *pszExePath = NULL;
2357#ifdef RT_OS_LINUX
2358 if (!supR3HardenedMainIsProcSelfExeAccssible())
2359 pszExePath = g_szSupLibHardenedExePath;
2360#endif
2361
2362 /* Assemble the IPRT init flags. We could probably just pass RTR3INIT_FLAGS_TRY_SUPLIB
2363 here and be done with it, but it's not too much hazzle to convert fFlags 1:1. */
2364 uint32_t fRtInit = 0;
2365 if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
2366 {
2367 if (fFlags & SUPSECMAIN_FLAGS_DRIVERLESS)
2368 fRtInit |= (SUPR3INIT_F_DRIVERLESS << RTR3INIT_FLAGS_SUPLIB_SHIFT) | RTR3INIT_FLAGS_TRY_SUPLIB;
2369 if (fFlags & SUPSECMAIN_FLAGS_DRIVERLESS_IEM_ALLOWED)
2370 fRtInit |= (SUPR3INIT_F_DRIVERLESS_IEM_ALLOWED << RTR3INIT_FLAGS_SUPLIB_SHIFT) | RTR3INIT_FLAGS_TRY_SUPLIB;
2371#ifdef VBOX_WITH_DRIVERLESS_NEM_FALLBACK
2372 if (fFlags & SUPSECMAIN_FLAGS_DRIVERLESS_NEM_FALLBACK)
2373 fRtInit |= (SUPR3INIT_F_DRIVERLESS_NEM_FALLBACK << RTR3INIT_FLAGS_SUPLIB_SHIFT) | RTR3INIT_FLAGS_TRY_SUPLIB;
2374#endif
2375 if (!(fRtInit & RTR3INIT_FLAGS_TRY_SUPLIB))
2376 fRtInit |= RTR3INIT_FLAGS_SUPLIB;
2377 }
2378
2379 /* Now do the IPRT init. */
2380 rc = pfnRTInitEx(RTR3INIT_VER_CUR, fRtInit, 0 /*cArgs*/, NULL /*papszArgs*/, pszExePath);
2381 if (RT_FAILURE(rc))
2382 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, rc,
2383 "RTR3InitEx failed with rc=%d (fRtFlags=%#x)", rc, fRtInit);
2384
2385#if defined(RT_OS_WINDOWS)
2386 /*
2387 * Windows: Create thread that terminates the process when the parent stub
2388 * process terminates (VBoxNetDHCP, Ctrl-C, etc).
2389 */
2390 if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
2391 supR3HardenedWinCreateParentWatcherThread(hMod);
2392#endif
2393}
2394
2395
2396/**
2397 * Construct the path to the DLL/SO/DYLIB containing the actual program.
2398 *
2399 * @returns VBox status code.
2400 * @param pszProgName The program name.
2401 * @param fMainFlags The flags passed to SUPR3HardenedMain.
2402 * @param pszPath The output buffer.
2403 * @param cbPath The size of the output buffer, in bytes. Must be at
2404 * least 128 bytes!
2405 */
2406static int supR3HardenedMainGetTrustedLib(const char *pszProgName, uint32_t fMainFlags, char *pszPath, size_t cbPath)
2407{
2408 supR3HardenedPathAppPrivateArch(pszPath, sizeof(cbPath) - 10);
2409 const char *pszSubDirSlash;
2410 switch (g_fSupHardenedMain & SUPSECMAIN_FLAGS_LOC_MASK)
2411 {
2412 case SUPSECMAIN_FLAGS_LOC_APP_BIN:
2413#ifdef RT_OS_DARWIN
2414 case SUPSECMAIN_FLAGS_LOC_OSX_HLP_APP:
2415#endif
2416 pszSubDirSlash = "/";
2417 break;
2418 case SUPSECMAIN_FLAGS_LOC_TESTCASE:
2419 pszSubDirSlash = "/testcase/";
2420 break;
2421 default:
2422 pszSubDirSlash = "/";
2423 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Unknown program binary location: %#x\n", g_fSupHardenedMain);
2424 }
2425#ifdef RT_OS_DARWIN
2426 if (fMainFlags & SUPSECMAIN_FLAGS_OSX_VM_APP)
2427 pszProgName = "VirtualBox";
2428#else
2429 RT_NOREF1(fMainFlags);
2430#endif
2431 size_t cch = suplibHardenedStrLen(pszPath);
2432 return suplibHardenedStrCopyEx(&pszPath[cch], cbPath - cch, pszSubDirSlash, pszProgName, SUPLIB_DLL_SUFF, NULL);
2433}
2434
2435
2436/**
2437 * Loads the DLL/SO/DYLIB containing the actual program and
2438 * resolves the TrustedError symbol.
2439 *
2440 * This is very similar to supR3HardenedMainGetTrustedMain().
2441 *
2442 * @returns Pointer to the trusted error symbol if it is exported, NULL
2443 * and no error messages otherwise.
2444 * @param pszProgName The program name.
2445 */
2446static PFNSUPTRUSTEDERROR supR3HardenedMainGetTrustedError(const char *pszProgName)
2447{
2448 /*
2449 * Don't bother if the main() function didn't advertise any TrustedError
2450 * export. It's both a waste of time and may trigger additional problems,
2451 * confusing or obscuring the original issue.
2452 */
2453 if (!(g_fSupHardenedMain & SUPSECMAIN_FLAGS_TRUSTED_ERROR))
2454 return NULL;
2455
2456 /*
2457 * Construct the name.
2458 */
2459 char szPath[RTPATH_MAX];
2460 supR3HardenedMainGetTrustedLib(pszProgName, g_fSupHardenedMain, szPath, sizeof(szPath));
2461
2462 /*
2463 * Open it and resolve the symbol.
2464 */
2465#if defined(RT_OS_WINDOWS)
2466 supR3HardenedWinEnableThreadCreation();
2467 HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/, 0 /*fMainFlags*/);
2468 if (!hMod)
2469 return NULL;
2470 FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedError"));
2471 if (!pfn)
2472 return NULL;
2473 return (PFNSUPTRUSTEDERROR)pfn;
2474
2475#else
2476 /* the dlopen crowd */
2477 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
2478 if (!pvMod)
2479 return NULL;
2480 void *pvSym = dlsym(pvMod, SUP_HARDENED_SYM("TrustedError"));
2481 if (!pvSym)
2482 return NULL;
2483 return (PFNSUPTRUSTEDERROR)(uintptr_t)pvSym;
2484#endif
2485}
2486
2487
2488/**
2489 * Loads the DLL/SO/DYLIB containing the actual program and
2490 * resolves the TrustedMain symbol.
2491 *
2492 * @returns Pointer to the trusted main of the actual program.
2493 * @param pszProgName The program name.
2494 * @param fMainFlags The flags passed to SUPR3HardenedMain.
2495 * @remarks This function will not return on failure.
2496 */
2497static PFNSUPTRUSTEDMAIN supR3HardenedMainGetTrustedMain(const char *pszProgName, uint32_t fMainFlags)
2498{
2499 /*
2500 * Construct the name.
2501 */
2502 char szPath[RTPATH_MAX];
2503 supR3HardenedMainGetTrustedLib(pszProgName, fMainFlags, szPath, sizeof(szPath));
2504
2505 /*
2506 * Open it and resolve the symbol.
2507 */
2508#if defined(RT_OS_WINDOWS)
2509 HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/, 0 /*fMainFlags*/);
2510 if (!hMod)
2511 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: LoadLibrary \"%s\" failed, rc=%d\n",
2512 szPath, RtlGetLastWin32Error());
2513 FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedMain"));
2514 if (!pfn)
2515 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\" (rc=%d)\n",
2516 szPath, RtlGetLastWin32Error());
2517 return (PFNSUPTRUSTEDMAIN)pfn;
2518
2519#else
2520 /* the dlopen crowd */
2521 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
2522 if (!pvMod)
2523 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: dlopen(\"%s\",) failed: %s\n",
2524 szPath, dlerror());
2525 void *pvSym = dlsym(pvMod, SUP_HARDENED_SYM("TrustedMain"));
2526 if (!pvSym)
2527 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\"!\ndlerror: %s\n",
2528 szPath, dlerror());
2529 return (PFNSUPTRUSTEDMAIN)(uintptr_t)pvSym;
2530#endif
2531}
2532
2533
2534DECLHIDDEN(int) SUPR3HardenedMain(const char *pszProgName, uint32_t fFlags, int argc, char **argv, char **envp)
2535{
2536 SUP_DPRINTF(("SUPR3HardenedMain: pszProgName=%s fFlags=%#x\n", pszProgName, fFlags));
2537 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_HARDENED_MAIN_CALLED;
2538
2539 /*
2540 * Note! At this point there is no IPRT, so we will have to stick
2541 * to basic CRT functions that everyone agree upon.
2542 */
2543 g_pszSupLibHardenedProgName = pszProgName;
2544 g_fSupHardenedMain = fFlags;
2545 g_SupPreInitData.u32Magic = SUPPREINITDATA_MAGIC;
2546 g_SupPreInitData.u32EndMagic = SUPPREINITDATA_MAGIC;
2547#ifdef RT_OS_WINDOWS
2548 if (!g_fSupEarlyProcessInit)
2549#endif
2550 g_SupPreInitData.Data.hDevice = SUP_HDEVICE_NIL;
2551
2552 /*
2553 * Determine the full exe path as we'll be needing it for the verify all
2554 * call(s) below. (We have to do this early on Linux because we * *might*
2555 * not be able to access /proc/self/exe after the seteuid call.)
2556 */
2557 supR3HardenedGetFullExePath();
2558#ifdef RT_OS_WINDOWS
2559 supR3HardenedWinInitAppBin(fFlags);
2560#endif
2561
2562#ifdef SUP_HARDENED_SUID
2563 /*
2564 * Grab any options from the environment.
2565 */
2566 supR3GrabOptions();
2567
2568 /*
2569 * Check that we're root, if we aren't then the installation is butchered.
2570 */
2571 g_uid = getuid();
2572 g_gid = getgid();
2573 if (geteuid() != 0 /* root */)
2574 supR3HardenedFatalMsg("SUPR3HardenedMain", kSupInitOp_RootCheck, VERR_PERMISSION_DENIED,
2575 "Effective UID is not root (euid=%d egid=%d uid=%d gid=%d)",
2576 geteuid(), getegid(), g_uid, g_gid);
2577#endif /* SUP_HARDENED_SUID */
2578
2579#ifdef RT_OS_WINDOWS
2580 /*
2581 * Windows: First respawn. On Windows we will respawn the process twice to establish
2582 * something we can put some kind of reliable trust in. The first respawning aims
2583 * at dropping compatibility layers and process "security" solutions.
2584 */
2585 if ( !g_fSupEarlyProcessInit
2586 && !(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)
2587 && supR3HardenedWinIsReSpawnNeeded(1 /*iWhich*/, argc, argv))
2588 {
2589 SUP_DPRINTF(("SUPR3HardenedMain: Respawn #1\n"));
2590 supR3HardenedWinInit(SUPSECMAIN_FLAGS_DONT_OPEN_DEV | SUPSECMAIN_FLAGS_FIRST_PROCESS, false /*fAvastKludge*/);
2591 supR3HardenedVerifyAll(true /* fFatal */, pszProgName, g_szSupLibHardenedExePath, fFlags);
2592 return supR3HardenedWinReSpawn(1 /*iWhich*/);
2593 }
2594
2595 /*
2596 * Windows: Initialize the image verification global data so we can verify the
2597 * signature of the process image and hook the core of the DLL loader API so we
2598 * can check the signature of all DLLs mapped into the process. (Already done
2599 * by early VM process init.)
2600 */
2601 if (!g_fSupEarlyProcessInit)
2602 supR3HardenedWinInit(fFlags, true /*fAvastKludge*/);
2603#endif /* RT_OS_WINDOWS */
2604
2605 /*
2606 * Validate the installation.
2607 */
2608 supR3HardenedVerifyAll(true /* fFatal */, pszProgName, g_szSupLibHardenedExePath, fFlags);
2609
2610 /*
2611 * The next steps are only taken if we actually need to access the support
2612 * driver. (Already done by early process init.)
2613 */
2614 if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
2615 {
2616#ifdef RT_OS_WINDOWS
2617 /*
2618 * Windows: Must have done early process init if we get here.
2619 */
2620 if (!g_fSupEarlyProcessInit)
2621 supR3HardenedFatalMsg("SUPR3HardenedMain", kSupInitOp_Integrity, VERR_WRONG_ORDER,
2622 "Early process init was somehow skipped.");
2623
2624 /*
2625 * Windows: The second respawn. This time we make a special arrangement
2626 * with vboxdrv to monitor access to the new process from its inception.
2627 */
2628 if (supR3HardenedWinIsReSpawnNeeded(2 /* iWhich*/, argc, argv))
2629 {
2630 SUP_DPRINTF(("SUPR3HardenedMain: Respawn #2\n"));
2631 return supR3HardenedWinReSpawn(2 /* iWhich*/);
2632 }
2633 SUP_DPRINTF(("SUPR3HardenedMain: Final process, opening VBoxDrv...\n"));
2634 supR3HardenedWinFlushLoaderCache();
2635
2636#else
2637 /*
2638 * Open the vboxdrv device.
2639 */
2640 supR3HardenedMainOpenDevice();
2641#endif /* !RT_OS_WINDOWS */
2642 }
2643
2644#ifdef RT_OS_WINDOWS
2645 /*
2646 * Windows: Enable the use of windows APIs to verify images at load time.
2647 */
2648 supR3HardenedWinEnableThreadCreation();
2649 supR3HardenedWinFlushLoaderCache();
2650 supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation(g_pszSupLibHardenedProgName);
2651 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_WIN_VERIFY_TRUST_READY;
2652#else /* !RT_OS_WINDOWS */
2653# if defined(RT_OS_DARWIN)
2654 supR3HardenedDarwinInit();
2655# elif !defined(RT_OS_FREEBSD) /** @todo Portme. */
2656 /*
2657 * Posix: Hook the load library interface interface.
2658 */
2659 supR3HardenedPosixInit();
2660# endif
2661#endif /* !RT_OS_WINDOWS */
2662
2663#ifdef SUP_HARDENED_SUID
2664 /*
2665 * Grab additional capabilities / privileges.
2666 */
2667 supR3HardenedMainGrabCapabilites();
2668
2669 /*
2670 * Drop any root privileges we might be holding (won't return on failure)
2671 */
2672 supR3HardenedMainDropPrivileges();
2673#endif
2674
2675 /*
2676 * Purge any environment variables and command line arguments considered harmful.
2677 */
2678 /** @todo May need to move this to a much earlier stage on windows. */
2679 supR3HardenedMainPurgeEnvironment(envp);
2680 supR3HardenedMainPurgeArgs(argc, argv, &argc, &argv);
2681
2682 /*
2683 * Load the IPRT, hand the SUPLib part the open driver and
2684 * call RTR3InitEx.
2685 */
2686 SUP_DPRINTF(("SUPR3HardenedMain: Load Runtime...\n"));
2687 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_INIT_RUNTIME;
2688 supR3HardenedMainInitRuntime(fFlags);
2689#ifdef RT_OS_WINDOWS
2690 supR3HardenedWinModifyDllSearchPath(fFlags, g_szSupLibHardenedAppBinPath);
2691#endif
2692
2693 /*
2694 * Load the DLL/SO/DYLIB containing the actual program
2695 * and pass control to it.
2696 */
2697 SUP_DPRINTF(("SUPR3HardenedMain: Load TrustedMain...\n"));
2698 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_GET_TRUSTED_MAIN;
2699 PFNSUPTRUSTEDMAIN pfnTrustedMain = supR3HardenedMainGetTrustedMain(pszProgName, fFlags);
2700
2701 SUP_DPRINTF(("SUPR3HardenedMain: Calling TrustedMain (%p)...\n", pfnTrustedMain));
2702 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_CALLED_TRUSTED_MAIN;
2703 return pfnTrustedMain(argc, argv, envp);
2704}
2705
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use