VirtualBox

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

Last change on this file since 78177 was 77912, checked in by vboxsync, 6 years ago

Main: bugref:7929: Fixed comilation issues

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

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