VirtualBox

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

Last change on this file since 66573 was 66573, checked in by vboxsync, 7 years ago

supR3HardenedFatalMsgV: close driver, postpone fork. bugref:8838

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

© 2023 Oracle
ContactPrivacy policyTerms of Use