VirtualBox

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

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

IPRT,SUP: Renamed RTPathProgram to RTPathExecDir to make it clear what it returns. Renamed hardened version of it as well.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.0 KB
Line 
1/* $Id: SUPR3HardenedMain.cpp 19924 2009-05-22 21:52:47Z 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 uint32_t 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 supR3HardenedPathExecDir(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 supR3HardenedPathExecDir(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 supR3HardenedPathExecDir(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 supR3HardenedPathExecDir(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("supR3HardenedExecDir: 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("supR3HardenedExecDir: _dyld_get_image_name(0) failed\n");
342 size_t cchImageName = strlen(pszImageName);
343 if (!cchImageName || cchImageName >= sizeof(g_szSupLibHardenedExePath))
344 supR3HardenedFatal("supR3HardenedExecDir: _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("supR3HardenedExecDir: 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 RTPathExecDir
384 */
385DECLHIDDEN(int) supR3HardenedPathExecDir(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 size_t 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("supR3HardenedPathExecDir: 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 "Kernel driver not installed");
574 case VERR_VM_DRIVER_NOT_ACCESSIBLE:
575 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
576 "Kernel 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 "Kernel driver version mismatch");
586 case VERR_ACCESS_DENIED:
587 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
588 "VERR_ACCESS_DENIED");
589 case VERR_NO_MEMORY:
590 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
591 "Kernel memory allocation/mapping failed");
592 default:
593 supR3HardenedFatalMsg("suplibOsInit", kSupInitOp_Driver, rc,
594 "Unknown rc=%d", rc);
595 }
596}
597
598
599#ifdef SUP_HARDENED_SUID
600
601/**
602 * Grabs extra non-root capabilities / privileges that we might require.
603 *
604 * This is currently only used for being able to do ICMP from the NAT engine.
605 *
606 * @note We still have root privileges at the time of this call.
607 */
608static void supR3HardenedMainGrabCapabilites(void)
609{
610# if defined(RT_OS_LINUX)
611 /*
612 * We are about to drop all our privileges. Remove all capabilities but
613 * keep the cap_net_raw capability for ICMP sockets for the NAT stack.
614 */
615 if (g_uCaps != 0)
616 {
617# ifdef USE_LIB_PCAP
618 /* XXX cap_net_bind_service */
619 if (!cap_set_proc(cap_from_text("all-eip cap_net_raw+ep")))
620 prctl(PR_SET_KEEPCAPS, /*keep=*/1, 0, 0, 0);
621# else
622 cap_user_header_t hdr = (cap_user_header_t)alloca(sizeof(*hdr));
623 cap_user_data_t cap = (cap_user_data_t)alloca(sizeof(*cap));
624 memset(hdr, 0, sizeof(*hdr));
625 hdr->version = _LINUX_CAPABILITY_VERSION;
626 memset(cap, 0, sizeof(*cap));
627 cap->effective = g_uCaps;
628 cap->permitted = g_uCaps;
629 if (!capset(hdr, cap))
630 prctl(PR_SET_KEEPCAPS, /*keep=*/1, 0, 0, 0);
631# endif /* !USE_LIB_PCAP */
632 }
633
634# elif defined(RT_OS_SOLARIS)
635 /*
636 * Add net_icmpaccess privilege to permitted, effective and inheritable privileges
637 * before dropping root privileges.
638 */
639 priv_set_t *pPrivSet = priv_str_to_set("basic", ",", NULL);
640 if (pPrivSet)
641 {
642 priv_addset(pPrivSet, PRIV_NET_ICMPACCESS);
643 int rc = setppriv(PRIV_SET, PRIV_INHERITABLE, pPrivSet);
644 if (!rc)
645 {
646 rc = setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet);
647 if (!rc)
648 {
649 rc = setppriv(PRIV_SET, PRIV_EFFECTIVE, pPrivSet);
650 if (rc)
651 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set effectives privilege set.\n");
652 }
653 else
654 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set permitted privilege set.\n");
655 }
656 else
657 supR3HardenedError(rc, false, "SUPR3HardenedMain: failed to set inheritable privilege set.\n");
658
659 priv_freeset(pPrivSet);
660 }
661 else
662 supR3HardenedError(-1, false, "SUPR3HardenedMain: failed to get basic privilege set.\n");
663
664# endif
665}
666
667/*
668 * Look at the environment for some special options.
669 */
670static void supR3GrabOptions(void)
671{
672 const char *pszOpt;
673
674# ifdef RT_OS_LINUX
675 g_uCaps = 0;
676
677 /*
678 * Do _not_ perform any capability-related system calls for root processes
679 * (leaving g_uCaps at 0).
680 * (Hint: getuid gets the real user id, not the effective.)
681 */
682 if (getuid() != 0)
683 {
684 /*
685 * CAP_NET_RAW.
686 * Default: enabled.
687 * Can be disabled with 'export VBOX_HARD_CAP_NET_RAW=0'.
688 */
689 pszOpt = getenv("VBOX_HARD_CAP_NET_RAW");
690 if ( !pszOpt
691 || memcmp(pszOpt, "0", sizeof("0")) != 0)
692 g_uCaps = CAP_TO_MASK(CAP_NET_RAW);
693
694 /*
695 * CAP_NET_BIND_SERVICE.
696 * Default: disabled.
697 * Can be enabled with 'export VBOX_HARD_CAP_NET_BIND_SERVICE=1'.
698 */
699 pszOpt = getenv("VBOX_HARD_CAP_NET_BIND_SERVICE");
700 if ( pszOpt
701 && memcmp(pszOpt, "0", sizeof("0")) != 0)
702 g_uCaps |= CAP_TO_MASK(CAP_NET_BIND_SERVICE);
703 }
704# endif
705}
706
707/**
708 * Drop any root privileges we might be holding.
709 */
710static void supR3HardenedMainDropPrivileges(void)
711{
712 /*
713 * Try use setre[ug]id since this will clear the save uid/gid and thus
714 * leave fewer traces behind that libs like GTK+ may pick up.
715 */
716 uid_t euid, ruid, suid;
717 gid_t egid, rgid, sgid;
718# if defined(RT_OS_DARWIN)
719 /* The really great thing here is that setreuid isn't available on
720 OS X 10.4, libc emulates it. While 10.4 have a sligtly different and
721 non-standard setuid implementation compared to 10.5, the following
722 works the same way with both version since we're super user (10.5 req).
723 The following will set all three variants of the group and user IDs. */
724 setgid(g_gid);
725 setuid(g_uid);
726 euid = geteuid();
727 ruid = suid = getuid();
728 egid = getegid();
729 rgid = sgid = getgid();
730
731# elif defined(RT_OS_SOLARIS)
732 /* Solaris doesn't have setresuid, but the setreuid interface is BSD
733 compatible and will set the saved uid to euid when we pass it a ruid
734 that isn't -1 (which we do). */
735 setregid(g_gid, g_gid);
736 setreuid(g_uid, g_uid);
737 euid = geteuid();
738 ruid = suid = getuid();
739 egid = getegid();
740 rgid = sgid = getgid();
741
742# else
743 /* This is the preferred one, full control no questions about semantics.
744 PORTME: If this isn't work, try join one of two other gangs above. */
745 setresgid(g_gid, g_gid, g_gid);
746 setresuid(g_uid, g_uid, g_uid);
747 if (getresuid(&ruid, &euid, &suid) != 0)
748 {
749 euid = geteuid();
750 ruid = suid = getuid();
751 }
752 if (getresgid(&rgid, &egid, &sgid) != 0)
753 {
754 egid = getegid();
755 rgid = sgid = getgid();
756 }
757# endif
758
759
760 /* Check that it worked out all right. */
761 if ( euid != g_uid
762 || ruid != g_uid
763 || suid != g_uid
764 || egid != g_gid
765 || rgid != g_gid
766 || sgid != g_gid)
767 supR3HardenedFatal("SUPR3HardenedMain: failed to drop root privileges!"
768 " (euid=%d ruid=%d suid=%d egid=%d rgid=%d sgid=%d; wanted uid=%d and gid=%d)\n",
769 euid, ruid, suid, egid, rgid, sgid, g_uid, g_gid);
770
771# if RT_OS_LINUX
772 /*
773 * Re-enable the cap_net_raw capability which was disabled during setresuid.
774 */
775 if (g_uCaps != 0)
776 {
777# ifdef USE_LIB_PCAP
778 /** @todo Warn if that does not work? */
779 /* XXX cap_net_bind_service */
780 cap_set_proc(cap_from_text("cap_net_raw+ep"));
781# else
782 cap_user_header_t hdr = (cap_user_header_t)alloca(sizeof(*hdr));
783 cap_user_data_t cap = (cap_user_data_t)alloca(sizeof(*cap));
784 memset(hdr, 0, sizeof(*hdr));
785 hdr->version = _LINUX_CAPABILITY_VERSION;
786 memset(cap, 0, sizeof(*cap));
787 cap->effective = g_uCaps;
788 cap->permitted = g_uCaps;
789 /** @todo Warn if that does not work? */
790 capset(hdr, cap);
791# endif /* !USE_LIB_PCAP */
792 }
793# endif
794}
795
796#endif /* SUP_HARDENED_SUID */
797
798/**
799 * Loads the VBoxRT DLL/SO/DYLIB, hands it the open driver,
800 * and calls RTR3Init.
801 *
802 * @param fFlags The SUPR3HardenedMain fFlags argument, passed to supR3PreInit.
803 *
804 * @remarks VBoxRT contains both IPRT and SUPR3.
805 * @remarks This function will not return on failure.
806 */
807static void supR3HardenedMainInitRuntime(uint32_t fFlags)
808{
809 /*
810 * Construct the name.
811 */
812 char szPath[RTPATH_MAX];
813 supR3HardenedPathSharedLibs(szPath, sizeof(szPath) - sizeof("/VBoxRT" SUPLIB_DLL_SUFF));
814 strcat(szPath, "/VBoxRT" SUPLIB_DLL_SUFF);
815
816 /*
817 * Open it and resolve the symbols.
818 */
819#if defined(RT_OS_WINDOWS)
820 /** @todo consider using LOAD_WITH_ALTERED_SEARCH_PATH here! */
821 HMODULE hMod = LoadLibraryEx(szPath, NULL /*hFile*/, 0 /* dwFlags */);
822 if (!hMod)
823 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_MODULE_NOT_FOUND,
824 "LoadLibraryEx(\"%s\",,) failed (rc=%d)",
825 szPath, GetLastError());
826 PFNRTR3INITEX pfnRTInitEx = (PFNRTR3INITEX)GetProcAddress(hMod, SUP_HARDENED_SYM("RTR3InitEx"));
827 if (!pfnRTInitEx)
828 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
829 "Entrypoint \"RTR3InitEx\" not found in \"%s\" (rc=%d)",
830 szPath, GetLastError());
831
832 PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)GetProcAddress(hMod, SUP_HARDENED_SYM("supR3PreInit"));
833 if (!pfnSUPPreInit)
834 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
835 "Entrypoint \"supR3PreInit\" not found in \"%s\" (rc=%d)",
836 szPath, GetLastError());
837
838#else
839 /* the dlopen crowd */
840 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
841 if (!pvMod)
842 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_MODULE_NOT_FOUND,
843 "dlopen(\"%s\",) failed: %s",
844 szPath, dlerror());
845 PFNRTR3INITEX pfnRTInitEx = (PFNRTR3INITEX)(uintptr_t)dlsym(pvMod, SUP_HARDENED_SYM("RTR3InitEx"));
846 if (!pfnRTInitEx)
847 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
848 "Entrypoint \"RTR3InitEx\" not found in \"%s\"!\ndlerror: %s",
849 szPath, dlerror());
850 PFNSUPR3PREINIT pfnSUPPreInit = (PFNSUPR3PREINIT)(uintptr_t)dlsym(pvMod, SUP_HARDENED_SYM("supR3PreInit"));
851 if (!pfnSUPPreInit)
852 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, VERR_SYMBOL_NOT_FOUND,
853 "Entrypoint \"supR3PreInit\" not found in \"%s\"!\ndlerror: %s",
854 szPath, dlerror());
855#endif
856
857 /*
858 * Make the calls.
859 */
860 supR3HardenedGetPreInitData(&g_SupPreInitData);
861 int rc = pfnSUPPreInit(&g_SupPreInitData, fFlags);
862 if (RT_FAILURE(rc))
863 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, rc,
864 "supR3PreInit failed with rc=%d", rc);
865 const char *pszExePath = NULL;
866#ifdef RT_OS_LINUX
867 if (!supR3HardenedMainIsProcSelfExeAccssible())
868 pszExePath = g_szSupLibHardenedExePath;
869#endif
870 rc = pfnRTInitEx(0, pszExePath, !(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV));
871 if (RT_FAILURE(rc))
872 supR3HardenedFatalMsg("supR3HardenedMainInitRuntime", kSupInitOp_IPRT, rc,
873 "RTR3Init failed with rc=%d", rc);
874}
875
876
877/**
878 * Loads the DLL/SO/DYLIB containing the actual program and
879 * resolves the TrustedError symbol.
880 *
881 * This is very similar to supR3HardenedMainGetTrustedMain().
882 *
883 * @returns Pointer to the trusted error symbol if it is exported, NULL
884 * and no error messages otherwise.
885 * @param pszProgName The program name.
886 */
887static PFNSUPTRUSTEDERROR supR3HardenedMainGetTrustedError(const char *pszProgName)
888{
889 /*
890 * Construct the name.
891 */
892 char szPath[RTPATH_MAX];
893 supR3HardenedPathAppPrivateArch(szPath, sizeof(szPath) - 10);
894 size_t cch = strlen(szPath);
895 supR3HardenedStrPrintf(&szPath[cch], sizeof(szPath) - cch, "/%s%s", pszProgName, SUPLIB_DLL_SUFF);
896
897 /*
898 * Open it and resolve the symbol.
899 */
900#if defined(RT_OS_WINDOWS)
901 /** @todo consider using LOAD_WITH_ALTERED_SEARCH_PATH here! */
902 HMODULE hMod = LoadLibraryEx(szPath, NULL /*hFile*/, 0 /* dwFlags */);
903 if (!hMod)
904 return NULL;
905 FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedError"));
906 if (!pfn)
907 return NULL;
908 return (PFNSUPTRUSTEDERROR)pfn;
909
910#else
911 /* the dlopen crowd */
912 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
913 if (!pvMod)
914 return NULL;
915 void *pvSym = dlsym(pvMod, SUP_HARDENED_SYM("TrustedError"));
916 if (!pvSym)
917 return NULL;
918 return (PFNSUPTRUSTEDERROR)(uintptr_t)pvSym;
919#endif
920}
921
922
923/**
924 * Loads the DLL/SO/DYLIB containing the actual program and
925 * resolves the TrustedMain symbol.
926 *
927 * @returns Pointer to the trusted main of the actual program.
928 * @param pszProgName The program name.
929 * @remarks This function will not return on failure.
930 */
931static PFNSUPTRUSTEDMAIN supR3HardenedMainGetTrustedMain(const char *pszProgName)
932{
933 /*
934 * Construct the name.
935 */
936 char szPath[RTPATH_MAX];
937 supR3HardenedPathAppPrivateArch(szPath, sizeof(szPath) - 10);
938 size_t cch = strlen(szPath);
939 supR3HardenedStrPrintf(&szPath[cch], sizeof(szPath) - cch, "/%s%s", pszProgName, SUPLIB_DLL_SUFF);
940
941 /*
942 * Open it and resolve the symbol.
943 */
944#if defined(RT_OS_WINDOWS)
945 /** @todo consider using LOAD_WITH_ALTERED_SEARCH_PATH here! */
946 HMODULE hMod = LoadLibraryEx(szPath, NULL /*hFile*/, 0 /* dwFlags */);
947 if (!hMod)
948 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: LoadLibraryEx(\"%s\",,) failed, rc=%d\n",
949 szPath, GetLastError());
950 FARPROC pfn = GetProcAddress(hMod, SUP_HARDENED_SYM("TrustedMain"));
951 if (!pfn)
952 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\" (rc=%d)\n",
953 szPath, GetLastError());
954 return (PFNSUPTRUSTEDMAIN)pfn;
955
956#else
957 /* the dlopen crowd */
958 void *pvMod = dlopen(szPath, RTLD_NOW | RTLD_GLOBAL);
959 if (!pvMod)
960 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: dlopen(\"%s\",) failed: %s\n",
961 szPath, dlerror());
962 void *pvSym = dlsym(pvMod, SUP_HARDENED_SYM("TrustedMain"));
963 if (!pvSym)
964 supR3HardenedFatal("supR3HardenedMainGetTrustedMain: Entrypoint \"TrustedMain\" not found in \"%s\"!\ndlerror: %s\n",
965 szPath, dlerror());
966 return (PFNSUPTRUSTEDMAIN)(uintptr_t)pvSym;
967#endif
968}
969
970
971/**
972 * Secure main.
973 *
974 * This is used for the set-user-ID-on-execute binaries on unixy systems
975 * and when using the open-vboxdrv-via-root-service setup on Windows.
976 *
977 * This function will perform the integrity checks of the VirtualBox
978 * installation, open the support driver, open the root service (later),
979 * and load the DLL corresponding to \a pszProgName and execute its main
980 * function.
981 *
982 * @returns Return code appropriate for main().
983 *
984 * @param pszProgName The program name. This will be used to figure out which
985 * DLL/SO/DYLIB to load and execute.
986 * @param fFlags Flags.
987 * @param argc The argument count.
988 * @param argv The argument vector.
989 * @param envp The environment vector.
990 */
991DECLHIDDEN(int) SUPR3HardenedMain(const char *pszProgName, uint32_t fFlags, int argc, char **argv, char **envp)
992{
993 /*
994 * Note! At this point there is no IPRT, so we will have to stick
995 * to basic CRT functions that everyone agree upon.
996 */
997 g_pszSupLibHardenedProgName = pszProgName;
998 g_SupPreInitData.u32Magic = SUPPREINITDATA_MAGIC;
999 g_SupPreInitData.Data.hDevice = NIL_RTFILE;
1000 g_SupPreInitData.u32EndMagic = SUPPREINITDATA_MAGIC;
1001
1002#ifdef SUP_HARDENED_SUID
1003# ifdef RT_OS_LINUX
1004 /*
1005 * On linux we have to make sure the path is initialized because we
1006 * *might* not be able to access /proc/self/exe after the seteuid call.
1007 */
1008 supR3HardenedGetFullExePath();
1009
1010# endif
1011
1012 /*
1013 * Grab any options from the environment.
1014 */
1015 supR3GrabOptions();
1016
1017 /*
1018 * Check that we're root, if we aren't then the installation is butchered.
1019 */
1020 g_uid = getuid();
1021 g_gid = getgid();
1022 if (geteuid() != 0 /* root */)
1023 supR3HardenedFatalMsg("SUPR3HardenedMain", kSupInitOp_RootCheck, VERR_PERMISSION_DENIED,
1024 "Effective UID is not root (euid=%d egid=%d uid=%d gid=%d)",
1025 geteuid(), getegid(), g_uid, g_gid);
1026#endif
1027
1028 /*
1029 * Validate the installation.
1030 */
1031 supR3HardenedVerifyAll(true /* fFatal */, false /* fLeaveFilesOpen */, pszProgName);
1032
1033 /*
1034 * Open the vboxdrv device.
1035 */
1036 if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_DEV))
1037 supR3HardenedMainOpenDevice();
1038
1039 /*
1040 * Open the root service connection.
1041 */
1042 //if (!(fFlags & SUPSECMAIN_FLAGS_DONT_OPEN_SVC))
1043 //supR3HardenedMainOpenService(&g_SupPreInitData, true /* fFatal */);
1044
1045#ifdef SUP_HARDENED_SUID
1046 /*
1047 * Grab additional capabilities / privileges.
1048 */
1049 supR3HardenedMainGrabCapabilites();
1050
1051 /*
1052 * Drop any root privileges we might be holding (won't return on failure)
1053 */
1054 supR3HardenedMainDropPrivileges();
1055#endif
1056
1057 /*
1058 * Load the IPRT, hand the SUPLib part the open driver and
1059 * call RTR3Init.
1060 */
1061 supR3HardenedMainInitRuntime(fFlags);
1062
1063 /*
1064 * Load the DLL/SO/DYLIB containing the actual program
1065 * and pass control to it.
1066 */
1067 PFNSUPTRUSTEDMAIN pfnTrustedMain = supR3HardenedMainGetTrustedMain(pszProgName);
1068 return pfnTrustedMain(argc, argv, envp);
1069}
1070
1071
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