VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp@ 103305

Last change on this file since 103305 was 103305, checked in by vboxsync, 3 months ago

HostDrivers/Support: Add VBoxXPCOMIPCD to the list of hardened libraries based on VBOX_WITH_XPCOMIPCD_IN_VBOX_SVC, bugref:10594

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 85.4 KB
Line 
1/* $Id: SUPR3HardenedVerify.cpp 103305 2024-02-12 09:00:29Z vboxsync $ */
2/** @file
3 * VirtualBox Support Library - Verification of Hardened Installation.
4 */
5
6/*
7 * Copyright (C) 2006-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#if defined(RT_OS_OS2)
42# define INCL_BASE
43# define INCL_ERRORS
44# include <os2.h>
45# include <stdio.h>
46# include <stdlib.h>
47# include <unistd.h>
48# include <sys/fcntl.h>
49# include <sys/errno.h>
50# include <sys/syslimits.h>
51
52#elif defined(RT_OS_WINDOWS)
53# include <iprt/nt/nt-and-windows.h>
54# ifndef IN_SUP_HARDENED_R3
55# include <stdio.h>
56# endif
57
58#else /* UNIXes */
59# include <sys/types.h>
60# include <stdio.h>
61# include <stdlib.h>
62# include <dirent.h>
63# include <dlfcn.h>
64# include <fcntl.h>
65# include <limits.h>
66# include <errno.h>
67# include <unistd.h>
68# include <sys/stat.h>
69# include <sys/time.h>
70# include <sys/fcntl.h>
71# include <pwd.h>
72# ifdef RT_OS_DARWIN
73# include <mach-o/dyld.h>
74# endif
75
76#endif
77
78#include <VBox/sup.h>
79#include <VBox/err.h>
80#include <iprt/asm.h>
81#include <iprt/ctype.h>
82#include <iprt/param.h>
83#include <iprt/path.h>
84#include <iprt/string.h>
85#include <iprt/utf16.h>
86
87#include "SUPLibInternal.h"
88#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
89# define SUPHNTVI_NO_NT_STUFF
90# include "win/SUPHardenedVerify-win.h"
91#endif
92
93
94/*********************************************************************************************************************************
95* Defined Constants And Macros *
96*********************************************************************************************************************************/
97/** The max path length acceptable for a trusted path. */
98#define SUPR3HARDENED_MAX_PATH 260U
99
100/** Enable to resolve symlinks using realpath() instead of cooking our own stuff. */
101#define SUP_HARDENED_VERIFY_FOLLOW_SYMLINKS_USE_REALPATH 1
102
103#ifdef RT_OS_SOLARIS
104# define dirfd(d) ((d)->d_fd)
105#endif
106
107/** Compare table file names with externally supplied names. */
108#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
109# define SUP_COMP_FILENAME RTStrICmp
110#else
111# define SUP_COMP_FILENAME suplibHardenedStrCmp
112#endif
113
114
115/*********************************************************************************************************************************
116* Global Variables *
117*********************************************************************************************************************************/
118/**
119 * The files that gets verified.
120 *
121 * @todo This needs reviewing against the linux packages.
122 * @todo The excessive use of kSupID_AppSharedLib needs to be reviewed at some point. For
123 * the time being we're building the linux packages with SharedLib pointing to
124 * AppPrivArch (lazy bird).
125 *
126 * @remarks If you add executables here, you might need to update
127 * g_apszSupNtVpAllowedVmExes in SUPHardenedVerifyProcess-win.cpp.
128 */
129static SUPINSTFILE const g_aSupInstallFiles[] =
130{
131 /* type, dir, fOpt, "pszFile" */
132 /* ---------------------------------------------------------------------- */
133 { kSupIFT_Dll, kSupID_AppPrivArch, false, "VMMR0.r0" },
134 { kSupIFT_Dll, kSupID_AppPrivArch, false, "VBoxDDR0.r0" },
135
136#ifdef VBOX_WITH_RAW_MODE
137 { kSupIFT_Rc, kSupID_AppPrivArch, false, "VMMRC.rc" },
138 { kSupIFT_Rc, kSupID_AppPrivArch, false, "VBoxDDRC.rc" },
139#endif
140
141 { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxRT" SUPLIB_DLL_SUFF },
142 { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxVMM" SUPLIB_DLL_SUFF },
143#if HC_ARCH_BITS == 32
144 { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxREM32" SUPLIB_DLL_SUFF },
145 { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxREM64" SUPLIB_DLL_SUFF },
146#endif
147 { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxDD" SUPLIB_DLL_SUFF },
148 { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxDD2" SUPLIB_DLL_SUFF },
149 { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxDDU" SUPLIB_DLL_SUFF },
150 { kSupIFT_Exe, kSupID_AppBin, true, "VBoxVMMPreload" SUPLIB_EXE_SUFF },
151 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxVMMPreload" SUPLIB_DLL_SUFF },
152
153//#ifdef VBOX_WITH_DEBUGGER_GUI
154 { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxDbg" SUPLIB_DLL_SUFF },
155 { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxDbg3" SUPLIB_DLL_SUFF },
156//#endif
157
158//#ifdef VBOX_WITH_SHARED_CLIPBOARD
159 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxSharedClipboard" SUPLIB_DLL_SUFF },
160//#endif
161//#ifdef VBOX_WITH_SHARED_FOLDERS
162 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxSharedFolders" SUPLIB_DLL_SUFF },
163//#endif
164//#ifdef VBOX_WITH_DRAG_AND_DROP
165 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxDragAndDropSvc" SUPLIB_DLL_SUFF },
166//#endif
167//#ifdef VBOX_WITH_GUEST_PROPS
168 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxGuestPropSvc" SUPLIB_DLL_SUFF },
169//#endif
170//#ifdef VBOX_WITH_GUEST_CONTROL
171 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxGuestControlSvc" SUPLIB_DLL_SUFF },
172//#endif
173 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxHostChannel" SUPLIB_DLL_SUFF },
174 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxSharedCrOpenGL" SUPLIB_DLL_SUFF },
175 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxOGLhostcrutil" SUPLIB_DLL_SUFF },
176 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxOGLhosterrorspu" SUPLIB_DLL_SUFF },
177 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxOGLrenderspu" SUPLIB_DLL_SUFF },
178
179 { kSupIFT_Exe, kSupID_AppBin, true, "VBoxManage" SUPLIB_EXE_SUFF },
180
181#ifdef VBOX_WITH_MAIN
182 { kSupIFT_Exe, kSupID_AppBin, false, "VBoxSVC" SUPLIB_EXE_SUFF },
183 #ifdef RT_OS_WINDOWS
184 { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxC" SUPLIB_DLL_SUFF },
185 #else
186# ifdef VBOX_WITH_XPCOMIPCD_IN_VBOX_SVC
187 { kSupIFT_Dll, kSupID_AppPrivArch, false, "VBoxXPCOMIPCD" SUPLIB_DLL_SUFF },
188# else
189 { kSupIFT_Exe, kSupID_AppPrivArch, false, "VBoxXPCOMIPCD" SUPLIB_EXE_SUFF },
190# endif
191 { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxXPCOM" SUPLIB_DLL_SUFF },
192 { kSupIFT_Dll, kSupID_AppPrivArchComp, false, "VBoxXPCOMIPCC" SUPLIB_DLL_SUFF },
193 { kSupIFT_Dll, kSupID_AppPrivArchComp, false, "VBoxC" SUPLIB_DLL_SUFF },
194 { kSupIFT_Dll, kSupID_AppPrivArchComp, false, "VBoxSVCM" SUPLIB_DLL_SUFF },
195 { kSupIFT_Data, kSupID_AppPrivArchComp, false, "VBoxXPCOMBase.xpt" },
196 #endif
197#endif
198
199 { kSupIFT_Dll, kSupID_AppSharedLib, true, "VRDPAuth" SUPLIB_DLL_SUFF },
200 { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxAuth" SUPLIB_DLL_SUFF },
201 { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxVRDP" SUPLIB_DLL_SUFF },
202
203//#ifdef VBOX_WITH_HEADLESS
204 { kSupIFT_Exe, kSupID_AppBin, true, "VBoxHeadless" SUPLIB_EXE_SUFF },
205 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxHeadless" SUPLIB_DLL_SUFF },
206 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxVideoRecFB" SUPLIB_DLL_SUFF },
207//#endif
208
209//#ifdef VBOX_WITH_QTGUI
210 { kSupIFT_Exe, kSupID_AppBin, true, "VirtualBox" SUPLIB_EXE_SUFF },
211# ifdef RT_OS_DARWIN
212 { kSupIFT_Exe, kSupID_AppMacHelper, true, "VirtualBoxVM" SUPLIB_EXE_SUFF },
213# else
214 { kSupIFT_Exe, kSupID_AppBin, true, "VirtualBoxVM" SUPLIB_EXE_SUFF },
215# endif
216 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VirtualBoxVM" SUPLIB_DLL_SUFF },
217 { kSupIFT_Dll, kSupID_AppPrivArch, true, "UICommon" SUPLIB_DLL_SUFF },
218# if !defined(RT_OS_DARWIN) && !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
219 { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxKeyboard" SUPLIB_DLL_SUFF },
220# endif
221//#endif
222
223//#ifdef VBOX_WITH_VBOXSDL
224 { kSupIFT_Exe, kSupID_AppBin, true, "VBoxSDL" SUPLIB_EXE_SUFF },
225 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxSDL" SUPLIB_DLL_SUFF },
226//#endif
227
228//#ifdef VBOX_WITH_WEBSERVICES
229 { kSupIFT_Exe, kSupID_AppBin, true, "vboxwebsrv" SUPLIB_EXE_SUFF },
230//#endif
231
232#ifdef RT_OS_LINUX
233 { kSupIFT_Exe, kSupID_AppBin, true, "VBoxTunctl" SUPLIB_EXE_SUFF },
234#endif
235
236//#ifdef VBOX_WITH_NETFLT
237 { kSupIFT_Exe, kSupID_AppBin, true, "VBoxNetDHCP" SUPLIB_EXE_SUFF },
238 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxNetDHCP" SUPLIB_DLL_SUFF },
239//#endif
240
241//#ifdef VBOX_WITH_LWIP_NAT
242 { kSupIFT_Exe, kSupID_AppBin, true, "VBoxNetNAT" SUPLIB_EXE_SUFF },
243 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxNetNAT" SUPLIB_DLL_SUFF },
244//#endif
245#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
246# define HARDENED_TESTCASE_BIN_ENTRY(a_szName) \
247 { kSupIFT_TestExe, kSupID_AppBin, true, a_szName SUPLIB_EXE_SUFF }, \
248 { kSupIFT_TestDll, kSupID_AppBin, true, a_szName SUPLIB_DLL_SUFF }
249 HARDENED_TESTCASE_BIN_ENTRY("tstMicro"),
250 HARDENED_TESTCASE_BIN_ENTRY("tstPDMAsyncCompletion"),
251 HARDENED_TESTCASE_BIN_ENTRY("tstPDMAsyncCompletionStress"),
252 HARDENED_TESTCASE_BIN_ENTRY("tstVMM"),
253 HARDENED_TESTCASE_BIN_ENTRY("tstVMREQ"),
254# define HARDENED_TESTCASE_ENTRY(a_szName) \
255 { kSupIFT_TestExe, kSupID_Testcase, true, a_szName SUPLIB_EXE_SUFF }, \
256 { kSupIFT_TestDll, kSupID_Testcase, true, a_szName SUPLIB_DLL_SUFF }
257 HARDENED_TESTCASE_ENTRY("tstCFGM"),
258 HARDENED_TESTCASE_ENTRY("tstGIP-2"),
259 HARDENED_TESTCASE_ENTRY("tstIntNet-1"),
260 HARDENED_TESTCASE_ENTRY("tstMMHyperHeap"),
261 HARDENED_TESTCASE_ENTRY("tstRTR0ThreadPreemptionDriver"),
262 HARDENED_TESTCASE_ENTRY("tstRTR0MemUserKernelDriver"),
263 HARDENED_TESTCASE_ENTRY("tstRTR0SemMutexDriver"),
264 HARDENED_TESTCASE_ENTRY("tstRTR0TimerDriver"),
265 HARDENED_TESTCASE_ENTRY("tstSSM"),
266#endif
267};
268
269
270/** Array parallel to g_aSupInstallFiles containing per-file status info. */
271static SUPVERIFIEDFILE g_aSupVerifiedFiles[RT_ELEMENTS(g_aSupInstallFiles)];
272
273/** Array index by install directory specifier containing info about verified directories. */
274static SUPVERIFIEDDIR g_aSupVerifiedDirs[kSupID_End];
275
276
277/**
278 * Assembles the path to a directory.
279 *
280 * @returns VINF_SUCCESS on success, some error code on failure (fFatal
281 * decides whether it returns or not).
282 *
283 * @param enmDir The directory.
284 * @param pszDst Where to assemble the path.
285 * @param cchDst The size of the buffer.
286 * @param fFatal Whether failures should be treated as fatal (true) or not (false).
287 * @param pFile The file (for darwin helper app paths).
288 */
289static int supR3HardenedMakePath(SUPINSTDIR enmDir, char *pszDst, size_t cchDst, bool fFatal, PCSUPINSTFILE pFile)
290{
291 int rc;
292 switch (enmDir)
293 {
294 case kSupID_AppBin:
295 rc = supR3HardenedPathAppBin(pszDst, cchDst);
296 break;
297 case kSupID_AppSharedLib:
298 rc = supR3HardenedPathAppSharedLibs(pszDst, cchDst);
299 break;
300 case kSupID_AppPrivArch:
301 rc = supR3HardenedPathAppPrivateArch(pszDst, cchDst);
302 break;
303 case kSupID_AppPrivArchComp:
304 rc = supR3HardenedPathAppPrivateArch(pszDst, cchDst);
305 if (RT_SUCCESS(rc))
306 {
307 size_t off = suplibHardenedStrLen(pszDst);
308 if (cchDst - off >= sizeof("/components"))
309 suplibHardenedMemCopy(&pszDst[off], "/components", sizeof("/components"));
310 else
311 rc = VERR_BUFFER_OVERFLOW;
312 }
313 break;
314 case kSupID_AppPrivNoArch:
315 rc = supR3HardenedPathAppPrivateNoArch(pszDst, cchDst);
316 break;
317 case kSupID_Testcase:
318 rc = supR3HardenedPathAppBin(pszDst, cchDst);
319 if (RT_SUCCESS(rc))
320 {
321 size_t off = suplibHardenedStrLen(pszDst);
322 if (cchDst - off >= sizeof("/testcase"))
323 suplibHardenedMemCopy(&pszDst[off], "/testcase", sizeof("/testcase"));
324 else
325 rc = VERR_BUFFER_OVERFLOW;
326 }
327 break;
328#ifdef RT_OS_DARWIN
329 case kSupID_AppMacHelper:
330 rc = supR3HardenedPathAppBin(pszDst, cchDst);
331 if (RT_SUCCESS(rc))
332 {
333 /* Up one level from the VirtualBox.app/Contents/MacOS directory: */
334 size_t offDst = suplibHardenedStrLen(pszDst);
335 while (offDst > 1 && pszDst[offDst - 1] == '/')
336 offDst--;
337 while (offDst > 1 && pszDst[offDst - 1] != '/')
338 offDst--;
339
340 /* Construct the path to the helper application's Contents/MacOS directory: */
341 size_t cchFile = suplibHardenedStrLen(pFile->pszFile);
342 if (offDst + cchFile + sizeof("Resources/.app/Contents/MacOS") <= cchDst)
343 {
344 suplibHardenedMemCopy(&pszDst[offDst], RT_STR_TUPLE("Resources/"));
345 offDst += sizeof("Resources/") - 1;
346 suplibHardenedMemCopy(&pszDst[offDst], pFile->pszFile, cchFile);
347 offDst += cchFile;
348 suplibHardenedMemCopy(&pszDst[offDst], RT_STR_TUPLE(".app/Contents/MacOS") + 1);
349 }
350 else
351 rc = VERR_BUFFER_OVERFLOW;
352 }
353 break;
354#endif
355 default:
356 return supR3HardenedError(VERR_INTERNAL_ERROR, fFatal,
357 "supR3HardenedMakePath: enmDir=%d\n", enmDir);
358 }
359 if (RT_FAILURE(rc))
360 supR3HardenedError(rc, fFatal,
361 "supR3HardenedMakePath: enmDir=%d rc=%d\n", enmDir, rc);
362 NOREF(pFile);
363 return rc;
364}
365
366
367
368/**
369 * Assembles the path to a file table entry, with or without the actual filename.
370 *
371 * @returns VINF_SUCCESS on success, some error code on failure (fFatal
372 * decides whether it returns or not).
373 *
374 * @param pFile The file table entry.
375 * @param pszDst Where to assemble the path.
376 * @param cchDst The size of the buffer.
377 * @param fWithFilename If set, the filename is included, otherwise it is omitted (no trailing slash).
378 * @param fFatal Whether failures should be treated as fatal (true) or not (false).
379 */
380static int supR3HardenedMakeFilePath(PCSUPINSTFILE pFile, char *pszDst, size_t cchDst, bool fWithFilename, bool fFatal)
381{
382 /*
383 * Combine supR3HardenedMakePath and the filename.
384 */
385 int rc = supR3HardenedMakePath(pFile->enmDir, pszDst, cchDst, fFatal, pFile);
386 if (RT_SUCCESS(rc) && fWithFilename)
387 {
388 size_t cchFile = suplibHardenedStrLen(pFile->pszFile);
389 size_t off = suplibHardenedStrLen(pszDst);
390 if (cchDst - off >= cchFile + 2)
391 {
392 pszDst[off++] = '/';
393 suplibHardenedMemCopy(&pszDst[off], pFile->pszFile, cchFile + 1);
394 }
395 else
396 rc = supR3HardenedError(VERR_BUFFER_OVERFLOW, fFatal,
397 "supR3HardenedMakeFilePath: pszFile=%s off=%lu\n",
398 pFile->pszFile, (long)off);
399 }
400 return rc;
401}
402
403
404/**
405 * Verifies a directory.
406 *
407 * @returns VINF_SUCCESS on success. On failure, an error code is returned if
408 * fFatal is clear and if it's set the function wont return.
409 * @param enmDir The directory specifier.
410 * @param fFatal Whether validation failures should be treated as
411 * fatal (true) or not (false).
412 * @param pFile The file (for darwin helper app paths).
413 */
414DECLHIDDEN(int) supR3HardenedVerifyFixedDir(SUPINSTDIR enmDir, bool fFatal, PCSUPINSTFILE pFile)
415{
416 /*
417 * Validate the index just to be on the safe side...
418 */
419 if (enmDir <= kSupID_Invalid || enmDir >= kSupID_End)
420 return supR3HardenedError(VERR_INTERNAL_ERROR, fFatal,
421 "supR3HardenedVerifyDir: enmDir=%d\n", enmDir);
422
423 /*
424 * Already validated?
425 */
426 if (g_aSupVerifiedDirs[enmDir].fValidated)
427 return VINF_SUCCESS; /** @todo revalidate? */
428
429 /* initialize the entry. */
430 if (g_aSupVerifiedDirs[enmDir].hDir != 0)
431 supR3HardenedError(VERR_INTERNAL_ERROR, fFatal,
432 "supR3HardenedVerifyDir: hDir=%p enmDir=%d\n",
433 (void *)g_aSupVerifiedDirs[enmDir].hDir, enmDir);
434 g_aSupVerifiedDirs[enmDir].hDir = -1;
435 g_aSupVerifiedDirs[enmDir].fValidated = false;
436
437 /*
438 * Make the path and open the directory.
439 */
440 char szPath[RTPATH_MAX];
441 int rc = supR3HardenedMakePath(enmDir, szPath, sizeof(szPath), fFatal, pFile);
442 if (RT_SUCCESS(rc))
443 {
444#if defined(RT_OS_WINDOWS)
445 PRTUTF16 pwszPath;
446 rc = RTStrToUtf16(szPath, &pwszPath);
447 if (RT_SUCCESS(rc))
448 {
449 HANDLE hDir = CreateFileW(pwszPath,
450 GENERIC_READ,
451 FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
452 NULL,
453 OPEN_EXISTING,
454 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
455 NULL);
456 if (hDir != INVALID_HANDLE_VALUE)
457 {
458 /** @todo check the type */
459 /* That's all on windows, for now at least... */
460 g_aSupVerifiedDirs[enmDir].hDir = (intptr_t)hDir;
461 g_aSupVerifiedDirs[enmDir].fValidated = true;
462 }
463 else if (enmDir == kSupID_Testcase)
464 {
465 g_aSupVerifiedDirs[enmDir].fValidated = true;
466 rc = VINF_SUCCESS; /* Optional directory, ignore if missing. */
467 }
468 else
469 {
470 int err = RtlGetLastWin32Error();
471 rc = supR3HardenedError(VERR_PATH_NOT_FOUND, fFatal,
472 "supR3HardenedVerifyDir: Failed to open \"%s\": err=%d\n",
473 szPath, err);
474 }
475 RTUtf16Free(pwszPath);
476 }
477 else
478 rc = supR3HardenedError(rc, fFatal,
479 "supR3HardenedVerifyDir: Failed to convert \"%s\" to UTF-16: err=%d\n", szPath, rc);
480
481#else /* UNIXY */
482 int fd = open(szPath, O_RDONLY, 0);
483 if (fd >= 0)
484 {
485 /*
486 * On unixy systems we'll make sure the directory is owned by root
487 * and not writable by the group and user.
488 */
489 struct stat st;
490 if (!fstat(fd, &st))
491 {
492
493 if ( st.st_uid == 0
494 && !(st.st_mode & (S_IWGRP | S_IWOTH))
495 && S_ISDIR(st.st_mode))
496 {
497 g_aSupVerifiedDirs[enmDir].hDir = fd;
498 g_aSupVerifiedDirs[enmDir].fValidated = true;
499 }
500 else
501 {
502 if (!S_ISDIR(st.st_mode))
503 rc = supR3HardenedError(VERR_NOT_A_DIRECTORY, fFatal,
504 "supR3HardenedVerifyDir: \"%s\" is not a directory\n",
505 szPath, (long)st.st_uid);
506 else if (st.st_uid)
507 rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal,
508 "supR3HardenedVerifyDir: Cannot trust the directory \"%s\": not owned by root (st_uid=%ld)\n",
509 szPath, (long)st.st_uid);
510 else
511 rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal,
512 "supR3HardenedVerifyDir: Cannot trust the directory \"%s\": group and/or other writable (st_mode=0%lo)\n",
513 szPath, (long)st.st_mode);
514 close(fd);
515 }
516 }
517 else
518 {
519 int err = errno;
520 rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal,
521 "supR3HardenedVerifyDir: Failed to fstat \"%s\": %s (%d)\n",
522 szPath, strerror(err), err);
523 close(fd);
524 }
525 }
526 else if (enmDir == kSupID_Testcase)
527 {
528 g_aSupVerifiedDirs[enmDir].fValidated = true;
529 rc = VINF_SUCCESS; /* Optional directory, ignore if missing. */
530 }
531 else
532 {
533 int err = errno;
534 rc = supR3HardenedError(VERR_PATH_NOT_FOUND, fFatal,
535 "supR3HardenedVerifyDir: Failed to open \"%s\": %s (%d)\n",
536 szPath, strerror(err), err);
537 }
538#endif /* UNIXY */
539 }
540
541 return rc;
542}
543
544
545#ifdef RT_OS_WINDOWS
546/**
547 * Opens the file for verification.
548 *
549 * @returns VINF_SUCCESS on success. On failure, an error code is returned if
550 * fFatal is clear and if it's set the function wont return.
551 * @param pFile The file entry.
552 * @param fFatal Whether validation failures should be treated as
553 * kl fatal (true) or not (false).
554 * @param phFile The file handle, set to -1 if we failed to open
555 * the file. The function may return VINF_SUCCESS
556 * and a -1 handle if the file is optional.
557 */
558static int supR3HardenedVerifyFileOpen(PCSUPINSTFILE pFile, bool fFatal, intptr_t *phFile)
559{
560 *phFile = -1;
561
562 char szPath[RTPATH_MAX];
563 int rc = supR3HardenedMakeFilePath(pFile, szPath, sizeof(szPath), true /*fWithFilename*/, fFatal);
564 if (RT_SUCCESS(rc))
565 {
566 PRTUTF16 pwszPath;
567 rc = RTStrToUtf16(szPath, &pwszPath);
568 if (RT_SUCCESS(rc))
569 {
570 HANDLE hFile = CreateFileW(pwszPath,
571 GENERIC_READ,
572 FILE_SHARE_READ,
573 NULL,
574 OPEN_EXISTING,
575 FILE_ATTRIBUTE_NORMAL,
576 NULL);
577 if (hFile != INVALID_HANDLE_VALUE)
578 {
579 *phFile = (intptr_t)hFile;
580 rc = VINF_SUCCESS;
581 }
582 else
583 {
584 int err = RtlGetLastWin32Error();
585 if ( !pFile->fOptional
586 || ( err != ERROR_FILE_NOT_FOUND
587 && (err != ERROR_PATH_NOT_FOUND || pFile->enmDir != kSupID_Testcase) ) )
588 rc = supR3HardenedError(VERR_PATH_NOT_FOUND, fFatal,
589 "supR3HardenedVerifyFileInternal: Failed to open '%s': err=%d\n", szPath, err);
590 }
591 RTUtf16Free(pwszPath);
592 }
593 else
594 rc = supR3HardenedError(rc, fFatal, "supR3HardenedVerifyFileInternal: Failed to convert '%s' to UTF-16: %Rrc\n",
595 szPath, rc);
596 }
597 return rc;
598}
599
600
601/**
602 * Worker for supR3HardenedVerifyFileInternal.
603 *
604 * @returns VINF_SUCCESS on success. On failure, an error code is returned if
605 * fFatal is clear and if it's set the function wont return.
606 * @param pFile The file entry.
607 * @param pVerified The verification record.
608 * @param fFatal Whether validation failures should be treated as
609 * fatal (true) or not (false).
610 * @param fLeaveFileOpen Whether the file should be left open.
611 */
612static int supR3HardenedVerifyFileSignature(PCSUPINSTFILE pFile, PSUPVERIFIEDFILE pVerified, bool fFatal, bool fLeaveFileOpen)
613{
614# if defined(VBOX_WITH_HARDENING) && !defined(IN_SUP_R3_STATIC) /* Latter: Not in VBoxCpuReport and friends. */
615
616 /*
617 * Open the file if we have to.
618 */
619 int rc;
620 intptr_t hFileOpened;
621 intptr_t hFile = pVerified->hFile;
622 if (hFile != -1)
623 hFileOpened = -1;
624 else
625 {
626 rc = supR3HardenedVerifyFileOpen(pFile, fFatal, &hFileOpened);
627 if (RT_FAILURE(rc))
628 return rc;
629 hFile = hFileOpened;
630 }
631
632 /*
633 * Verify the signature.
634 */
635 char szErr[1024];
636 RTERRINFO ErrInfo;
637 RTErrInfoInit(&ErrInfo, szErr, sizeof(szErr));
638
639 uint32_t fFlags = SUPHNTVI_F_REQUIRE_BUILD_CERT;
640 if (pFile->enmType == kSupIFT_Rc)
641 fFlags |= SUPHNTVI_F_RC_IMAGE;
642
643 rc = supHardenedWinVerifyImageByHandleNoName((HANDLE)hFile, fFlags, &ErrInfo);
644 if (RT_SUCCESS(rc))
645 pVerified->fCheckedSignature = true;
646 else
647 {
648 pVerified->fCheckedSignature = false;
649 rc = supR3HardenedError(rc, fFatal, "supR3HardenedVerifyFileInternal: '%s': Image verify error rc=%Rrc: %s\n",
650 pFile->pszFile, rc, szErr);
651
652 }
653
654 /*
655 * Close the handle if we opened the file and we should close it.
656 */
657 if (hFileOpened != -1)
658 {
659 if (fLeaveFileOpen && RT_SUCCESS(rc))
660 pVerified->hFile = hFileOpened;
661 else
662 NtClose((HANDLE)hFileOpened);
663 }
664
665 return rc;
666
667# else /* Not checking signatures. */
668 RT_NOREF4(pFile, pVerified, fFatal, fLeaveFileOpen);
669 return VINF_SUCCESS;
670# endif /* Not checking signatures. */
671}
672#endif
673
674
675/**
676 * Verifies a file entry.
677 *
678 * @returns VINF_SUCCESS on success. On failure, an error code is returned if
679 * fFatal is clear and if it's set the function wont return.
680 *
681 * @param iFile The file table index of the file to be verified.
682 * @param fFatal Whether validation failures should be treated as
683 * fatal (true) or not (false).
684 * @param fLeaveFileOpen Whether the file should be left open.
685 * @param fVerifyAll Set if this is an verify all call and we will
686 * postpone signature checking.
687 */
688static int supR3HardenedVerifyFileInternal(int iFile, bool fFatal, bool fLeaveFileOpen, bool fVerifyAll)
689{
690#ifndef RT_OS_WINDOWS
691 RT_NOREF1(fVerifyAll);
692#endif
693 PCSUPINSTFILE pFile = &g_aSupInstallFiles[iFile];
694 PSUPVERIFIEDFILE pVerified = &g_aSupVerifiedFiles[iFile];
695
696 /*
697 * Already done validation? Do signature validation if we haven't yet.
698 */
699 if (pVerified->fValidated)
700 {
701 /** @todo revalidate? Check that the file hasn't been replace or similar. */
702#ifdef RT_OS_WINDOWS
703 if (!pVerified->fCheckedSignature && !fVerifyAll)
704 return supR3HardenedVerifyFileSignature(pFile, pVerified, fFatal, fLeaveFileOpen);
705#endif
706 return VINF_SUCCESS;
707 }
708
709
710 /* initialize the entry. */
711 if (pVerified->hFile != 0)
712 supR3HardenedError(VERR_INTERNAL_ERROR, fFatal,
713 "supR3HardenedVerifyFileInternal: hFile=%p (%s)\n",
714 (void *)pVerified->hFile, pFile->pszFile);
715 pVerified->hFile = -1;
716 pVerified->fValidated = false;
717#ifdef RT_OS_WINDOWS
718 pVerified->fCheckedSignature = false;
719#endif
720
721 /*
722 * Verify the directory then proceed to open it.
723 * (This'll make sure the directory is opened and that we can (later)
724 * use openat if we wish.)
725 */
726 int rc = supR3HardenedVerifyFixedDir(pFile->enmDir, fFatal, pFile);
727 if (RT_SUCCESS(rc))
728 {
729#if defined(RT_OS_WINDOWS)
730 rc = supR3HardenedVerifyFileOpen(pFile, fFatal, &pVerified->hFile);
731 if (RT_SUCCESS(rc))
732 {
733 if (!fVerifyAll)
734 rc = supR3HardenedVerifyFileSignature(pFile, pVerified, fFatal, fLeaveFileOpen);
735 if (RT_SUCCESS(rc))
736 {
737 pVerified->fValidated = true;
738 if (!fLeaveFileOpen)
739 {
740 NtClose((HANDLE)pVerified->hFile);
741 pVerified->hFile = -1;
742 }
743 }
744 }
745#else /* !RT_OS_WINDOWS */
746 char szPath[RTPATH_MAX];
747 rc = supR3HardenedMakeFilePath(pFile, szPath, sizeof(szPath), true /*fWithFilename*/, fFatal);
748 if (RT_SUCCESS(rc))
749 {
750 int fd = open(szPath, O_RDONLY, 0);
751 if (fd >= 0)
752 {
753 /*
754 * On unixy systems we'll make sure the file is owned by root
755 * and not writable by the group and user.
756 */
757 struct stat st;
758 if (!fstat(fd, &st))
759 {
760 if ( st.st_uid == 0
761 && !(st.st_mode & (S_IWGRP | S_IWOTH))
762 && S_ISREG(st.st_mode))
763 {
764 /* it's valid. */
765 if (fLeaveFileOpen)
766 pVerified->hFile = fd;
767 else
768 close(fd);
769 pVerified->fValidated = true;
770 }
771 else
772 {
773 if (!S_ISREG(st.st_mode))
774 rc = supR3HardenedError(VERR_IS_A_DIRECTORY, fFatal,
775 "supR3HardenedVerifyFileInternal: \"%s\" is not a regular file\n",
776 szPath, (long)st.st_uid);
777 else if (st.st_uid)
778 rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal,
779 "supR3HardenedVerifyFileInternal: Cannot trust the file \"%s\": not owned by root (st_uid=%ld)\n",
780 szPath, (long)st.st_uid);
781 else
782 rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal,
783 "supR3HardenedVerifyFileInternal: Cannot trust the file \"%s\": group and/or other writable (st_mode=0%lo)\n",
784 szPath, (long)st.st_mode);
785 close(fd);
786 }
787 }
788 else
789 {
790 int err = errno;
791 rc = supR3HardenedError(VERR_ACCESS_DENIED, fFatal,
792 "supR3HardenedVerifyFileInternal: Failed to fstat \"%s\": %s (%d)\n",
793 szPath, strerror(err), err);
794 close(fd);
795 }
796 }
797 else
798 {
799 int err = errno;
800 if (!pFile->fOptional || err != ENOENT)
801 rc = supR3HardenedError(VERR_PATH_NOT_FOUND, fFatal,
802 "supR3HardenedVerifyFileInternal: Failed to open \"%s\": %s (%d)\n",
803 szPath, strerror(err), err);
804 }
805 }
806#endif /* !RT_OS_WINDOWS */
807 }
808
809 return rc;
810}
811
812
813/**
814 * Verifies that the specified table entry matches the given filename.
815 *
816 * @returns VINF_SUCCESS if matching. On mismatch fFatal indicates whether an
817 * error is returned or we terminate the application.
818 *
819 * @param iFile The file table index.
820 * @param pszFilename The filename.
821 * @param fFatal Whether validation failures should be treated as
822 * fatal (true) or not (false).
823 */
824static int supR3HardenedVerifySameFile(int iFile, const char *pszFilename, bool fFatal)
825{
826 PCSUPINSTFILE pFile = &g_aSupInstallFiles[iFile];
827
828 /*
829 * Construct the full path for the file table entry
830 * and compare it with the specified file.
831 */
832 char szName[RTPATH_MAX];
833 int rc = supR3HardenedMakeFilePath(pFile, szName, sizeof(szName), true /*fWithFilename*/, fFatal);
834 if (RT_FAILURE(rc))
835 return rc;
836 if (SUP_COMP_FILENAME(szName, pszFilename))
837 {
838 /*
839 * Normalize the two paths and compare again.
840 */
841 rc = VERR_NOT_SAME_DEVICE;
842#if defined(RT_OS_WINDOWS)
843 LPSTR pszIgnored;
844 char szName2[RTPATH_MAX]; /** @todo Must use UTF-16 here! Code is mixing UTF-8 and native. */
845 if ( GetFullPathName(szName, RT_ELEMENTS(szName2), &szName2[0], &pszIgnored)
846 && GetFullPathName(pszFilename, RT_ELEMENTS(szName), &szName[0], &pszIgnored))
847 if (!SUP_COMP_FILENAME(szName2, szName))
848 rc = VINF_SUCCESS;
849#else
850 AssertCompile(RTPATH_MAX >= PATH_MAX);
851 char szName2[RTPATH_MAX];
852 if ( realpath(szName, szName2) != NULL
853 && realpath(pszFilename, szName) != NULL)
854 if (!SUP_COMP_FILENAME(szName2, szName))
855 rc = VINF_SUCCESS;
856#endif
857
858 if (RT_FAILURE(rc))
859 {
860 supR3HardenedMakeFilePath(pFile, szName, sizeof(szName), true /*fWithFilename*/, fFatal);
861 return supR3HardenedError(rc, fFatal,
862 "supR3HardenedVerifySameFile: \"%s\" isn't the same as \"%s\"\n",
863 pszFilename, szName);
864 }
865 }
866
867 /*
868 * Check more stuff like the stat info if it's an already open file?
869 */
870
871
872
873 return VINF_SUCCESS;
874}
875
876
877/**
878 * Verifies a file.
879 *
880 * @returns VINF_SUCCESS on success.
881 * VERR_NOT_FOUND if the file isn't in the table, this isn't ever a fatal error.
882 * On verification failure, an error code will be returned when fFatal is clear,
883 * otherwise the program will be terminated.
884 *
885 * @param pszFilename The filename.
886 * @param fFatal Whether validation failures should be treated as
887 * fatal (true) or not (false).
888 */
889DECLHIDDEN(int) supR3HardenedVerifyFixedFile(const char *pszFilename, bool fFatal)
890{
891 /*
892 * Lookup the file and check if it's the same file.
893 */
894 const char *pszName = supR3HardenedPathFilename(pszFilename);
895 for (unsigned iFile = 0; iFile < RT_ELEMENTS(g_aSupInstallFiles); iFile++)
896 if (!SUP_COMP_FILENAME(pszName, g_aSupInstallFiles[iFile].pszFile))
897 {
898 int rc = supR3HardenedVerifySameFile(iFile, pszFilename, fFatal);
899 if (RT_SUCCESS(rc))
900 rc = supR3HardenedVerifyFileInternal(iFile, fFatal, false /* fLeaveFileOpen */, false /* fVerifyAll */);
901 return rc;
902 }
903
904 return VERR_NOT_FOUND;
905}
906
907
908/**
909 * Verifies a program, worker for supR3HardenedVerifyAll.
910 *
911 * @returns See supR3HardenedVerifyAll.
912 * @param pszProgName See supR3HardenedVerifyAll.
913 * @param pszExePath The path to the executable.
914 * @param fFatal See supR3HardenedVerifyAll.
915 * @param fLeaveOpen The leave open setting used by
916 * supR3HardenedVerifyAll.
917 * @param fMainFlags Flags supplied to SUPR3HardenedMain.
918 */
919static int supR3HardenedVerifyProgram(const char *pszProgName, const char *pszExePath, bool fFatal,
920 bool fLeaveOpen, uint32_t fMainFlags)
921{
922 /*
923 * Search the table looking for the executable and the DLL/DYLIB/SO.
924 * Note! On darwin we have a hack in place for VirtualBoxVM helper app
925 * to share VirtualBox.dylib with the VirtualBox app. This ASSUMES
926 * that cchProgNameDll is equal or shorter to the exe name.
927 */
928 int rc = VINF_SUCCESS;
929 bool fExe = false;
930 bool fDll = false;
931 size_t const cchProgNameExe = suplibHardenedStrLen(pszProgName);
932#ifndef RT_OS_DARWIN
933 size_t const cchProgNameDll = cchProgNameExe;
934 NOREF(fMainFlags);
935#else
936 size_t const cchProgNameDll = fMainFlags & SUPSECMAIN_FLAGS_OSX_VM_APP
937 ? sizeof("VirtualBox") - 1
938 : cchProgNameExe;
939 if (cchProgNameDll > cchProgNameExe)
940 return supR3HardenedError(VERR_INTERNAL_ERROR, fFatal,
941 "supR3HardenedVerifyProgram: SUPSECMAIN_FLAGS_OSX_VM_APP + '%s'", pszProgName);
942#endif
943 for (unsigned iFile = 0; iFile < RT_ELEMENTS(g_aSupInstallFiles); iFile++)
944 if (!suplibHardenedStrNCmp(pszProgName, g_aSupInstallFiles[iFile].pszFile, cchProgNameDll))
945 {
946 if ( ( g_aSupInstallFiles[iFile].enmType == kSupIFT_Dll
947 || g_aSupInstallFiles[iFile].enmType == kSupIFT_TestDll)
948 && !suplibHardenedStrCmp(&g_aSupInstallFiles[iFile].pszFile[cchProgNameDll], SUPLIB_DLL_SUFF))
949 {
950 /* This only has to be found (once). */
951 if (fDll)
952 rc = supR3HardenedError(VERR_INTERNAL_ERROR, fFatal,
953 "supR3HardenedVerifyProgram: duplicate DLL entry for \"%s\"\n", pszProgName);
954 else
955 rc = supR3HardenedVerifyFileInternal(iFile, fFatal, fLeaveOpen,
956 true /* fVerifyAll - check sign later, only final process need check it on load. */);
957 fDll = true;
958 }
959 else if ( ( g_aSupInstallFiles[iFile].enmType == kSupIFT_Exe
960 || g_aSupInstallFiles[iFile].enmType == kSupIFT_TestExe)
961 && ( cchProgNameExe == cchProgNameDll
962 || !suplibHardenedStrNCmp(pszProgName, g_aSupInstallFiles[iFile].pszFile, cchProgNameExe))
963 && !suplibHardenedStrCmp(&g_aSupInstallFiles[iFile].pszFile[cchProgNameExe], SUPLIB_EXE_SUFF))
964 {
965 /* Here we'll have to check that the specific program is the same as the entry. */
966 if (fExe)
967 rc = supR3HardenedError(VERR_INTERNAL_ERROR, fFatal,
968 "supR3HardenedVerifyProgram: duplicate EXE entry for \"%s\"\n", pszProgName);
969 else
970 rc = supR3HardenedVerifyFileInternal(iFile, fFatal, fLeaveOpen, false /* fVerifyAll */);
971 fExe = true;
972
973 supR3HardenedVerifySameFile(iFile, pszExePath, fFatal);
974 }
975 }
976
977 /*
978 * Check the findings.
979 */
980 if (RT_SUCCESS(rc))
981 {
982 if (!fDll && !fExe)
983 rc = supR3HardenedError(VERR_NOT_FOUND, fFatal,
984 "supR3HardenedVerifyProgram: Couldn't find the program \"%s\"\n", pszProgName);
985 else if (!fExe)
986 rc = supR3HardenedError(VERR_NOT_FOUND, fFatal,
987 "supR3HardenedVerifyProgram: Couldn't find the EXE entry for \"%s\"\n", pszProgName);
988 else if (!fDll)
989 rc = supR3HardenedError(VERR_NOT_FOUND, fFatal,
990 "supR3HardenedVerifyProgram: Couldn't find the DLL entry for \"%s\"\n", pszProgName);
991 }
992 return rc;
993}
994
995
996/**
997 * Verifies all the known files (called from SUPR3HardenedMain).
998 *
999 * @returns VINF_SUCCESS on success.
1000 * On verification failure, an error code will be returned when fFatal is clear,
1001 * otherwise the program will be terminated.
1002 *
1003 * @param fFatal Whether validation failures should be treated as
1004 * fatal (true) or not (false).
1005 * @param pszProgName The program name. This is used to verify that
1006 * both the executable and corresponding
1007 * DLL/DYLIB/SO are valid.
1008 * @param pszExePath The path to the executable.
1009 * @param fMainFlags Flags supplied to SUPR3HardenedMain.
1010 */
1011DECLHIDDEN(int) supR3HardenedVerifyAll(bool fFatal, const char *pszProgName, const char *pszExePath, uint32_t fMainFlags)
1012{
1013 /*
1014 * On windows
1015 */
1016#if defined(RT_OS_WINDOWS)
1017 bool fLeaveOpen = true;
1018#else
1019 bool fLeaveOpen = false;
1020#endif
1021
1022 /*
1023 * The verify all the files.
1024 */
1025 int rc = VINF_SUCCESS;
1026 for (unsigned iFile = 0; iFile < RT_ELEMENTS(g_aSupInstallFiles); iFile++)
1027 {
1028 int rc2 = supR3HardenedVerifyFileInternal(iFile, fFatal, fLeaveOpen, true /* fVerifyAll */);
1029 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1030 rc = rc2;
1031 }
1032
1033 /*
1034 * Verify the program name, that is to say, check that it's in the table
1035 * (thus verified above) and verify the signature on platforms where we
1036 * sign things.
1037 */
1038 int rc2 = supR3HardenedVerifyProgram(pszProgName, pszExePath, fFatal, fLeaveOpen, fMainFlags);
1039 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1040 rc2 = rc;
1041
1042 return rc;
1043}
1044
1045
1046/**
1047 * Copies the N messages into the error buffer and returns @a rc.
1048 *
1049 * @returns Returns @a rc
1050 * @param rc The return code.
1051 * @param pErrInfo The error info structure.
1052 * @param cMsgs The number of messages in the ellipsis.
1053 * @param ... Message parts.
1054 */
1055static int supR3HardenedSetErrorN(int rc, PRTERRINFO pErrInfo, unsigned cMsgs, ...)
1056{
1057 if (pErrInfo)
1058 {
1059 size_t cbErr = pErrInfo->cbMsg;
1060 char *pszErr = pErrInfo->pszMsg;
1061
1062 va_list va;
1063 va_start(va, cMsgs);
1064 while (cMsgs-- > 0 && cbErr > 0)
1065 {
1066 const char *pszMsg = va_arg(va, const char *);
1067 size_t cchMsg = RT_VALID_PTR(pszMsg) ? suplibHardenedStrLen(pszMsg) : 0;
1068 if (cchMsg >= cbErr)
1069 cchMsg = cbErr - 1;
1070 suplibHardenedMemCopy(pszErr, pszMsg, cchMsg);
1071 pszErr[cchMsg] = '\0';
1072 pszErr += cchMsg;
1073 cbErr -= cchMsg;
1074 }
1075 va_end(va);
1076
1077 pErrInfo->rc = rc;
1078 pErrInfo->fFlags |= RTERRINFO_FLAGS_SET;
1079 }
1080
1081 return rc;
1082}
1083
1084
1085#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX)
1086/**
1087 * Copies the four messages into the error buffer and returns @a rc.
1088 *
1089 * @returns Returns @a rc
1090 * @param rc The return code.
1091 * @param pErrInfo The error info structure.
1092 * @param pszMsg1 The first message part.
1093 * @param pszMsg2 The second message part.
1094 * @param pszMsg3 The third message part.
1095 * @param pszMsg4 The fourth message part.
1096 */
1097static int supR3HardenedSetError4(int rc, PRTERRINFO pErrInfo, const char *pszMsg1,
1098 const char *pszMsg2, const char *pszMsg3, const char *pszMsg4)
1099{
1100 return supR3HardenedSetErrorN(rc, pErrInfo, 4, pszMsg1, pszMsg2, pszMsg3, pszMsg4);
1101}
1102#endif
1103
1104
1105/**
1106 * Copies the three messages into the error buffer and returns @a rc.
1107 *
1108 * @returns Returns @a rc
1109 * @param rc The return code.
1110 * @param pErrInfo The error info structure.
1111 * @param pszMsg1 The first message part.
1112 * @param pszMsg2 The second message part.
1113 * @param pszMsg3 The third message part.
1114 */
1115static int supR3HardenedSetError3(int rc, PRTERRINFO pErrInfo, const char *pszMsg1,
1116 const char *pszMsg2, const char *pszMsg3)
1117{
1118 return supR3HardenedSetErrorN(rc, pErrInfo, 3, pszMsg1, pszMsg2, pszMsg3);
1119}
1120
1121
1122#ifdef SOME_UNUSED_FUNCTION
1123/**
1124 * Copies the two messages into the error buffer and returns @a rc.
1125 *
1126 * @returns Returns @a rc
1127 * @param rc The return code.
1128 * @param pErrInfo The error info structure.
1129 * @param pszMsg1 The first message part.
1130 * @param pszMsg2 The second message part.
1131 */
1132static int supR3HardenedSetError2(int rc, PRTERRINFO pErrInfo, const char *pszMsg1,
1133 const char *pszMsg2)
1134{
1135 return supR3HardenedSetErrorN(rc, pErrInfo, 2, pszMsg1, pszMsg2);
1136}
1137#endif
1138
1139
1140#ifndef SUP_HARDENED_VERIFY_FOLLOW_SYMLINKS_USE_REALPATH
1141# if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX)
1142/**
1143 * Copies the error message to the error buffer and returns @a rc.
1144 *
1145 * @returns Returns @a rc
1146 * @param rc The return code.
1147 * @param pErrInfo The error info structure.
1148 * @param pszMsg The message.
1149 */
1150static int supR3HardenedSetError(int rc, PRTERRINFO pErrInfo, const char *pszMsg)
1151{
1152 return supR3HardenedSetErrorN(rc, pErrInfo, 1, pszMsg);
1153}
1154# endif
1155#endif
1156
1157
1158/**
1159 * Output from a successfull supR3HardenedVerifyPathSanity call.
1160 */
1161typedef struct SUPR3HARDENEDPATHINFO
1162{
1163 /** The length of the path in szCopy. */
1164 uint16_t cch;
1165 /** The number of path components. */
1166 uint16_t cComponents;
1167 /** Set if the path ends with slash, indicating that it's a directory
1168 * reference and not a file reference. The slash has been removed from
1169 * the copy. */
1170 bool fDirSlash;
1171 /** The offset where each path component starts, i.e. the char after the
1172 * slash. The array has cComponents + 1 entries, where the final one is
1173 * cch + 1 so that one can always terminate the current component by
1174 * szPath[aoffComponent[i] - 1] = '\0'. */
1175 uint16_t aoffComponents[32+1];
1176 /** A normalized copy of the path.
1177 * Reserve some extra space so we can be more relaxed about overflow
1178 * checks and terminator paddings, especially when recursing. */
1179 char szPath[SUPR3HARDENED_MAX_PATH * 2];
1180} SUPR3HARDENEDPATHINFO;
1181/** Pointer to a parsed path. */
1182typedef SUPR3HARDENEDPATHINFO *PSUPR3HARDENEDPATHINFO;
1183
1184
1185/**
1186 * Verifies that the path is absolutely sane, it also parses the path.
1187 *
1188 * A sane path starts at the root (w/ drive letter on DOS derived systems) and
1189 * does not have any relative bits (/../) or unnecessary slashes (/bin//ls).
1190 * Sane paths are less or equal to SUPR3HARDENED_MAX_PATH bytes in length. UNC
1191 * paths are not supported.
1192 *
1193 * @returns VBox status code.
1194 * @param pszPath The path to check.
1195 * @param pErrInfo The error info structure.
1196 * @param pInfo Where to return a copy of the path along with
1197 * parsing information.
1198 */
1199static int supR3HardenedVerifyPathSanity(const char *pszPath, PRTERRINFO pErrInfo, PSUPR3HARDENEDPATHINFO pInfo)
1200{
1201 const char *pszSrc = pszPath;
1202 char *pszDst = pInfo->szPath;
1203
1204 /*
1205 * Check that it's an absolute path and copy the volume/root specifier.
1206 */
1207#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
1208 if ( !RT_C_IS_ALPHA(pszSrc[0])
1209 || pszSrc[1] != ':'
1210 || !RTPATH_IS_SLASH(pszSrc[2]))
1211 return supR3HardenedSetError3(VERR_SUPLIB_PATH_NOT_ABSOLUTE, pErrInfo, "The path is not absolute: '", pszPath, "'");
1212
1213 *pszDst++ = RT_C_TO_UPPER(pszSrc[0]);
1214 *pszDst++ = ':';
1215 *pszDst++ = RTPATH_SLASH;
1216 pszSrc += 3;
1217
1218#else
1219 if (!RTPATH_IS_SLASH(pszSrc[0]))
1220 return supR3HardenedSetError3(VERR_SUPLIB_PATH_NOT_ABSOLUTE, pErrInfo, "The path is not absolute: '", pszPath, "'");
1221
1222 *pszDst++ = RTPATH_SLASH;
1223 pszSrc += 1;
1224#endif
1225
1226 /*
1227 * No path specifying the root or something very shortly thereafter will
1228 * be approved of.
1229 */
1230 if (pszSrc[0] == '\0')
1231 return supR3HardenedSetError3(VERR_SUPLIB_PATH_IS_ROOT, pErrInfo, "The path is root: '", pszPath, "'");
1232 if ( pszSrc[1] == '\0'
1233 || pszSrc[2] == '\0')
1234 return supR3HardenedSetError3(VERR_SUPLIB_PATH_TOO_SHORT, pErrInfo, "The path is too short: '", pszPath, "'");
1235
1236#if RTPATH_STYLE == RTPATH_STR_F_STYLE_UNIX
1237 /*
1238 * Skip double slashes.
1239 */
1240 while (RTPATH_IS_SLASH(*pszSrc))
1241 pszSrc++;
1242#else
1243 /*
1244 * The root slash should be alone to avoid UNC confusion.
1245 */
1246 if (RTPATH_IS_SLASH(pszSrc[0]))
1247 return supR3HardenedSetError3(VERR_SUPLIB_PATH_NOT_CLEAN, pErrInfo,
1248 "The path is not clean of leading double slashes: '", pszPath, "'");
1249#endif
1250 /*
1251 * Check each component. No parent references.
1252 */
1253 pInfo->cComponents = 0;
1254 pInfo->fDirSlash = false;
1255 while (pszSrc[0])
1256 {
1257 /* Sanity checks. */
1258 if ( pszSrc[0] == '.'
1259 && pszSrc[1] == '.'
1260 && RTPATH_IS_SLASH(pszSrc[2]))
1261 return supR3HardenedSetError3(VERR_SUPLIB_PATH_NOT_ABSOLUTE, pErrInfo,
1262 "The path is not absolute: '", pszPath, "'");
1263
1264 /* Record the start of the component. */
1265 if (pInfo->cComponents >= RT_ELEMENTS(pInfo->aoffComponents) - 1)
1266 return supR3HardenedSetError3(VERR_SUPLIB_PATH_TOO_MANY_COMPONENTS, pErrInfo,
1267 "The path has too many components: '", pszPath, "'");
1268 pInfo->aoffComponents[pInfo->cComponents++] = pszDst - &pInfo->szPath[0];
1269
1270 /* Traverse to the end of the component, copying it as we go along. */
1271 while (pszSrc[0])
1272 {
1273 if (RTPATH_IS_SLASH(pszSrc[0]))
1274 {
1275 pszSrc++;
1276 if (*pszSrc)
1277 *pszDst++ = RTPATH_SLASH;
1278 else
1279 pInfo->fDirSlash = true;
1280 break;
1281 }
1282 *pszDst++ = *pszSrc++;
1283 if ((uintptr_t)(pszDst - &pInfo->szPath[0]) >= SUPR3HARDENED_MAX_PATH)
1284 return supR3HardenedSetError3(VERR_SUPLIB_PATH_TOO_LONG, pErrInfo,
1285 "The path is too long: '", pszPath, "'");
1286 }
1287
1288 /* Skip double slashes. */
1289 while (RTPATH_IS_SLASH(*pszSrc))
1290 pszSrc++;
1291 }
1292
1293 /* Terminate the string and enter its length. */
1294 pszDst[0] = '\0';
1295 pszDst[1] = '\0'; /* for aoffComponents */
1296 pInfo->cch = (uint16_t)(pszDst - &pInfo->szPath[0]);
1297 pInfo->aoffComponents[pInfo->cComponents] = pInfo->cch + 1;
1298
1299 return VINF_SUCCESS;
1300}
1301
1302
1303/**
1304 * The state information collected by supR3HardenedVerifyFsObject.
1305 *
1306 * This can be used to verify that a directory we've opened for enumeration is
1307 * the same as the one that supR3HardenedVerifyFsObject just verified. It can
1308 * equally be used to verify a native specfied by the user.
1309 */
1310typedef struct SUPR3HARDENEDFSOBJSTATE
1311{
1312#ifdef RT_OS_WINDOWS
1313 /** Not implemented for windows yet. */
1314 char chTodo;
1315#else
1316 /** The stat output. */
1317 struct stat Stat;
1318#endif
1319} SUPR3HARDENEDFSOBJSTATE;
1320/** Pointer to a file system object state. */
1321typedef SUPR3HARDENEDFSOBJSTATE *PSUPR3HARDENEDFSOBJSTATE;
1322/** Pointer to a const file system object state. */
1323typedef SUPR3HARDENEDFSOBJSTATE const *PCSUPR3HARDENEDFSOBJSTATE;
1324
1325
1326/**
1327 * Query information about a file system object by path.
1328 *
1329 * @returns VBox status code, error buffer filled on failure.
1330 * @param pszPath The path to the object.
1331 * @param pFsObjState Where to return the state information.
1332 * @param pErrInfo The error info structure.
1333 */
1334static int supR3HardenedQueryFsObjectByPath(char const *pszPath, PSUPR3HARDENEDFSOBJSTATE pFsObjState, PRTERRINFO pErrInfo)
1335{
1336#if defined(RT_OS_WINDOWS)
1337 /** @todo Windows hardening. */
1338 pFsObjState->chTodo = 0;
1339 RT_NOREF2(pszPath, pErrInfo);
1340 return VINF_SUCCESS;
1341
1342#else
1343 /*
1344 * Stat the object, do not follow links.
1345 */
1346 if (lstat(pszPath, &pFsObjState->Stat) != 0)
1347 {
1348 /* Ignore access errors */
1349 if (errno != EACCES)
1350 return supR3HardenedSetErrorN(VERR_SUPLIB_STAT_FAILED, pErrInfo,
1351 5, "stat failed with ", strerror(errno), " on: '", pszPath, "'");
1352 }
1353
1354 /*
1355 * Read ACLs.
1356 */
1357 /** @todo */
1358
1359 return VINF_SUCCESS;
1360#endif
1361}
1362
1363
1364/**
1365 * Query information about a file system object by native handle.
1366 *
1367 * @returns VBox status code, error buffer filled on failure.
1368 * @param hNative The native handle to the object @a pszPath
1369 * specifies and this should be verified to be the
1370 * same file system object.
1371 * @param pFsObjState Where to return the state information.
1372 * @param pszPath The path to the object. (For the error message
1373 * only.)
1374 * @param pErrInfo The error info structure.
1375 */
1376static int supR3HardenedQueryFsObjectByHandle(RTHCUINTPTR hNative, PSUPR3HARDENEDFSOBJSTATE pFsObjState,
1377 char const *pszPath, PRTERRINFO pErrInfo)
1378{
1379#if defined(RT_OS_WINDOWS)
1380 /** @todo Windows hardening. */
1381 pFsObjState->chTodo = 0;
1382 RT_NOREF3(hNative, pszPath, pErrInfo);
1383 return VINF_SUCCESS;
1384
1385#else
1386 /*
1387 * Stat the object, do not follow links.
1388 */
1389 if (fstat((int)hNative, &pFsObjState->Stat) != 0)
1390 return supR3HardenedSetErrorN(VERR_SUPLIB_STAT_FAILED, pErrInfo,
1391 5, "fstat failed with ", strerror(errno), " on '", pszPath, "'");
1392
1393 /*
1394 * Read ACLs.
1395 */
1396 /** @todo */
1397
1398 return VINF_SUCCESS;
1399#endif
1400}
1401
1402
1403/**
1404 * Verifies that the file system object indicated by the native handle is the
1405 * same as the one @a pFsObjState indicates.
1406 *
1407 * @returns VBox status code, error buffer filled on failure.
1408 * @param pFsObjState1 File system object information/state by path.
1409 * @param pFsObjState2 File system object information/state by handle.
1410 * @param pszPath The path to the object @a pFsObjState
1411 * describes. (For the error message.)
1412 * @param pErrInfo The error info structure.
1413 */
1414static int supR3HardenedIsSameFsObject(PCSUPR3HARDENEDFSOBJSTATE pFsObjState1, PCSUPR3HARDENEDFSOBJSTATE pFsObjState2,
1415 const char *pszPath, PRTERRINFO pErrInfo)
1416{
1417#if defined(RT_OS_WINDOWS)
1418 /** @todo Windows hardening. */
1419 RT_NOREF4(pFsObjState1, pFsObjState2, pszPath, pErrInfo);
1420 return VINF_SUCCESS;
1421
1422#elif defined(RT_OS_OS2)
1423 RT_NOREF4(pFsObjState1, pFsObjState2, pszPath, pErrInfo);
1424 return VINF_SUCCESS;
1425
1426#else
1427 /*
1428 * Compare the ino+dev, then the uid+gid and finally the important mode
1429 * bits. Technically the first one should be enough, but we're paranoid.
1430 */
1431 if ( pFsObjState1->Stat.st_ino != pFsObjState2->Stat.st_ino
1432 || pFsObjState1->Stat.st_dev != pFsObjState2->Stat.st_dev)
1433 return supR3HardenedSetError3(VERR_SUPLIB_NOT_SAME_OBJECT, pErrInfo,
1434 "The native handle is not the same as '", pszPath, "' (ino/dev)");
1435 if ( pFsObjState1->Stat.st_uid != pFsObjState2->Stat.st_uid
1436 || pFsObjState1->Stat.st_gid != pFsObjState2->Stat.st_gid)
1437 return supR3HardenedSetError3(VERR_SUPLIB_NOT_SAME_OBJECT, pErrInfo,
1438 "The native handle is not the same as '", pszPath, "' (uid/gid)");
1439 if ( (pFsObjState1->Stat.st_mode & (S_IFMT | S_IWUSR | S_IWGRP | S_IWOTH))
1440 != (pFsObjState2->Stat.st_mode & (S_IFMT | S_IWUSR | S_IWGRP | S_IWOTH)))
1441 return supR3HardenedSetError3(VERR_SUPLIB_NOT_SAME_OBJECT, pErrInfo,
1442 "The native handle is not the same as '", pszPath, "' (mode)");
1443 return VINF_SUCCESS;
1444#endif
1445}
1446
1447
1448/**
1449 * Verifies a file system object (file or directory).
1450 *
1451 * @returns VBox status code, error buffer filled on failure.
1452 * @param pFsObjState The file system object information/state to be
1453 * verified.
1454 * @param fDir Whether this is a directory or a file.
1455 * @param fRelaxed Whether we can be more relaxed about this
1456 * directory (only used for grand parent
1457 * directories).
1458 * @param fSymlinksAllowed Flag whether symlinks are allowed or not.
1459 * If allowed the symlink object is verified not the target.
1460 * @param pszPath The path to the object. For error messages and
1461 * securing a couple of hacks.
1462 * @param pErrInfo The error info structure.
1463 */
1464static int supR3HardenedVerifyFsObject(PCSUPR3HARDENEDFSOBJSTATE pFsObjState, bool fDir, bool fRelaxed,
1465 bool fSymlinksAllowed, const char *pszPath, PRTERRINFO pErrInfo)
1466{
1467#if defined(RT_OS_WINDOWS)
1468 /** @todo Windows hardening. */
1469 RT_NOREF(pFsObjState, fDir, fRelaxed, fSymlinksAllowed, pszPath, pErrInfo);
1470 return VINF_SUCCESS;
1471
1472#elif defined(RT_OS_OS2)
1473 /* No hardening here - it's a single user system. */
1474 RT_NOREF(pFsObjState, fDir, fRelaxed, fSymlinksAllowed, pszPath, pErrInfo);
1475 return VINF_SUCCESS;
1476
1477#else
1478 /*
1479 * The owner must be root.
1480 *
1481 * This can be extended to include predefined system users if necessary.
1482 */
1483 if (pFsObjState->Stat.st_uid != 0)
1484 return supR3HardenedSetError3(VERR_SUPLIB_OWNER_NOT_ROOT, pErrInfo, "The owner is not root: '", pszPath, "'");
1485
1486 /*
1487 * The object type must be directory or file. It can be a symbolic link
1488 * if explicitely allowed. Otherwise this and other risky stuff is not allowed
1489 * (sorry dude, but we're paranoid on purpose here).
1490 */
1491 if ( !S_ISLNK(pFsObjState->Stat.st_mode)
1492 || !fSymlinksAllowed)
1493 {
1494
1495 if ( !S_ISDIR(pFsObjState->Stat.st_mode)
1496 && !S_ISREG(pFsObjState->Stat.st_mode))
1497 {
1498 if (S_ISLNK(pFsObjState->Stat.st_mode))
1499 return supR3HardenedSetError3(VERR_SUPLIB_SYMLINKS_ARE_NOT_PERMITTED, pErrInfo,
1500 "Symlinks are not permitted: '", pszPath, "'");
1501 return supR3HardenedSetError3(VERR_SUPLIB_NOT_DIR_NOT_FILE, pErrInfo,
1502 "Not regular file or directory: '", pszPath, "'");
1503 }
1504 if (fDir != !!S_ISDIR(pFsObjState->Stat.st_mode))
1505 {
1506 if (S_ISDIR(pFsObjState->Stat.st_mode))
1507 return supR3HardenedSetError3(VERR_SUPLIB_IS_DIRECTORY, pErrInfo,
1508 "Expected file but found directory: '", pszPath, "'");
1509 return supR3HardenedSetError3(VERR_SUPLIB_IS_FILE, pErrInfo,
1510 "Expected directory but found file: '", pszPath, "'");
1511 }
1512 }
1513
1514 /*
1515 * The group does not matter if it does not have write access, if it has
1516 * write access it must be group 0 (root/wheel/whatever).
1517 *
1518 * This can be extended to include predefined system groups or groups that
1519 * only root is member of.
1520 */
1521 if ( (pFsObjState->Stat.st_mode & S_IWGRP)
1522 && pFsObjState->Stat.st_gid != 0)
1523 {
1524# ifdef RT_OS_DARWIN
1525 /* HACK ALERT: On Darwin /Applications is root:admin with admin having
1526 full access. So, to work around we relax the hardening a bit and
1527 permit grand parents and beyond to be group writable by admin. */
1528 /** @todo dynamically resolve the admin group? */
1529 bool fBad = !fRelaxed || pFsObjState->Stat.st_gid != 80 /*admin*/ || suplibHardenedStrCmp(pszPath, "/Applications");
1530
1531# elif defined(RT_OS_FREEBSD)
1532 /* HACK ALERT: PC-BSD 9 has group-writable /usr/pib directory which is
1533 similar to /Applications on OS X (see above).
1534 On FreeBSD root is normally the only member of this group, on
1535 PC-BSD the default user is a member. */
1536 /** @todo dynamically resolve the operator group? */
1537 bool fBad = !fRelaxed || pFsObjState->Stat.st_gid != 5 /*operator*/ || suplibHardenedStrCmp(pszPath, "/usr/pbi");
1538 NOREF(fRelaxed);
1539# elif defined(RT_OS_SOLARIS)
1540 /* HACK ALERT: Solaris has group-writable /usr/lib/iconv directory from
1541 which the appropriate module is loaded.
1542 By default only root and daemon are part of that group.
1543 . */
1544 /** @todo dynamically resolve the bin group? */
1545 bool fBad = !fRelaxed || pFsObjState->Stat.st_gid != 2 /*bin*/ || suplibHardenedStrCmp(pszPath, "/usr/lib/iconv");
1546# else
1547 NOREF(fRelaxed);
1548 bool fBad = true;
1549# endif
1550 if (fBad)
1551 return supR3HardenedSetError3(VERR_SUPLIB_WRITE_NON_SYS_GROUP, pErrInfo,
1552 "An unknown (and thus untrusted) group has write access to '", pszPath,
1553 "' and we therefore cannot trust the directory content or that of any subdirectory");
1554 }
1555
1556 /*
1557 * World must not have write access. There is no relaxing this rule.
1558 * Linux exception: Symbolic links are always give permission 0777, there
1559 * is no lchmod or lchown APIs. The permissions on parent
1560 * directory that contains the symbolic link is what is
1561 * decising wrt to modifying it. (Caller is expected not
1562 * to allow symbolic links in the first path component.)
1563 */
1564 if ( (pFsObjState->Stat.st_mode & S_IWOTH)
1565# ifdef RT_OS_LINUX
1566 && ( !S_ISLNK(pFsObjState->Stat.st_mode)
1567 || !fSymlinksAllowed /* paranoia */)
1568# endif
1569 )
1570 return supR3HardenedSetError3(VERR_SUPLIB_WORLD_WRITABLE, pErrInfo,
1571 "World writable: '", pszPath, "'");
1572
1573 /*
1574 * Check the ACLs.
1575 */
1576 /** @todo */
1577
1578 return VINF_SUCCESS;
1579#endif
1580}
1581
1582
1583/**
1584 * Verifies that the file system object indicated by the native handle is the
1585 * same as the one @a pFsObjState indicates.
1586 *
1587 * @returns VBox status code, error buffer filled on failure.
1588 * @param hNative The native handle to the object @a pszPath
1589 * specifies and this should be verified to be the
1590 * same file system object.
1591 * @param pFsObjState The information/state returned by a previous
1592 * query call.
1593 * @param pszPath The path to the object @a pFsObjState
1594 * describes. (For the error message.)
1595 * @param pErrInfo The error info structure.
1596 */
1597static int supR3HardenedVerifySameFsObject(RTHCUINTPTR hNative, PCSUPR3HARDENEDFSOBJSTATE pFsObjState,
1598 const char *pszPath, PRTERRINFO pErrInfo)
1599{
1600 SUPR3HARDENEDFSOBJSTATE FsObjState2;
1601 int rc = supR3HardenedQueryFsObjectByHandle(hNative, &FsObjState2, pszPath, pErrInfo);
1602 if (RT_SUCCESS(rc))
1603 rc = supR3HardenedIsSameFsObject(pFsObjState, &FsObjState2, pszPath, pErrInfo);
1604 return rc;
1605}
1606
1607
1608/**
1609 * Does the recursive directory enumeration.
1610 *
1611 * @returns VBox status code, error buffer filled on failure.
1612 * @param pszDirPath The path buffer containing the subdirectory to
1613 * enumerate followed by a slash (this is never
1614 * the root slash). The buffer is RTPATH_MAX in
1615 * size and anything starting at @a cchDirPath
1616 * - 1 and beyond is scratch space.
1617 * @param cchDirPath The length of the directory path + slash.
1618 * @param pFsObjState Pointer to the file system object state buffer.
1619 * On input this will hold the stats for
1620 * the directory @a pszDirPath indicates and will
1621 * be used to verified that we're opening the same
1622 * thing.
1623 * @param fRecursive Whether to recurse into subdirectories.
1624 * @param pErrInfo The error info structure.
1625 */
1626static int supR3HardenedVerifyDirRecursive(char *pszDirPath, size_t cchDirPath, PSUPR3HARDENEDFSOBJSTATE pFsObjState,
1627 bool fRecursive, PRTERRINFO pErrInfo)
1628{
1629#if defined(RT_OS_WINDOWS)
1630 /** @todo Windows hardening. */
1631 RT_NOREF5(pszDirPath, cchDirPath, pFsObjState, fRecursive, pErrInfo);
1632 return VINF_SUCCESS;
1633
1634#elif defined(RT_OS_OS2)
1635 /* No hardening here - it's a single user system. */
1636 RT_NOREF5(pszDirPath, cchDirPath, pFsObjState, fRecursive, pErrInfo);
1637 return VINF_SUCCESS;
1638
1639#else
1640 /*
1641 * Open the directory. Now, we could probably eliminate opendir here
1642 * and go down on kernel API level (open + getdents for instance), however
1643 * that's not very portable and hopefully not necessary.
1644 */
1645 DIR *pDir = opendir(pszDirPath);
1646 if (!pDir)
1647 {
1648 /* Ignore access errors. */
1649 if (errno == EACCES)
1650 return VINF_SUCCESS;
1651 return supR3HardenedSetErrorN(VERR_SUPLIB_DIR_ENUM_FAILED, pErrInfo,
1652 5, "opendir failed with ", strerror(errno), " on '", pszDirPath, "'");
1653 }
1654 if (dirfd(pDir) != -1)
1655 {
1656 int rc = supR3HardenedVerifySameFsObject(dirfd(pDir), pFsObjState, pszDirPath, pErrInfo);
1657 if (RT_FAILURE(rc))
1658 {
1659 closedir(pDir);
1660 return rc;
1661 }
1662 }
1663
1664 /*
1665 * Enumerate the directory, check all the requested bits.
1666 */
1667 int rc = VINF_SUCCESS;
1668 for (;;)
1669 {
1670 pszDirPath[cchDirPath] = '\0'; /* for error messages. */
1671
1672 struct dirent Entry;
1673 struct dirent *pEntry;
1674#if RT_GNUC_PREREQ(4, 6)
1675# pragma GCC diagnostic push
1676# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1677#endif
1678 int iErr = readdir_r(pDir, &Entry, &pEntry);
1679#if RT_GNUC_PREREQ(4, 6)
1680# pragma GCC diagnostic pop
1681#endif
1682 if (iErr)
1683 {
1684 rc = supR3HardenedSetErrorN(VERR_SUPLIB_DIR_ENUM_FAILED, pErrInfo,
1685 5, "readdir_r failed with ", strerror(iErr), " in '", pszDirPath, "'");
1686 break;
1687 }
1688 if (!pEntry)
1689 break;
1690
1691 /*
1692 * Check the length and copy it into the path buffer so it can be
1693 * stat()'ed.
1694 */
1695 size_t cchName = suplibHardenedStrLen(pEntry->d_name);
1696 if (cchName + cchDirPath > SUPR3HARDENED_MAX_PATH)
1697 {
1698 rc = supR3HardenedSetErrorN(VERR_SUPLIB_PATH_TOO_LONG, pErrInfo,
1699 4, "Path grew too long during recursion: '", pszDirPath, pEntry->d_name, "'");
1700 break;
1701 }
1702 suplibHardenedMemCopy(&pszDirPath[cchDirPath], pEntry->d_name, cchName + 1);
1703
1704 /*
1705 * Query the information about the entry and verify it.
1706 * (We don't bother skipping '.' and '..' at this point, a little bit
1707 * of extra checks doesn't hurt and neither requires relaxed handling.)
1708 */
1709 rc = supR3HardenedQueryFsObjectByPath(pszDirPath, pFsObjState, pErrInfo);
1710 if (RT_SUCCESS(rc))
1711 break;
1712 rc = supR3HardenedVerifyFsObject(pFsObjState, S_ISDIR(pFsObjState->Stat.st_mode), false /*fRelaxed*/,
1713 false /*fSymlinksAllowed*/, pszDirPath, pErrInfo);
1714 if (RT_FAILURE(rc))
1715 break;
1716
1717 /*
1718 * Recurse into subdirectories if requested.
1719 */
1720 if ( fRecursive
1721 && S_ISDIR(pFsObjState->Stat.st_mode)
1722 && suplibHardenedStrCmp(pEntry->d_name, ".")
1723 && suplibHardenedStrCmp(pEntry->d_name, ".."))
1724 {
1725 pszDirPath[cchDirPath + cchName] = RTPATH_SLASH;
1726 pszDirPath[cchDirPath + cchName + 1] = '\0';
1727
1728 rc = supR3HardenedVerifyDirRecursive(pszDirPath, cchDirPath + cchName + 1, pFsObjState,
1729 fRecursive, pErrInfo);
1730 if (RT_FAILURE(rc))
1731 break;
1732 }
1733 }
1734
1735 closedir(pDir);
1736 return rc;
1737#endif
1738}
1739
1740
1741/**
1742 * Worker for SUPR3HardenedVerifyDir.
1743 *
1744 * @returns See SUPR3HardenedVerifyDir.
1745 * @param pszDirPath See SUPR3HardenedVerifyDir.
1746 * @param fRecursive See SUPR3HardenedVerifyDir.
1747 * @param fCheckFiles See SUPR3HardenedVerifyDir.
1748 * @param pErrInfo See SUPR3HardenedVerifyDir.
1749 */
1750DECLHIDDEN(int) supR3HardenedVerifyDir(const char *pszDirPath, bool fRecursive, bool fCheckFiles, PRTERRINFO pErrInfo)
1751{
1752 /*
1753 * Validate the input path and parse it.
1754 */
1755 SUPR3HARDENEDPATHINFO Info;
1756 int rc = supR3HardenedVerifyPathSanity(pszDirPath, pErrInfo, &Info);
1757 if (RT_FAILURE(rc))
1758 return rc;
1759
1760 /*
1761 * Verify each component from the root up.
1762 */
1763 SUPR3HARDENEDFSOBJSTATE FsObjState;
1764 uint32_t const cComponents = Info.cComponents;
1765 for (uint32_t iComponent = 0; iComponent < cComponents; iComponent++)
1766 {
1767 bool fRelaxed = iComponent + 2 < cComponents;
1768 Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = '\0';
1769 rc = supR3HardenedQueryFsObjectByPath(Info.szPath, &FsObjState, pErrInfo);
1770 if (RT_SUCCESS(rc))
1771 rc = supR3HardenedVerifyFsObject(&FsObjState, true /*fDir*/, fRelaxed,
1772 false /*fSymlinksAllowed*/, Info.szPath, pErrInfo);
1773 if (RT_FAILURE(rc))
1774 return rc;
1775 Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = iComponent + 1 != cComponents ? RTPATH_SLASH : '\0';
1776 }
1777
1778 /*
1779 * Check files and subdirectories if requested.
1780 */
1781 if (fCheckFiles || fRecursive)
1782 {
1783 Info.szPath[Info.cch] = RTPATH_SLASH;
1784 Info.szPath[Info.cch + 1] = '\0';
1785 return supR3HardenedVerifyDirRecursive(Info.szPath, Info.cch + 1, &FsObjState,
1786 fRecursive, pErrInfo);
1787 }
1788
1789 return VINF_SUCCESS;
1790}
1791
1792
1793/**
1794 * Verfies a file.
1795 *
1796 * @returns VBox status code, error buffer filled on failure.
1797 * @param pszFilename The file to verify.
1798 * @param hNativeFile Handle to the file, verify that it's the same
1799 * as we ended up with when verifying the path.
1800 * RTHCUINTPTR_MAX means NIL here.
1801 * @param fMaybe3rdParty Set if the file is could be a supplied by a
1802 * third party. Different validation rules may
1803 * apply to 3rd party code on some platforms.
1804 * @param pErrInfo Where to return extended error information.
1805 * Optional.
1806 */
1807DECLHIDDEN(int) supR3HardenedVerifyFile(const char *pszFilename, RTHCUINTPTR hNativeFile,
1808 bool fMaybe3rdParty, PRTERRINFO pErrInfo)
1809{
1810 /*
1811 * Validate the input path and parse it.
1812 */
1813 SUPR3HARDENEDPATHINFO Info;
1814 int rc = supR3HardenedVerifyPathSanity(pszFilename, pErrInfo, &Info);
1815 if (RT_FAILURE(rc))
1816 return rc;
1817 if (Info.fDirSlash)
1818 return supR3HardenedSetError3(VERR_SUPLIB_IS_DIRECTORY, pErrInfo,
1819 "The file path specifies a directory: '", pszFilename, "'");
1820
1821 /*
1822 * Verify each component from the root up.
1823 */
1824 SUPR3HARDENEDFSOBJSTATE FsObjState;
1825 uint32_t const cComponents = Info.cComponents;
1826 for (uint32_t iComponent = 0; iComponent < cComponents; iComponent++)
1827 {
1828 bool fFinal = iComponent + 1 == cComponents;
1829 bool fRelaxed = iComponent + 2 < cComponents;
1830 Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = '\0';
1831 rc = supR3HardenedQueryFsObjectByPath(Info.szPath, &FsObjState, pErrInfo);
1832 if (RT_SUCCESS(rc))
1833 rc = supR3HardenedVerifyFsObject(&FsObjState, !fFinal /*fDir*/, fRelaxed,
1834 false /*fSymlinksAllowed*/, Info.szPath, pErrInfo);
1835 if (RT_FAILURE(rc))
1836 return rc;
1837 Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = !fFinal ? RTPATH_SLASH : '\0';
1838 }
1839
1840 /*
1841 * Verify the file handle against the last component, if specified.
1842 */
1843 if (hNativeFile != RTHCUINTPTR_MAX)
1844 {
1845 rc = supR3HardenedVerifySameFsObject(hNativeFile, &FsObjState, Info.szPath, pErrInfo);
1846 if (RT_FAILURE(rc))
1847 return rc;
1848 }
1849
1850#ifdef RT_OS_WINDOWS
1851 /*
1852 * The files shall be signed on windows, verify that.
1853 */
1854 rc = VINF_SUCCESS;
1855 HANDLE hVerify;
1856 if (hNativeFile == RTHCUINTPTR_MAX)
1857 {
1858 PRTUTF16 pwszPath;
1859 rc = RTStrToUtf16(pszFilename, &pwszPath);
1860 if (RT_SUCCESS(rc))
1861 {
1862 hVerify = CreateFileW(pwszPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1863 RTUtf16Free(pwszPath);
1864 }
1865 else
1866 {
1867 rc = RTErrInfoSetF(pErrInfo, rc, "Error converting '%s' to UTF-16: %Rrc", pszFilename, rc);
1868 hVerify = INVALID_HANDLE_VALUE;
1869 }
1870 }
1871 else
1872 {
1873 NTSTATUS rcNt = NtDuplicateObject(NtCurrentProcess(), (HANDLE)hNativeFile, NtCurrentProcess(), &hVerify,
1874 GENERIC_READ, 0 /*HandleAttributes*/, 0 /*Options*/);
1875 if (!NT_SUCCESS(rcNt))
1876 hVerify = INVALID_HANDLE_VALUE;
1877 }
1878 if (hVerify != INVALID_HANDLE_VALUE)
1879 {
1880# ifdef VBOX_WITH_HARDENING
1881 uint32_t fFlags = SUPHNTVI_F_REQUIRE_KERNEL_CODE_SIGNING;
1882 if (!fMaybe3rdParty)
1883 fFlags = SUPHNTVI_F_REQUIRE_BUILD_CERT;
1884 const char *pszSuffix = RTPathSuffix(pszFilename);
1885 if ( pszSuffix
1886 && pszSuffix[0] == '.'
1887 && ( RT_C_TO_LOWER(pszSuffix[1]) == 'r'
1888 || RT_C_TO_LOWER(pszSuffix[1]) == 'g')
1889 && RT_C_TO_LOWER(pszSuffix[2]) == 'c'
1890 && pszSuffix[3] == '\0' )
1891 fFlags |= SUPHNTVI_F_RC_IMAGE;
1892# ifndef IN_SUP_R3_STATIC /* Not in VBoxCpuReport and friends. */
1893 rc = supHardenedWinVerifyImageByHandleNoName(hVerify, fFlags, pErrInfo);
1894# endif
1895# else
1896 RT_NOREF1(fMaybe3rdParty);
1897# endif
1898 NtClose(hVerify);
1899 }
1900 else if (RT_SUCCESS(rc))
1901 rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
1902 "Error %u trying to open (or duplicate handle for) '%s'", RtlGetLastWin32Error(), pszFilename);
1903 if (RT_FAILURE(rc))
1904 return rc;
1905#else
1906 RT_NOREF1(fMaybe3rdParty);
1907#endif
1908
1909 return VINF_SUCCESS;
1910}
1911
1912
1913#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX)
1914/**
1915 * Verfies a file following symlinks.
1916 *
1917 * @returns VBox status code, error buffer filled on failure.
1918 * @param pszFilename The file to verify.
1919 * @param hNativeFile Handle to the file, verify that it's the same
1920 * as we ended up with when verifying the path.
1921 * RTHCUINTPTR_MAX means NIL here.
1922 * @param fMaybe3rdParty Set if the file is could be a supplied by a
1923 * third party. Different validation rules may
1924 * apply to 3rd party code on some platforms.
1925 * @param pErrInfo Where to return extended error information.
1926 * Optional.
1927 *
1928 * @note This is only used on OS X for libraries loaded with dlopen() because
1929 * the frameworks use symbolic links to point to the relevant library.
1930 *
1931 * @sa supR3HardenedVerifyFile
1932 */
1933DECLHIDDEN(int) supR3HardenedVerifyFileFollowSymlinks(const char *pszFilename, RTHCUINTPTR hNativeFile, bool fMaybe3rdParty,
1934 PRTERRINFO pErrInfo)
1935{
1936 RT_NOREF1(fMaybe3rdParty);
1937
1938 /*
1939 * Validate the input path and parse it.
1940 */
1941 SUPR3HARDENEDPATHINFO Info;
1942 int rc = supR3HardenedVerifyPathSanity(pszFilename, pErrInfo, &Info);
1943 if (RT_FAILURE(rc))
1944 return rc;
1945 if (Info.fDirSlash)
1946 return supR3HardenedSetError3(VERR_SUPLIB_IS_DIRECTORY, pErrInfo,
1947 "The file path specifies a directory: '", pszFilename, "'");
1948
1949 /*
1950 * Verify each component from the root up.
1951 */
1952#ifndef SUP_HARDENED_VERIFY_FOLLOW_SYMLINKS_USE_REALPATH
1953 uint32_t iLoops = 0;
1954#endif
1955 SUPR3HARDENEDFSOBJSTATE FsObjState;
1956 uint32_t iComponent = 0;
1957 while (iComponent < Info.cComponents)
1958 {
1959 bool fFinal = iComponent + 1 == Info.cComponents;
1960 bool fRelaxed = iComponent + 2 < Info.cComponents;
1961 Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = '\0';
1962 rc = supR3HardenedQueryFsObjectByPath(Info.szPath, &FsObjState, pErrInfo);
1963 if (RT_SUCCESS(rc))
1964 {
1965 /*
1966 * In case the component is a symlink expand it and start from the beginning after
1967 * verifying it has the proper access rights.
1968 * Furthermore only allow symlinks which don't contain any .. or . in the target
1969 * (enforced by supR3HardenedVerifyPathSanity).
1970 */
1971 rc = supR3HardenedVerifyFsObject(&FsObjState, !fFinal /*fDir*/, fRelaxed,
1972 true /*fSymlinksAllowed*/, Info.szPath, pErrInfo);
1973 if ( RT_SUCCESS(rc)
1974 && S_ISLNK(FsObjState.Stat.st_mode))
1975 {
1976#if SUP_HARDENED_VERIFY_FOLLOW_SYMLINKS_USE_REALPATH /* Another approach using realpath() and verifying the result when encountering a symlink. */
1977 char *pszFilenameResolved = realpath(pszFilename, NULL);
1978 if (pszFilenameResolved)
1979 {
1980 rc = supR3HardenedVerifyFile(pszFilenameResolved, hNativeFile, fMaybe3rdParty, pErrInfo);
1981 free(pszFilenameResolved);
1982 return rc;
1983 }
1984 else
1985 {
1986 int iErr = errno;
1987 supR3HardenedError(VERR_ACCESS_DENIED, false /*fFatal*/,
1988 "supR3HardenedVerifyFileFollowSymlinks: Failed to resolve the real path '%s': %s (%d)\n",
1989 pszFilename, strerror(iErr), iErr);
1990 return supR3HardenedSetError4(VERR_ACCESS_DENIED, pErrInfo,
1991 "realpath failed for '", pszFilename, "': ", strerror(iErr));
1992 }
1993#else
1994 /* Don't loop forever. */
1995 iLoops++;
1996 if (iLoops < 8)
1997 {
1998 /*
1999 * Construct new path by replacing the current component by the symlink value.
2000 * Note! readlink() is a weird API that doesn't necessarily indicates if the
2001 * buffer is too small.
2002 */
2003 char szPath[RTPATH_MAX];
2004 size_t const cchBefore = Info.aoffComponents[iComponent]; /* includes slash */
2005 size_t const cchAfter = fFinal ? 0 : 1 /*slash*/ + Info.cch - Info.aoffComponents[iComponent + 1];
2006 if (sizeof(szPath) > cchBefore + cchAfter + 2)
2007 {
2008 ssize_t cchTarget = readlink(Info.szPath, szPath, sizeof(szPath) - 1);
2009 if (cchTarget > 0)
2010 {
2011 /* Some serious paranoia against embedded zero terminator and weird return values. */
2012 szPath[cchTarget] = '\0';
2013 size_t cchLink = strlen(szPath);
2014
2015 /* Strip trailing dirslashes of non-final link. */
2016 if (!fFinal)
2017 while (cchLink > 1 and szPath[cchLink - 1] == '/')
2018 cchLink--;
2019
2020 /* Check link value sanity and buffer size. */
2021 if (cchLink == 0)
2022 return supR3HardenedSetError3(VERR_ACCESS_DENIED, pErrInfo,
2023 "Bad readlink return for '", Info.szPath, "'");
2024 if (szPath[0] == '/')
2025 return supR3HardenedSetError3(VERR_ACCESS_DENIED, pErrInfo,
2026 "Absolute symbolic link not allowed: '", szPath, "'");
2027 if (cchBefore + cchLink + cchAfter + 1 /*terminator*/ > sizeof(szPath))
2028 return supR3HardenedSetError(VERR_SUPLIB_PATH_TOO_LONG, pErrInfo,
2029 "Symlinks causing too long path!");
2030
2031 /* Construct the new path. */
2032 if (cchBefore)
2033 memmove(&szPath[cchBefore], &szPath[0], cchLink);
2034 memcpy(&szPath[0], Info.szPath, cchBefore);
2035 if (!cchAfter)
2036 szPath[cchBefore + cchLink] = '\0';
2037 else
2038 {
2039 szPath[cchBefore + cchLink] = RTPATH_SLASH;
2040 memcpy(&szPath[cchBefore + cchLink + 1],
2041 &Info.szPath[Info.aoffComponents[iComponent + 1]],
2042 cchAfter); /* cchAfter includes a zero terminator */
2043 }
2044
2045 /* Parse, copy and check the sanity (no '..' or '.') of the altered path. */
2046 rc = supR3HardenedVerifyPathSanity(szPath, pErrInfo, &Info);
2047 if (RT_FAILURE(rc))
2048 return rc;
2049 if (Info.fDirSlash)
2050 return supR3HardenedSetError3(VERR_SUPLIB_IS_DIRECTORY, pErrInfo,
2051 "The file path specifies a directory: '", szPath, "'");
2052
2053 /* Restart from the current component. */
2054 continue;
2055 }
2056 int iErr = errno;
2057 supR3HardenedError(VERR_ACCESS_DENIED, false /*fFatal*/,
2058 "supR3HardenedVerifyFileFollowSymlinks: Failed to readlink '%s': %s (%d)\n",
2059 Info.szPath, strerror(iErr), iErr);
2060 return supR3HardenedSetError4(VERR_ACCESS_DENIED, pErrInfo,
2061 "readlink failed for '", Info.szPath, "': ", strerror(iErr));
2062 }
2063 return supR3HardenedSetError(VERR_SUPLIB_PATH_TOO_LONG, pErrInfo, "Path too long for symlink replacing!");
2064 }
2065 else
2066 return supR3HardenedSetError3(VERR_TOO_MANY_SYMLINKS, pErrInfo,
2067 "Too many symbolic links: '", pszFilename, "'");
2068#endif
2069 }
2070 }
2071 if (RT_FAILURE(rc))
2072 return rc;
2073 Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = !fFinal ? RTPATH_SLASH : '\0';
2074 iComponent++;
2075 }
2076
2077 /*
2078 * Verify the file handle against the last component, if specified.
2079 */
2080 if (hNativeFile != RTHCUINTPTR_MAX)
2081 {
2082 rc = supR3HardenedVerifySameFsObject(hNativeFile, &FsObjState, Info.szPath, pErrInfo);
2083 if (RT_FAILURE(rc))
2084 return rc;
2085 }
2086
2087 return VINF_SUCCESS;
2088}
2089#endif /* RT_OS_DARWIN || RT_OS_LINUX */
2090
2091
2092/**
2093 * Gets the pre-init data for the hand-over to the other version
2094 * of this code.
2095 *
2096 * The reason why we pass this information on is that it contains
2097 * open directories and files. Later it may include even more info
2098 * (int the verified arrays mostly).
2099 *
2100 * The receiver is supR3HardenedRecvPreInitData.
2101 *
2102 * @param pPreInitData Where to store it.
2103 */
2104DECLHIDDEN(void) supR3HardenedGetPreInitData(PSUPPREINITDATA pPreInitData)
2105{
2106 pPreInitData->cInstallFiles = RT_ELEMENTS(g_aSupInstallFiles);
2107 pPreInitData->paInstallFiles = &g_aSupInstallFiles[0];
2108 pPreInitData->paVerifiedFiles = &g_aSupVerifiedFiles[0];
2109
2110 pPreInitData->cVerifiedDirs = RT_ELEMENTS(g_aSupVerifiedDirs);
2111 pPreInitData->paVerifiedDirs = &g_aSupVerifiedDirs[0];
2112}
2113
2114
2115/**
2116 * Receives the pre-init data from the static executable stub.
2117 *
2118 * @returns VBox status code. Will not bitch on failure since the
2119 * runtime isn't ready for it, so that is left to the exe stub.
2120 *
2121 * @param pPreInitData The hand-over data.
2122 */
2123DECLHIDDEN(int) supR3HardenedRecvPreInitData(PCSUPPREINITDATA pPreInitData)
2124{
2125 /*
2126 * Compare the array lengths and the contents of g_aSupInstallFiles.
2127 */
2128 if ( pPreInitData->cInstallFiles != RT_ELEMENTS(g_aSupInstallFiles)
2129 || pPreInitData->cVerifiedDirs != RT_ELEMENTS(g_aSupVerifiedDirs))
2130 return VERR_VERSION_MISMATCH;
2131 SUPINSTFILE const *paInstallFiles = pPreInitData->paInstallFiles;
2132 for (unsigned iFile = 0; iFile < RT_ELEMENTS(g_aSupInstallFiles); iFile++)
2133 if ( g_aSupInstallFiles[iFile].enmDir != paInstallFiles[iFile].enmDir
2134 || g_aSupInstallFiles[iFile].enmType != paInstallFiles[iFile].enmType
2135 || g_aSupInstallFiles[iFile].fOptional != paInstallFiles[iFile].fOptional
2136 || suplibHardenedStrCmp(g_aSupInstallFiles[iFile].pszFile, paInstallFiles[iFile].pszFile))
2137 return VERR_VERSION_MISMATCH;
2138
2139 /*
2140 * Check that we're not called out of order.
2141 * If dynamic linking it screwed up, we may end up here.
2142 */
2143 if ( !ASMMemIsZero(&g_aSupVerifiedFiles[0], sizeof(g_aSupVerifiedFiles))
2144 || !ASMMemIsZero(&g_aSupVerifiedDirs[0], sizeof(g_aSupVerifiedDirs)))
2145 return VERR_WRONG_ORDER;
2146
2147 /*
2148 * Copy the verification data over.
2149 */
2150 suplibHardenedMemCopy(&g_aSupVerifiedFiles[0], pPreInitData->paVerifiedFiles, sizeof(g_aSupVerifiedFiles));
2151 suplibHardenedMemCopy(&g_aSupVerifiedDirs[0], pPreInitData->paVerifiedDirs, sizeof(g_aSupVerifiedDirs));
2152 return VINF_SUCCESS;
2153}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use