VirtualBox

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

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

Linux hardened: use direct capset() calls to prevent a dependency to either libcap1 or libcap2

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