VirtualBox

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

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

Support/Hardening: Use realpath() to get absolute paths upon encountering the first symlinks

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

© 2023 Oracle
ContactPrivacy policyTerms of Use