VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp@ 52941

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

Use our own heap in the hope that we can use it before ntdll is really initialized. Fixed RtlExitProcess issue. More early import work.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 56.6 KB
Line 
1/* $Id: SUPR3HardenedMain.cpp 52941 2014-10-03 19:58:01Z vboxsync $ */
2/** @file
3 * VirtualBox Support Library - Hardened main().
4 */
5
6/*
7 * Copyright (C) 2006-2014 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 <dlfcn.h>
37# include <unistd.h>
38
39#elif RT_OS_WINDOWS
40# include <iprt/nt/nt-and-windows.h>
41
42#else /* UNIXes */
43# include <iprt/types.h> /* stdint fun on darwin. */
44
45# include <stdio.h>
46# include <stdlib.h>
47# include <dlfcn.h>
48# include <limits.h>
49# include <errno.h>
50# include <unistd.h>
51# include <sys/stat.h>
52# include <sys/time.h>
53# include <sys/types.h>
54# if defined(RT_OS_LINUX)
55# undef USE_LIB_PCAP /* don't depend on libcap as we had to depend on either
56 libcap1 or libcap2 */
57
58# undef _POSIX_SOURCE
59# include <linux/types.h> /* sys/capabilities from uek-headers require this */
60# include <sys/capability.h>
61# include <sys/prctl.h>
62# ifndef CAP_TO_MASK
63# define CAP_TO_MASK(cap) RT_BIT(cap)
64# endif
65# elif defined(RT_OS_FREEBSD)
66# include <sys/param.h>
67# include <sys/sysctl.h>
68# elif defined(RT_OS_SOLARIS)
69# include <priv.h>
70# endif
71# include <pwd.h>
72# ifdef RT_OS_DARWIN
73# include <mach-o/dyld.h>
74# endif
75
76#endif
77
78#include <VBox/sup.h>
79#include <VBox/err.h>
80#ifdef RT_OS_WINDOWS
81# include <VBox/version.h>
82#endif
83#include <iprt/ctype.h>
84#include <iprt/string.h>
85#include <iprt/initterm.h>
86#include <iprt/param.h>
87
88#include "SUPLibInternal.h"
89
90
91/*******************************************************************************
92* Defined Constants And Macros *
93*******************************************************************************/
94/** @def SUP_HARDENED_SUID
95 * Whether we're employing set-user-ID-on-execute in the hardening.
96 */
97#if !defined(RT_OS_OS2) && !defined(RT_OS_WINDOWS) && !defined(RT_OS_L4)
98# define SUP_HARDENED_SUID
99#else
100# undef SUP_HARDENED_SUID
101#endif
102
103/** @def SUP_HARDENED_SYM
104 * Decorate a symbol that's resolved dynamically.
105 */
106#ifdef RT_OS_OS2
107# define SUP_HARDENED_SYM(sym) "_" sym
108#else
109# define SUP_HARDENED_SYM(sym) sym
110#endif
111
112
113/*******************************************************************************
114* Structures and Typedefs *
115*******************************************************************************/
116/** @see RTR3InitEx */
117typedef DECLCALLBACK(int) FNRTR3INITEX(uint32_t iVersion, uint32_t fFlags, int cArgs,
118 char **papszArgs, const char *pszProgramPath);
119typedef FNRTR3INITEX *PFNRTR3INITEX;
120
121
122/*******************************************************************************
123* Global Variables *
124*******************************************************************************/
125/** The pre-init data we pass on to SUPR3 (residing in VBoxRT). */
126static SUPPREINITDATA g_SupPreInitData;
127/** The program executable path. */
128#ifndef RT_OS_WINDOWS
129static
130#endif
131char g_szSupLibHardenedExePath[RTPATH_MAX];
132/** The program directory path. */
133static char g_szSupLibHardenedDirPath[RTPATH_MAX];
134
135/** The program name. */
136static const char *g_pszSupLibHardenedProgName;
137/** The flags passed to SUPR3HardenedMain. */
138static uint32_t g_fSupHardenedMain;
139
140#ifdef SUP_HARDENED_SUID
141/** The real UID at startup. */
142static uid_t g_uid;
143/** The real GID at startup. */
144static gid_t g_gid;
145# ifdef RT_OS_LINUX
146static uint32_t g_uCaps;
147# endif
148#endif
149
150/** The startup log file. */
151#ifdef RT_OS_WINDOWS
152static HANDLE g_hStartupLog = NULL;
153#else
154static int g_hStartupLog = -1;
155#endif
156/** The number of bytes we've written to the startup log. */
157static uint32_t volatile g_cbStartupLog = 0;
158
159/** The current SUPR3HardenedMain state / location. */
160SUPR3HARDENEDMAINSTATE g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_NOT_YET_CALLED;
161
162
163/*******************************************************************************
164* Internal Functions *
165*******************************************************************************/
166#ifdef SUP_HARDENED_SUID
167static void supR3HardenedMainDropPrivileges(void);
168#endif
169static PFNSUPTRUSTEDERROR supR3HardenedMainGetTrustedError(const char *pszProgName);
170
171
172/**
173 * Safely copy one or more strings into the given buffer.
174 *
175 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
176 * @param pszDst The destionation buffer.
177 * @param cbDst The size of the destination buffer.
178 * @param ... One or more zero terminated strings, ending with
179 * a NULL.
180 */
181static int suplibHardenedStrCopyEx(char *pszDst, size_t cbDst, ...)
182{
183 int rc = VINF_SUCCESS;
184
185 if (cbDst == 0)
186 return VERR_BUFFER_OVERFLOW;
187
188 va_list va;
189 va_start(va, cbDst);
190 for (;;)
191 {
192 const char *pszSrc = va_arg(va, const char *);
193 if (!pszSrc)
194 break;
195
196 size_t cchSrc = suplibHardenedStrLen(pszSrc);
197 if (cchSrc < cbDst)
198 {
199 suplibHardenedMemCopy(pszDst, pszSrc, cchSrc);
200 pszDst += cchSrc;
201 cbDst -= cchSrc;
202 }
203 else
204 {
205 rc = VERR_BUFFER_OVERFLOW;
206 if (cbDst > 1)
207 {
208 suplibHardenedMemCopy(pszDst, pszSrc, cbDst - 1);
209 pszDst += cbDst - 1;
210 cbDst = 1;
211 }
212 }
213 *pszDst = '\0';
214 }
215 va_end(va);
216
217 return rc;
218}
219
220
221/**
222 * Exit current process in the quickest possible fashion.
223 *
224 * @param rcExit The exit code.
225 */
226DECLNORETURN(void) suplibHardenedExit(RTEXITCODE rcExit)
227{
228 for (;;)
229 {
230#ifdef RT_OS_WINDOWS
231 if (g_enmSupR3HardenedMainState >= SUPR3HARDENEDMAINSTATE_WIN_IMPORTS_RESOLVED)
232 ExitProcess(rcExit);
233 if (RtlExitUserProcess != NULL)
234 RtlExitUserProcess(rcExit);
235 NtTerminateProcess(NtCurrentProcess(), rcExit);
236#else
237 _Exit(rcExit);
238#endif
239 }
240}
241
242
243/**
244 * Writes a substring to standard error.
245 *
246 * @param pch The start of the substring.
247 * @param cch The length of the substring.
248 */
249static void suplibHardenedPrintStrN(const char *pch, size_t cch)
250{
251#ifdef RT_OS_WINDOWS
252 HANDLE hStdOut = NtCurrentPeb()->ProcessParameters->StandardOutput;
253 if (hStdOut != NULL)
254 {
255# if 0 /* Windows 7 and earlier uses fake handles, with the last two bits set ((hStdOut & 3) == 3). */
256 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
257 NtWriteFile(hStdOut, NULL /*Event*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/,
258 &Ios, (PVOID)pch, (ULONG)cch, NULL /*ByteOffset*/, NULL /*Key*/);
259# else
260 DWORD cbWritten;
261 WriteFile(hStdOut, pch, (DWORD)cch, &cbWritten, NULL);
262# endif
263 }
264#else
265 (void)write(2, pch, cch);
266#endif
267}
268
269
270/**
271 * Writes a string to standard error.
272 *
273 * @param psz The string.
274 */
275static void suplibHardenedPrintStr(const char *psz)
276{
277 suplibHardenedPrintStrN(psz, suplibHardenedStrLen(psz));
278}
279
280
281/**
282 * Writes a char to standard error.
283 *
284 * @param ch The character value to write.
285 */
286static void suplibHardenedPrintChr(char ch)
287{
288 suplibHardenedPrintStrN(&ch, 1);
289}
290
291
292/**
293 * Writes a decimal number to stdard error.
294 *
295 * @param uValue The value.
296 */
297static void suplibHardenedPrintDecimal(uint64_t uValue)
298{
299 char szBuf[64];
300 char *pszEnd = &szBuf[sizeof(szBuf) - 1];
301 char *psz = pszEnd;
302
303 *psz-- = '\0';
304
305 do
306 {
307 *psz-- = '0' + (uValue % 10);
308 uValue /= 10;
309 } while (uValue > 0);
310
311 psz++;
312 suplibHardenedPrintStrN(psz, pszEnd - psz);
313}
314
315
316/**
317 * Writes a hexadecimal or octal number to standard error.
318 *
319 * @param uValue The value.
320 * @param uBase The base (16 or 8).
321 * @param fFlags Format flags.
322 */
323static void suplibHardenedPrintHexOctal(uint64_t uValue, unsigned uBase, uint32_t fFlags)
324{
325 static char const s_achDigitsLower[17] = "0123456789abcdef";
326 static char const s_achDigitsUpper[17] = "0123456789ABCDEF";
327 const char *pchDigits = !(fFlags & RTSTR_F_CAPITAL) ? s_achDigitsLower : s_achDigitsUpper;
328 unsigned cShift = uBase == 16 ? 4 : 3;
329 unsigned fDigitMask = uBase == 16 ? 0xf : 7;
330 char szBuf[64];
331 char *pszEnd = &szBuf[sizeof(szBuf) - 1];
332 char *psz = pszEnd;
333
334 *psz-- = '\0';
335
336 do
337 {
338 *psz-- = pchDigits[uValue & fDigitMask];
339 uValue >>= cShift;
340 } while (uValue > 0);
341
342 if ((fFlags & RTSTR_F_SPECIAL) && uBase == 16)
343 {
344 *psz-- = !(fFlags & RTSTR_F_CAPITAL) ? 'x' : 'X';
345 *psz-- = '0';
346 }
347
348 psz++;
349 suplibHardenedPrintStrN(psz, pszEnd - psz);
350}
351
352
353/**
354 * Writes a wide character string to standard error.
355 *
356 * @param pwsz The string.
357 */
358static void suplibHardenedPrintWideStr(PCRTUTF16 pwsz)
359{
360 for (;;)
361 {
362 RTUTF16 wc = *pwsz++;
363 if (!wc)
364 return;
365 if ( (wc < 0x7f && wc >= 0x20)
366 || wc == '\n'
367 || wc == '\r')
368 suplibHardenedPrintChr((char)wc);
369 else
370 {
371 suplibHardenedPrintStrN(RT_STR_TUPLE("\\x"));
372 suplibHardenedPrintHexOctal(wc, 16, 0);
373 }
374 }
375}
376
377#ifdef IPRT_NO_CRT
378
379/** Buffer structure used by suplibHardenedOutput. */
380struct SUPLIBHARDENEDOUTPUTBUF
381{
382 size_t off;
383 char szBuf[2048];
384};
385
386/** Callback for RTStrFormatV, see FNRTSTROUTPUT. */
387static DECLCALLBACK(size_t) suplibHardenedOutput(void *pvArg, const char *pachChars, size_t cbChars)
388{
389 SUPLIBHARDENEDOUTPUTBUF *pBuf = (SUPLIBHARDENEDOUTPUTBUF *)pvArg;
390 size_t cbTodo = cbChars;
391 for (;;)
392 {
393 size_t cbSpace = sizeof(pBuf->szBuf) - pBuf->off - 1;
394
395 /* Flush the buffer? */
396 if ( cbSpace == 0
397 || (cbTodo == 0 && pBuf->off))
398 {
399 suplibHardenedPrintStrN(pBuf->szBuf, pBuf->off);
400# ifdef RT_OS_WINDOWS
401 OutputDebugString(pBuf->szBuf);
402# endif
403 pBuf->off = 0;
404 cbSpace = sizeof(pBuf->szBuf) - 1;
405 }
406
407 /* Copy the string into the buffer. */
408 if (cbTodo == 1)
409 {
410 pBuf->szBuf[pBuf->off++] = *pachChars;
411 break;
412 }
413 if (cbSpace >= cbTodo)
414 {
415 memcpy(&pBuf->szBuf[pBuf->off], pachChars, cbTodo);
416 pBuf->off += cbTodo;
417 break;
418 }
419 memcpy(&pBuf->szBuf[pBuf->off], pachChars, cbSpace);
420 pBuf->off += cbSpace;
421 cbTodo -= cbSpace;
422 }
423 pBuf->szBuf[pBuf->off] = '\0';
424
425 return cbChars;
426}
427
428#endif /* IPRT_NO_CRT */
429
430/**
431 * Simple printf to standard error.
432 *
433 * @param pszFormat The format string.
434 * @param va Arguments to format.
435 */
436DECLHIDDEN(void) suplibHardenedPrintFV(const char *pszFormat, va_list va)
437{
438#ifdef IPRT_NO_CRT
439 /*
440 * Use buffered output here to avoid character mixing on the windows
441 * console and to enable us to use OutputDebugString.
442 */
443 SUPLIBHARDENEDOUTPUTBUF Buf;
444 Buf.off = 0;
445 Buf.szBuf[0] = '\0';
446 RTStrFormatV(suplibHardenedOutput, &Buf, NULL, NULL, pszFormat, va);
447
448#else /* !IPRT_NO_CRT */
449 /*
450 * Format loop.
451 */
452 char ch;
453 const char *pszLast = pszFormat;
454 for (;;)
455 {
456 ch = *pszFormat;
457 if (!ch)
458 break;
459 pszFormat++;
460
461 if (ch == '%')
462 {
463 /*
464 * Format argument.
465 */
466
467 /* Flush unwritten bits. */
468 if (pszLast != pszFormat - 1)
469 suplibHardenedPrintStrN(pszLast, pszFormat - pszLast - 1);
470 pszLast = pszFormat;
471 ch = *pszFormat++;
472
473 /* flags. */
474 uint32_t fFlags = 0;
475 for (;;)
476 {
477 if (ch == '#') fFlags |= RTSTR_F_SPECIAL;
478 else if (ch == '-') fFlags |= RTSTR_F_LEFT;
479 else if (ch == '+') fFlags |= RTSTR_F_PLUS;
480 else if (ch == ' ') fFlags |= RTSTR_F_BLANK;
481 else if (ch == '0') fFlags |= RTSTR_F_ZEROPAD;
482 else if (ch == '\'') fFlags |= RTSTR_F_THOUSAND_SEP;
483 else break;
484 ch = *pszFormat++;
485 }
486
487 /* Width and precision - ignored. */
488 while (RT_C_IS_DIGIT(ch))
489 ch = *pszFormat++;
490 if (ch == '*')
491 va_arg(va, int);
492 if (ch == '.')
493 {
494 do ch = *pszFormat++;
495 while (RT_C_IS_DIGIT(ch));
496 if (ch == '*')
497 va_arg(va, int);
498 }
499
500 /* Size. */
501 char chArgSize = 0;
502 switch (ch)
503 {
504 case 'z':
505 case 'L':
506 case 'j':
507 case 't':
508 chArgSize = ch;
509 ch = *pszFormat++;
510 break;
511
512 case 'l':
513 chArgSize = ch;
514 ch = *pszFormat++;
515 if (ch == 'l')
516 {
517 chArgSize = 'L';
518 ch = *pszFormat++;
519 }
520 break;
521
522 case 'h':
523 chArgSize = ch;
524 ch = *pszFormat++;
525 if (ch == 'h')
526 {
527 chArgSize = 'H';
528 ch = *pszFormat++;
529 }
530 break;
531 }
532
533 /*
534 * Do type specific formatting.
535 */
536 switch (ch)
537 {
538 case 'c':
539 ch = (char)va_arg(va, int);
540 suplibHardenedPrintChr(ch);
541 break;
542
543 case 's':
544 if (chArgSize == 'l')
545 {
546 PCRTUTF16 pwszStr = va_arg(va, PCRTUTF16 );
547 if (RT_VALID_PTR(pwszStr))
548 suplibHardenedPrintWideStr(pwszStr);
549 else
550 suplibHardenedPrintStr("<NULL>");
551 }
552 else
553 {
554 const char *pszStr = va_arg(va, const char *);
555 if (!RT_VALID_PTR(pszStr))
556 pszStr = "<NULL>";
557 suplibHardenedPrintStr(pszStr);
558 }
559 break;
560
561 case 'd':
562 case 'i':
563 {
564 int64_t iValue;
565 if (chArgSize == 'L' || chArgSize == 'j')
566 iValue = va_arg(va, int64_t);
567 else if (chArgSize == 'l')
568 iValue = va_arg(va, signed long);
569 else if (chArgSize == 'z' || chArgSize == 't')
570 iValue = va_arg(va, intptr_t);
571 else
572 iValue = va_arg(va, signed int);
573 if (iValue < 0)
574 {
575 suplibHardenedPrintChr('-');
576 iValue = -iValue;
577 }
578 suplibHardenedPrintDecimal(iValue);
579 break;
580 }
581
582 case 'p':
583 case 'x':
584 case 'X':
585 case 'u':
586 case 'o':
587 {
588 unsigned uBase = 10;
589 uint64_t uValue;
590
591 switch (ch)
592 {
593 case 'p':
594 fFlags |= RTSTR_F_ZEROPAD; /* Note not standard behaviour (but I like it this way!) */
595 uBase = 16;
596 break;
597 case 'X':
598 fFlags |= RTSTR_F_CAPITAL;
599 case 'x':
600 uBase = 16;
601 break;
602 case 'u':
603 uBase = 10;
604 break;
605 case 'o':
606 uBase = 8;
607 break;
608 }
609
610 if (ch == 'p' || chArgSize == 'z' || chArgSize == 't')
611 uValue = va_arg(va, uintptr_t);
612 else if (chArgSize == 'L' || chArgSize == 'j')
613 uValue = va_arg(va, uint64_t);
614 else if (chArgSize == 'l')
615 uValue = va_arg(va, unsigned long);
616 else
617 uValue = va_arg(va, unsigned int);
618
619 if (uBase == 10)
620 suplibHardenedPrintDecimal(uValue);
621 else
622 suplibHardenedPrintHexOctal(uValue, uBase, fFlags);
623 break;
624 }
625
626 case 'R':
627 if (pszFormat[0] == 'r' && pszFormat[1] == 'c')
628 {
629 int iValue = va_arg(va, int);
630 if (iValue < 0)
631 {
632 suplibHardenedPrintChr('-');
633 iValue = -iValue;
634 }
635 suplibHardenedPrintDecimal(iValue);
636 pszFormat += 2;
637 break;
638 }
639 /* fall thru */
640
641 /*
642 * Custom format.
643 */
644 default:
645 suplibHardenedPrintStr("[bad format: ");
646 suplibHardenedPrintStrN(pszLast, pszFormat - pszLast);
647 suplibHardenedPrintChr(']');
648 break;
649 }
650
651 /* continue */
652 pszLast = pszFormat;
653 }
654 }
655
656 /* Flush the last bits of the string. */
657 if (pszLast != pszFormat)
658 suplibHardenedPrintStrN(pszLast, pszFormat - pszLast);
659#endif /* !IPRT_NO_CRT */
660}
661
662
663/**
664 * Prints to standard error.
665 *
666 * @param pszFormat The format string.
667 * @param ... Arguments to format.
668 */
669DECLHIDDEN(void) suplibHardenedPrintF(const char *pszFormat, ...)
670{
671 va_list va;
672 va_start(va, pszFormat);
673 suplibHardenedPrintFV(pszFormat, va);
674 va_end(va);
675}
676
677
678/**
679 * @copydoc RTPathStripFilename.
680 */
681static void suplibHardenedPathStripFilename(char *pszPath)
682{
683 char *psz = pszPath;
684 char *pszLastSep = pszPath;
685
686 for (;; psz++)
687 {
688 switch (*psz)
689 {
690 /* handle separators. */
691#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
692 case ':':
693 pszLastSep = psz + 1;
694 break;
695
696 case '\\':
697#endif
698 case '/':
699 pszLastSep = psz;
700 break;
701
702 /* the end */
703 case '\0':
704 if (pszLastSep == pszPath)
705 *pszLastSep++ = '.';
706 *pszLastSep = '\0';
707 return;
708 }
709 }
710 /* will never get here */
711}
712
713
714/**
715 * @copydoc RTPathFilename
716 */
717DECLHIDDEN(char *) supR3HardenedPathFilename(const char *pszPath)
718{
719 const char *psz = pszPath;
720 const char *pszLastComp = pszPath;
721
722 for (;; psz++)
723 {
724 switch (*psz)
725 {
726 /* handle separators. */
727#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
728 case ':':
729 pszLastComp = psz + 1;
730 break;
731
732 case '\\':
733#endif
734 case '/':
735 pszLastComp = psz + 1;
736 break;
737
738 /* the end */
739 case '\0':
740 if (*pszLastComp)
741 return (char *)(void *)pszLastComp;
742 return NULL;
743 }
744 }
745
746 /* will never get here */
747 return NULL;
748}
749
750
751/**
752 * @copydoc RTPathAppPrivateNoArch
753 */
754DECLHIDDEN(int) supR3HardenedPathAppPrivateNoArch(char *pszPath, size_t cchPath)
755{
756#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE)
757 const char *pszSrcPath = RTPATH_APP_PRIVATE;
758 size_t cchPathPrivateNoArch = suplibHardenedStrLen(pszSrcPath);
759 if (cchPathPrivateNoArch >= cchPath)
760 supR3HardenedFatal("supR3HardenedPathAppPrivateNoArch: Buffer overflow, %zu >= %zu\n", cchPathPrivateNoArch, cchPath);
761 suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathPrivateNoArch + 1);
762 return VINF_SUCCESS;
763
764#else
765 return supR3HardenedPathExecDir(pszPath, cchPath);
766#endif
767}
768
769
770/**
771 * @copydoc RTPathAppPrivateArch
772 */
773DECLHIDDEN(int) supR3HardenedPathAppPrivateArch(char *pszPath, size_t cchPath)
774{
775#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE_ARCH)
776 const char *pszSrcPath = RTPATH_APP_PRIVATE_ARCH;
777 size_t cchPathPrivateArch = suplibHardenedStrLen(pszSrcPath);
778 if (cchPathPrivateArch >= cchPath)
779 supR3HardenedFatal("supR3HardenedPathAppPrivateArch: Buffer overflow, %zu >= %zu\n", cchPathPrivateArch, cchPath);
780 suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathPrivateArch + 1);
781 return VINF_SUCCESS;
782
783#else
784 return supR3HardenedPathExecDir(pszPath, cchPath);
785#endif
786}
787
788
789/**
790 * @copydoc RTPathSharedLibs
791 */
792DECLHIDDEN(int) supR3HardenedPathSharedLibs(char *pszPath, size_t cchPath)
793{
794#if !defined(RT_OS_WINDOWS) && defined(RTPATH_SHARED_LIBS)
795 const char *pszSrcPath = RTPATH_SHARED_LIBS;
796 size_t cchPathSharedLibs = suplibHardenedStrLen(pszSrcPath);
797 if (cchPathSharedLibs >= cchPath)
798 supR3HardenedFatal("supR3HardenedPathSharedLibs: Buffer overflow, %zu >= %zu\n", cchPathSharedLibs, cchPath);
799 suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathSharedLibs + 1);
800 return VINF_SUCCESS;
801
802#else
803 return supR3HardenedPathExecDir(pszPath, cchPath);
804#endif
805}
806
807
808/**
809 * @copydoc RTPathAppDocs
810 */
811DECLHIDDEN(int) supR3HardenedPathAppDocs(char *pszPath, size_t cchPath)
812{
813#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_DOCS)
814 const char *pszSrcPath = RTPATH_APP_DOCS;
815 size_t cchPathAppDocs = suplibHardenedStrLen(pszSrcPath);
816 if (cchPathAppDocs >= cchPath)
817 supR3HardenedFatal("supR3HardenedPathAppDocs: Buffer overflow, %zu >= %zu\n", cchPathAppDocs, cchPath);
818 suplibHardenedMemCopy(pszPath, pszSrcPath, cchPathAppDocs + 1);
819 return VINF_SUCCESS;
820
821#else
822 return supR3HardenedPathExecDir(pszPath, cchPath);
823#endif
824}
825
826
827/**
828 * Returns the full path to the executable.
829 *
830 * @returns IPRT status code.
831 * @param pszPath Where to store it.
832 * @param cchPath How big that buffer is.
833 */
834static void supR3HardenedGetFullExePath(void)
835{
836 /*
837 * Get the program filename.
838 *
839 * Most UNIXes have no API for obtaining the executable path, but provides a symbolic
840 * link in the proc file system that tells who was exec'ed. The bad thing about this
841 * is that we have to use readlink, one of the weirder UNIX APIs.
842 *
843 * Darwin, OS/2 and Windows all have proper APIs for getting the program file name.
844 */
845#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_SOLARIS)
846# ifdef RT_OS_LINUX
847 int cchLink = readlink("/proc/self/exe", &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath) - 1);
848
849# elif defined(RT_OS_SOLARIS)
850 char szFileBuf[PATH_MAX + 1];
851 sprintf(szFileBuf, "/proc/%ld/path/a.out", (long)getpid());
852 int cchLink = readlink(szFileBuf, &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath) - 1);
853
854# else /* RT_OS_FREEBSD */
855 int aiName[4];
856 aiName[0] = CTL_KERN;
857 aiName[1] = KERN_PROC;
858 aiName[2] = KERN_PROC_PATHNAME;
859 aiName[3] = getpid();
860
861 size_t cbPath = sizeof(g_szSupLibHardenedExePath);
862 if (sysctl(aiName, RT_ELEMENTS(aiName), g_szSupLibHardenedExePath, &cbPath, NULL, 0) < 0)
863 supR3HardenedFatal("supR3HardenedExecDir: sysctl failed\n");
864 g_szSupLibHardenedExePath[sizeof(g_szSupLibHardenedExePath) - 1] = '\0';
865 int cchLink = suplibHardenedStrLen(g_szSupLibHardenedExePath); /* paranoid? can't we use cbPath? */
866
867# endif
868 if (cchLink < 0 || cchLink == sizeof(g_szSupLibHardenedExePath) - 1)
869 supR3HardenedFatal("supR3HardenedExecDir: couldn't read \"%s\", errno=%d cchLink=%d\n",
870 g_szSupLibHardenedExePath, errno, cchLink);
871 g_szSupLibHardenedExePath[cchLink] = '\0';
872
873#elif defined(RT_OS_OS2) || defined(RT_OS_L4)
874 _execname(g_szSupLibHardenedExePath, sizeof(g_szSupLibHardenedExePath));
875
876#elif defined(RT_OS_DARWIN)
877 const char *pszImageName = _dyld_get_image_name(0);
878 if (!pszImageName)
879 supR3HardenedFatal("supR3HardenedExecDir: _dyld_get_image_name(0) failed\n");
880 size_t cchImageName = suplibHardenedStrLen(pszImageName);
881 if (!cchImageName || cchImageName >= sizeof(g_szSupLibHardenedExePath))
882 supR3HardenedFatal("supR3HardenedExecDir: _dyld_get_image_name(0) failed, cchImageName=%d\n", cchImageName);
883 suplibHardenedMemCopy(g_szSupLibHardenedExePath, pszImageName, cchImageName + 1);
884
885#elif defined(RT_OS_WINDOWS)
886 char *pszDst = g_szSupLibHardenedExePath;
887 int rc = RTUtf16ToUtf8Ex(g_wszSupLibHardenedExePath, RTSTR_MAX, &pszDst, sizeof(g_szSupLibHardenedExePath), NULL);
888 if (RT_FAILURE(rc))
889 supR3HardenedFatal("supR3HardenedExecDir: RTUtf16ToUtf8Ex failed, rc=%Rrc\n", rc);
890#else
891# error needs porting.
892#endif
893
894 /*
895 * Strip off the filename part (RTPathStripFilename()).
896 */
897 suplibHardenedStrCopy(g_szSupLibHardenedDirPath, g_szSupLibHardenedExePath);
898 suplibHardenedPathStripFilename(g_szSupLibHardenedDirPath);
899}
900
901
902#ifdef RT_OS_LINUX
903/**
904 * Checks if we can read /proc/self/exe.
905 *
906 * This is used on linux to see if we have to call init
907 * with program path or not.
908 *
909 * @returns true / false.
910 */
911static bool supR3HardenedMainIsProcSelfExeAccssible(void)
912{
913 char szPath[RTPATH_MAX];
914 int cchLink = readlink("/proc/self/exe", szPath, sizeof(szPath));
915 return cchLink != -1;
916}
917#endif /* RT_OS_LINUX */
918
919
920
921/**
922 * @copydoc RTPathExecDir
923 */
924DECLHIDDEN(int) supR3HardenedPathExecDir(char *pszPath, size_t cchPath)
925{
926 /*
927 * Lazy init (probably not required).
928 */
929 if (!g_szSupLibHardenedDirPath[0])
930 supR3HardenedGetFullExePath();
931
932 /*
933 * Calc the length and check if there is space before copying.
934 */
935 size_t cch = suplibHardenedStrLen(g_szSupLibHardenedDirPath) + 1;
936 if (cch <= cchPath)
937 {
938 suplibHardenedMemCopy(pszPath, g_szSupLibHardenedDirPath, cch + 1);
939 return VINF_SUCCESS;
940 }
941
942 supR3HardenedFatal("supR3HardenedPathExecDir: Buffer too small (%u < %u)\n", cchPath, cch);
943 return VERR_BUFFER_OVERFLOW;
944}
945
946
947#ifdef RT_OS_WINDOWS
948extern "C" uint32_t g_uNtVerCombined;
949#endif
950
951DECLHIDDEN(void) supR3HardenedOpenLog(int *pcArgs, char **papszArgs)
952{
953 static const char s_szLogOption[] = "--sup-startup-log=";
954
955 /*
956 * Scan the argument vector.
957 */
958 int cArgs = *pcArgs;
959 for (int iArg = 1; iArg < cArgs; iArg++)
960 if (strncmp(papszArgs[iArg], s_szLogOption, sizeof(s_szLogOption) - 1) == 0)
961 {
962 const char *pszLogFile = &papszArgs[iArg][sizeof(s_szLogOption) - 1];
963
964 /*
965 * Drop the argument from the vector (has trailing NULL entry).
966 */
967 memmove(&papszArgs[iArg], &papszArgs[iArg + 1], (cArgs - iArg) * sizeof(papszArgs[0]));
968 *pcArgs -= 1;
969 cArgs -= 1;
970
971 /*
972 * Open the log file, unless we've already opened one.
973 * First argument takes precedence
974 */
975#ifdef RT_OS_WINDOWS
976 if (g_hStartupLog == NULL)
977 {
978 PRTUTF16 pwszPath;
979 int rc = RTStrToUtf16(pszLogFile, &pwszPath);
980 if (RT_SUCCESS(rc))
981 {
982 g_hStartupLog = CreateFileW(pwszPath,
983 GENERIC_WRITE,
984 FILE_SHARE_READ | FILE_SHARE_WRITE,
985 NULL,
986 OPEN_ALWAYS,
987 FILE_ATTRIBUTE_NORMAL /*| FILE_FLAG_WRITE_THROUGH*/,
988 NULL);
989 RTUtf16Free(pwszPath);
990 }
991 SUP_DPRINTF(("Log file opened: " VBOX_VERSION_STRING "r%u g_hStartupLog=%p g_uNtVerCombined=%#x\n",
992 VBOX_SVN_REV, g_hStartupLog, g_uNtVerCombined));
993 }
994#else
995 //g_hStartupLog = open()
996#endif
997 }
998}
999
1000
1001DECLHIDDEN(void) supR3HardenedLogV(const char *pszFormat, va_list va)
1002{
1003#ifdef RT_OS_WINDOWS
1004 if ( g_hStartupLog != NULL
1005 && g_cbStartupLog < 128*_1M)
1006 {
1007 char szBuf[5120];
1008 PCLIENT_ID pSelfId = &((PTEB)NtCurrentTeb())->ClientId;
1009 size_t cchPrefix = RTStrPrintf(szBuf, sizeof(szBuf), "%x.%x: ", pSelfId->UniqueProcess, pSelfId->UniqueThread);
1010 size_t cch = RTStrPrintfV(&szBuf[cchPrefix], sizeof(szBuf) - cchPrefix, pszFormat, va) + cchPrefix;
1011
1012 if ((size_t)cch >= sizeof(szBuf))
1013 cch = sizeof(szBuf) - 1;
1014
1015 if (!cch || szBuf[cch - 1] != '\n')
1016 szBuf[cch++] = '\n';
1017
1018 ASMAtomicAddU32(&g_cbStartupLog, (uint32_t)cch);
1019
1020 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1021 LARGE_INTEGER Offset;
1022 Offset.QuadPart = -1; /* Write to end of file. */
1023 NtWriteFile(g_hStartupLog, NULL /*Event*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/,
1024 &Ios, szBuf, (ULONG)cch, &Offset, NULL /*Key*/);
1025 }
1026#else
1027 /* later */
1028#endif
1029}
1030
1031
1032DECLHIDDEN(void) supR3HardenedLog(const char *pszFormat, ...)
1033{
1034 va_list va;
1035 va_start(va, pszFormat);
1036 supR3HardenedLogV(pszFormat, va);
1037 va_end(va);
1038}
1039
1040
1041/**
1042 * Prints the message prefix.
1043 */
1044static void suplibHardenedPrintPrefix(void)
1045{
1046 if (g_pszSupLibHardenedProgName)
1047 suplibHardenedPrintStr(g_pszSupLibHardenedProgName);
1048 suplibHardenedPrintStr(": ");
1049}
1050
1051
1052DECLHIDDEN(void) supR3HardenedFatalMsgV(const char *pszWhere, SUPINITOP enmWhat, int rc, const char *pszMsgFmt, va_list va)
1053{
1054 /*
1055 * First to the log.
1056 */
1057 supR3HardenedLog("Error %d in %s! (enmWhat=%d)\n", rc, pszWhere, enmWhat);
1058 va_list vaCopy;
1059 va_copy(vaCopy, va);
1060 supR3HardenedLogV(pszMsgFmt, vaCopy);
1061 va_end(vaCopy);
1062
1063 /*
1064 * Then to the console.
1065 */
1066 suplibHardenedPrintPrefix();
1067 suplibHardenedPrintF("Error %d in %s!\n", rc, pszWhere);
1068
1069 suplibHardenedPrintPrefix();
1070 va_copy(vaCopy, va);
1071 suplibHardenedPrintFV(pszMsgFmt, vaCopy);
1072 va_end(vaCopy);
1073 suplibHardenedPrintChr('\n');
1074
1075 switch (enmWhat)
1076 {
1077 case kSupInitOp_Driver:
1078 suplibHardenedPrintChr('\n');
1079 suplibHardenedPrintPrefix();
1080 suplibHardenedPrintStr("Tip! Make sure the kernel module is loaded. It may also help to reinstall VirtualBox.\n");
1081 break;
1082
1083 case kSupInitOp_Misc:
1084 case kSupInitOp_IPRT:
1085 case kSupInitOp_Integrity:
1086 case kSupInitOp_RootCheck:
1087 suplibHardenedPrintChr('\n');
1088 suplibHardenedPrintPrefix();
1089 suplibHardenedPrintStr("Tip! It may help to reinstall VirtualBox.\n");
1090 break;
1091
1092 default:
1093 /* no hints here */
1094 break;
1095 }
1096
1097#ifdef SUP_HARDENED_SUID
1098 /*
1099 * Drop any root privileges we might be holding, this won't return
1100 * if it fails but end up calling supR3HardenedFatal[V].
1101 */
1102 supR3HardenedMainDropPrivileges();
1103#endif /* SUP_HARDENED_SUID */
1104
1105 /*
1106 * Now try resolve and call the TrustedError entry point if we can
1107 * find it. We'll fork before we attempt this because that way the
1108 * session management in main will see us exiting immediately (if
1109 * it's involved with us).
1110 */
1111#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
1112 int pid = fork();
1113 if (pid <= 0)
1114#endif
1115 {
1116 static volatile bool s_fRecursive = false; /* Loader hooks may cause recursion. */
1117 if (!s_fRecursive)
1118 {
1119 s_fRecursive = true;
1120
1121 PFNSUPTRUSTEDERROR pfnTrustedError = supR3HardenedMainGetTrustedError(g_pszSupLibHardenedProgName);
1122 if (pfnTrustedError)
1123 pfnTrustedError(pszWhere, enmWhat, rc, pszMsgFmt, va);
1124
1125 s_fRecursive = false;
1126 }
1127 }
1128
1129 /*
1130 * Quit
1131 */
1132 suplibHardenedExit(RTEXITCODE_FAILURE);
1133}
1134
1135
1136DECLHIDDEN(void) supR3HardenedFatalMsg(const char *pszWhere, SUPINITOP enmWhat, int rc, const char *pszMsgFmt, ...)
1137{
1138 va_list va;
1139 va_start(va, pszMsgFmt);
1140 supR3HardenedFatalMsgV(pszWhere, enmWhat, rc, pszMsgFmt, va);
1141 va_end(va);
1142}
1143
1144
1145DECLHIDDEN(void) supR3HardenedFatalV(const char *pszFormat, va_list va)
1146{
1147 supR3HardenedLog("Fatal error:\n");
1148 va_list vaCopy;
1149 va_copy(vaCopy, va);
1150 supR3HardenedLogV(pszFormat, vaCopy);
1151 va_end(vaCopy);
1152
1153 suplibHardenedPrintPrefix();
1154 suplibHardenedPrintFV(pszFormat, va);
1155 suplibHardenedExit(RTEXITCODE_FAILURE);
1156}
1157
1158
1159DECLHIDDEN(void) supR3HardenedFatal(const char *pszFormat, ...)
1160{
1161 va_list va;
1162 va_start(va, pszFormat);
1163 supR3HardenedFatalV(pszFormat, va);
1164 va_end(va);
1165}
1166
1167
1168DECLHIDDEN(int) supR3HardenedErrorV(int rc, bool fFatal, const char *pszFormat, va_list va)
1169{
1170 if (fFatal)
1171 supR3HardenedFatalV(pszFormat, va);
1172
1173 supR3HardenedLog("Error (rc=%d):\n", rc);
1174 va_list vaCopy;
1175 va_copy(vaCopy, va);
1176 supR3HardenedLogV(pszFormat, vaCopy);
1177 va_end(vaCopy);
1178
1179 suplibHardenedPrintPrefix();
1180 suplibHardenedPrintFV(pszFormat, va);
1181 return rc;
1182}
1183
1184
1185DECLHIDDEN(int) supR3HardenedError(int rc, bool fFatal, const char *pszFormat, ...)
1186{
1187 va_list va;
1188 va_start(va, pszFormat);
1189 supR3HardenedErrorV(rc, fFatal, pszFormat, va);
1190 va_end(va);
1191 return rc;
1192}
1193
1194
1195
1196/**
1197 * Attempts to open /dev/vboxdrv (or equvivalent).
1198 *
1199 * @remarks This function will not return on failure.
1200 */
1201static void supR3HardenedMainOpenDevice(void)
1202{
1203 int rc = suplibOsInit(&g_SupPreInitData.Data, false /*fPreInit*/, true /*fUnrestricted*/);
1204 if (RT_SUCCESS(rc))
1205 return;
1206
1207 switch (rc)
1208 {
1209 /** @todo better messages! */
1210 case VERR_VM_DRIVER_NOT_INSTALLED:
1211 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
1212 "Kernel driver not installed");
1213 case VERR_VM_DRIVER_NOT_ACCESSIBLE:
1214 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
1215 "Kernel driver not accessible");
1216 case VERR_VM_DRIVER_LOAD_ERROR:
1217 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
1218 "VERR_VM_DRIVER_LOAD_ERROR");
1219 case VERR_VM_DRIVER_OPEN_ERROR:
1220 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
1221 "VERR_VM_DRIVER_OPEN_ERROR");
1222 case VERR_VM_DRIVER_VERSION_MISMATCH:
1223 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
1224 "Kernel driver version mismatch");
1225 case VERR_ACCESS_DENIED:
1226 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
1227 "VERR_ACCESS_DENIED");
1228 case VERR_NO_MEMORY:
1229 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
1230 "Kernel memory allocation/mapping failed");
1231 case VERR_SUPDRV_HARDENING_EVIL_HANDLE:
1232 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_SUPDRV_HARDENING_EVIL_HANDLE");
1233 case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_0:
1234 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_0");
1235 case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_1:
1236 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_1");
1237 case VERR_SUPLIB_NT_PROCESS_UNTRUSTED_2:
1238 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc, "VERR_SUPLIB_NT_PROCESS_UNTRUSTED_2");
1239 default:
1240 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
1241 "Unknown rc=%d", rc);
1242 }
1243}
1244
1245
1246#ifdef SUP_HARDENED_SUID
1247
1248/**
1249 * Grabs extra non-root capabilities / privileges that we might require.
1250 *
1251 * This is currently only used for being able to do ICMP from the NAT engine.
1252 *
1253 * @note We still have root privileges at the time of this call.
1254 */
1255static void supR3HardenedMainGrabCapabilites(void)
1256{
1257# if defined(RT_OS_LINUX)
1258 /*
1259 * We are about to drop all our privileges. Remove all capabilities but
1260 * keep the cap_net_raw capability for ICMP sockets for the NAT stack.
1261 */
1262 if (g_uCaps != 0)
1263 {
1264# ifdef USE_LIB_PCAP
1265 /* XXX cap_net_bind_service */
1266 if (!cap_set_proc(cap_from_text("all-eip cap_net_raw+ep")))
1267 prctl(PR_SET_KEEPCAPS, 1 /*keep=*/, 0, 0, 0);
1268 prctl(PR_SET_DUMPABLE, 1 /*dump*/, 0, 0, 0);
1269# else
1270 cap_user_header_t hdr = (cap_user_header_t)alloca(sizeof(*hdr));
1271 cap_user_data_t cap = (cap_user_data_t)alloca(sizeof(*cap));
1272 memset(hdr, 0, sizeof(*hdr));
1273 hdr->version = _LINUX_CAPABILITY_VERSION;
1274 memset(cap, 0, sizeof(*cap));
1275 cap->effective = g_uCaps;
1276 cap->permitted = g_uCaps;
1277 if (!capset(hdr, cap))
1278 prctl(PR_SET_KEEPCAPS, 1 /*keep*/, 0, 0, 0);
1279 prctl(PR_SET_DUMPABLE, 1 /*dump*/, 0, 0, 0);
1280# endif /* !USE_LIB_PCAP */
1281 }
1282
1283# elif defined(RT_OS_SOLARIS)
1284 /*
1285 * Add net_icmpaccess privilege to effective privileges and limit
1286 * permitted privileges before completely dropping root privileges.
1287 * This requires dropping root privileges temporarily to get the normal
1288 * user's privileges.
1289 */
1290 seteuid(g_uid);
1291 priv_set_t *pPrivEffective = priv_allocset();
1292 priv_set_t *pPrivNew = priv_allocset();
1293 if (pPrivEffective && pPrivNew)
1294 {
1295 int rc = getppriv(PRIV_EFFECTIVE, pPrivEffective);
1296 seteuid(0);
1297 if (!rc)
1298 {
1299 priv_copyset(pPrivEffective, pPrivNew);
1300 rc = priv_addset(pPrivNew, PRIV_NET_ICMPACCESS);
1301 if (!rc)
1302 {
1303 /* Order is important, as one can't set a privilege which is
1304 * not in the permitted privilege set. */
1305 rc = setppriv(PRIV_SET, PRIV_EFFECTIVE, pPrivNew);
1306 if (rc)
1307 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set effective privilege set.\n");
1308 rc = setppriv(PRIV_SET, PRIV_PERMITTED, pPrivNew);
1309 if (rc)
1310 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set permitted privilege set.\n");
1311 }
1312 else
1313 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to add NET_ICMPACCESS privilege.\n");
1314 }
1315 }
1316 else
1317 {
1318 /* for memory allocation failures just continue */
1319 seteuid(0);
1320 }
1321
1322 if (pPrivEffective)
1323 priv_freeset(pPrivEffective);
1324 if (pPrivNew)
1325 priv_freeset(pPrivNew);
1326# endif
1327}
1328
1329/*
1330 * Look at the environment for some special options.
1331 */
1332static void supR3GrabOptions(void)
1333{
1334 const char *pszOpt;
1335
1336# ifdef RT_OS_LINUX
1337 g_uCaps = 0;
1338
1339 /*
1340 * Do _not_ perform any capability-related system calls for root processes
1341 * (leaving g_uCaps at 0).
1342 * (Hint: getuid gets the real user id, not the effective.)
1343 */
1344 if (getuid() != 0)
1345 {
1346 /*
1347 * CAP_NET_RAW.
1348 * Default: enabled.
1349 * Can be disabled with 'export VBOX_HARD_CAP_NET_RAW=0'.
1350 */
1351 pszOpt = getenv("VBOX_HARD_CAP_NET_RAW");
1352 if ( !pszOpt
1353 || memcmp(pszOpt, "0", sizeof("0")) != 0)
1354 g_uCaps = CAP_TO_MASK(CAP_NET_RAW);
1355
1356 /*
1357 * CAP_NET_BIND_SERVICE.
1358 * Default: disabled.
1359 * Can be enabled with 'export VBOX_HARD_CAP_NET_BIND_SERVICE=1'.
1360 */
1361 pszOpt = getenv("VBOX_HARD_CAP_NET_BIND_SERVICE");
1362 if ( pszOpt
1363 && memcmp(pszOpt, "0", sizeof("0")) != 0)
1364 g_uCaps |= CAP_TO_MASK(CAP_NET_BIND_SERVICE);
1365 }
1366# endif
1367}
1368
1369/**
1370 * Drop any root privileges we might be holding.
1371 */
1372static void supR3HardenedMainDropPrivileges(void)
1373{
1374 /*
1375 * Try use setre[ug]id since this will clear the save uid/gid and thus
1376 * leave fewer traces behind that libs like GTK+ may pick up.
1377 */
1378 uid_t euid, ruid, suid;
1379 gid_t egid, rgid, sgid;
1380# if defined(RT_OS_DARWIN)
1381 /* The really great thing here is that setreuid isn't available on
1382 OS X 10.4, libc emulates it. While 10.4 have a slightly different and
1383 non-standard setuid implementation compared to 10.5, the following
1384 works the same way with both version since we're super user (10.5 req).
1385 The following will set all three variants of the group and user IDs. */
1386 setgid(g_gid);
1387 setuid(g_uid);
1388 euid = geteuid();
1389 ruid = suid = getuid();
1390 egid = getegid();
1391 rgid = sgid = getgid();
1392
1393# elif defined(RT_OS_SOLARIS)
1394 /* Solaris doesn't have setresuid, but the setreuid interface is BSD
1395 compatible and will set the saved uid to euid when we pass it a ruid
1396 that isn't -1 (which we do). */
1397 setregid(g_gid, g_gid);
1398 setreuid(g_uid, g_uid);
1399 euid = geteuid();
1400 ruid = suid = getuid();
1401 egid = getegid();
1402 rgid = sgid = getgid();
1403
1404# else
1405 /* This is the preferred one, full control no questions about semantics.
1406 PORTME: If this isn't work, try join one of two other gangs above. */
1407 setresgid(g_gid, g_gid, g_gid);
1408 setresuid(g_uid, g_uid, g_uid);
1409 if (getresuid(&ruid, &euid, &suid) != 0)
1410 {
1411 euid = geteuid();
1412 ruid = suid = getuid();
1413 }
1414 if (getresgid(&rgid, &egid, &sgid) != 0)
1415 {
1416 egid = getegid();
1417 rgid = sgid = getgid();
1418 }
1419# endif
1420
1421
1422 /* Check that it worked out all right. */
1423 if ( euid != g_uid
1424 || ruid != g_uid
1425 || suid != g_uid
1426 || egid != g_gid
1427 || rgid != g_gid
1428 || sgid != g_gid)
1429 supR3HardenedFatal("SUPR3HardenedMain: failed to drop root privileges!"
1430 " (euid=%d ruid=%d suid=%d egid=%d rgid=%d sgid=%d; wanted uid=%d and gid=%d)\n",
1431 euid, ruid, suid, egid, rgid, sgid, g_uid, g_gid);
1432
1433# if RT_OS_LINUX
1434 /*
1435 * Re-enable the cap_net_raw capability which was disabled during setresuid.
1436 */
1437 if (g_uCaps != 0)
1438 {
1439# ifdef USE_LIB_PCAP
1440 /** @todo Warn if that does not work? */
1441 /* XXX cap_net_bind_service */
1442 cap_set_proc(cap_from_text("cap_net_raw+ep"));
1443# else
1444 cap_user_header_t hdr = (cap_user_header_t)alloca(sizeof(*hdr));
1445 cap_user_data_t cap = (cap_user_data_t)alloca(sizeof(*cap));
1446 memset(hdr, 0, sizeof(*hdr));
1447 hdr->version = _LINUX_CAPABILITY_VERSION;
1448 memset(cap, 0, sizeof(*cap));
1449 cap->effective = g_uCaps;
1450 cap->permitted = g_uCaps;
1451 /** @todo Warn if that does not work? */
1452 capset(hdr, cap);
1453# endif /* !USE_LIB_PCAP */
1454 }
1455# endif
1456}
1457
1458#endif /* SUP_HARDENED_SUID */
1459
1460/**
1461 * Loads the VBoxRT DLL/SO/DYLIB, hands it the open driver,
1462 * and calls RTR3InitEx.
1463 *
1464 * @param fFlags The SUPR3HardenedMain fFlags argument, passed to supR3PreInit.
1465 *
1466 * @remarks VBoxRT contains both IPRT and SUPR3.
1467 * @remarks This function will not return on failure.
1468 */
1469static void supR3HardenedMainInitRuntime(uint32_t fFlags)
1470{
1471 /*
1472 * Construct the name.
1473 */
1474 char szPath[RTPATH_MAX];
1475 supR3HardenedPathSharedLibs(szPath, sizeof(szPath) - sizeof("/VBoxRT" SUPLIB_DLL_SUFF));
1476 suplibHardenedStrCat(szPath, "/VBoxRT" SUPLIB_DLL_SUFF);
1477
1478 /*
1479 * Open it and resolve the symbols.
1480 */
1481#if defined(RT_OS_WINDOWS)
1482 HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/);
1483 if (!hMod)
1484 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_MODULE_NOT_FOUND,
1485 "LoadLibrary \"%s\" failed (rc=%d)",
1486 szPath, RtlGetLastWin32Error());
1487 PFNRTR3INITEX pfnRTInitEx = (PFNRTR3INITEX)GetProcAddress(hMod, SUP_HARDENED_SYM("RTR3InitEx"));
1488 if (!pfnRTInitEx)
1489 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
1490 "Entrypoint \"RTR3InitEx\" not found in \"%s\" (rc=%d)",
1491 szPath, RtlGetLastWin32Error());
1492
1493 PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)GetProcAddress(hMod, SUP_HARDENED_SYM("supR3PreInit"));
1494 if (!pfnSUPPreInit)
1495 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
1496 "Entrypoint \"supR3PreInit\" not found in \"%s\" (rc=%d)",
1497 szPath, RtlGetLastWin32Error());
1498
1499#else
1500 /* the dlopen crowd */
1501 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
1502 if (!pvMod)
1503 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_MODULE_NOT_FOUND,
1504 "dlopen(\"%s\",) failed: %s",
1505 szPath, dlerror());
1506 PFNRTR3INITEX pfnRTInitEx = (PFNRTR3INITEX)(uintptr_t)dlsym(pvMod, SUP_HARDENED_SYM("RTR3InitEx"));
1507 if (!pfnRTInitEx)
1508 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
1509 "Entrypoint \"RTR3InitEx\" not found in \"%s\"!\ndlerror: %s",
1510 szPath, dlerror());
1511 PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)(uintptr_t)dlsym(pvMod, SUP_HARDENED_SYM("supR3PreInit"));
1512 if (!pfnSUPPreInit)
1513 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
1514 "Entrypoint \"supR3PreInit\" not found in \"%s\"!\ndlerror: %s",
1515 szPath, dlerror());
1516#endif
1517
1518 /*
1519 * Make the calls.
1520 */
1521 supR3HardenedGetPreInitData(&g_SupPreInitData);
1522 int rc = pfnSUPPreInit(&g_SupPreInitData, fFlags);
1523 if (RT_FAILURE(rc))
1524 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, rc,
1525 "supR3PreInit failed with rc=%d", rc);
1526 const char *pszExePath = NULL;
1527#ifdef RT_OS_LINUX
1528 if (!supR3HardenedMainIsProcSelfExeAccssible())
1529 pszExePath = g_szSupLibHardenedExePath;
1530#endif
1531 rc = pfnRTInitEx(RTR3INIT_VER_1,
1532 fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV ? 0 : RTR3INIT_FLAGS_SUPLIB,
1533 0 /*cArgs*/, NULL /*papszArgs*/, pszExePath);
1534 if (RT_FAILURE(rc))
1535 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, rc,
1536 "RTR3InitEx failed with rc=%d", rc);
1537
1538#if defined(RT_OS_WINDOWS)
1539 /*
1540 * Windows: Create thread that terminates the process when the parent stub
1541 * process terminates (VBoxNetDHCP, Ctrl-C, etc).
1542 */
1543 if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
1544 supR3HardenedWinCreateParentWatcherThread(hMod);
1545#endif
1546}
1547
1548
1549/**
1550 * Loads the DLL/SO/DYLIB containing the actual program and
1551 * resolves the TrustedError symbol.
1552 *
1553 * This is very similar to supR3HardenedMainGetTrustedMain().
1554 *
1555 * @returns Pointer to the trusted error symbol if it is exported, NULL
1556 * and no error messages otherwise.
1557 * @param pszProgName The program name.
1558 */
1559static PFNSUPTRUSTEDERROR supR3HardenedMainGetTrustedError(const char *pszProgName)
1560{
1561 /*
1562 * Don't bother if the main() function didn't advertise any TrustedError
1563 * export. It's both a waste of time and may trigger additional problems,
1564 * confusing or obscuring the original issue.
1565 */
1566 if (!(g_fSupHardenedMain & SUPSECMAIN_FLAGS_TRUSTED_ERROR))
1567 return NULL;
1568
1569 /*
1570 * Construct the name.
1571 */
1572 char szPath[RTPATH_MAX];
1573 supR3HardenedPathAppPrivateArch(szPath, sizeof(szPath) - 10);
1574 size_t cch = suplibHardenedStrLen(szPath);
1575 suplibHardenedStrCopyEx(&szPath[cch], sizeof(szPath) - cch, "/", pszProgName, SUPLIB_DLL_SUFF, NULL);
1576
1577 /*
1578 * Open it and resolve the symbol.
1579 */
1580#if defined(RT_OS_WINDOWS)
1581 supR3HardenedWinEnableThreadCreation();
1582 HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/);
1583 if (!hMod)
1584 return NULL;
1585 FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedError"));
1586 if (!pfn)
1587 return NULL;
1588 return (PFNSUPTRUSTEDERROR)pfn;
1589
1590#else
1591 /* the dlopen crowd */
1592 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
1593 if (!pvMod)
1594 return NULL;
1595 void *pvSym = dlsym(pvMod, SUP_HARDENED_SYM("TrustedError"));
1596 if (!pvSym)
1597 return NULL;
1598 return (PFNSUPTRUSTEDERROR)(uintptr_t)pvSym;
1599#endif
1600}
1601
1602
1603/**
1604 * Loads the DLL/SO/DYLIB containing the actual program and
1605 * resolves the TrustedMain symbol.
1606 *
1607 * @returns Pointer to the trusted main of the actual program.
1608 * @param pszProgName The program name.
1609 * @remarks This function will not return on failure.
1610 */
1611static PFNSUPTRUSTEDMAIN supR3HardenedMainGetTrustedMain(const char *pszProgName)
1612{
1613 /*
1614 * Construct the name.
1615 */
1616 char szPath[RTPATH_MAX];
1617 supR3HardenedPathAppPrivateArch(szPath, sizeof(szPath) - 10);
1618 size_t cch = suplibHardenedStrLen(szPath);
1619 suplibHardenedStrCopyEx(&szPath[cch], sizeof(szPath) - cch, "/", pszProgName, SUPLIB_DLL_SUFF, NULL);
1620
1621 /*
1622 * Open it and resolve the symbol.
1623 */
1624#if defined(RT_OS_WINDOWS)
1625 HMODULE hMod = (HMODULE)supR3HardenedWinLoadLibrary(szPath, false /*fSystem32Only*/);
1626 if (!hMod)
1627 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: LoadLibrary \"%s\" failed, rc=%d\n",
1628 szPath, RtlGetLastWin32Error());
1629 FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedMain"));
1630 if (!pfn)
1631 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\" (rc=%d)\n",
1632 szPath, RtlGetLastWin32Error());
1633 return (PFNSUPTRUSTEDMAIN)pfn;
1634
1635#else
1636 /* the dlopen crowd */
1637 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
1638 if (!pvMod)
1639 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: dlopen(\"%s\",) failed: %s\n",
1640 szPath, dlerror());
1641 void *pvSym = dlsym(pvMod, SUP_HARDENED_SYM("TrustedMain"));
1642 if (!pvSym)
1643 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\"!\ndlerror: %s\n",
1644 szPath, dlerror());
1645 return (PFNSUPTRUSTEDMAIN)(uintptr_t)pvSym;
1646#endif
1647}
1648
1649
1650/**
1651 * Secure main.
1652 *
1653 * This is used for the set-user-ID-on-execute binaries on unixy systems
1654 * and when using the open-vboxdrv-via-root-service setup on Windows.
1655 *
1656 * This function will perform the integrity checks of the VirtualBox
1657 * installation, open the support driver, open the root service (later),
1658 * and load the DLL corresponding to \a pszProgName and execute its main
1659 * function.
1660 *
1661 * @returns Return code appropriate for main().
1662 *
1663 * @param pszProgName The program name. This will be used to figure out which
1664 * DLL/SO/DYLIB to load and execute.
1665 * @param fFlags Flags.
1666 * @param argc The argument count.
1667 * @param argv The argument vector.
1668 * @param envp The environment vector.
1669 */
1670DECLHIDDEN(int) SUPR3HardenedMain(const char *pszProgName, uint32_t fFlags, int argc, char **argv, char **envp)
1671{
1672 SUP_DPRINTF(("SUPR3HardenedMain: pszProgName=%s fFlags=%#x\n", pszProgName, fFlags));
1673
1674 /*
1675 * Note! At this point there is no IPRT, so we will have to stick
1676 * to basic CRT functions that everyone agree upon.
1677 */
1678 g_pszSupLibHardenedProgName = pszProgName;
1679 g_fSupHardenedMain = fFlags;
1680 g_SupPreInitData.u32Magic = SUPPREINITDATA_MAGIC;
1681 g_SupPreInitData.Data.hDevice = SUP_HDEVICE_NIL;
1682 g_SupPreInitData.u32EndMagic = SUPPREINITDATA_MAGIC;
1683
1684#ifdef SUP_HARDENED_SUID
1685# ifdef RT_OS_LINUX
1686 /*
1687 * On linux we have to make sure the path is initialized because we
1688 * *might* not be able to access /proc/self/exe after the seteuid call.
1689 */
1690 supR3HardenedGetFullExePath();
1691
1692# endif
1693
1694 /*
1695 * Grab any options from the environment.
1696 */
1697 supR3GrabOptions();
1698
1699 /*
1700 * Check that we're root, if we aren't then the installation is butchered.
1701 */
1702 g_uid = getuid();
1703 g_gid = getgid();
1704 if (geteuid() != 0 /* root */)
1705 supR3HardenedFatalMsg("SUPR3HardenedMain", kSupInitOp_RootCheck, VERR_PERMISSION_DENIED,
1706 "Effective UID is not root (euid=%d egid=%d uid=%d gid=%d)",
1707 geteuid(), getegid(), g_uid, g_gid);
1708#endif /* SUP_HARDENED_SUID */
1709
1710#ifdef RT_OS_WINDOWS
1711 /*
1712 * Windows: First respawn. On Windows we will respawn the process twice to establish
1713 * something we can put some kind of reliable trust in. The first respawning aims
1714 * at dropping compatibility layers and process "security" solutions.
1715 */
1716 if ( !(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV)
1717 && supR3HardenedWinIsReSpawnNeeded(1 /*iWhich*/, argc, argv))
1718 {
1719 SUP_DPRINTF(("SUPR3HardenedMain: Respawn #1\n"));
1720 supR3HardenedWinInit(SUPSECMAIN_FLAGS_DONT_OPEN_DEV);
1721 supR3HardenedVerifyAll(true /* fFatal */, pszProgName);
1722 return supR3HardenedWinReSpawn(1 /*iWhich*/);
1723 }
1724
1725 /*
1726 * Windows: Initialize the image verification global data so we can verify the
1727 * signature of the process image and hook the core of the DLL loader API so we
1728 * can check the signature of all DLLs mapped into the process.
1729 */
1730 supR3HardenedWinInit(fFlags);
1731#endif /* RT_OS_WINDOWS */
1732
1733 /*
1734 * Validate the installation.
1735 */
1736 supR3HardenedVerifyAll(true /* fFatal */, pszProgName);
1737
1738 /*
1739 * The next steps are only taken if we actually need to access the support
1740 * driver.
1741 */
1742 if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
1743 {
1744#ifdef RT_OS_WINDOWS
1745 /*
1746 * Windows: Verify the process (repeated by the kernel later.
1747 */
1748 supR3HardenedWinVerifyProcess();
1749
1750 /*
1751 * Windows: The second respawn. This time we make a special arrangement
1752 * with vboxdrv to monitor access to the new process from its inception.
1753 */
1754 if (supR3HardenedWinIsReSpawnNeeded(2 /* iWhich*/, argc, argv))
1755 {
1756 SUP_DPRINTF(("SUPR3HardenedMain: Respawn #2\n"));
1757 return supR3HardenedWinReSpawn(2 /* iWhich*/);
1758 }
1759 SUP_DPRINTF(("SUPR3HardenedMain: Final process, opening VBoxDrv...\n"));
1760 supR3HardenedWinFlushLoaderCache();
1761#endif /* RT_OS_WINDOWS */
1762
1763 /*
1764 * Open the vboxdrv device.
1765 */
1766 supR3HardenedMainOpenDevice();
1767 }
1768
1769#ifdef RT_OS_WINDOWS
1770 /*
1771 * Windows: Enable the use of windows APIs to verify images at load time.
1772 */
1773 supR3HardenedWinEnableThreadCreation();
1774 supR3HardenedWinFlushLoaderCache();
1775 supR3HardenedWinResolveVerifyTrustApiAndHookThreadCreation(g_pszSupLibHardenedProgName);
1776 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_VERIFY_TRUST_READY;
1777#endif
1778
1779#ifdef SUP_HARDENED_SUID
1780 /*
1781 * Grab additional capabilities / privileges.
1782 */
1783 supR3HardenedMainGrabCapabilites();
1784
1785 /*
1786 * Drop any root privileges we might be holding (won't return on failure)
1787 */
1788 supR3HardenedMainDropPrivileges();
1789#endif
1790
1791 /*
1792 * Load the IPRT, hand the SUPLib part the open driver and
1793 * call RTR3InitEx.
1794 */
1795 SUP_DPRINTF(("SUPR3HardenedMain: Load Runtime...\n"));
1796 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_INIT_RUNTIME;
1797 supR3HardenedMainInitRuntime(fFlags);
1798
1799 /*
1800 * Load the DLL/SO/DYLIB containing the actual program
1801 * and pass control to it.
1802 */
1803 SUP_DPRINTF(("SUPR3HardenedMain: Load TrustedMain...\n"));
1804 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_GET_TRUSTED_MAIN;
1805 PFNSUPTRUSTEDMAIN pfnTrustedMain = supR3HardenedMainGetTrustedMain(pszProgName);
1806
1807 SUP_DPRINTF(("SUPR3HardenedMain: Calling TrustedMain (%p)...\n", pfnTrustedMain));
1808 g_enmSupR3HardenedMainState = SUPR3HARDENEDMAINSTATE_CALLED_TRUSTED_MAIN;
1809 return pfnTrustedMain(argc, argv, envp);
1810}
1811
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