VirtualBox

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

Last change on this file since 16067 was 16067, checked in by vboxsync, 15 years ago

sles10.1 fix

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

© 2023 Oracle
ContactPrivacy policyTerms of Use