VirtualBox

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

Last change on this file since 54371 was 52940, checked in by vboxsync, 10 years ago

Eliminating some more kernel32.dll dependencies, marking APIs we like to use early as OK.

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

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