VirtualBox

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

Last change on this file since 16048 was 16048, checked in by vboxsync, 16 years ago

SUPR3: added environment options VBOX_HARD_CAP_NET_RAW and VBOX_HARD_CAP_NET_BIND_SERVICE

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