VirtualBox

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

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

header conflict with Linux ~ 2.6.16

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