VirtualBox

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

Last change on this file since 38183 was 38076, checked in by vboxsync, 13 years ago

SUPR3HardenedMain: free privset if allocated.

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