VirtualBox

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

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

SUP: Put a hard size limit on the startup log.

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