VirtualBox

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

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

SUP: some cleanups.

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