VirtualBox

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

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

Config.kmk,Main,libs/xpcom,HostDrivers/Support,Installer: Make VBOX_WITH_XPCOMIPCD_IN_VBOX_SVC the default and remove the deprecated code, bugref:10594

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 85.3 KB
RevLine 
[11725]1/* $Id: SUPR3HardenedVerify.cpp 103331 2024-02-13 10:45:22Z vboxsync $ */
2/** @file
3 * VirtualBox Support Library - Verification of Hardened Installation.
4 */
5
6/*
[98103]7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[11725]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
[11725]11 *
[96407]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 *
[11725]25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
[96407]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
[11725]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.
[96407]33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
[11725]35 */
36
[57358]37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
[11725]41#if defined(RT_OS_OS2)
42# define INCL_BASE
43# define INCL_ERRORS
44# include <os2.h>
45# include <stdio.h>
[13908]46# include <stdlib.h>
47# include <unistd.h>
48# include <sys/fcntl.h>
49# include <sys/errno.h>
50# include <sys/syslimits.h>
[11725]51
52#elif defined(RT_OS_WINDOWS)
[52163]53# include <iprt/nt/nt-and-windows.h>
[49211]54# ifndef IN_SUP_HARDENED_R3
55# include <stdio.h>
56# endif
[11725]57
58#else /* UNIXes */
59# include <sys/types.h>
60# include <stdio.h>
61# include <stdlib.h>
[33701]62# include <dirent.h>
[11725]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>
[29250]80#include <iprt/asm.h>
[33739]81#include <iprt/ctype.h>
[11725]82#include <iprt/param.h>
[33701]83#include <iprt/path.h>
[33739]84#include <iprt/string.h>
[76409]85#include <iprt/utf16.h>
[11725]86
87#include "SUPLibInternal.h"
[51770]88#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
89# define SUPHNTVI_NO_NT_STUFF
90# include "win/SUPHardenedVerify-win.h"
91#endif
[11725]92
93
[57358]94/*********************************************************************************************************************************
95* Defined Constants And Macros *
96*********************************************************************************************************************************/
[33738]97/** The max path length acceptable for a trusted path. */
98#define SUPR3HARDENED_MAX_PATH 260U
[11725]99
[66842]100/** Enable to resolve symlinks using realpath() instead of cooking our own stuff. */
101#define SUP_HARDENED_VERIFY_FOLLOW_SYMLINKS_USE_REALPATH 1
102
[33872]103#ifdef RT_OS_SOLARIS
[33874]104# define dirfd(d) ((d)->d_fd)
[33872]105#endif
[11725]106
[51770]107/** Compare table file names with externally supplied names. */
108#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
[52160]109# define SUP_COMP_FILENAME RTStrICmp
[51770]110#else
111# define SUP_COMP_FILENAME suplibHardenedStrCmp
112#endif
[33872]113
[51770]114
[57358]115/*********************************************************************************************************************************
116* Global Variables *
117*********************************************************************************************************************************/
[11725]118/**
119 * The files that gets verified.
120 *
121 * @todo This needs reviewing against the linux packages.
[56733]122 * @todo The excessive use of kSupID_AppSharedLib needs to be reviewed at some point. For
[11725]123 * the time being we're building the linux packages with SharedLib pointing to
124 * AppPrivArch (lazy bird).
[57351]125 *
126 * @remarks If you add executables here, you might need to update
127 * g_apszSupNtVpAllowedVmExes in SUPHardenedVerifyProcess-win.cpp.
[11725]128 */
129static SUPINSTFILE const g_aSupInstallFiles[] =
130{
[18154]131 /* type, dir, fOpt, "pszFile" */
[11725]132 /* ---------------------------------------------------------------------- */
133 { kSupIFT_Dll, kSupID_AppPrivArch, false, "VMMR0.r0" },
134 { kSupIFT_Dll, kSupID_AppPrivArch, false, "VBoxDDR0.r0" },
135
[40370]136#ifdef VBOX_WITH_RAW_MODE
[56284]137 { kSupIFT_Rc, kSupID_AppPrivArch, false, "VMMRC.rc" },
138 { kSupIFT_Rc, kSupID_AppPrivArch, false, "VBoxDDRC.rc" },
[40370]139#endif
[11725]140
[56733]141 { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxRT" SUPLIB_DLL_SUFF },
142 { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxVMM" SUPLIB_DLL_SUFF },
[15353]143#if HC_ARCH_BITS == 32
[56733]144 { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxREM32" SUPLIB_DLL_SUFF },
145 { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxREM64" SUPLIB_DLL_SUFF },
[15327]146#endif
[56733]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 },
[58374]150 { kSupIFT_Exe, kSupID_AppBin, true, "VBoxVMMPreload" SUPLIB_EXE_SUFF },
151 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxVMMPreload" SUPLIB_DLL_SUFF },
[11725]152
153//#ifdef VBOX_WITH_DEBUGGER_GUI
[56733]154 { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxDbg" SUPLIB_DLL_SUFF },
155 { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxDbg3" SUPLIB_DLL_SUFF },
[11725]156//#endif
157
[17791]158//#ifdef VBOX_WITH_SHARED_CLIPBOARD
[18154]159 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxSharedClipboard" SUPLIB_DLL_SUFF },
[17791]160//#endif
161//#ifdef VBOX_WITH_SHARED_FOLDERS
[18154]162 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxSharedFolders" SUPLIB_DLL_SUFF },
[17791]163//#endif
[42331]164//#ifdef VBOX_WITH_DRAG_AND_DROP
165 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxDragAndDropSvc" SUPLIB_DLL_SUFF },
166//#endif
[17791]167//#ifdef VBOX_WITH_GUEST_PROPS
[18154]168 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxGuestPropSvc" SUPLIB_DLL_SUFF },
[17791]169//#endif
[28545]170//#ifdef VBOX_WITH_GUEST_CONTROL
171 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxGuestControlSvc" SUPLIB_DLL_SUFF },
172//#endif
[43457]173 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxHostChannel" SUPLIB_DLL_SUFF },
[18154]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 },
[17794]178
[18154]179 { kSupIFT_Exe, kSupID_AppBin, true, "VBoxManage" SUPLIB_EXE_SUFF },
[17794]180
181#ifdef VBOX_WITH_MAIN
[11725]182 { kSupIFT_Exe, kSupID_AppBin, false, "VBoxSVC" SUPLIB_EXE_SUFF },
[17794]183 #ifdef RT_OS_WINDOWS
[56733]184 { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxC" SUPLIB_DLL_SUFF },
[17794]185 #else
[103305]186 { kSupIFT_Dll, kSupID_AppPrivArch, false, "VBoxXPCOMIPCD" SUPLIB_DLL_SUFF },
[56733]187 { kSupIFT_Dll, kSupID_AppSharedLib, false, "VBoxXPCOM" SUPLIB_DLL_SUFF },
[11725]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" },
[17794]192 #endif
[11725]193#endif
194
[56733]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 },
[11725]198
199//#ifdef VBOX_WITH_HEADLESS
[18154]200 { kSupIFT_Exe, kSupID_AppBin, true, "VBoxHeadless" SUPLIB_EXE_SUFF },
201 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxHeadless" SUPLIB_DLL_SUFF },
[42477]202 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxVideoRecFB" SUPLIB_DLL_SUFF },
[11725]203//#endif
204
[16174]205//#ifdef VBOX_WITH_QTGUI
[18154]206 { kSupIFT_Exe, kSupID_AppBin, true, "VirtualBox" SUPLIB_EXE_SUFF },
[83033]207# ifdef RT_OS_DARWIN
208 { kSupIFT_Exe, kSupID_AppMacHelper, true, "VirtualBoxVM" SUPLIB_EXE_SUFF },
209# else
[56817]210 { kSupIFT_Exe, kSupID_AppBin, true, "VirtualBoxVM" SUPLIB_EXE_SUFF },
[83033]211# endif
[72247]212 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VirtualBoxVM" SUPLIB_DLL_SUFF },
[79365]213 { kSupIFT_Dll, kSupID_AppPrivArch, true, "UICommon" SUPLIB_DLL_SUFF },
[11725]214# if !defined(RT_OS_DARWIN) && !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
[56733]215 { kSupIFT_Dll, kSupID_AppSharedLib, true, "VBoxKeyboard" SUPLIB_DLL_SUFF },
[11725]216# endif
217//#endif
218
219//#ifdef VBOX_WITH_VBOXSDL
[18154]220 { kSupIFT_Exe, kSupID_AppBin, true, "VBoxSDL" SUPLIB_EXE_SUFF },
221 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxSDL" SUPLIB_DLL_SUFF },
[11725]222//#endif
223
224//#ifdef VBOX_WITH_WEBSERVICES
[18154]225 { kSupIFT_Exe, kSupID_AppBin, true, "vboxwebsrv" SUPLIB_EXE_SUFF },
[11725]226//#endif
227
228#ifdef RT_OS_LINUX
[18154]229 { kSupIFT_Exe, kSupID_AppBin, true, "VBoxTunctl" SUPLIB_EXE_SUFF },
[11725]230#endif
[18003]231
232//#ifdef VBOX_WITH_NETFLT
[18154]233 { kSupIFT_Exe, kSupID_AppBin, true, "VBoxNetDHCP" SUPLIB_EXE_SUFF },
234 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxNetDHCP" SUPLIB_DLL_SUFF },
[18003]235//#endif
[47883]236
237//#ifdef VBOX_WITH_LWIP_NAT
[47894]238 { kSupIFT_Exe, kSupID_AppBin, true, "VBoxNetNAT" SUPLIB_EXE_SUFF },
239 { kSupIFT_Dll, kSupID_AppPrivArch, true, "VBoxNetNAT" SUPLIB_DLL_SUFF },
[47883]240//#endif
[51909]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"),
[57351]254 HARDENED_TESTCASE_ENTRY("tstGIP-2"),
[51909]255 HARDENED_TESTCASE_ENTRY("tstIntNet-1"),
256 HARDENED_TESTCASE_ENTRY("tstMMHyperHeap"),
[54453]257 HARDENED_TESTCASE_ENTRY("tstRTR0ThreadPreemptionDriver"),
[51909]258 HARDENED_TESTCASE_ENTRY("tstRTR0MemUserKernelDriver"),
259 HARDENED_TESTCASE_ENTRY("tstRTR0SemMutexDriver"),
260 HARDENED_TESTCASE_ENTRY("tstRTR0TimerDriver"),
261 HARDENED_TESTCASE_ENTRY("tstSSM"),
262#endif
[11725]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/**
[33540]274 * Assembles the path to a directory.
[11725]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).
[83033]283 * @param pFile The file (for darwin helper app paths).
[11725]284 */
[83033]285static int supR3HardenedMakePath(SUPINSTDIR enmDir, char *pszDst, size_t cchDst, bool fFatal, PCSUPINSTFILE pFile)
[11725]286{
287 int rc;
288 switch (enmDir)
289 {
[56733]290 case kSupID_AppBin:
291 rc = supR3HardenedPathAppBin(pszDst, cchDst);
[11725]292 break;
[56733]293 case kSupID_AppSharedLib:
294 rc = supR3HardenedPathAppSharedLibs(pszDst, cchDst);
[11725]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 {
[49211]303 size_t off = suplibHardenedStrLen(pszDst);
[11725]304 if (cchDst - off >= sizeof("/components"))
[49211]305 suplibHardenedMemCopy(&pszDst[off], "/components", sizeof("/components"));
[11725]306 else
307 rc = VERR_BUFFER_OVERFLOW;
308 }
309 break;
310 case kSupID_AppPrivNoArch:
311 rc = supR3HardenedPathAppPrivateNoArch(pszDst, cchDst);
312 break;
[51909]313 case kSupID_Testcase:
[56733]314 rc = supR3HardenedPathAppBin(pszDst, cchDst);
[51909]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;
[83033]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
[11725]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);
[83033]358 NOREF(pFile);
[11725]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 */
[83033]381 int rc = supR3HardenedMakePath(pFile->enmDir, pszDst, cchDst, fFatal, pFile);
[39086]382 if (RT_SUCCESS(rc) && fWithFilename)
[11725]383 {
[49211]384 size_t cchFile = suplibHardenedStrLen(pFile->pszFile);
385 size_t off = suplibHardenedStrLen(pszDst);
[11725]386 if (cchDst - off >= cchFile + 2)
387 {
388 pszDst[off++] = '/';
[49211]389 suplibHardenedMemCopy(&pszDst[off], pFile->pszFile, cchFile + 1);
[11725]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).
[83033]408 * @param pFile The file (for darwin helper app paths).
[11725]409 */
[83033]410DECLHIDDEN(int) supR3HardenedVerifyFixedDir(SUPINSTDIR enmDir, bool fFatal, PCSUPINSTFILE pFile)
[11725]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];
[83033]437 int rc = supR3HardenedMakePath(enmDir, szPath, sizeof(szPath), fFatal, pFile);
[11725]438 if (RT_SUCCESS(rc))
439 {
[51912]440#if defined(RT_OS_WINDOWS)
441 PRTUTF16 pwszPath;
442 rc = RTStrToUtf16(szPath, &pwszPath);
443 if (RT_SUCCESS(rc))
[11725]444 {
[51912]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 {
[52940]466 int err = RtlGetLastWin32Error();
[51912]467 rc = supR3HardenedError(VERR_PATH_NOT_FOUND, fFatal,
468 "supR3HardenedVerifyDir: Failed to open \"%s\": err=%d\n",
469 szPath, err);
470 }
471 RTUtf16Free(pwszPath);
[11725]472 }
473 else
[51912]474 rc = supR3HardenedError(rc, fFatal,
475 "supR3HardenedVerifyDir: Failed to convert \"%s\" to UTF-16: err=%d\n", szPath, rc);
476
[11725]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 }
[51909]522 else if (enmDir == kSupID_Testcase)
523 {
524 g_aSupVerifiedDirs[enmDir].fValidated = true;
525 rc = VINF_SUCCESS; /* Optional directory, ignore if missing. */
526 }
[11725]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
[52083]541#ifdef RT_OS_WINDOWS
[11725]542/**
[52083]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);
[52355]573 if (hFile != INVALID_HANDLE_VALUE)
[52083]574 {
575 *phFile = (intptr_t)hFile;
576 rc = VINF_SUCCESS;
577 }
578 else
579 {
[52940]580 int err = RtlGetLastWin32Error();
[52083]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
[52940]658 NtClose((HANDLE)hFileOpened);
[52083]659 }
660
661 return rc;
662
663# else /* Not checking signatures. */
[62675]664 RT_NOREF4(pFile, pVerified, fFatal, fLeaveFileOpen);
[52083]665 return VINF_SUCCESS;
666# endif /* Not checking signatures. */
667}
668#endif
669
670
671/**
[11725]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.
[52083]681 * @param fVerifyAll Set if this is an verify all call and we will
682 * postpone signature checking.
[11725]683 */
[52083]684static int supR3HardenedVerifyFileInternal(int iFile, bool fFatal, bool fLeaveFileOpen, bool fVerifyAll)
[11725]685{
[62877]686#ifndef RT_OS_WINDOWS
687 RT_NOREF1(fVerifyAll);
688#endif
[11725]689 PCSUPINSTFILE pFile = &g_aSupInstallFiles[iFile];
690 PSUPVERIFIEDFILE pVerified = &g_aSupVerifiedFiles[iFile];
691
692 /*
[52083]693 * Already done validation? Do signature validation if we haven't yet.
[11725]694 */
695 if (pVerified->fValidated)
[52083]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 }
[11725]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;
[52084]713#ifdef RT_OS_WINDOWS
[52083]714 pVerified->fCheckedSignature = false;
[52084]715#endif
[11725]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 */
[83033]722 int rc = supR3HardenedVerifyFixedDir(pFile->enmDir, fFatal, pFile);
[11725]723 if (RT_SUCCESS(rc))
724 {
[52083]725#if defined(RT_OS_WINDOWS)
726 rc = supR3HardenedVerifyFileOpen(pFile, fFatal, &pVerified->hFile);
[11725]727 if (RT_SUCCESS(rc))
728 {
[52083]729 if (!fVerifyAll)
730 rc = supR3HardenedVerifyFileSignature(pFile, pVerified, fFatal, fLeaveFileOpen);
[51803]731 if (RT_SUCCESS(rc))
[11725]732 {
[52083]733 pVerified->fValidated = true;
734 if (!fLeaveFileOpen)
[51803]735 {
[52940]736 NtClose((HANDLE)pVerified->hFile);
[52083]737 pVerified->hFile = -1;
[51770]738 }
[11725]739 }
[52083]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 {
[11725]746 int fd = open(szPath, O_RDONLY, 0);
747 if (fd >= 0)
748 {
749 /*
[52083]750 * On unixy systems we'll make sure the file is owned by root
[11725]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 }
[52083]802#endif /* !RT_OS_WINDOWS */
[11725]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;
[51770]832 if (SUP_COMP_FILENAME(szName, pszFilename))
[11725]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;
[52163]840 char szName2[RTPATH_MAX]; /** @todo Must use UTF-16 here! Code is mixing UTF-8 and native. */
[11725]841 if ( GetFullPathName(szName, RT_ELEMENTS(szName2), &szName2[0], &pszIgnored)
842 && GetFullPathName(pszFilename, RT_ELEMENTS(szName), &szName[0], &pszIgnored))
[51770]843 if (!SUP_COMP_FILENAME(szName2, szName))
[11725]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)
[51770]850 if (!SUP_COMP_FILENAME(szName2, szName))
[11725]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.
[33540]878 * On verification failure, an error code will be returned when fFatal is clear,
879 * otherwise the program will be terminated.
[11725]880 *
881 * @param pszFilename The filename.
882 * @param fFatal Whether validation failures should be treated as
883 * fatal (true) or not (false).
884 */
[33701]885DECLHIDDEN(int) supR3HardenedVerifyFixedFile(const char *pszFilename, bool fFatal)
[11725]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++)
[51770]892 if (!SUP_COMP_FILENAME(pszName, g_aSupInstallFiles[iFile].pszFile))
[11725]893 {
894 int rc = supR3HardenedVerifySameFile(iFile, pszFilename, fFatal);
895 if (RT_SUCCESS(rc))
[52083]896 rc = supR3HardenedVerifyFileInternal(iFile, fFatal, false /* fLeaveFileOpen */, false /* fVerifyAll */);
[11725]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.
[56733]909 * @param pszExePath The path to the executable.
[11725]910 * @param fFatal See supR3HardenedVerifyAll.
[52083]911 * @param fLeaveOpen The leave open setting used by
912 * supR3HardenedVerifyAll.
[56817]913 * @param fMainFlags Flags supplied to SUPR3HardenedMain.
[11725]914 */
[56817]915static int supR3HardenedVerifyProgram(const char *pszProgName, const char *pszExePath, bool fFatal,
916 bool fLeaveOpen, uint32_t fMainFlags)
[11725]917{
918 /*
919 * Search the table looking for the executable and the DLL/DYLIB/SO.
[56817]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.
[11725]923 */
924 int rc = VINF_SUCCESS;
925 bool fExe = false;
926 bool fDll = false;
[56817]927 size_t const cchProgNameExe = suplibHardenedStrLen(pszProgName);
928#ifndef RT_OS_DARWIN
929 size_t const cchProgNameDll = cchProgNameExe;
[62675]930 NOREF(fMainFlags);
[56817]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
[11725]939 for (unsigned iFile = 0; iFile < RT_ELEMENTS(g_aSupInstallFiles); iFile++)
[56817]940 if (!suplibHardenedStrNCmp(pszProgName, g_aSupInstallFiles[iFile].pszFile, cchProgNameDll))
[11725]941 {
[56817]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))
[11725]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);
[52083]950 else
[52417]951 rc = supR3HardenedVerifyFileInternal(iFile, fFatal, fLeaveOpen,
952 true /* fVerifyAll - check sign later, only final process need check it on load. */);
[11725]953 fDll = true;
954 }
[51909]955 else if ( ( g_aSupInstallFiles[iFile].enmType == kSupIFT_Exe
956 || g_aSupInstallFiles[iFile].enmType == kSupIFT_TestExe)
[56817]957 && ( cchProgNameExe == cchProgNameDll
958 || !suplibHardenedStrNCmp(pszProgName, g_aSupInstallFiles[iFile].pszFile, cchProgNameExe))
959 && !suplibHardenedStrCmp(&g_aSupInstallFiles[iFile].pszFile[cchProgNameExe], SUPLIB_EXE_SUFF))
[11725]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);
[52083]965 else
966 rc = supR3HardenedVerifyFileInternal(iFile, fFatal, fLeaveOpen, false /* fVerifyAll */);
[11725]967 fExe = true;
968
[56733]969 supR3HardenedVerifySameFile(iFile, pszExePath, fFatal);
[11725]970 }
971 }
972
973 /*
974 * Check the findings.
975 */
[52083]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 }
[11725]988 return rc;
989}
990
991
992/**
[52083]993 * Verifies all the known files (called from SUPR3HardenedMain).
[11725]994 *
995 * @returns VINF_SUCCESS on success.
[33540]996 * On verification failure, an error code will be returned when fFatal is clear,
997 * otherwise the program will be terminated.
[11725]998 *
999 * @param fFatal Whether validation failures should be treated as
1000 * fatal (true) or not (false).
[52083]1001 * @param pszProgName The program name. This is used to verify that
1002 * both the executable and corresponding
[11725]1003 * DLL/DYLIB/SO are valid.
[56733]1004 * @param pszExePath The path to the executable.
[56817]1005 * @param fMainFlags Flags supplied to SUPR3HardenedMain.
[11725]1006 */
[56817]1007DECLHIDDEN(int) supR3HardenedVerifyAll(bool fFatal, const char *pszProgName, const char *pszExePath, uint32_t fMainFlags)
[11725]1008{
1009 /*
[52083]1010 * On windows
1011 */
1012#if defined(RT_OS_WINDOWS)
1013 bool fLeaveOpen = true;
1014#else
1015 bool fLeaveOpen = false;
1016#endif
1017
1018 /*
[11725]1019 * The verify all the files.
1020 */
1021 int rc = VINF_SUCCESS;
1022 for (unsigned iFile = 0; iFile < RT_ELEMENTS(g_aSupInstallFiles); iFile++)
1023 {
[52083]1024 int rc2 = supR3HardenedVerifyFileInternal(iFile, fFatal, fLeaveOpen, true /* fVerifyAll */);
[11725]1025 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1026 rc = rc2;
1027 }
1028
1029 /*
[52083]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.
[11725]1033 */
[56817]1034 int rc2 = supR3HardenedVerifyProgram(pszProgName, pszExePath, fFatal, fLeaveOpen, fMainFlags);
[52083]1035 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1036 rc2 = rc;
[11725]1037
1038 return rc;
1039}
1040
1041
[33738]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.
[35188]1047 * @param pErrInfo The error info structure.
[33738]1048 * @param cMsgs The number of messages in the ellipsis.
1049 * @param ... Message parts.
1050 */
[35188]1051static int supR3HardenedSetErrorN(int rc, PRTERRINFO pErrInfo, unsigned cMsgs, ...)
[33738]1052{
[35188]1053 if (pErrInfo)
[33738]1054 {
[35188]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 *);
[90780]1063 size_t cchMsg = RT_VALID_PTR(pszMsg) ? suplibHardenedStrLen(pszMsg) : 0;
[35188]1064 if (cchMsg >= cbErr)
1065 cchMsg = cbErr - 1;
[49211]1066 suplibHardenedMemCopy(pszErr, pszMsg, cchMsg);
[35188]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;
[33738]1075 }
[33701]1076
[33738]1077 return rc;
1078}
1079
[66632]1080
1081#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX)
[66526]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}
[66632]1098#endif
[33738]1099
[66526]1100
[11725]1101/**
[33738]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.
[35188]1106 * @param pErrInfo The error info structure.
[33738]1107 * @param pszMsg1 The first message part.
1108 * @param pszMsg2 The second message part.
1109 * @param pszMsg3 The third message part.
1110 */
[35188]1111static int supR3HardenedSetError3(int rc, PRTERRINFO pErrInfo, const char *pszMsg1,
[33738]1112 const char *pszMsg2, const char *pszMsg3)
1113{
[35188]1114 return supR3HardenedSetErrorN(rc, pErrInfo, 3, pszMsg1, pszMsg2, pszMsg3);
[33738]1115}
1116
[66632]1117
[39091]1118#ifdef SOME_UNUSED_FUNCTION
[33738]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.
[35188]1124 * @param pErrInfo The error info structure.
[33738]1125 * @param pszMsg1 The first message part.
1126 * @param pszMsg2 The second message part.
1127 */
[35188]1128static int supR3HardenedSetError2(int rc, PRTERRINFO pErrInfo, const char *pszMsg1,
[33738]1129 const char *pszMsg2)
1130{
[35188]1131 return supR3HardenedSetErrorN(rc, pErrInfo, 2, pszMsg1, pszMsg2);
[33738]1132}
[66632]1133#endif
[33738]1134
1135
[66842]1136#ifndef SUP_HARDENED_VERIFY_FOLLOW_SYMLINKS_USE_REALPATH
1137# if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX)
[33738]1138/**
[33701]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.
[35188]1143 * @param pErrInfo The error info structure.
[33701]1144 * @param pszMsg The message.
1145 */
[35188]1146static int supR3HardenedSetError(int rc, PRTERRINFO pErrInfo, const char *pszMsg)
[33701]1147{
[35188]1148 return supR3HardenedSetErrorN(rc, pErrInfo, 1, pszMsg);
[33701]1149}
[66842]1150# endif
[66526]1151#endif
[33701]1152
[66526]1153
[33701]1154/**
[33738]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.
[33701]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).
[33738]1186 * Sane paths are less or equal to SUPR3HARDENED_MAX_PATH bytes in length. UNC
1187 * paths are not supported.
[33701]1188 *
1189 * @returns VBox status code.
1190 * @param pszPath The path to check.
[35188]1191 * @param pErrInfo The error info structure.
[33738]1192 * @param pInfo Where to return a copy of the path along with
1193 * parsing information.
[33701]1194 */
[35188]1195static int supR3HardenedVerifyPathSanity(const char *pszPath, PRTERRINFO pErrInfo, PSUPR3HARDENEDPATHINFO pInfo)
[33701]1196{
[33738]1197 const char *pszSrc = pszPath;
1198 char *pszDst = pInfo->szPath;
[33701]1199
1200 /*
[33738]1201 * Check that it's an absolute path and copy the volume/root specifier.
[33701]1202 */
1203#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
[51770]1204 if ( !RT_C_IS_ALPHA(pszSrc[0])
[33738]1205 || pszSrc[1] != ':'
1206 || !RTPATH_IS_SLASH(pszSrc[2]))
[35188]1207 return supR3HardenedSetError3(VERR_SUPLIB_PATH_NOT_ABSOLUTE, pErrInfo, "The path is not absolute: '", pszPath, "'");
[33738]1208
1209 *pszDst++ = RT_C_TO_UPPER(pszSrc[0]);
1210 *pszDst++ = ':';
1211 *pszDst++ = RTPATH_SLASH;
1212 pszSrc += 3;
1213
[33701]1214#else
[33738]1215 if (!RTPATH_IS_SLASH(pszSrc[0]))
[35188]1216 return supR3HardenedSetError3(VERR_SUPLIB_PATH_NOT_ABSOLUTE, pErrInfo, "The path is not absolute: '", pszPath, "'");
[33738]1217
1218 *pszDst++ = RTPATH_SLASH;
1219 pszSrc += 1;
[33701]1220#endif
1221
1222 /*
[33738]1223 * No path specifying the root or something very shortly thereafter will
1224 * be approved of.
1225 */
1226 if (pszSrc[0] == '\0')
[35188]1227 return supR3HardenedSetError3(VERR_SUPLIB_PATH_IS_ROOT, pErrInfo, "The path is root: '", pszPath, "'");
[33738]1228 if ( pszSrc[1] == '\0'
1229 || pszSrc[2] == '\0')
[35188]1230 return supR3HardenedSetError3(VERR_SUPLIB_PATH_TOO_SHORT, pErrInfo, "The path is too short: '", pszPath, "'");
[33738]1231
[68344]1232#if RTPATH_STYLE == RTPATH_STR_F_STYLE_UNIX
[33738]1233 /*
[68344]1234 * Skip double slashes.
1235 */
1236 while (RTPATH_IS_SLASH(*pszSrc))
1237 pszSrc++;
1238#else
1239 /*
[66608]1240 * The root slash should be alone to avoid UNC confusion.
[33701]1241 */
[66608]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, "'");
[68344]1245#endif
[66608]1246 /*
1247 * Check each component. No parent references.
1248 */
[33859]1249 pInfo->cComponents = 0;
1250 pInfo->fDirSlash = false;
[33738]1251 while (pszSrc[0])
[33701]1252 {
[33738]1253 /* Sanity checks. */
1254 if ( pszSrc[0] == '.'
1255 && pszSrc[1] == '.'
1256 && RTPATH_IS_SLASH(pszSrc[2]))
[35188]1257 return supR3HardenedSetError3(VERR_SUPLIB_PATH_NOT_ABSOLUTE, pErrInfo,
[33738]1258 "The path is not absolute: '", pszPath, "'");
1259
1260 /* Record the start of the component. */
1261 if (pInfo->cComponents >= RT_ELEMENTS(pInfo->aoffComponents) - 1)
[35188]1262 return supR3HardenedSetError3(VERR_SUPLIB_PATH_TOO_MANY_COMPONENTS, pErrInfo,
[33738]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])
[33701]1268 {
[33738]1269 if (RTPATH_IS_SLASH(pszSrc[0]))
[33701]1270 {
[33738]1271 pszSrc++;
1272 if (*pszSrc)
1273 *pszDst++ = RTPATH_SLASH;
1274 else
1275 pInfo->fDirSlash = true;
[33701]1276 break;
1277 }
[33738]1278 *pszDst++ = *pszSrc++;
[33832]1279 if ((uintptr_t)(pszDst - &pInfo->szPath[0]) >= SUPR3HARDENED_MAX_PATH)
[35188]1280 return supR3HardenedSetError3(VERR_SUPLIB_PATH_TOO_LONG, pErrInfo,
[33738]1281 "The path is too long: '", pszPath, "'");
[33701]1282 }
[66608]1283
1284 /* Skip double slashes. */
1285 while (RTPATH_IS_SLASH(*pszSrc))
1286 pszSrc++;
[33701]1287 }
1288
[33738]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.
[35188]1328 * @param pErrInfo The error info structure.
[33738]1329 */
[35188]1330static int supR3HardenedQueryFsObjectByPath(char const *pszPath, PSUPR3HARDENEDFSOBJSTATE pFsObjState, PRTERRINFO pErrInfo)
[33738]1331{
1332#if defined(RT_OS_WINDOWS)
1333 /** @todo Windows hardening. */
1334 pFsObjState->chTodo = 0;
[62675]1335 RT_NOREF2(pszPath, pErrInfo);
[33738]1336 return VINF_SUCCESS;
1337
1338#else
[33701]1339 /*
[33738]1340 * Stat the object, do not follow links.
[33701]1341 */
[33738]1342 if (lstat(pszPath, &pFsObjState->Stat) != 0)
1343 {
1344 /* Ignore access errors */
1345 if (errno != EACCES)
[35188]1346 return supR3HardenedSetErrorN(VERR_SUPLIB_STAT_FAILED, pErrInfo,
[33738]1347 5, "stat failed with ", strerror(errno), " on: '", pszPath, "'");
1348 }
[33701]1349
[33738]1350 /*
1351 * Read ACLs.
1352 */
1353 /** @todo */
1354
[33701]1355 return VINF_SUCCESS;
[33738]1356#endif
[33701]1357}
1358
1359
1360/**
[33738]1361 * Query information about a file system object by native handle.
[33701]1362 *
1363 * @returns VBox status code, error buffer filled on failure.
[33738]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.)
[35188]1370 * @param pErrInfo The error info structure.
[33738]1371 */
1372static int supR3HardenedQueryFsObjectByHandle(RTHCUINTPTR hNative, PSUPR3HARDENEDFSOBJSTATE pFsObjState,
[35188]1373 char const *pszPath, PRTERRINFO pErrInfo)
[33738]1374{
1375#if defined(RT_OS_WINDOWS)
1376 /** @todo Windows hardening. */
1377 pFsObjState->chTodo = 0;
[62675]1378 RT_NOREF3(hNative, pszPath, pErrInfo);
[33738]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)
[35188]1386 return supR3HardenedSetErrorN(VERR_SUPLIB_STAT_FAILED, pErrInfo,
[33738]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.)
[35188]1408 * @param pErrInfo The error info structure.
[33738]1409 */
1410static int supR3HardenedIsSameFsObject(PCSUPR3HARDENEDFSOBJSTATE pFsObjState1, PCSUPR3HARDENEDFSOBJSTATE pFsObjState2,
[35188]1411 const char *pszPath, PRTERRINFO pErrInfo)
[33738]1412{
1413#if defined(RT_OS_WINDOWS)
1414 /** @todo Windows hardening. */
[62675]1415 RT_NOREF4(pFsObjState1, pFsObjState2, pszPath, pErrInfo);
[33738]1416 return VINF_SUCCESS;
1417
1418#elif defined(RT_OS_OS2)
[62675]1419 RT_NOREF4(pFsObjState1, pFsObjState2, pszPath, pErrInfo);
[33738]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)
[35188]1429 return supR3HardenedSetError3(VERR_SUPLIB_NOT_SAME_OBJECT, pErrInfo,
[33738]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)
[35188]1433 return supR3HardenedSetError3(VERR_SUPLIB_NOT_SAME_OBJECT, pErrInfo,
[33738]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)))
[35188]1437 return supR3HardenedSetError3(VERR_SUPLIB_NOT_SAME_OBJECT, pErrInfo,
[33738]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.
[33701]1450 * @param fDir Whether this is a directory or a file.
[33738]1451 * @param fRelaxed Whether we can be more relaxed about this
1452 * directory (only used for grand parent
1453 * directories).
[66526]1454 * @param fSymlinksAllowed Flag whether symlinks are allowed or not.
1455 * If allowed the symlink object is verified not the target.
[39133]1456 * @param pszPath The path to the object. For error messages and
1457 * securing a couple of hacks.
[35188]1458 * @param pErrInfo The error info structure.
[33701]1459 */
[33738]1460static int supR3HardenedVerifyFsObject(PCSUPR3HARDENEDFSOBJSTATE pFsObjState, bool fDir, bool fRelaxed,
[66526]1461 bool fSymlinksAllowed, const char *pszPath, PRTERRINFO pErrInfo)
[33701]1462{
1463#if defined(RT_OS_WINDOWS)
1464 /** @todo Windows hardening. */
[66527]1465 RT_NOREF(pFsObjState, fDir, fRelaxed, fSymlinksAllowed, pszPath, pErrInfo);
[33701]1466 return VINF_SUCCESS;
1467
1468#elif defined(RT_OS_OS2)
1469 /* No hardening here - it's a single user system. */
[66527]1470 RT_NOREF(pFsObjState, fDir, fRelaxed, fSymlinksAllowed, pszPath, pErrInfo);
[33701]1471 return VINF_SUCCESS;
1472
1473#else
1474 /*
[33738]1475 * The owner must be root.
1476 *
1477 * This can be extended to include predefined system users if necessary.
[33701]1478 */
[33738]1479 if (pFsObjState->Stat.st_uid != 0)
[35188]1480 return supR3HardenedSetError3(VERR_SUPLIB_OWNER_NOT_ROOT, pErrInfo, "The owner is not root: '", pszPath, "'");
[33738]1481
1482 /*
[66526]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).
[35403]1486 */
[66526]1487 if ( !S_ISLNK(pFsObjState->Stat.st_mode)
1488 || !fSymlinksAllowed)
[35403]1489 {
[66526]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 }
[35403]1508 }
1509
1510 /*
[33738]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)
[33701]1519 {
[66627]1520# ifdef RT_OS_DARWIN
[33738]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. */
[39133]1524 /** @todo dynamically resolve the admin group? */
[49211]1525 bool fBad = !fRelaxed || pFsObjState->Stat.st_gid != 80 /*admin*/ || suplibHardenedStrCmp(pszPath, "/Applications");
[39091]1526
[66627]1527# elif defined(RT_OS_FREEBSD)
[39133]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? */
[49211]1533 bool fBad = !fRelaxed || pFsObjState->Stat.st_gid != 5 /*operator*/ || suplibHardenedStrCmp(pszPath, "/usr/pbi");
[39091]1534 NOREF(fRelaxed);
[66627]1535# elif defined(RT_OS_SOLARIS)
[66554]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");
[66627]1542# else
[39091]1543 NOREF(fRelaxed);
1544 bool fBad = true;
[66627]1545# endif
[39091]1546 if (fBad)
[35188]1547 return supR3HardenedSetError3(VERR_SUPLIB_WRITE_NON_SYS_GROUP, pErrInfo,
[52790]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");
[33701]1550 }
1551
[33738]1552 /*
1553 * World must not have write access. There is no relaxing this rule.
[66632]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.)
[33738]1559 */
[66632]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 )
[35188]1566 return supR3HardenedSetError3(VERR_SUPLIB_WORLD_WRITABLE, pErrInfo,
[33738]1567 "World writable: '", pszPath, "'");
[33701]1568
[33738]1569 /*
1570 * Check the ACLs.
1571 */
1572 /** @todo */
1573
[33701]1574 return VINF_SUCCESS;
1575#endif
1576}
1577
1578
1579/**
[33738]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.)
[35188]1591 * @param pErrInfo The error info structure.
[33738]1592 */
1593static int supR3HardenedVerifySameFsObject(RTHCUINTPTR hNative, PCSUPR3HARDENEDFSOBJSTATE pFsObjState,
[35188]1594 const char *pszPath, PRTERRINFO pErrInfo)
[33738]1595{
1596 SUPR3HARDENEDFSOBJSTATE FsObjState2;
[35188]1597 int rc = supR3HardenedQueryFsObjectByHandle(hNative, &FsObjState2, pszPath, pErrInfo);
[33738]1598 if (RT_SUCCESS(rc))
[35188]1599 rc = supR3HardenedIsSameFsObject(pFsObjState, &FsObjState2, pszPath, pErrInfo);
[33738]1600 return rc;
1601}
1602
1603
1604/**
[33701]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.
[33738]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.
[33701]1619 * @param fRecursive Whether to recurse into subdirectories.
[35188]1620 * @param pErrInfo The error info structure.
[33701]1621 */
[33738]1622static int supR3HardenedVerifyDirRecursive(char *pszDirPath, size_t cchDirPath, PSUPR3HARDENEDFSOBJSTATE pFsObjState,
[35188]1623 bool fRecursive, PRTERRINFO pErrInfo)
[33701]1624{
1625#if defined(RT_OS_WINDOWS)
1626 /** @todo Windows hardening. */
[62675]1627 RT_NOREF5(pszDirPath, cchDirPath, pFsObjState, fRecursive, pErrInfo);
[33701]1628 return VINF_SUCCESS;
1629
1630#elif defined(RT_OS_OS2)
1631 /* No hardening here - it's a single user system. */
[62675]1632 RT_NOREF5(pszDirPath, cchDirPath, pFsObjState, fRecursive, pErrInfo);
[33701]1633 return VINF_SUCCESS;
1634
1635#else
[33738]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 */
[33701]1641 DIR *pDir = opendir(pszDirPath);
1642 if (!pDir)
1643 {
1644 /* Ignore access errors. */
1645 if (errno == EACCES)
1646 return VINF_SUCCESS;
[35188]1647 return supR3HardenedSetErrorN(VERR_SUPLIB_DIR_ENUM_FAILED, pErrInfo,
[33738]1648 5, "opendir failed with ", strerror(errno), " on '", pszDirPath, "'");
[33701]1649 }
[33738]1650 if (dirfd(pDir) != -1)
1651 {
[35188]1652 int rc = supR3HardenedVerifySameFsObject(dirfd(pDir), pFsObjState, pszDirPath, pErrInfo);
[33738]1653 if (RT_FAILURE(rc))
1654 {
1655 closedir(pDir);
1656 return rc;
1657 }
1658 }
[33701]1659
[33738]1660 /*
1661 * Enumerate the directory, check all the requested bits.
1662 */
[33701]1663 int rc = VINF_SUCCESS;
1664 for (;;)
1665 {
[33738]1666 pszDirPath[cchDirPath] = '\0'; /* for error messages. */
1667
[33701]1668 struct dirent Entry;
1669 struct dirent *pEntry;
[63677]1670#if RT_GNUC_PREREQ(4, 6)
1671# pragma GCC diagnostic push
1672# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1673#endif
[33701]1674 int iErr = readdir_r(pDir, &Entry, &pEntry);
[63677]1675#if RT_GNUC_PREREQ(4, 6)
1676# pragma GCC diagnostic pop
1677#endif
[33701]1678 if (iErr)
1679 {
[35188]1680 rc = supR3HardenedSetErrorN(VERR_SUPLIB_DIR_ENUM_FAILED, pErrInfo,
[33738]1681 5, "readdir_r failed with ", strerror(iErr), " in '", pszDirPath, "'");
[33701]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 */
[49211]1691 size_t cchName = suplibHardenedStrLen(pEntry->d_name);
[33738]1692 if (cchName + cchDirPath > SUPR3HARDENED_MAX_PATH)
[33701]1693 {
[35188]1694 rc = supR3HardenedSetErrorN(VERR_SUPLIB_PATH_TOO_LONG, pErrInfo,
[33738]1695 4, "Path grew too long during recursion: '", pszDirPath, pEntry->d_name, "'");
[33701]1696 break;
1697 }
[93049]1698 suplibHardenedMemCopy(&pszDirPath[cchDirPath], pEntry->d_name, cchName + 1);
[33701]1699
1700 /*
[33738]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.)
[33701]1704 */
[35188]1705 rc = supR3HardenedQueryFsObjectByPath(pszDirPath, pFsObjState, pErrInfo);
[33738]1706 if (RT_SUCCESS(rc))
[33701]1707 break;
[33738]1708 rc = supR3HardenedVerifyFsObject(pFsObjState, S_ISDIR(pFsObjState->Stat.st_mode), false /*fRelaxed*/,
[66526]1709 false /*fSymlinksAllowed*/, pszDirPath, pErrInfo);
[33738]1710 if (RT_FAILURE(rc))
1711 break;
[33701]1712
1713 /*
[33738]1714 * Recurse into subdirectories if requested.
[33701]1715 */
1716 if ( fRecursive
[33738]1717 && S_ISDIR(pFsObjState->Stat.st_mode)
[49211]1718 && suplibHardenedStrCmp(pEntry->d_name, ".")
1719 && suplibHardenedStrCmp(pEntry->d_name, ".."))
[33701]1720 {
1721 pszDirPath[cchDirPath + cchName] = RTPATH_SLASH;
1722 pszDirPath[cchDirPath + cchName + 1] = '\0';
1723
[33738]1724 rc = supR3HardenedVerifyDirRecursive(pszDirPath, cchDirPath + cchName + 1, pFsObjState,
[35188]1725 fRecursive, pErrInfo);
[33701]1726 if (RT_FAILURE(rc))
1727 break;
1728 }
1729 }
1730
1731 closedir(pDir);
[93033]1732 return rc;
[33701]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.
[35188]1744 * @param pErrInfo See SUPR3HardenedVerifyDir.
[33701]1745 */
[35188]1746DECLHIDDEN(int) supR3HardenedVerifyDir(const char *pszDirPath, bool fRecursive, bool fCheckFiles, PRTERRINFO pErrInfo)
[33701]1747{
1748 /*
[33738]1749 * Validate the input path and parse it.
[33701]1750 */
[33738]1751 SUPR3HARDENEDPATHINFO Info;
[35188]1752 int rc = supR3HardenedVerifyPathSanity(pszDirPath, pErrInfo, &Info);
[33701]1753 if (RT_FAILURE(rc))
1754 return rc;
1755
1756 /*
[33738]1757 * Verify each component from the root up.
[33701]1758 */
[33738]1759 SUPR3HARDENEDFSOBJSTATE FsObjState;
1760 uint32_t const cComponents = Info.cComponents;
1761 for (uint32_t iComponent = 0; iComponent < cComponents; iComponent++)
[33701]1762 {
[33738]1763 bool fRelaxed = iComponent + 2 < cComponents;
1764 Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = '\0';
[35188]1765 rc = supR3HardenedQueryFsObjectByPath(Info.szPath, &FsObjState, pErrInfo);
[33738]1766 if (RT_SUCCESS(rc))
[66526]1767 rc = supR3HardenedVerifyFsObject(&FsObjState, true /*fDir*/, fRelaxed,
1768 false /*fSymlinksAllowed*/, Info.szPath, pErrInfo);
[33701]1769 if (RT_FAILURE(rc))
1770 return rc;
[34280]1771 Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = iComponent + 1 != cComponents ? RTPATH_SLASH : '\0';
[33701]1772 }
1773
1774 /*
[33738]1775 * Check files and subdirectories if requested.
[33701]1776 */
1777 if (fCheckFiles || fRecursive)
1778 {
[33738]1779 Info.szPath[Info.cch] = RTPATH_SLASH;
1780 Info.szPath[Info.cch + 1] = '\0';
1781 return supR3HardenedVerifyDirRecursive(Info.szPath, Info.cch + 1, &FsObjState,
[35188]1782 fRecursive, pErrInfo);
[33701]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.
[33738]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.
[51770]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.
[35188]1800 * @param pErrInfo Where to return extended error information.
1801 * Optional.
[33701]1802 */
[51770]1803DECLHIDDEN(int) supR3HardenedVerifyFile(const char *pszFilename, RTHCUINTPTR hNativeFile,
1804 bool fMaybe3rdParty, PRTERRINFO pErrInfo)
[33701]1805{
1806 /*
[33738]1807 * Validate the input path and parse it.
[33701]1808 */
[33738]1809 SUPR3HARDENEDPATHINFO Info;
[35188]1810 int rc = supR3HardenedVerifyPathSanity(pszFilename, pErrInfo, &Info);
[33701]1811 if (RT_FAILURE(rc))
1812 return rc;
[33738]1813 if (Info.fDirSlash)
[35188]1814 return supR3HardenedSetError3(VERR_SUPLIB_IS_DIRECTORY, pErrInfo,
[33738]1815 "The file path specifies a directory: '", pszFilename, "'");
[33701]1816
1817 /*
[33738]1818 * Verify each component from the root up.
[33701]1819 */
[33738]1820 SUPR3HARDENEDFSOBJSTATE FsObjState;
1821 uint32_t const cComponents = Info.cComponents;
1822 for (uint32_t iComponent = 0; iComponent < cComponents; iComponent++)
[33701]1823 {
[33738]1824 bool fFinal = iComponent + 1 == cComponents;
1825 bool fRelaxed = iComponent + 2 < cComponents;
1826 Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = '\0';
[35188]1827 rc = supR3HardenedQueryFsObjectByPath(Info.szPath, &FsObjState, pErrInfo);
[33738]1828 if (RT_SUCCESS(rc))
[66526]1829 rc = supR3HardenedVerifyFsObject(&FsObjState, !fFinal /*fDir*/, fRelaxed,
1830 false /*fSymlinksAllowed*/, Info.szPath, pErrInfo);
[33701]1831 if (RT_FAILURE(rc))
1832 return rc;
[34280]1833 Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = !fFinal ? RTPATH_SLASH : '\0';
[33701]1834 }
1835
[33738]1836 /*
[51770]1837 * Verify the file handle against the last component, if specified.
[33738]1838 */
1839 if (hNativeFile != RTHCUINTPTR_MAX)
[51770]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 */
[51912]1850 rc = VINF_SUCCESS;
[51770]1851 HANDLE hVerify;
[51912]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
[62675]1862 {
[51912]1863 rc = RTErrInfoSetF(pErrInfo, rc, "Error converting '%s' to UTF-16: %Rrc", pszFilename, rc);
[62675]1864 hVerify = INVALID_HANDLE_VALUE;
1865 }
[51912]1866 }
[52163]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 }
[51770]1874 if (hVerify != INVALID_HANDLE_VALUE)
1875 {
[51772]1876# ifdef VBOX_WITH_HARDENING
[51770]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;
[51772]1888# ifndef IN_SUP_R3_STATIC /* Not in VBoxCpuReport and friends. */
[51770]1889 rc = supHardenedWinVerifyImageByHandleNoName(hVerify, fFlags, pErrInfo);
[51772]1890# endif
[62675]1891# else
1892 RT_NOREF1(fMaybe3rdParty);
[51770]1893# endif
[52940]1894 NtClose(hVerify);
[51770]1895 }
[51912]1896 else if (RT_SUCCESS(rc))
[52940]1897 rc = RTErrInfoSetF(pErrInfo, RTErrConvertFromWin32(RtlGetLastWin32Error()),
1898 "Error %u trying to open (or duplicate handle for) '%s'", RtlGetLastWin32Error(), pszFilename);
[51770]1899 if (RT_FAILURE(rc))
1900 return rc;
[62675]1901#else
1902 RT_NOREF1(fMaybe3rdParty);
[51770]1903#endif
1904
[33701]1905 return VINF_SUCCESS;
1906}
1907
1908
[66632]1909#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX)
[33701]1910/**
[66526]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 */
[66842]1948#ifndef SUP_HARDENED_VERIFY_FOLLOW_SYMLINKS_USE_REALPATH
[66526]1949 uint32_t iLoops = 0;
[66842]1950#endif
[66526]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 {
[66842]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
[66526]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, "'");
[66842]2064#endif
[66526]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}
[66632]2085#endif /* RT_OS_DARWIN || RT_OS_LINUX */
[66526]2086
2087
2088/**
[11725]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
[49211]2132 || suplibHardenedStrCmp(g_aSupInstallFiles[iFile].pszFile, paInstallFiles[iFile].pszFile))
[11725]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 */
[59747]2139 if ( !ASMMemIsZero(&g_aSupVerifiedFiles[0], sizeof(g_aSupVerifiedFiles))
2140 || !ASMMemIsZero(&g_aSupVerifiedDirs[0], sizeof(g_aSupVerifiedDirs)))
[11725]2141 return VERR_WRONG_ORDER;
2142
2143 /*
2144 * Copy the verification data over.
2145 */
[49211]2146 suplibHardenedMemCopy(&g_aSupVerifiedFiles[0], pPreInitData->paVerifiedFiles, sizeof(g_aSupVerifiedFiles));
2147 suplibHardenedMemCopy(&g_aSupVerifiedDirs[0], pPreInitData->paVerifiedDirs, sizeof(g_aSupVerifiedDirs));
[11725]2148 return VINF_SUCCESS;
2149}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use