VirtualBox

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

Last change on this file since 38895 was 38636, checked in by vboxsync, 13 years ago

*,IPRT: Redid the ring-3 init to always convert the arguments to UTF-8.

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