VirtualBox

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

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

fixed for older Linux kernels

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