VirtualBox

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

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

Copyright year updates by scm.

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