VirtualBox

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

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

Copyright year updates by scm.

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

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