VirtualBox

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

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

SUPR3HardenedMain: split out code grabing privs/caps from the code dropping it.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette