VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/darwin/SUPR3HardenedMain-darwin.cpp@ 103914

Last change on this file since 103914 was 99739, checked in by vboxsync, 19 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.4 KB
Line 
1/* $Id: SUPR3HardenedMain-darwin.cpp 99739 2023-05-11 01:01:08Z vboxsync $ */
2/** @file
3 * VirtualBox Support Library - Hardened main(), posix bits.
4 */
5
6/*
7 * Copyright (C) 2017-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <VBox/err.h>
42#include <VBox/sup.h>
43
44#include <iprt/path.h>
45#include <iprt/string.h>
46
47#include <dlfcn.h>
48#include <sys/mman.h>
49#include <errno.h>
50#include <sys/sysctl.h> /* sysctlbyname() */
51#include <stdio.h>
52#include <stdint.h>
53#include <unistd.h> /* issetugid() */
54#include <mach-o/dyld.h>
55
56#include "SUPLibInternal.h"
57
58
59/*********************************************************************************************************************************
60* Defined Constants And Macros *
61*********************************************************************************************************************************/
62
63
64/*********************************************************************************************************************************
65* Structures and Typedefs *
66*********************************************************************************************************************************/
67
68/**
69 * Interpose table entry.
70 */
71typedef struct DYLDINTERPOSE
72{
73 /** The symbol address to replace with. */
74 const void *pvReplacement;
75 /** The replaced symbol address. */
76 const void *pvReplacee;
77} DYLDINTERPOSE;
78/** Pointer to an interposer table entry. */
79typedef DYLDINTERPOSE *PDYLDINTERPOSE;
80/** Pointer to a const interposer table entry. */
81typedef const DYLDINTERPOSE *PCDYLDINTERPOSE;
82
83/** @sa dyld_dynamic_interpose(). */
84typedef const mach_header *FNDYLDDYNAMICINTERPOSE(const struct mach_header *mh, PCDYLDINTERPOSE paSym, size_t cSyms);
85/** Pointer to dyld_dynamic_interpose. */
86typedef FNDYLDDYNAMICINTERPOSE *PFNDYLDDYNAMICINTERPOSE;
87
88/** @sa dlopen(). */
89typedef void *FNDLOPEN(const char *path, int mode);
90/** Pointer to dlopen. */
91typedef FNDLOPEN *PFNDLOPEN;
92
93
94/*********************************************************************************************************************************
95* Internal Functions *
96*********************************************************************************************************************************/
97extern "C" void _dyld_register_func_for_add_image(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide));
98
99static void *supR3HardenedDarwinDlopenInterpose(const char *path, int mode);
100static int supR3HardenedDarwinIssetugidInterpose(void);
101
102
103/*********************************************************************************************************************************
104* Global Variables *
105*********************************************************************************************************************************/
106/** Flag whether macOS 11.x (BigSur) or later was detected.
107 * See comments in supR3HardenedDarwinDlopenInterpose for details. */
108static bool g_fMacOs11Plus = false;
109/** Resolved dyld_dynamic_interpose() value. */
110static PFNDYLDDYNAMICINTERPOSE g_pfnDyldDynamicInterpose = NULL;
111/** Pointer to the real dlopen() function used from the interposer when verification succeeded. */
112static PFNDLOPEN g_pfnDlopenReal = NULL;
113/**
114 * The interposer table.
115 */
116static const DYLDINTERPOSE g_aInterposers[] =
117{
118 { (const void *)(uintptr_t)&supR3HardenedDarwinDlopenInterpose, (const void *)(uintptr_t)&dlopen },
119 { (const void *)(uintptr_t)&supR3HardenedDarwinIssetugidInterpose, (const void *)(uintptr_t)&issetugid }
120};
121
122
123/**
124 * dlopen() interposer which verifies that the path to be loaded meets the criteria for hardened builds.
125 *
126 * @sa dlopen() man page.
127 */
128static void *supR3HardenedDarwinDlopenInterpose(const char *path, int mode)
129{
130 /*
131 * Giving NULL as the filename indicates opening the main program which is fine
132 * We are already loaded and executing after all.
133 *
134 * Filenames without any path component (whether absolute or relative) are allowed
135 * unconditionally too as the loader will only search the default paths configured by root.
136 */
137 if ( path
138 && strchr(path, '/') != NULL)
139 {
140 int rc = VINF_SUCCESS;
141
142 /*
143 * Starting with macOS 11.0 (BigSur) system provided libraries
144 * under /System/Libraries are not stored on the filesystem anymore
145 * but in a dynamic linker cache. The integrity of the linker cache
146 * is maintained by the system and dyld. Our verification code fails because
147 * it can't find the file.
148 * The obvious solution is to exclude paths starting with /System/Libraries
149 * when we run on BigSur. Other paths are still subject to verification.
150 */
151 if ( !g_fMacOs11Plus
152 || strncmp(path, RT_STR_TUPLE("/System/Library")))
153 rc = supR3HardenedVerifyFileFollowSymlinks(path, RTHCUINTPTR_MAX, true /* fMaybe3rdParty */,
154 NULL /* pErrInfo */);
155 if (RT_FAILURE(rc))
156 return NULL;
157 }
158
159 return g_pfnDlopenReal(path, mode);
160}
161
162
163/**
164 * Override this one to try hide the fact that we're setuid to root orginially.
165 *
166 * @sa issetugid() man page.
167 *
168 * Mac OS X: Really ugly hack to bypass a set-uid check in AppKit.
169 *
170 * This will modify the issetugid() function to always return zero. This must
171 * be done _before_ AppKit is initialized, otherwise it will refuse to play ball
172 * with us as it distrusts set-uid processes since Snow Leopard. We, however,
173 * have carefully dropped all root privileges at this point and there should be
174 * no reason for any security concern here.
175 */
176static int supR3HardenedDarwinIssetugidInterpose(void)
177{
178#ifdef DEBUG
179 Dl_info Info = {0};
180 char szMsg[512];
181 size_t cchMsg;
182 const void * uCaller = __builtin_return_address(0);
183 if (dladdr(uCaller, &Info))
184 cchMsg = snprintf(szMsg, sizeof(szMsg), "DEBUG: issetugid_for_AppKit was called by %p %s::%s+%p (via %p)\n",
185 uCaller, Info.dli_fname, Info.dli_sname, (void *)((uintptr_t)uCaller - (uintptr_t)Info.dli_saddr), __builtin_return_address(1));
186 else
187 cchMsg = snprintf(szMsg, sizeof(szMsg), "DEBUG: issetugid_for_AppKit was called by %p (via %p)\n", uCaller, __builtin_return_address(1));
188 write(2, szMsg, cchMsg);
189#endif
190 return 0;
191}
192
193
194/**
195 * Callback to get notified of new images being loaded to be able to apply our dlopn() interposer.
196 *
197 * @param mh Pointer to the mach header of the loaded image.
198 * @param vmaddr_slide The slide value for ASLR.
199 */
200static DECLCALLBACK(void) supR3HardenedDarwinAddImage(const struct mach_header *mh, intptr_t vmaddr_slide)
201{
202 RT_NOREF(vmaddr_slide);
203
204 g_pfnDyldDynamicInterpose(mh, &g_aInterposers[0], RT_ELEMENTS(g_aInterposers));
205}
206
207
208/**
209 * Hardening initialization for macOS hosts.
210 *
211 * @note Doesn't return on error.
212 */
213DECLHIDDEN(void) supR3HardenedDarwinInit(void)
214{
215 /*
216 * Check whether we are running on macOS BigSur by checking kern.osproductversion
217 * available since some point in 2018.
218 */
219 char szVers[256]; RT_ZERO(szVers);
220 size_t cbVers = sizeof(szVers);
221 int rc = sysctlbyname("kern.osproductversion", &szVers[0], &cbVers, NULL, 0);
222 if ( !rc
223 && memcmp(&szVers[0], RT_STR_TUPLE("10.16")) >= 0)
224 g_fMacOs11Plus = true;
225
226 /* Saved to call real dlopen() later on, as we will interpose dlopen() from the main binary in the next step as well. */
227 g_pfnDlopenReal = (PFNDLOPEN)dlsym(RTLD_DEFAULT, "dlopen");
228 g_pfnDyldDynamicInterpose = (PFNDYLDDYNAMICINTERPOSE)dlsym(RTLD_DEFAULT, "dyld_dynamic_interpose");
229 if (!g_pfnDyldDynamicInterpose)
230 supR3HardenedFatalMsg("supR3HardenedDarwinInit", kSupInitOp_Integrity, VERR_SYMBOL_NOT_FOUND,
231 "Failed to find dyld_dynamic_interpose()");
232
233 /*
234 * The following will causes our add image notification to be called for all images loaded so far.
235 * The callback will set up the interposer.
236 */
237 _dyld_register_func_for_add_image(supR3HardenedDarwinAddImage);
238}
239
240
241
242/*
243 * assert.cpp
244 *
245 * ASSUMES working DECLHIDDEN or there will be symbol confusion!
246 */
247
248RTDATADECL(char) g_szRTAssertMsg1[1024];
249RTDATADECL(char) g_szRTAssertMsg2[4096];
250RTDATADECL(const char * volatile) g_pszRTAssertExpr;
251RTDATADECL(const char * volatile) g_pszRTAssertFile;
252RTDATADECL(uint32_t volatile) g_u32RTAssertLine;
253RTDATADECL(const char * volatile) g_pszRTAssertFunction;
254
255RTDECL(bool) RTAssertMayPanic(void)
256{
257 return true;
258}
259
260
261RTDECL(void) RTAssertMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
262{
263 /*
264 * Fill in the globals.
265 */
266 g_pszRTAssertExpr = pszExpr;
267 g_pszRTAssertFile = pszFile;
268 g_pszRTAssertFunction = pszFunction;
269 g_u32RTAssertLine = uLine;
270 snprintf(g_szRTAssertMsg1, sizeof(g_szRTAssertMsg1),
271 "\n!!Assertion Failed!!\n"
272 "Expression: %s\n"
273 "Location : %s(%u) %s\n",
274 pszExpr, pszFile, uLine, pszFunction);
275}
276
277
278RTDECL(void) RTAssertMsg2V(const char *pszFormat, va_list va)
279{
280 vsnprintf(g_szRTAssertMsg2, sizeof(g_szRTAssertMsg2), pszFormat, va);
281 if (g_enmSupR3HardenedMainState < SUPR3HARDENEDMAINSTATE_CALLED_TRUSTED_MAIN)
282 supR3HardenedFatalMsg(g_pszRTAssertExpr, kSupInitOp_Misc, VERR_INTERNAL_ERROR,
283 "%s%s", g_szRTAssertMsg1, g_szRTAssertMsg2);
284 else
285 supR3HardenedError(VERR_INTERNAL_ERROR, false/*fFatal*/, "%s%s", g_szRTAssertMsg1, g_szRTAssertMsg2);
286}
287
Note: See TracBrowser for help on using the repository browser.

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