VirtualBox

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

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

Support: scm updates

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

© 2023 Oracle
ContactPrivacy policyTerms of Use