VirtualBox

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

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

header (C) fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.2 KB
Line 
1/* $Id: SUPR3HardenedMain.cpp 44528 2013-02-04 14:27:54Z vboxsync $ */
2/** @file
3 * VirtualBox Support Library - Hardened main().
4 */
5
6/*
7 * Copyright (C) 2006-2012 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
38#elif RT_OS_WINDOWS
39# include <Windows.h>
40# include <stdio.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 <stdio.h>
54# include <sys/types.h>
55# if defined(RT_OS_LINUX)
56# undef USE_LIB_PCAP /* don't depend on libcap as we had to depend on either
57 libcap1 or libcap2 */
58
59# undef _POSIX_SOURCE
60# include <linux/types.h> /* sys/capabilities from uek-headers require this */
61# include <sys/capability.h>
62# include <sys/prctl.h>
63# ifndef CAP_TO_MASK
64# define CAP_TO_MASK(cap) RT_BIT(cap)
65# endif
66# elif defined(RT_OS_FREEBSD)
67# include <sys/param.h>
68# include <sys/sysctl.h>
69# elif defined(RT_OS_SOLARIS)
70# include <priv.h>
71# endif
72# include <pwd.h>
73# ifdef RT_OS_DARWIN
74# include <mach-o/dyld.h>
75# endif
76
77#endif
78
79#include <VBox/sup.h>
80#include <VBox/err.h>
81#include <iprt/string.h>
82#include <iprt/initterm.h>
83#include <iprt/param.h>
84
85#include "SUPLibInternal.h"
86
87
88/*******************************************************************************
89* Defined Constants And Macros *
90*******************************************************************************/
91/** @def SUP_HARDENED_SUID
92 * Whether we're employing set-user-ID-on-execute in the hardening.
93 */
94#if !defined(RT_OS_OS2) && !defined(RT_OS_WINDOWS) && !defined(RT_OS_L4)
95# define SUP_HARDENED_SUID
96#else
97# undef SUP_HARDENED_SUID
98#endif
99
100/** @def SUP_HARDENED_SYM
101 * Decorate a symbol that's resolved dynamically.
102 */
103#ifdef RT_OS_OS2
104# define SUP_HARDENED_SYM(sym) "_" sym
105#else
106# define SUP_HARDENED_SYM(sym) sym
107#endif
108
109
110/*******************************************************************************
111* Structures and Typedefs *
112*******************************************************************************/
113/** @see RTR3InitEx */
114typedef DECLCALLBACK(int) FNRTR3INITEX(uint32_t iVersion, uint32_t fFlags, int cArgs,
115 char **papszArgs, const char *pszProgramPath);
116typedef FNRTR3INITEX *PFNRTR3INITEX;
117
118
119/*******************************************************************************
120* Global Variables *
121*******************************************************************************/
122/** The pre-init data we pass on to SUPR3 (residing in VBoxRT). */
123static SUPPREINITDATA g_SupPreInitData;
124/** The program executable path. */
125static char g_szSupLibHardenedExePath[RTPATH_MAX];
126/** The program directory path. */
127static char g_szSupLibHardenedDirPath[RTPATH_MAX];
128
129/** The program name. */
130static const char *g_pszSupLibHardenedProgName;
131
132#ifdef SUP_HARDENED_SUID
133/** The real UID at startup. */
134static uid_t g_uid;
135/** The real GID at startup. */
136static gid_t g_gid;
137# ifdef RT_OS_LINUX
138static uint32_t g_uCaps;
139# endif
140#endif
141
142
143/*******************************************************************************
144* Internal Functions *
145*******************************************************************************/
146#ifdef SUP_HARDENED_SUID
147static void supR3HardenedMainDropPrivileges(void);
148#endif
149static PFNSUPTRUSTEDERROR supR3HardenedMainGetTrustedError(const char *pszProgName);
150
151
152/**
153 * @copydoc RTPathStripFilename.
154 */
155static void suplibHardenedPathStripFilename(char *pszPath)
156{
157 char *psz = pszPath;
158 char *pszLastSep = pszPath;
159
160 for (;; psz++)
161 {
162 switch (*psz)
163 {
164 /* handle separators. */
165#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
166 case ':':
167 pszLastSep = psz + 1;
168 break;
169
170 case '\\':
171#endif
172 case '/':
173 pszLastSep = psz;
174 break;
175
176 /* the end */
177 case '\0':
178 if (pszLastSep == pszPath)
179 *pszLastSep++ = '.';
180 *pszLastSep = '\0';
181 return;
182 }
183 }
184 /* will never get here */
185}
186
187
188/**
189 * @copydoc RTPathFilename
190 */
191DECLHIDDEN(char *) supR3HardenedPathFilename(const char *pszPath)
192{
193 const char *psz = pszPath;
194 const char *pszLastComp = pszPath;
195
196 for (;; psz++)
197 {
198 switch (*psz)
199 {
200 /* handle separators. */
201#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
202 case ':':
203 pszLastComp = psz + 1;
204 break;
205
206 case '\\':
207#endif
208 case '/':
209 pszLastComp = psz + 1;
210 break;
211
212 /* the end */
213 case '\0':
214 if (*pszLastComp)
215 return (char *)(void *)pszLastComp;
216 return NULL;
217 }
218 }
219
220 /* will never get here */
221 return NULL;
222}
223
224
225/**
226 * @copydoc RTPathAppPrivateNoArch
227 */
228DECLHIDDEN(int) supR3HardenedPathAppPrivateNoArch(char *pszPath, size_t cchPath)
229{
230#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE)
231 const char *pszSrcPath = RTPATH_APP_PRIVATE;
232 size_t cchPathPrivateNoArch = strlen(pszSrcPath);
233 if (cchPathPrivateNoArch >= cchPath)
234 supR3HardenedFatal("supR3HardenedPathAppPrivateNoArch: Buffer overflow, %lu >= %lu\n",
235 (unsigned long)cchPathPrivateNoArch, (unsigned long)cchPath);
236 memcpy(pszPath, pszSrcPath, cchPathPrivateNoArch + 1);
237 return VINF_SUCCESS;
238
239#else
240 return supR3HardenedPathExecDir(pszPath, cchPath);
241#endif
242}
243
244
245/**
246 * @copydoc RTPathAppPrivateArch
247 */
248DECLHIDDEN(int) supR3HardenedPathAppPrivateArch(char *pszPath, size_t cchPath)
249{
250#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE_ARCH)
251 const char *pszSrcPath = RTPATH_APP_PRIVATE_ARCH;
252 size_t cchPathPrivateArch = strlen(pszSrcPath);
253 if (cchPathPrivateArch >= cchPath)
254 supR3HardenedFatal("supR3HardenedPathAppPrivateArch: Buffer overflow, %lu >= %lu\n",
255 (unsigned long)cchPathPrivateArch, (unsigned long)cchPath);
256 memcpy(pszPath, pszSrcPath, cchPathPrivateArch + 1);
257 return VINF_SUCCESS;
258
259#else
260 return supR3HardenedPathExecDir(pszPath, cchPath);
261#endif
262}
263
264
265/**
266 * @copydoc RTPathSharedLibs
267 */
268DECLHIDDEN(int) supR3HardenedPathSharedLibs(char *pszPath, size_t cchPath)
269{
270#if !defined(RT_OS_WINDOWS) && defined(RTPATH_SHARED_LIBS)
271 const char *pszSrcPath = RTPATH_SHARED_LIBS;
272 size_t cchPathSharedLibs = strlen(pszSrcPath);
273 if (cchPathSharedLibs >= cchPath)
274 supR3HardenedFatal("supR3HardenedPathSharedLibs: Buffer overflow, %lu >= %lu\n",
275 (unsigned long)cchPathSharedLibs, (unsigned long)cchPath);
276 memcpy(pszPath, pszSrcPath, cchPathSharedLibs + 1);
277 return VINF_SUCCESS;
278
279#else
280 return supR3HardenedPathExecDir(pszPath, cchPath);
281#endif
282}
283
284
285/**
286 * @copydoc RTPathAppDocs
287 */
288DECLHIDDEN(int) supR3HardenedPathAppDocs(char *pszPath, size_t cchPath)
289{
290#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_DOCS)
291 const char *pszSrcPath = RTPATH_APP_DOCS;
292 size_t cchPathAppDocs = strlen(pszSrcPath);
293 if (cchPathAppDocs >= cchPath)
294 supR3HardenedFatal("supR3HardenedPathAppDocs: Buffer overflow, %lu >= %lu\n",
295 (unsigned long)cchPathAppDocs, (unsigned long)cchPath);
296 memcpy(pszPath, pszSrcPath, cchPathAppDocs + 1);
297 return VINF_SUCCESS;
298
299#else
300 return supR3HardenedPathExecDir(pszPath, cchPath);
301#endif
302}
303
304
305/**
306 * Returns the full path to the executable.
307 *
308 * @returns IPRT status code.
309 * @param pszPath Where to store it.
310 * @param cchPath How big that buffer is.
311 */
312static void supR3HardenedGetFullExePath(void)
313{
314 /*
315 * Get the program filename.
316 *
317 * Most UNIXes have no API for obtaining the executable path, but provides a symbolic
318 * link in the proc file system that tells who was exec'ed. The bad thing about this
319 * is that we have to use readlink, one of the weirder UNIX APIs.
320 *
321 * Darwin, OS/2 and Windows all have proper APIs for getting the program file name.
322 */
323#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_SOLARIS)
324# ifdef RT_OS_LINUX
325 int cchLink = readlink("/proc/self/exe", &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath) - 1);
326
327# elif defined(RT_OS_SOLARIS)
328 char szFileBuf[PATH_MAX + 1];
329 sprintf(szFileBuf, "/proc/%ld/path/a.out", (long)getpid());
330 int cchLink = readlink(szFileBuf, &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath) - 1);
331
332# else /* RT_OS_FREEBSD */
333 int aiName[4];
334 aiName[0] = CTL_KERN;
335 aiName[1] = KERN_PROC;
336 aiName[2] = KERN_PROC_PATHNAME;
337 aiName[3] = getpid();
338
339 size_t cbPath = sizeof(g_szSupLibHardenedExePath);
340 if (sysctl(aiName, RT_ELEMENTS(aiName), g_szSupLibHardenedExePath, &cbPath, NULL, 0) < 0)
341 supR3HardenedFatal("supR3HardenedExecDir: sysctl failed\n");
342 g_szSupLibHardenedExePath[sizeof(g_szSupLibHardenedExePath) - 1] = '\0';
343 int cchLink = strlen(g_szSupLibHardenedExePath); /* paranoid? can't we use cbPath? */
344
345# endif
346 if (cchLink < 0 || cchLink == sizeof(g_szSupLibHardenedExePath) - 1)
347 supR3HardenedFatal("supR3HardenedExecDir: couldn't read \"%s\", errno=%d cchLink=%d\n",
348 g_szSupLibHardenedExePath, errno, cchLink);
349 g_szSupLibHardenedExePath[cchLink] = '\0';
350
351#elif defined(RT_OS_OS2) || defined(RT_OS_L4)
352 _execname(g_szSupLibHardenedExePath, sizeof(g_szSupLibHardenedExePath));
353
354#elif defined(RT_OS_DARWIN)
355 const char *pszImageName = _dyld_get_image_name(0);
356 if (!pszImageName)
357 supR3HardenedFatal("supR3HardenedExecDir: _dyld_get_image_name(0) failed\n");
358 size_t cchImageName = strlen(pszImageName);
359 if (!cchImageName || cchImageName >= sizeof(g_szSupLibHardenedExePath))
360 supR3HardenedFatal("supR3HardenedExecDir: _dyld_get_image_name(0) failed, cchImageName=%d\n", cchImageName);
361 memcpy(g_szSupLibHardenedExePath, pszImageName, cchImageName + 1);
362
363#elif defined(RT_OS_WINDOWS)
364 HMODULE hExe = GetModuleHandle(NULL);
365 if (!GetModuleFileName(hExe, &g_szSupLibHardenedExePath[0], sizeof(g_szSupLibHardenedExePath)))
366 supR3HardenedFatal("supR3HardenedExecDir: GetModuleFileName failed, rc=%d\n", GetLastError());
367#else
368# error needs porting.
369#endif
370
371 /*
372 * Strip off the filename part (RTPathStripFilename()).
373 */
374 strcpy(g_szSupLibHardenedDirPath, g_szSupLibHardenedExePath);
375 suplibHardenedPathStripFilename(g_szSupLibHardenedDirPath);
376}
377
378
379#ifdef RT_OS_LINUX
380/**
381 * Checks if we can read /proc/self/exe.
382 *
383 * This is used on linux to see if we have to call init
384 * with program path or not.
385 *
386 * @returns true / false.
387 */
388static bool supR3HardenedMainIsProcSelfExeAccssible(void)
389{
390 char szPath[RTPATH_MAX];
391 int cchLink = readlink("/proc/self/exe", szPath, sizeof(szPath));
392 return cchLink != -1;
393}
394#endif /* RT_OS_LINUX */
395
396
397
398/**
399 * @copydoc RTPathExecDir
400 */
401DECLHIDDEN(int) supR3HardenedPathExecDir(char *pszPath, size_t cchPath)
402{
403 /*
404 * Lazy init (probably not required).
405 */
406 if (!g_szSupLibHardenedDirPath[0])
407 supR3HardenedGetFullExePath();
408
409 /*
410 * Calc the length and check if there is space before copying.
411 */
412 size_t cch = strlen(g_szSupLibHardenedDirPath) + 1;
413 if (cch <= cchPath)
414 {
415 memcpy(pszPath, g_szSupLibHardenedDirPath, cch + 1);
416 return VINF_SUCCESS;
417 }
418
419 supR3HardenedFatal("supR3HardenedPathExecDir: Buffer too small (%u < %u)\n", cchPath, cch);
420 return VERR_BUFFER_OVERFLOW;
421}
422
423
424DECLHIDDEN(void) supR3HardenedFatalMsgV(const char *pszWhere, SUPINITOP enmWhat, int rc, const char *pszMsgFmt, va_list va)
425{
426 /*
427 * To the console first, like supR3HardenedFatalV.
428 */
429 fprintf(stderr, "%s: Error %d in %s!\n", g_pszSupLibHardenedProgName, rc, pszWhere);
430 fprintf(stderr, "%s: ", g_pszSupLibHardenedProgName);
431 va_list vaCopy;
432 va_copy(vaCopy, va);
433 vfprintf(stderr, pszMsgFmt, vaCopy);
434 va_end(vaCopy);
435 fprintf(stderr, "\n");
436
437 switch (enmWhat)
438 {
439 case kSupInitOp_Driver:
440 fprintf(stderr,
441 "\n"
442 "%s: Tip! Make sure the kernel module is loaded. It may also help to reinstall VirtualBox.\n",
443 g_pszSupLibHardenedProgName);
444 break;
445
446 case kSupInitOp_IPRT:
447 case kSupInitOp_Integrity:
448 case kSupInitOp_RootCheck:
449 fprintf(stderr,
450 "\n"
451 "%s: Tip! It may help to reinstall VirtualBox.\n",
452 g_pszSupLibHardenedProgName);
453 break;
454
455 default:
456 /* no hints here */
457 break;
458 }
459
460#ifdef SUP_HARDENED_SUID
461 /*
462 * Drop any root privileges we might be holding, this won't return
463 * if it fails but end up calling supR3HardenedFatal[V].
464 */
465 supR3HardenedMainDropPrivileges();
466#endif /* SUP_HARDENED_SUID */
467
468 /*
469 * Now try resolve and call the TrustedError entry point if we can
470 * find it. We'll fork before we attempt this because that way the
471 * session management in main will see us exiting immediately (if
472 * it's involved with us).
473 */
474#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
475 int pid = fork();
476 if (pid <= 0)
477#endif
478 {
479 PFNSUPTRUSTEDERROR pfnTrustedError = supR3HardenedMainGetTrustedError(g_pszSupLibHardenedProgName);
480 if (pfnTrustedError)
481 pfnTrustedError(pszWhere, enmWhat, rc, pszMsgFmt, va);
482 }
483
484 /*
485 * Quit
486 */
487 for (;;)
488#ifdef _MSC_VER
489 exit(1);
490#else
491 _Exit(1);
492#endif
493}
494
495
496DECLHIDDEN(void) supR3HardenedFatalMsg(const char *pszWhere, SUPINITOP enmWhat, int rc, const char *pszMsgFmt, ...)
497{
498 va_list va;
499 va_start(va, pszMsgFmt);
500 supR3HardenedFatalMsgV(pszWhere, enmWhat, rc, pszMsgFmt, va);
501 va_end(va);
502}
503
504
505DECLHIDDEN(void) supR3HardenedFatalV(const char *pszFormat, va_list va)
506{
507 fprintf(stderr, "%s: ", g_pszSupLibHardenedProgName);
508 vfprintf(stderr, pszFormat, va);
509 for (;;)
510#ifdef _MSC_VER
511 exit(1);
512#else
513 _Exit(1);
514#endif
515}
516
517
518DECLHIDDEN(void) supR3HardenedFatal(const char *pszFormat, ...)
519{
520 va_list va;
521 va_start(va, pszFormat);
522 supR3HardenedFatalV(pszFormat, va);
523 va_end(va);
524}
525
526
527DECLHIDDEN(int) supR3HardenedErrorV(int rc, bool fFatal, const char *pszFormat, va_list va)
528{
529 if (fFatal)
530 supR3HardenedFatalV(pszFormat, va);
531
532 fprintf(stderr, "%s: ", g_pszSupLibHardenedProgName);
533 vfprintf(stderr, pszFormat, va);
534 return rc;
535}
536
537
538DECLHIDDEN(int) supR3HardenedError(int rc, bool fFatal, const char *pszFormat, ...)
539{
540 va_list va;
541 va_start(va, pszFormat);
542 supR3HardenedErrorV(rc, fFatal, pszFormat, va);
543 va_end(va);
544 return rc;
545}
546
547
548/**
549 * Wrapper around snprintf which will throw a fatal error on buffer overflow.
550 *
551 * @returns Number of chars in the result string.
552 * @param pszDst The destination buffer.
553 * @param cchDst The size of the buffer.
554 * @param pszFormat The format string.
555 * @param ... Format arguments.
556 */
557static size_t supR3HardenedStrPrintf(char *pszDst, size_t cchDst, const char *pszFormat, ...)
558{
559 va_list va;
560 va_start(va, pszFormat);
561#ifdef _MSC_VER
562 int cch = _vsnprintf(pszDst, cchDst, pszFormat, va);
563#else
564 int cch = vsnprintf(pszDst, cchDst, pszFormat, va);
565#endif
566 va_end(va);
567 if ((unsigned)cch >= cchDst || cch < 0)
568 supR3HardenedFatal("supR3HardenedStrPrintf: buffer overflow, %d >= %lu\n", cch, (long)cchDst);
569 return cch;
570}
571
572
573/**
574 * Attempts to open /dev/vboxdrv (or equvivalent).
575 *
576 * @remarks This function will not return on failure.
577 */
578static void supR3HardenedMainOpenDevice(void)
579{
580 int rc = suplibOsInit(&g_SupPreInitData.Data, false /*fPreInit*/, true /*fUnrestricted*/);
581 if (RT_SUCCESS(rc))
582 return;
583
584 switch (rc)
585 {
586 /** @todo better messages! */
587 case VERR_VM_DRIVER_NOT_INSTALLED:
588 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
589 "Kernel driver not installed");
590 case VERR_VM_DRIVER_NOT_ACCESSIBLE:
591 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
592 "Kernel driver not accessible");
593 case VERR_VM_DRIVER_LOAD_ERROR:
594 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
595 "VERR_VM_DRIVER_LOAD_ERROR");
596 case VERR_VM_DRIVER_OPEN_ERROR:
597 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
598 "VERR_VM_DRIVER_OPEN_ERROR");
599 case VERR_VM_DRIVER_VERSION_MISMATCH:
600 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
601 "Kernel driver version mismatch");
602 case VERR_ACCESS_DENIED:
603 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
604 "VERR_ACCESS_DENIED");
605 case VERR_NO_MEMORY:
606 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
607 "Kernel memory allocation/mapping failed");
608 default:
609 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
610 "Unknown rc=%d", rc);
611 }
612}
613
614
615#ifdef SUP_HARDENED_SUID
616
617/**
618 * Grabs extra non-root capabilities / privileges that we might require.
619 *
620 * This is currently only used for being able to do ICMP from the NAT engine.
621 *
622 * @note We still have root privileges at the time of this call.
623 */
624static void supR3HardenedMainGrabCapabilites(void)
625{
626# if defined(RT_OS_LINUX)
627 /*
628 * We are about to drop all our privileges. Remove all capabilities but
629 * keep the cap_net_raw capability for ICMP sockets for the NAT stack.
630 */
631 if (g_uCaps != 0)
632 {
633# ifdef USE_LIB_PCAP
634 /* XXX cap_net_bind_service */
635 if (!cap_set_proc(cap_from_text("all-eip cap_net_raw+ep")))
636 prctl(PR_SET_KEEPCAPS, 1 /*keep=*/, 0, 0, 0);
637 prctl(PR_SET_DUMPABLE, 1 /*dump*/, 0, 0, 0);
638# else
639 cap_user_header_t hdr = (cap_user_header_t)alloca(sizeof(*hdr));
640 cap_user_data_t cap = (cap_user_data_t)alloca(sizeof(*cap));
641 memset(hdr, 0, sizeof(*hdr));
642 hdr->version = _LINUX_CAPABILITY_VERSION;
643 memset(cap, 0, sizeof(*cap));
644 cap->effective = g_uCaps;
645 cap->permitted = g_uCaps;
646 if (!capset(hdr, cap))
647 prctl(PR_SET_KEEPCAPS, 1 /*keep*/, 0, 0, 0);
648 prctl(PR_SET_DUMPABLE, 1 /*dump*/, 0, 0, 0);
649# endif /* !USE_LIB_PCAP */
650 }
651
652# elif defined(RT_OS_SOLARIS)
653 /*
654 * Add net_icmpaccess privilege to effective privileges and limit
655 * permitted privileges before completely dropping root privileges.
656 * This requires dropping root privileges temporarily to get the normal
657 * user's privileges.
658 */
659 seteuid(g_uid);
660 priv_set_t *pPrivEffective = priv_allocset();
661 priv_set_t *pPrivNew = priv_allocset();
662 if (pPrivEffective && pPrivNew)
663 {
664 int rc = getppriv(PRIV_EFFECTIVE, pPrivEffective);
665 seteuid(0);
666 if (!rc)
667 {
668 priv_copyset(pPrivEffective, pPrivNew);
669 rc = priv_addset(pPrivNew, PRIV_NET_ICMPACCESS);
670 if (!rc)
671 {
672 /* Order is important, as one can't set a privilege which is
673 * not in the permitted privilege set. */
674 rc = setppriv(PRIV_SET, PRIV_EFFECTIVE, pPrivNew);
675 if (rc)
676 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set effective privilege set.\n");
677 rc = setppriv(PRIV_SET, PRIV_PERMITTED, pPrivNew);
678 if (rc)
679 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set permitted privilege set.\n");
680 }
681 else
682 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to add NET_ICMPACCESS privilege.\n");
683 }
684 }
685 else
686 {
687 /* for memory allocation failures just continue */
688 seteuid(0);
689 }
690
691 if (pPrivEffective)
692 priv_freeset(pPrivEffective);
693 if (pPrivNew)
694 priv_freeset(pPrivNew);
695# endif
696}
697
698/*
699 * Look at the environment for some special options.
700 */
701static void supR3GrabOptions(void)
702{
703 const char *pszOpt;
704
705# ifdef RT_OS_LINUX
706 g_uCaps = 0;
707
708 /*
709 * Do _not_ perform any capability-related system calls for root processes
710 * (leaving g_uCaps at 0).
711 * (Hint: getuid gets the real user id, not the effective.)
712 */
713 if (getuid() != 0)
714 {
715 /*
716 * CAP_NET_RAW.
717 * Default: enabled.
718 * Can be disabled with 'export VBOX_HARD_CAP_NET_RAW=0'.
719 */
720 pszOpt = getenv("VBOX_HARD_CAP_NET_RAW");
721 if ( !pszOpt
722 || memcmp(pszOpt, "0", sizeof("0")) != 0)
723 g_uCaps = CAP_TO_MASK(CAP_NET_RAW);
724
725 /*
726 * CAP_NET_BIND_SERVICE.
727 * Default: disabled.
728 * Can be enabled with 'export VBOX_HARD_CAP_NET_BIND_SERVICE=1'.
729 */
730 pszOpt = getenv("VBOX_HARD_CAP_NET_BIND_SERVICE");
731 if ( pszOpt
732 && memcmp(pszOpt, "0", sizeof("0")) != 0)
733 g_uCaps |= CAP_TO_MASK(CAP_NET_BIND_SERVICE);
734 }
735# endif
736}
737
738/**
739 * Drop any root privileges we might be holding.
740 */
741static void supR3HardenedMainDropPrivileges(void)
742{
743 /*
744 * Try use setre[ug]id since this will clear the save uid/gid and thus
745 * leave fewer traces behind that libs like GTK+ may pick up.
746 */
747 uid_t euid, ruid, suid;
748 gid_t egid, rgid, sgid;
749# if defined(RT_OS_DARWIN)
750 /* The really great thing here is that setreuid isn't available on
751 OS X 10.4, libc emulates it. While 10.4 have a slightly different and
752 non-standard setuid implementation compared to 10.5, the following
753 works the same way with both version since we're super user (10.5 req).
754 The following will set all three variants of the group and user IDs. */
755 setgid(g_gid);
756 setuid(g_uid);
757 euid = geteuid();
758 ruid = suid = getuid();
759 egid = getegid();
760 rgid = sgid = getgid();
761
762# elif defined(RT_OS_SOLARIS)
763 /* Solaris doesn't have setresuid, but the setreuid interface is BSD
764 compatible and will set the saved uid to euid when we pass it a ruid
765 that isn't -1 (which we do). */
766 setregid(g_gid, g_gid);
767 setreuid(g_uid, g_uid);
768 euid = geteuid();
769 ruid = suid = getuid();
770 egid = getegid();
771 rgid = sgid = getgid();
772
773# else
774 /* This is the preferred one, full control no questions about semantics.
775 PORTME: If this isn't work, try join one of two other gangs above. */
776 setresgid(g_gid, g_gid, g_gid);
777 setresuid(g_uid, g_uid, g_uid);
778 if (getresuid(&ruid, &euid, &suid) != 0)
779 {
780 euid = geteuid();
781 ruid = suid = getuid();
782 }
783 if (getresgid(&rgid, &egid, &sgid) != 0)
784 {
785 egid = getegid();
786 rgid = sgid = getgid();
787 }
788# endif
789
790
791 /* Check that it worked out all right. */
792 if ( euid != g_uid
793 || ruid != g_uid
794 || suid != g_uid
795 || egid != g_gid
796 || rgid != g_gid
797 || sgid != g_gid)
798 supR3HardenedFatal("SUPR3HardenedMain: failed to drop root privileges!"
799 " (euid=%d ruid=%d suid=%d egid=%d rgid=%d sgid=%d; wanted uid=%d and gid=%d)\n",
800 euid, ruid, suid, egid, rgid, sgid, g_uid, g_gid);
801
802# if RT_OS_LINUX
803 /*
804 * Re-enable the cap_net_raw capability which was disabled during setresuid.
805 */
806 if (g_uCaps != 0)
807 {
808# ifdef USE_LIB_PCAP
809 /** @todo Warn if that does not work? */
810 /* XXX cap_net_bind_service */
811 cap_set_proc(cap_from_text("cap_net_raw+ep"));
812# else
813 cap_user_header_t hdr = (cap_user_header_t)alloca(sizeof(*hdr));
814 cap_user_data_t cap = (cap_user_data_t)alloca(sizeof(*cap));
815 memset(hdr, 0, sizeof(*hdr));
816 hdr->version = _LINUX_CAPABILITY_VERSION;
817 memset(cap, 0, sizeof(*cap));
818 cap->effective = g_uCaps;
819 cap->permitted = g_uCaps;
820 /** @todo Warn if that does not work? */
821 capset(hdr, cap);
822# endif /* !USE_LIB_PCAP */
823 }
824# endif
825}
826
827#endif /* SUP_HARDENED_SUID */
828
829/**
830 * Loads the VBoxRT DLL/SO/DYLIB, hands it the open driver,
831 * and calls RTR3InitEx.
832 *
833 * @param fFlags The SUPR3HardenedMain fFlags argument, passed to supR3PreInit.
834 *
835 * @remarks VBoxRT contains both IPRT and SUPR3.
836 * @remarks This function will not return on failure.
837 */
838static void supR3HardenedMainInitRuntime(uint32_t fFlags)
839{
840 /*
841 * Construct the name.
842 */
843 char szPath[RTPATH_MAX];
844 supR3HardenedPathSharedLibs(szPath, sizeof(szPath) - sizeof("/VBoxRT" SUPLIB_DLL_SUFF));
845 strcat(szPath, "/VBoxRT" SUPLIB_DLL_SUFF);
846
847 /*
848 * Open it and resolve the symbols.
849 */
850#if defined(RT_OS_WINDOWS)
851 /** @todo consider using LOAD_WITH_ALTERED_SEARCH_PATH here! */
852 HMODULE hMod = LoadLibraryEx(szPath, NULL /*hFile*/, 0 /* dwFlags */);
853 if (!hMod)
854 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_MODULE_NOT_FOUND,
855 "LoadLibraryEx(\"%s\",,) failed (rc=%d)",
856 szPath, GetLastError());
857 PFNRTR3INITEX pfnRTInitEx = (PFNRTR3INITEX)GetProcAddress(hMod, SUP_HARDENED_SYM("RTR3InitEx"));
858 if (!pfnRTInitEx)
859 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
860 "Entrypoint \"RTR3InitEx\" not found in \"%s\" (rc=%d)",
861 szPath, GetLastError());
862
863 PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)GetProcAddress(hMod, SUP_HARDENED_SYM("supR3PreInit"));
864 if (!pfnSUPPreInit)
865 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
866 "Entrypoint \"supR3PreInit\" not found in \"%s\" (rc=%d)",
867 szPath, GetLastError());
868
869#else
870 /* the dlopen crowd */
871 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
872 if (!pvMod)
873 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_MODULE_NOT_FOUND,
874 "dlopen(\"%s\",) failed: %s",
875 szPath, dlerror());
876 PFNRTR3INITEX pfnRTInitEx = (PFNRTR3INITEX)(uintptr_t)dlsym(pvMod, SUP_HARDENED_SYM("RTR3InitEx"));
877 if (!pfnRTInitEx)
878 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
879 "Entrypoint \"RTR3InitEx\" not found in \"%s\"!\ndlerror: %s",
880 szPath, dlerror());
881 PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)(uintptr_t)dlsym(pvMod, SUP_HARDENED_SYM("supR3PreInit"));
882 if (!pfnSUPPreInit)
883 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
884 "Entrypoint \"supR3PreInit\" not found in \"%s\"!\ndlerror: %s",
885 szPath, dlerror());
886#endif
887
888 /*
889 * Make the calls.
890 */
891 supR3HardenedGetPreInitData(&g_SupPreInitData);
892 int rc = pfnSUPPreInit(&g_SupPreInitData, fFlags);
893 if (RT_FAILURE(rc))
894 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, rc,
895 "supR3PreInit failed with rc=%d", rc);
896 const char *pszExePath = NULL;
897#ifdef RT_OS_LINUX
898 if (!supR3HardenedMainIsProcSelfExeAccssible())
899 pszExePath = g_szSupLibHardenedExePath;
900#endif
901 rc = pfnRTInitEx(RTR3INIT_VER_1,
902 fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV ? 0 : RTR3INIT_FLAGS_SUPLIB,
903 0 /*cArgs*/, NULL /*papszArgs*/, pszExePath);
904 if (RT_FAILURE(rc))
905 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, rc,
906 "RTR3InitEx failed with rc=%d", rc);
907}
908
909
910/**
911 * Loads the DLL/SO/DYLIB containing the actual program and
912 * resolves the TrustedError symbol.
913 *
914 * This is very similar to supR3HardenedMainGetTrustedMain().
915 *
916 * @returns Pointer to the trusted error symbol if it is exported, NULL
917 * and no error messages otherwise.
918 * @param pszProgName The program name.
919 */
920static PFNSUPTRUSTEDERROR supR3HardenedMainGetTrustedError(const char *pszProgName)
921{
922 /*
923 * Construct the name.
924 */
925 char szPath[RTPATH_MAX];
926 supR3HardenedPathAppPrivateArch(szPath, sizeof(szPath) - 10);
927 size_t cch = strlen(szPath);
928 supR3HardenedStrPrintf(&szPath[cch], sizeof(szPath) - cch, "/%s%s", pszProgName, SUPLIB_DLL_SUFF);
929
930 /*
931 * Open it and resolve the symbol.
932 */
933#if defined(RT_OS_WINDOWS)
934 /** @todo consider using LOAD_WITH_ALTERED_SEARCH_PATH here! */
935 HMODULE hMod = LoadLibraryEx(szPath, NULL /*hFile*/, 0 /* dwFlags */);
936 if (!hMod)
937 return NULL;
938 FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedError"));
939 if (!pfn)
940 return NULL;
941 return (PFNSUPTRUSTEDERROR)pfn;
942
943#else
944 /* the dlopen crowd */
945 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
946 if (!pvMod)
947 return NULL;
948 void *pvSym = dlsym(pvMod, SUP_HARDENED_SYM("TrustedError"));
949 if (!pvSym)
950 return NULL;
951 return (PFNSUPTRUSTEDERROR)(uintptr_t)pvSym;
952#endif
953}
954
955
956/**
957 * Loads the DLL/SO/DYLIB containing the actual program and
958 * resolves the TrustedMain symbol.
959 *
960 * @returns Pointer to the trusted main of the actual program.
961 * @param pszProgName The program name.
962 * @remarks This function will not return on failure.
963 */
964static PFNSUPTRUSTEDMAIN supR3HardenedMainGetTrustedMain(const char *pszProgName)
965{
966 /*
967 * Construct the name.
968 */
969 char szPath[RTPATH_MAX];
970 supR3HardenedPathAppPrivateArch(szPath, sizeof(szPath) - 10);
971 size_t cch = strlen(szPath);
972 supR3HardenedStrPrintf(&szPath[cch], sizeof(szPath) - cch, "/%s%s", pszProgName, SUPLIB_DLL_SUFF);
973
974 /*
975 * Open it and resolve the symbol.
976 */
977#if defined(RT_OS_WINDOWS)
978 /** @todo consider using LOAD_WITH_ALTERED_SEARCH_PATH here! */
979 HMODULE hMod = LoadLibraryEx(szPath, NULL /*hFile*/, 0 /* dwFlags */);
980 if (!hMod)
981 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: LoadLibraryEx(\"%s\",,) failed, rc=%d\n",
982 szPath, GetLastError());
983 FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedMain"));
984 if (!pfn)
985 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\" (rc=%d)\n",
986 szPath, GetLastError());
987 return (PFNSUPTRUSTEDMAIN)pfn;
988
989#else
990 /* the dlopen crowd */
991 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
992 if (!pvMod)
993 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: dlopen(\"%s\",) failed: %s\n",
994 szPath, dlerror());
995 void *pvSym = dlsym(pvMod, SUP_HARDENED_SYM("TrustedMain"));
996 if (!pvSym)
997 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\"!\ndlerror: %s\n",
998 szPath, dlerror());
999 return (PFNSUPTRUSTEDMAIN)(uintptr_t)pvSym;
1000#endif
1001}
1002
1003
1004/**
1005 * Secure main.
1006 *
1007 * This is used for the set-user-ID-on-execute binaries on unixy systems
1008 * and when using the open-vboxdrv-via-root-service setup on Windows.
1009 *
1010 * This function will perform the integrity checks of the VirtualBox
1011 * installation, open the support driver, open the root service (later),
1012 * and load the DLL corresponding to \a pszProgName and execute its main
1013 * function.
1014 *
1015 * @returns Return code appropriate for main().
1016 *
1017 * @param pszProgName The program name. This will be used to figure out which
1018 * DLL/SO/DYLIB to load and execute.
1019 * @param fFlags Flags.
1020 * @param argc The argument count.
1021 * @param argv The argument vector.
1022 * @param envp The environment vector.
1023 */
1024DECLHIDDEN(int) SUPR3HardenedMain(const char *pszProgName, uint32_t fFlags, int argc, char **argv, char **envp)
1025{
1026 /*
1027 * Note! At this point there is no IPRT, so we will have to stick
1028 * to basic CRT functions that everyone agree upon.
1029 */
1030 g_pszSupLibHardenedProgName = pszProgName;
1031 g_SupPreInitData.u32Magic = SUPPREINITDATA_MAGIC;
1032 g_SupPreInitData.Data.hDevice = SUP_HDEVICE_NIL;
1033 g_SupPreInitData.u32EndMagic = SUPPREINITDATA_MAGIC;
1034
1035#ifdef SUP_HARDENED_SUID
1036# ifdef RT_OS_LINUX
1037 /*
1038 * On linux we have to make sure the path is initialized because we
1039 * *might* not be able to access /proc/self/exe after the seteuid call.
1040 */
1041 supR3HardenedGetFullExePath();
1042
1043# endif
1044
1045 /*
1046 * Grab any options from the environment.
1047 */
1048 supR3GrabOptions();
1049
1050 /*
1051 * Check that we're root, if we aren't then the installation is butchered.
1052 */
1053 g_uid = getuid();
1054 g_gid = getgid();
1055 if (geteuid() != 0 /* root */)
1056 supR3HardenedFatalMsg("SUPR3HardenedMain", kSupInitOp_RootCheck, VERR_PERMISSION_DENIED,
1057 "Effective UID is not root (euid=%d egid=%d uid=%d gid=%d)",
1058 geteuid(), getegid(), g_uid, g_gid);
1059#endif
1060
1061 /*
1062 * Validate the installation.
1063 */
1064 supR3HardenedVerifyAll(true /* fFatal */, false /* fLeaveFilesOpen */, pszProgName);
1065
1066 /*
1067 * Open the vboxdrv device.
1068 */
1069 if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
1070 supR3HardenedMainOpenDevice();
1071
1072 /*
1073 * Open the root service connection.
1074 */
1075 //if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_SVC))
1076 //supR3HardenedMainOpenService(&g_SupPreInitData, true /* fFatal */);
1077
1078#ifdef SUP_HARDENED_SUID
1079 /*
1080 * Grab additional capabilities / privileges.
1081 */
1082 supR3HardenedMainGrabCapabilites();
1083
1084 /*
1085 * Drop any root privileges we might be holding (won't return on failure)
1086 */
1087 supR3HardenedMainDropPrivileges();
1088#endif
1089
1090 /*
1091 * Load the IPRT, hand the SUPLib part the open driver and
1092 * call RTR3InitEx.
1093 */
1094 supR3HardenedMainInitRuntime(fFlags);
1095
1096 /*
1097 * Load the DLL/SO/DYLIB containing the actual program
1098 * and pass control to it.
1099 */
1100 PFNSUPTRUSTEDMAIN pfnTrustedMain = supR3HardenedMainGetTrustedMain(pszProgName);
1101 return pfnTrustedMain(argc, argv, envp);
1102}
1103
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