VirtualBox

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

Last change on this file since 25472 was 25472, checked in by vboxsync, 14 years ago

FreeBSD: Use sysctl instead of procfs to retrieve executable path and arguments. Contributed by Bernhard Froehlich and Baptiste Daroussin

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

© 2023 Oracle
ContactPrivacy policyTerms of Use