VirtualBox

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

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

Another sanity check.

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