VirtualBox

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

Last change on this file since 49966 was 49500, checked in by vboxsync, 11 years ago

Warning.

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