VirtualBox

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

Last change on this file since 49211 was 49211, checked in by vboxsync, 12 years ago

SUP: some minor improvements.

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

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