VirtualBox

source: vbox/trunk/src/VBox/Main/VBoxExtPackHelperApp.cpp@ 35279

最後變更 在這個檔案從35279是 35279,由 vboxsync 提交於 14 年 前

ExtPack: Send the HWND down to the helper app so we can later make the UAC dialog go to the foreground. This is currently not possible because the main GUI thread is in an API call and does not respond to messages sent by ShellExecuteEx, thus creating a deadlock.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 68.3 KB
 
1/* $Id: VBoxExtPackHelperApp.cpp 35279 2010-12-21 16:44:39Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Extension Pack Helper Application, usually set-uid-to-root.
4 */
5
6/*
7 * Copyright (C) 2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include "include/ExtPackUtil.h"
23
24#include <iprt/buildconfig.h>
25#include <iprt/dir.h>
26#include <iprt/env.h>
27#include <iprt/file.h>
28#include <iprt/fs.h>
29#include <iprt/getopt.h>
30#include <iprt/initterm.h>
31#include <iprt/manifest.h>
32#include <iprt/message.h>
33#include <iprt/param.h>
34#include <iprt/path.h>
35#include <iprt/process.h>
36#include <iprt/string.h>
37#include <iprt/stream.h>
38#include <iprt/vfs.h>
39#include <iprt/zip.h>
40#include <iprt/cpp/ministring.h>
41
42#include <VBox/log.h>
43#include <VBox/err.h>
44#include <VBox/sup.h>
45#include <VBox/version.h>
46
47#ifdef RT_OS_WINDOWS
48# define _WIN32_WINNT 0x0501
49# include <Objbase.h> /* CoInitializeEx */
50# include <Windows.h> /* ShellExecuteEx, ++ */
51# ifdef DEBUG
52# include <Sddl.h>
53# endif
54#endif
55
56#ifdef RT_OS_DARWIN
57# include <Security/Authorization.h>
58# include <Security/AuthorizationTags.h>
59# include <CoreFoundation/CoreFoundation.h>
60#endif
61
62#if !defined(RT_OS_OS2)
63# include <stdio.h>
64# include <errno.h>
65# if !defined(RT_OS_WINDOWS)
66# include <sys/types.h>
67# include <unistd.h> /* geteuid */
68# endif
69#endif
70
71
72/*******************************************************************************
73* Defined Constants And Macros *
74*******************************************************************************/
75/** Enable elevation on Windows and Darwin. */
76#if !defined(RT_OS_OS2) || defined(DOXYGEN_RUNNING)
77# define WITH_ELEVATION
78#endif
79
80
81/** @name Command and option names
82 * @{ */
83#define CMD_INSTALL 1000
84#define CMD_UNINSTALL 1001
85#define CMD_CLEANUP 1002
86#ifdef WITH_ELEVATION
87# define OPT_ELEVATED 1090
88# define OPT_STDOUT 1091
89# define OPT_STDERR 1092
90#endif
91#define OPT_DISP_INFO_HACK 1093
92/** @} */
93
94
95/*******************************************************************************
96* Global Variables *
97*******************************************************************************/
98#ifdef RT_OS_WINDOWS
99static HINSTANCE g_hInstance;
100#endif
101
102#ifdef IN_RT_R3
103/* Override RTAssertShouldPanic to prevent gdb process creation. */
104RTDECL(bool) RTAssertShouldPanic(void)
105{
106 return true;
107}
108#endif
109
110
111
112/**
113 * Handle the special standard options when these are specified after the
114 * command.
115 *
116 * @param ch The option character.
117 */
118static RTEXITCODE DoStandardOption(int ch)
119{
120 switch (ch)
121 {
122 case 'h':
123 {
124 RTMsgInfo(VBOX_PRODUCT " Extension Pack Helper App\n"
125 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
126 "All rights reserved.\n"
127 "\n"
128 "This NOT intended for general use, please use VBoxManage instead\n"
129 "or call the IExtPackManager API directly.\n"
130 "\n"
131 "Usage: %s <command> [options]\n"
132 "Commands:\n"
133 " install --base-dir <dir> --cert-dir <dir> --name <name> \\\n"
134 " --tarball <tarball> --tarball-fd <fd>\n"
135 " uninstall --base-dir <dir> --name <name>\n"
136 " cleanup --base-dir <dir>\n"
137 , RTProcShortName());
138 return RTEXITCODE_SUCCESS;
139 }
140
141 case 'V':
142 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
143 return RTEXITCODE_SUCCESS;
144
145 default:
146 AssertFailedReturn(RTEXITCODE_FAILURE);
147 }
148}
149
150
151/**
152 * Checks if the cerficiate directory is valid.
153 *
154 * @returns true if it is valid, false if it isn't.
155 * @param pszCertDir The certificate directory to validate.
156 */
157static bool IsValidCertificateDir(const char *pszCertDir)
158{
159 /*
160 * Just be darn strict for now.
161 */
162 char szCorrect[RTPATH_MAX];
163 int rc = RTPathAppPrivateNoArch(szCorrect, sizeof(szCorrect));
164 if (RT_FAILURE(rc))
165 return false;
166 rc = RTPathAppend(szCorrect, sizeof(szCorrect), VBOX_EXTPACK_CERT_DIR);
167 if (RT_FAILURE(rc))
168 return false;
169
170 return RTPathCompare(szCorrect, pszCertDir) == 0;
171}
172
173
174/**
175 * Checks if the base directory is valid.
176 *
177 * @returns true if it is valid, false if it isn't.
178 * @param pszBaesDir The base directory to validate.
179 */
180static bool IsValidBaseDir(const char *pszBaseDir)
181{
182 /*
183 * Just be darn strict for now.
184 */
185 char szCorrect[RTPATH_MAX];
186 int rc = RTPathAppPrivateArchTop(szCorrect, sizeof(szCorrect));
187 if (RT_FAILURE(rc))
188 return false;
189 rc = RTPathAppend(szCorrect, sizeof(szCorrect), VBOX_EXTPACK_INSTALL_DIR);
190 if (RT_FAILURE(rc))
191 return false;
192
193 return RTPathCompare(szCorrect, pszBaseDir) == 0;
194}
195
196
197/**
198 * Cleans up a temporary extension pack directory.
199 *
200 * This is used by 'uninstall', 'cleanup' and in the failure path of 'install'.
201 *
202 * @returns The program exit code.
203 * @param pszDir The directory to clean up. The caller is
204 * responsible for making sure this is valid.
205 * @param fTemporary Whether this is a temporary install directory or
206 * not.
207 */
208static RTEXITCODE RemoveExtPackDir(const char *pszDir, bool fTemporary)
209{
210 /** @todo May have to undo 555 modes here later. */
211 int rc = RTDirRemoveRecursive(pszDir, RTDIRRMREC_F_CONTENT_AND_DIR);
212 if (RT_FAILURE(rc))
213 return RTMsgErrorExit(RTEXITCODE_FAILURE,
214 "Failed to delete the %sextension pack directory: %Rrc ('%s')",
215 fTemporary ? "temporary " : "", rc, pszDir);
216 return RTEXITCODE_SUCCESS;
217}
218
219
220/**
221 * Common uninstall worker used by both uninstall and install --replace.
222 *
223 * @returns success or failure, message displayed on failure.
224 * @param pszExtPackDir The extension pack directory name.
225 */
226static RTEXITCODE CommonUninstallWorker(const char *pszExtPackDir)
227{
228 /* Rename the extension pack directory before deleting it to prevent new
229 VM processes from picking it up. */
230 char szExtPackUnInstDir[RTPATH_MAX];
231 int rc = RTStrCopy(szExtPackUnInstDir, sizeof(szExtPackUnInstDir), pszExtPackDir);
232 if (RT_SUCCESS(rc))
233 rc = RTStrCat(szExtPackUnInstDir, sizeof(szExtPackUnInstDir), "-_-uninst");
234 if (RT_FAILURE(rc))
235 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to construct temporary extension pack path: %Rrc", rc);
236
237 rc = RTDirRename(pszExtPackDir, szExtPackUnInstDir, RTPATHRENAME_FLAGS_NO_REPLACE);
238 if (RT_FAILURE(rc))
239 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to rename the extension pack directory: %Rrc", rc);
240
241 /* Recursively delete the directory content. */
242 return RemoveExtPackDir(szExtPackUnInstDir, false /*fTemporary*/);
243}
244
245
246/**
247 * Wrapper around VBoxExtPackOpenTarFss.
248 *
249 * @returns success or failure, message displayed on failure.
250 * @param hTarballFile The handle to the tarball file.
251 * @param phTarFss Where to return the filesystem stream handle.
252 */
253static RTEXITCODE OpenTarFss(RTFILE hTarballFile, PRTVFSFSSTREAM phTarFss)
254{
255 char szError[8192];
256 int rc = VBoxExtPackOpenTarFss(hTarballFile, szError, sizeof(szError), phTarFss);
257 if (RT_FAILURE(rc))
258 {
259 Assert(szError[0]);
260 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s", szError);
261 }
262 Assert(!szError[0]);
263 return RTEXITCODE_SUCCESS;
264}
265
266
267/**
268 * Sets the permissions of the temporary extension pack directory just before
269 * renaming it.
270 *
271 * By default the temporary directory is only accessible by root, this function
272 * will make it world readable and browseable.
273 *
274 * @returns The program exit code.
275 * @param pszDir The temporary extension pack directory.
276 */
277static RTEXITCODE SetExtPackPermissions(const char *pszDir)
278{
279 RTMsgInfo("Setting permissions...");
280#if !defined(RT_OS_WINDOWS)
281 int rc = RTPathSetMode(pszDir, 0755);
282 if (RT_FAILURE(rc))
283 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to set directory permissions: %Rrc ('%s')", rc, pszDir);
284#else
285 /** @todo */
286#endif
287
288 return RTEXITCODE_SUCCESS;
289}
290
291
292/**
293 * Wrapper around VBoxExtPackValidateMember.
294 *
295 * @returns Program exit code, failure with message.
296 * @param pszName The name of the directory.
297 * @param enmType The object type.
298 * @param hVfsObj The VFS object.
299 */
300static RTEXITCODE ValidateMemberOfExtPack(const char *pszName, RTVFSOBJTYPE enmType, RTVFSOBJ hVfsObj)
301{
302 char szError[8192];
303 int rc = VBoxExtPackValidateMember(pszName, enmType, hVfsObj, szError, sizeof(szError));
304 if (RT_FAILURE(rc))
305 {
306 Assert(szError[0]);
307 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s", szError);
308 }
309 Assert(!szError[0]);
310 return RTEXITCODE_SUCCESS;
311}
312
313
314/**
315 * Validates the extension pack tarball prior to unpacking.
316 *
317 * Operations performed:
318 * - Hardening checks.
319 *
320 * @returns The program exit code.
321 * @param pszDir The directory where the extension pack has been
322 * unpacked.
323 * @param pszExtPackName The expected extension pack name.
324 * @param pszTarball The name of the tarball in case we have to
325 * complain about something.
326 */
327static RTEXITCODE ValidateUnpackedExtPack(const char *pszDir, const char *pszTarball, const char *pszExtPackName)
328{
329 RTMsgInfo("Validating unpacked extension pack...");
330
331 RTERRINFOSTATIC ErrInfo;
332 RTErrInfoInitStatic(&ErrInfo);
333 int rc = SUPR3HardenedVerifyDir(pszDir, true /*fRecursive*/, true /*fCheckFiles*/, &ErrInfo.Core);
334 if (RT_FAILURE(rc))
335 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Hardening check failed with %Rrc: %s", rc, ErrInfo.Core.pszMsg);
336 return RTEXITCODE_SUCCESS;
337}
338
339
340/**
341 * Unpacks a directory from an extension pack tarball.
342 *
343 * @returns Program exit code, failure with message.
344 * @param pszDstDirName The name of the unpacked directory.
345 * @param hVfsObj The source object for the directory.
346 */
347static RTEXITCODE UnpackExtPackDir(const char *pszDstDirName, RTVFSOBJ hVfsObj)
348{
349 int rc = RTDirCreate(pszDstDirName, 0755);
350 if (RT_FAILURE(rc))
351 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create directory '%s': %Rrc", pszDstDirName, rc);
352 /** @todo Ownership tricks on windows? */
353 return RTEXITCODE_SUCCESS;
354}
355
356
357/**
358 * Unpacks a file from an extension pack tarball.
359 *
360 * @returns Program exit code, failure with message.
361 * @param pszName The name in the tarball.
362 * @param pszDstFilename The name of the unpacked file.
363 * @param hVfsIosSrc The source stream for the file.
364 * @param hUnpackManifest The manifest to add the file digest to.
365 */
366static RTEXITCODE UnpackExtPackFile(const char *pszName, const char *pszDstFilename,
367 RTVFSIOSTREAM hVfsIosSrc, RTMANIFEST hUnpackManifest)
368{
369 /*
370 * Query the object info, we'll need it for buffer sizing as well as
371 * setting the file mode.
372 */
373 RTFSOBJINFO ObjInfo;
374 int rc = RTVfsIoStrmQueryInfo(hVfsIosSrc, &ObjInfo, RTFSOBJATTRADD_NOTHING);
375 if (RT_FAILURE(rc))
376 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsIoStrmQueryInfo failed with %Rrc on '%s'", rc, pszDstFilename);
377
378 /*
379 * Create the file.
380 */
381 uint32_t fFlags = RTFILE_O_WRITE | RTFILE_O_DENY_ALL | RTFILE_O_CREATE | (0600 << RTFILE_O_CREATE_MODE_SHIFT);
382 RTFILE hFile;
383 rc = RTFileOpen(&hFile, pszDstFilename, fFlags);
384 if (RT_FAILURE(rc))
385 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create '%s': %Rrc", pszDstFilename, rc);
386
387 /*
388 * Create a I/O stream for the destination file, stack a manifest entry
389 * creator on top of it.
390 */
391 RTVFSIOSTREAM hVfsIosDst2;
392 rc = RTVfsIoStrmFromRTFile(hFile, fFlags, true /*fLeaveOpen*/, &hVfsIosDst2);
393 if (RT_SUCCESS(rc))
394 {
395 RTVFSIOSTREAM hVfsIosDst;
396 rc = RTManifestEntryAddPassthruIoStream(hUnpackManifest, hVfsIosDst2, pszName,
397 RTMANIFEST_ATTR_SIZE | RTMANIFEST_ATTR_SHA256,
398 false /*fReadOrWrite*/, &hVfsIosDst);
399 RTVfsIoStrmRelease(hVfsIosDst2);
400 if (RT_SUCCESS(rc))
401 {
402 /*
403 * Pump the data thru.
404 */
405 rc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, (uint32_t)RT_MIN(ObjInfo.cbObject, _1G));
406 if (RT_SUCCESS(rc))
407 {
408 rc = RTManifestPtIosAddEntryNow(hVfsIosDst);
409 if (RT_SUCCESS(rc))
410 {
411 RTVfsIoStrmRelease(hVfsIosDst);
412 hVfsIosDst = NIL_RTVFSIOSTREAM;
413
414 /*
415 * Set the mode mask.
416 */
417 ObjInfo.Attr.fMode &= ~(RTFS_UNIX_IWOTH | RTFS_UNIX_IWGRP);
418 rc = RTFileSetMode(hFile, ObjInfo.Attr.fMode);
419 /** @todo Windows needs to do more here, I think. */
420 if (RT_SUCCESS(rc))
421 {
422 RTFileClose(hFile);
423 return RTEXITCODE_SUCCESS;
424 }
425
426 RTMsgError("Failed to set the mode of '%s' to %RTfmode: %Rrc", pszDstFilename, ObjInfo.Attr.fMode, rc);
427 }
428 else
429 RTMsgError("RTManifestPtIosAddEntryNow failed for '%s': %Rrc", pszDstFilename, rc);
430 }
431 else
432 RTMsgError("RTVfsUtilPumpIoStreams failed for '%s': %Rrc", pszDstFilename, rc);
433 RTVfsIoStrmRelease(hVfsIosDst);
434 }
435 else
436 RTMsgError("RTManifestEntryAddPassthruIoStream failed: %Rrc", rc);
437 }
438 else
439 RTMsgError("RTVfsIoStrmFromRTFile failed: %Rrc", rc);
440 RTFileClose(hFile);
441 return RTEXITCODE_FAILURE;
442}
443
444
445/**
446 * Unpacks the extension pack into the specified directory.
447 *
448 * This will apply ownership and permission changes to all the content, the
449 * exception is @a pszDirDst which will be handled by SetExtPackPermissions.
450 *
451 * @returns The program exit code.
452 * @param hTarballFile The tarball to unpack.
453 * @param pszDirDst Where to unpack it.
454 * @param hValidManifest The manifest we've validated.
455 * @param pszTarball The name of the tarball in case we have to
456 * complain about something.
457 */
458static RTEXITCODE UnpackExtPack(RTFILE hTarballFile, const char *pszDirDst, RTMANIFEST hValidManifest,
459 const char *pszTarball)
460{
461 RTMsgInfo("Unpacking extension pack into '%s'...", pszDirDst);
462
463 /*
464 * Set up the destination path.
465 */
466 char szDstPath[RTPATH_MAX];
467 int rc = RTPathAbs(pszDirDst, szDstPath, sizeof(szDstPath) - VBOX_EXTPACK_MAX_MEMBER_NAME_LENGTH - 2);
468 if (RT_FAILURE(rc))
469 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs('%s',,) failed: %Rrc", pszDirDst, rc);
470 size_t offDstPath = RTPathStripTrailingSlash(szDstPath);
471 szDstPath[offDstPath++] = '/';
472 szDstPath[offDstPath] = '\0';
473
474 /*
475 * Open the tar.gz filesystem stream and set up an manifest in-memory file.
476 */
477 RTVFSFSSTREAM hTarFss;
478 RTEXITCODE rcExit = OpenTarFss(hTarballFile, &hTarFss);
479 if (rcExit != RTEXITCODE_SUCCESS)
480 return rcExit;
481
482 RTMANIFEST hUnpackManifest;
483 rc = RTManifestCreate(0 /*fFlags*/, &hUnpackManifest);
484 if (RT_SUCCESS(rc))
485 {
486 /*
487 * Process the tarball (would be nice to move this to a function).
488 */
489 for (;;)
490 {
491 /*
492 * Get the next stream object.
493 */
494 char *pszName;
495 RTVFSOBJ hVfsObj;
496 RTVFSOBJTYPE enmType;
497 rc = RTVfsFsStrmNext(hTarFss, &pszName, &enmType, &hVfsObj);
498 if (RT_FAILURE(rc))
499 {
500 if (rc != VERR_EOF)
501 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsFsStrmNext failed: %Rrc", rc);
502 break;
503 }
504 const char *pszAdjName = pszName[0] == '.' && pszName[1] == '/' ? &pszName[2] : pszName;
505
506 /*
507 * Check the type & name validity then unpack it.
508 */
509 rcExit = ValidateMemberOfExtPack(pszName, enmType, hVfsObj);
510 if (rcExit == RTEXITCODE_SUCCESS)
511 {
512 szDstPath[offDstPath] = '\0';
513 rc = RTStrCopy(&szDstPath[offDstPath], sizeof(szDstPath) - offDstPath, pszAdjName);
514 if (RT_SUCCESS(rc))
515 {
516 if ( enmType == RTVFSOBJTYPE_FILE
517 || enmType == RTVFSOBJTYPE_IO_STREAM)
518 {
519 RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
520 rcExit = UnpackExtPackFile(pszAdjName, szDstPath, hVfsIos, hUnpackManifest);
521 RTVfsIoStrmRelease(hVfsIos);
522 }
523 else if (*pszAdjName && strcmp(pszAdjName, "."))
524 rcExit = UnpackExtPackDir(szDstPath, hVfsObj);
525 }
526 else
527 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Name is too long: '%s' (%Rrc)", pszAdjName, rc);
528 }
529
530 /*
531 * Clean up and break out on failure.
532 */
533 RTVfsObjRelease(hVfsObj);
534 RTStrFree(pszName);
535 if (rcExit != RTEXITCODE_SUCCESS)
536 break;
537 }
538
539 /*
540 * Check that what we just extracted matches the already verified
541 * manifest.
542 */
543 if (rcExit == RTEXITCODE_SUCCESS)
544 {
545 char szError[RTPATH_MAX];
546 rc = RTManifestEqualsEx(hUnpackManifest, hValidManifest, NULL /*papszIgnoreEntries*/, NULL /*papszIgnoreAttr*/,
547 0 /*fFlags*/, szError, sizeof(szError));
548 if (RT_SUCCESS(rc))
549 rc = RTEXITCODE_SUCCESS;
550 else if (rc == VERR_NOT_EQUAL && szError[0])
551 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Manifest mismatch: %s", szError);
552 else
553 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTManifestEqualsEx failed: %Rrc", rc);
554 }
555#if 0
556 RTVFSIOSTREAM hVfsIosStdOut = NIL_RTVFSIOSTREAM;
557 RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, RTFILE_O_WRITE, true, &hVfsIosStdOut);
558 RTVfsIoStrmWrite(hVfsIosStdOut, "Unpack:\n", sizeof("Unpack:\n") - 1, true, NULL);
559 RTManifestWriteStandard(hUnpackManifest, hVfsIosStdOut);
560 RTVfsIoStrmWrite(hVfsIosStdOut, "Valid:\n", sizeof("Valid:\n") - 1, true, NULL);
561 RTManifestWriteStandard(hValidManifest, hVfsIosStdOut);
562#endif
563 RTManifestRelease(hUnpackManifest);
564 }
565 RTVfsFsStrmRelease(hTarFss);
566
567 return rcExit;
568}
569
570
571
572/**
573 * Wrapper around VBoxExtPackValidateTarball.
574 *
575 * @returns The program exit code.
576 * @param hTarballFile The handle to open the @a pszTarball file.
577 * @param pszExtPackName The name of the extension pack name.
578 * @param pszTarball The name of the tarball in case we have to
579 * complain about something.
580 * @param phValidManifest Where to return the handle to fully validated
581 * the manifest for the extension pack. This
582 * includes all files.
583 */
584static RTEXITCODE ValidateExtPackTarball(RTFILE hTarballFile, const char *pszExtPackName, const char *pszTarball,
585 PRTMANIFEST phValidManifest)
586{
587 *phValidManifest = NIL_RTMANIFEST;
588 RTMsgInfo("Validating extension pack '%s' ('%s')...", pszTarball, pszExtPackName);
589
590 char szError[8192];
591 int rc = VBoxExtPackValidateTarball(hTarballFile, pszExtPackName, pszTarball,
592 szError, sizeof(szError), phValidManifest, NULL /*phXmlFile*/);
593 if (RT_FAILURE(rc))
594 {
595 Assert(szError[0]);
596 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s", szError);
597 }
598 Assert(!szError[0]);
599 return RTEXITCODE_SUCCESS;
600}
601
602
603/**
604 * The 2nd part of the installation process.
605 *
606 * @returns The program exit code.
607 * @param pszBaseDir The base directory.
608 * @param pszCertDir The certificat directory.
609 * @param pszTarball The tarball name.
610 * @param hTarballFile The handle to open the @a pszTarball file.
611 * @param hTarballFileOpt The tarball file handle (optional).
612 * @param pszName The extension pack name.
613 * @param pszMangledName The mangled extension pack name.
614 * @param fReplace Whether to replace any existing ext pack.
615 */
616static RTEXITCODE DoInstall2(const char *pszBaseDir, const char *pszCertDir, const char *pszTarball,
617 RTFILE hTarballFile, RTFILE hTarballFileOpt,
618 const char *pszName, const char *pszMangledName, bool fReplace)
619{
620 /*
621 * Do some basic validation of the tarball file.
622 */
623 RTFSOBJINFO ObjInfo;
624 int rc = RTFileQueryInfo(hTarballFile, &ObjInfo, RTFSOBJATTRADD_UNIX);
625 if (RT_FAILURE(rc))
626 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTFileQueryInfo failed with %Rrc on '%s'", rc, pszTarball);
627 if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
628 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Not a regular file: %s", pszTarball);
629
630 if (hTarballFileOpt != NIL_RTFILE)
631 {
632 RTFSOBJINFO ObjInfo2;
633 rc = RTFileQueryInfo(hTarballFileOpt, &ObjInfo2, RTFSOBJATTRADD_UNIX);
634 if (RT_FAILURE(rc))
635 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTFileQueryInfo failed with %Rrc on --tarball-fd", rc);
636 if ( ObjInfo.Attr.u.Unix.INodeIdDevice != ObjInfo2.Attr.u.Unix.INodeIdDevice
637 || ObjInfo.Attr.u.Unix.INodeId != ObjInfo2.Attr.u.Unix.INodeId)
638 return RTMsgErrorExit(RTEXITCODE_FAILURE, "--tarball and --tarball-fd does not match");
639 }
640
641 /*
642 * Construct the paths to the two directories we'll be using.
643 */
644 char szFinalPath[RTPATH_MAX];
645 rc = RTPathJoin(szFinalPath, sizeof(szFinalPath), pszBaseDir, pszMangledName);
646 if (RT_FAILURE(rc))
647 return RTMsgErrorExit(RTEXITCODE_FAILURE,
648 "Failed to construct the path to the final extension pack directory: %Rrc", rc);
649
650 char szTmpPath[RTPATH_MAX];
651 rc = RTPathJoin(szTmpPath, sizeof(szTmpPath) - 64, pszBaseDir, pszMangledName);
652 if (RT_SUCCESS(rc))
653 {
654 size_t cchTmpPath = strlen(szTmpPath);
655 RTStrPrintf(&szTmpPath[cchTmpPath], sizeof(szTmpPath) - cchTmpPath, "-_-inst-%u", (uint32_t)RTProcSelf());
656 }
657 if (RT_FAILURE(rc))
658 return RTMsgErrorExit(RTEXITCODE_FAILURE,
659 "Failed to construct the path to the temporary extension pack directory: %Rrc", rc);
660
661 /*
662 * Check that they don't exist at this point in time, unless fReplace=true.
663 */
664 rc = RTPathQueryInfoEx(szFinalPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
665 if (RT_SUCCESS(rc) && RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
666 {
667 if (!fReplace)
668 return RTMsgErrorExit(RTEXITCODE_FAILURE,
669 "The extension pack is already installed. You must uninstall the old one first.");
670 }
671 else if (RT_SUCCESS(rc))
672 return RTMsgErrorExit(RTEXITCODE_FAILURE,
673 "Found non-directory file system object where the extension pack would be installed ('%s')",
674 szFinalPath);
675 else if (rc != VERR_FILE_NOT_FOUND && rc != VERR_PATH_NOT_FOUND)
676 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unexpected RTPathQueryInfoEx status code %Rrc for '%s'", rc, szFinalPath);
677
678 rc = RTPathQueryInfoEx(szTmpPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
679 if (rc != VERR_FILE_NOT_FOUND && rc != VERR_PATH_NOT_FOUND)
680 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unexpected RTPathQueryInfoEx status code %Rrc for '%s'", rc, szFinalPath);
681
682 /*
683 * Create the temporary directory and prepare the extension pack within it.
684 * If all checks out correctly, rename it to the final directory.
685 */
686 RTDirCreate(pszBaseDir, 0755);
687 rc = RTDirCreate(szTmpPath, 0700);
688 if (RT_FAILURE(rc))
689 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create temporary directory: %Rrc ('%s')", rc, szTmpPath);
690
691 RTMANIFEST hValidManifest = NIL_RTMANIFEST;
692 RTEXITCODE rcExit = ValidateExtPackTarball(hTarballFile, pszName, pszTarball, &hValidManifest);
693 if (rcExit == RTEXITCODE_SUCCESS)
694 rcExit = UnpackExtPack(hTarballFile, szTmpPath, hValidManifest, pszTarball);
695 if (rcExit == RTEXITCODE_SUCCESS)
696 rcExit = ValidateUnpackedExtPack(szTmpPath, pszTarball, pszName);
697 if (rcExit == RTEXITCODE_SUCCESS)
698 rcExit = SetExtPackPermissions(szTmpPath);
699 RTManifestRelease(hValidManifest);
700
701 if (rcExit == RTEXITCODE_SUCCESS)
702 {
703 rc = RTDirRename(szTmpPath, szFinalPath, RTPATHRENAME_FLAGS_NO_REPLACE);
704 if ( RT_FAILURE(rc)
705 && fReplace
706 && RTDirExists(szFinalPath))
707 {
708 /* Automatic uninstall if --replace was given. */
709 rcExit = CommonUninstallWorker(szFinalPath);
710 if (rcExit == RTEXITCODE_SUCCESS)
711 rc = RTDirRename(szTmpPath, szFinalPath, RTPATHRENAME_FLAGS_NO_REPLACE);
712 }
713 if (RT_SUCCESS(rc))
714 RTMsgInfo("Successfully installed '%s' (%s)", pszName, pszTarball);
715 else if (rcExit == RTEXITCODE_SUCCESS)
716 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
717 "Failed to rename the temporary directory to the final one: %Rrc ('%s' -> '%s')",
718 rc, szTmpPath, szFinalPath);
719 }
720
721 /*
722 * Clean up the temporary directory on failure.
723 */
724 if (rcExit != RTEXITCODE_SUCCESS)
725 RemoveExtPackDir(szTmpPath, true /*fTemporary*/);
726
727 return rcExit;
728}
729
730
731/**
732 * Implements the 'install' command.
733 *
734 * @returns The program exit code.
735 * @param argc The number of program arguments.
736 * @param argv The program arguments.
737 */
738static RTEXITCODE DoInstall(int argc, char **argv)
739{
740 /*
741 * Parse the parameters.
742 *
743 * Note! The --base-dir and --cert-dir are only for checking that the
744 * caller and this help applications have the same idea of where
745 * things are. Likewise, the --name is for verifying assumptions
746 * the caller made about the name. The optional --tarball-fd option
747 * is just for easing the paranoia on the user side.
748 */
749 static const RTGETOPTDEF s_aOptions[] =
750 {
751 { "--base-dir", 'b', RTGETOPT_REQ_STRING },
752 { "--cert-dir", 'c', RTGETOPT_REQ_STRING },
753 { "--name", 'n', RTGETOPT_REQ_STRING },
754 { "--tarball", 't', RTGETOPT_REQ_STRING },
755 { "--tarball-fd", 'd', RTGETOPT_REQ_UINT64 },
756 { "--replace", 'r', RTGETOPT_REQ_NOTHING }
757 };
758 RTGETOPTSTATE GetState;
759 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /*fFlags*/);
760 if (RT_FAILURE(rc))
761 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
762
763 const char *pszBaseDir = NULL;
764 const char *pszCertDir = NULL;
765 const char *pszName = NULL;
766 const char *pszTarball = NULL;
767 RTFILE hTarballFileOpt = NIL_RTFILE;
768 bool fReplace = false;
769 RTGETOPTUNION ValueUnion;
770 int ch;
771 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
772 {
773 switch (ch)
774 {
775 case 'b':
776 if (pszBaseDir)
777 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --base-dir options");
778 pszBaseDir = ValueUnion.psz;
779 if (!IsValidBaseDir(pszBaseDir))
780 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid base directory: '%s'", pszBaseDir);
781 break;
782
783 case 'c':
784 if (pszCertDir)
785 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --cert-dir options");
786 pszCertDir = ValueUnion.psz;
787 if (!IsValidCertificateDir(pszCertDir))
788 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid certificate directory: '%s'", pszCertDir);
789 break;
790
791 case 'n':
792 if (pszName)
793 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --name options");
794 pszName = ValueUnion.psz;
795 if (!VBoxExtPackIsValidName(pszName))
796 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid extension pack name: '%s'", pszName);
797 break;
798
799 case 't':
800 if (pszTarball)
801 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --tarball options");
802 pszTarball = ValueUnion.psz;
803 break;
804
805 case 'd':
806 {
807 if (hTarballFileOpt != NIL_RTFILE)
808 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --tarball-fd options");
809 RTHCUINTPTR hNative = (RTHCUINTPTR)ValueUnion.u64;
810 if (hNative != ValueUnion.u64)
811 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The --tarball-fd value is out of range: %#RX64", ValueUnion.u64);
812 rc = RTFileFromNative(&hTarballFileOpt, hNative);
813 if (RT_FAILURE(rc))
814 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "RTFileFromNative failed on --target-fd value: %Rrc", rc);
815 break;
816 }
817
818 case 'r':
819 fReplace = true;
820 break;
821
822 case 'h':
823 case 'V':
824 return DoStandardOption(ch);
825
826 default:
827 return RTGetOptPrintError(ch, &ValueUnion);
828 }
829 }
830 if (!pszName)
831 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --name option");
832 if (!pszBaseDir)
833 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --base-dir option");
834 if (!pszCertDir)
835 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --cert-dir option");
836 if (!pszTarball)
837 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --tarball option");
838
839 /*
840 * Ok, down to business.
841 */
842 iprt::MiniString *pstrMangledName = VBoxExtPackMangleName(pszName);
843 if (!pstrMangledName)
844 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to mangle name ('%s)", pszName);
845
846 RTEXITCODE rcExit;
847 RTFILE hTarballFile;
848 rc = RTFileOpen(&hTarballFile, pszTarball, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
849 if (RT_SUCCESS(rc))
850 {
851 rcExit = DoInstall2(pszBaseDir, pszCertDir, pszTarball, hTarballFile, hTarballFileOpt,
852 pszName, pstrMangledName->c_str(), fReplace);
853 RTFileClose(hTarballFile);
854 }
855 else
856 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open the extension pack tarball: %Rrc ('%s')", rc, pszTarball);
857
858 delete pstrMangledName;
859 return rcExit;
860}
861
862
863/**
864 * Implements the 'uninstall' command.
865 *
866 * @returns The program exit code.
867 * @param argc The number of program arguments.
868 * @param argv The program arguments.
869 */
870static RTEXITCODE DoUninstall(int argc, char **argv)
871{
872 /*
873 * Parse the parameters.
874 *
875 * Note! The --base-dir is only for checking that the caller and this help
876 * applications have the same idea of where things are.
877 */
878 static const RTGETOPTDEF s_aOptions[] =
879 {
880 { "--base-dir", 'b', RTGETOPT_REQ_STRING },
881 { "--name", 'n', RTGETOPT_REQ_STRING }
882 };
883 RTGETOPTSTATE GetState;
884 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /*fFlags*/);
885 if (RT_FAILURE(rc))
886 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
887
888 const char *pszBaseDir = NULL;
889 const char *pszName = NULL;
890 RTGETOPTUNION ValueUnion;
891 int ch;
892 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
893 {
894 switch (ch)
895 {
896 case 'b':
897 if (pszBaseDir)
898 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --base-dir options");
899 pszBaseDir = ValueUnion.psz;
900 if (!IsValidBaseDir(pszBaseDir))
901 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid base directory: '%s'", pszBaseDir);
902 break;
903
904 case 'n':
905 if (pszName)
906 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --name options");
907 pszName = ValueUnion.psz;
908 if (!VBoxExtPackIsValidName(pszName))
909 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid extension pack name: '%s'", pszName);
910 break;
911
912 case 'h':
913 case 'V':
914 return DoStandardOption(ch);
915
916 default:
917 return RTGetOptPrintError(ch, &ValueUnion);
918 }
919 }
920 if (!pszName)
921 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --name option");
922 if (!pszBaseDir)
923 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --base-dir option");
924
925 /*
926 * Mangle the name so we can construct the directory names.
927 */
928 iprt::MiniString *pstrMangledName = VBoxExtPackMangleName(pszName);
929 if (!pstrMangledName)
930 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to mangle name ('%s)", pszName);
931 iprt::MiniString strMangledName(*pstrMangledName);
932 delete pstrMangledName;
933
934 /*
935 * Ok, down to business.
936 */
937 /* Check that it exists. */
938 char szExtPackDir[RTPATH_MAX];
939 rc = RTPathJoin(szExtPackDir, sizeof(szExtPackDir), pszBaseDir, strMangledName.c_str());
940 if (RT_FAILURE(rc))
941 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to construct extension pack path: %Rrc", rc);
942
943 if (!RTDirExists(szExtPackDir))
944 {
945 RTMsgInfo("Extension pack not installed. Nothing to do.");
946 return RTEXITCODE_SUCCESS;
947 }
948
949 RTEXITCODE rcExit = CommonUninstallWorker(szExtPackDir);
950 if (rcExit == RTEXITCODE_SUCCESS)
951 RTMsgInfo("Successfully removed extension pack '%s'\n", pszName);
952
953 return rcExit;
954}
955
956/**
957 * Implements the 'cleanup' command.
958 *
959 * @returns The program exit code.
960 * @param argc The number of program arguments.
961 * @param argv The program arguments.
962 */
963static RTEXITCODE DoCleanup(int argc, char **argv)
964{
965 /*
966 * Parse the parameters.
967 *
968 * Note! The --base-dir is only for checking that the caller and this help
969 * applications have the same idea of where things are.
970 */
971 static const RTGETOPTDEF s_aOptions[] =
972 {
973 { "--base-dir", 'b', RTGETOPT_REQ_STRING },
974 };
975 RTGETOPTSTATE GetState;
976 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /*fFlags*/);
977 if (RT_FAILURE(rc))
978 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
979
980 const char *pszBaseDir = NULL;
981 RTGETOPTUNION ValueUnion;
982 int ch;
983 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
984 {
985 switch (ch)
986 {
987 case 'b':
988 if (pszBaseDir)
989 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --base-dir options");
990 pszBaseDir = ValueUnion.psz;
991 if (!IsValidBaseDir(pszBaseDir))
992 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid base directory: '%s'", pszBaseDir);
993 break;
994
995 case 'h':
996 case 'V':
997 return DoStandardOption(ch);
998
999 default:
1000 return RTGetOptPrintError(ch, &ValueUnion);
1001 }
1002 }
1003 if (!pszBaseDir)
1004 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --base-dir option");
1005
1006 /*
1007 * Ok, down to business.
1008 */
1009 PRTDIR pDir;
1010 rc = RTDirOpen(&pDir, pszBaseDir);
1011 if (RT_FAILURE(rc))
1012 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed open the base directory: %Rrc ('%s')", rc, pszBaseDir);
1013
1014 uint32_t cCleaned = 0;
1015 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1016 for (;;)
1017 {
1018 RTDIRENTRYEX Entry;
1019 rc = RTDirReadEx(pDir, &Entry, NULL /*pcbDirEntry*/, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1020 if (RT_FAILURE(rc))
1021 {
1022 if (rc != VERR_NO_MORE_FILES)
1023 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDirReadEx returns %Rrc", rc);
1024 break;
1025 }
1026
1027 /*
1028 * Only directories which conform with our temporary install/uninstall
1029 * naming scheme are candidates for cleaning.
1030 */
1031 if ( RTFS_IS_DIRECTORY(Entry.Info.Attr.fMode)
1032 && strcmp(Entry.szName, ".") != 0
1033 && strcmp(Entry.szName, "..") != 0)
1034 {
1035 bool fCandidate = false;
1036 char *pszMarker = strstr(Entry.szName, "-_-");
1037 if ( pszMarker
1038 && ( !strcmp(pszMarker, "-_-uninst")
1039 || !strncmp(pszMarker, "-_-inst", sizeof("-_-inst") - 1)))
1040 fCandidate = VBoxExtPackIsValidMangledName(Entry.szName, pszMarker - &Entry.szName[0]);
1041 if (fCandidate)
1042 {
1043 /*
1044 * Recursive delete, safe.
1045 */
1046 char szPath[RTPATH_MAX];
1047 rc = RTPathJoin(szPath, sizeof(szPath), pszBaseDir, Entry.szName);
1048 if (RT_SUCCESS(rc))
1049 {
1050 RTEXITCODE rcExit2 = RemoveExtPackDir(szPath, true /*fTemporary*/);
1051 if (rcExit2 == RTEXITCODE_SUCCESS)
1052 RTMsgInfo("Successfully removed '%s'.", Entry.szName);
1053 else if (rcExit == RTEXITCODE_SUCCESS)
1054 rcExit = rcExit2;
1055 }
1056 else
1057 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathJoin failed with %Rrc for '%s'", rc, Entry.szName);
1058 cCleaned++;
1059 }
1060 }
1061 }
1062 RTDirClose(pDir);
1063 if (!cCleaned)
1064 RTMsgInfo("Nothing to clean.");
1065 return rcExit;
1066}
1067
1068#ifdef WITH_ELEVATION
1069
1070#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_DARWIN)
1071/**
1072 * Looks in standard locations for a suitable exec tool.
1073 *
1074 * @returns true if found, false if not.
1075 * @param pszPath Where to store the path to the tool on
1076 * successs.
1077 * @param cbPath The size of the buffer @a pszPath points to.
1078 * @param pszName The name of the tool we're looking for.
1079 */
1080static bool FindExecTool(char *pszPath, size_t cbPath, const char *pszName)
1081{
1082 static const char * const s_apszPaths[] =
1083 {
1084 "/bin",
1085 "/usr/bin",
1086 "/usr/local/bin",
1087 "/sbin",
1088 "/usr/sbin",
1089 "/usr/local/sbin",
1090#ifdef RT_OS_SOLARIS
1091 "/usr/sfw/bin",
1092 "/usr/gnu/bin",
1093 "/usr/xpg4/bin",
1094 "/usr/xpg6/bin",
1095 "/usr/openwin/bin",
1096 "/usr/ucb"
1097#endif
1098 };
1099
1100 for (unsigned i = 0; i < RT_ELEMENTS(s_apszPaths); i++)
1101 {
1102 int rc = RTPathJoin(pszPath, cbPath, s_apszPaths[i], pszName);
1103 if (RT_SUCCESS(rc))
1104 {
1105 RTFSOBJINFO ObjInfo;
1106 rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_FOLLOW_LINK);
1107 if (RT_SUCCESS(rc))
1108 {
1109 if (!(ObjInfo.Attr.fMode & RTFS_UNIX_IWOTH))
1110 return true;
1111 }
1112 }
1113 }
1114 return false;
1115}
1116#endif
1117
1118
1119/**
1120 * Copies the content of a file to a stream.
1121 *
1122 * @param hSrc The source file.
1123 * @param pDst The destination stream.
1124 * @param fComplain Whether to complain about errors (i.e. is this
1125 * stderr, if not keep the trap shut because it
1126 * may be missing when running under VBoxSVC.)
1127 */
1128static void CopyFileToStdXxx(RTFILE hSrc, PRTSTREAM pDst, bool fComplain)
1129{
1130 int rc;
1131 for (;;)
1132 {
1133 char abBuf[0x1000];
1134 size_t cbRead;
1135 rc = RTFileRead(hSrc, abBuf, sizeof(abBuf), &cbRead);
1136 if (RT_FAILURE(rc))
1137 {
1138 RTMsgError("RTFileRead failed: %Rrc", rc);
1139 break;
1140 }
1141 if (!cbRead)
1142 break;
1143 rc = RTStrmWrite(pDst, abBuf, cbRead);
1144 if (RT_FAILURE(rc))
1145 {
1146 if (fComplain)
1147 RTMsgError("RTStrmWrite failed: %Rrc", rc);
1148 break;
1149 }
1150 }
1151 rc = RTStrmFlush(pDst);
1152 if (RT_FAILURE(rc) && fComplain)
1153 RTMsgError("RTStrmFlush failed: %Rrc", rc);
1154}
1155
1156
1157/**
1158 * Relaunches ourselves as a elevated process using platform specific facilities.
1159 *
1160 * @returns Program exit code.
1161 * @param pszExecPath The executable path.
1162 * @param papszArgs The arguments.
1163 * @param cSuArgs The number of argument entries reserved for the
1164 * 'su' like programs at the start of papszArgs.
1165 * @param cMyArgs The number of arguments following @a cSuArgs.
1166 * @param iCmd The command that is being executed. (For
1167 * selecting messages.)
1168 * @param pszDisplayInfoHack Display information hack. Platform specific++.
1169 */
1170static RTEXITCODE RelaunchElevatedNative(const char *pszExecPath, const char **papszArgs, int cSuArgs, int cMyArgs,
1171 int iCmd, const char *pszDisplayInfoHack)
1172{
1173 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
1174#ifdef RT_OS_WINDOWS
1175 NOREF(iCmd);
1176
1177 MSG Msg;
1178 PeekMessage(&Msg, NULL, 0, 0, PM_NOREMOVE);
1179 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
1180
1181 SHELLEXECUTEINFOW Info;
1182
1183 Info.cbSize = sizeof(Info);
1184 Info.fMask = SEE_MASK_NOCLOSEPROCESS;
1185 Info.hwnd = NULL;
1186 Info.lpVerb = L"runas";
1187 int rc = RTStrToUtf16(pszExecPath, (PRTUTF16 *)&Info.lpFile);
1188 if (RT_SUCCESS(rc))
1189 {
1190 char *pszCmdLine;
1191 rc = RTGetOptArgvToString(&pszCmdLine, &papszArgs[cSuArgs + 1], RTGETOPTARGV_CNV_QUOTE_MS_CRT);
1192 if (RT_SUCCESS(rc))
1193 {
1194 rc = RTStrToUtf16(pszCmdLine, (PRTUTF16 *)&Info.lpParameters);
1195 if (RT_SUCCESS(rc))
1196 {
1197 Info.lpDirectory = NULL;
1198 Info.nShow = SW_SHOWMAXIMIZED;
1199 Info.hInstApp = NULL;
1200 Info.lpIDList = NULL;
1201 Info.lpClass = NULL;
1202 Info.hkeyClass = NULL;
1203 Info.dwHotKey = 0;
1204 Info.hMonitor = NULL;
1205 Info.hProcess = INVALID_HANDLE_VALUE;
1206
1207#if 0 /* This deadlocks with the GUI because the GUI thread is stuck in the API call :/ */
1208 /* Apply display hacks. */
1209 if (pszDisplayInfoHack)
1210 {
1211 const char *pszArg = strstr(pszDisplayInfoHack, "hwnd=");
1212 if (pszArg)
1213 {
1214 uint64_t u64Hwnd;
1215 rc = RTStrToUInt64Ex(pszArg + sizeof("hwnd=") - 1, NULL, 0, &u64Hwnd);
1216 if (RT_SUCCESS(rc))
1217 {
1218 HWND hwnd = (HWND)(uintptr_t)u64Hwnd;
1219 Info.hwnd = hwnd;
1220 Info.hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
1221 }
1222 }
1223 }
1224 if (Info.hMonitor == NULL)
1225 {
1226 POINT Pt = {0,0};
1227 Info.hMonitor = MonitorFromPoint(Pt, MONITOR_DEFAULTTOPRIMARY);
1228 }
1229 if (Info.hMonitor != NULL)
1230 Info.fMask |= SEE_MASK_HMONITOR;
1231 #endif
1232
1233 if (ShellExecuteExW(&Info))
1234 {
1235 if (Info.hProcess != INVALID_HANDLE_VALUE)
1236 {
1237 /*
1238 * Wait for the process, make sure the deal with messages.
1239 */
1240 for (;;)
1241 {
1242 DWORD dwRc = MsgWaitForMultipleObjects(1, &Info.hProcess, FALSE, 5000/*ms*/, QS_ALLEVENTS);
1243 if (dwRc == WAIT_OBJECT_0)
1244 break;
1245 if ( dwRc != WAIT_TIMEOUT
1246 && dwRc != WAIT_OBJECT_0 + 1)
1247 {
1248 RTMsgError("MsgWaitForMultipleObjects returned: %#x (%d), err=%u", dwRc, dwRc, GetLastError());
1249 break;
1250 }
1251 MSG Msg;
1252 while (PeekMessageW(&Msg, NULL, 0, 0, PM_REMOVE))
1253 {
1254 TranslateMessage(&Msg);
1255 DispatchMessageW(&Msg);
1256 }
1257 }
1258
1259 DWORD dwExitCode;
1260 if (GetExitCodeProcess(Info.hProcess, &dwExitCode))
1261 {
1262 if (dwExitCode >= 0 && dwExitCode < 128)
1263 rcExit = (RTEXITCODE)dwExitCode;
1264 else
1265 rcExit = RTEXITCODE_FAILURE;
1266 }
1267 CloseHandle(Info.hProcess);
1268 }
1269 else
1270 RTMsgError("ShellExecuteExW return INVALID_HANDLE_VALUE as Info.hProcess");
1271 }
1272 else
1273 RTMsgError("ShellExecuteExW failed: %u (%#x)", GetLastError(), GetLastError());
1274
1275
1276 RTUtf16Free((PRTUTF16)Info.lpParameters);
1277 }
1278 RTStrFree(pszCmdLine);
1279 }
1280
1281 RTUtf16Free((PRTUTF16)Info.lpFile);
1282 }
1283 else
1284 RTMsgError("RTStrToUtf16 failed: %Rc", rc);
1285
1286#elif defined(RT_OS_DARWIN)
1287 char szIconName[RTPATH_MAX];
1288 int rc = RTPathAppPrivateArch(szIconName, sizeof(szIconName));
1289 if (RT_SUCCESS(rc))
1290 rc = RTPathAppend(szIconName, sizeof(szIconName), "../Resources/virtualbox.png");
1291 if (RT_FAILURE(rc))
1292 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to construct icon path: %Rrc", rc);
1293
1294 AuthorizationRef AuthRef;
1295 OSStatus orc = AuthorizationCreate(NULL, 0, kAuthorizationFlagDefaults, &AuthRef);
1296 if (orc == errAuthorizationSuccess)
1297 {
1298 /*
1299 * Preautorize the privileged execution of ourselves.
1300 */
1301 AuthorizationItem AuthItem = { kAuthorizationRightExecute, 0, NULL, 0 };
1302 AuthorizationRights AuthRights = { 1, &AuthItem };
1303
1304 NOREF(iCmd);
1305 static char s_szPrompt[] = "VirtualBox needs further rights to make changes to your installation.\n\n";
1306 AuthorizationItem aAuthEnvItems[] =
1307 {
1308 { kAuthorizationEnvironmentPrompt, strlen(s_szPrompt), s_szPrompt, 0 },
1309 { kAuthorizationEnvironmentIcon, strlen(szIconName), szIconName, 0 }
1310 };
1311 AuthorizationEnvironment AuthEnv = { RT_ELEMENTS(aAuthEnvItems), aAuthEnvItems };
1312
1313 orc = AuthorizationCopyRights(AuthRef, &AuthRights, &AuthEnv,
1314 kAuthorizationFlagPreAuthorize | kAuthorizationFlagInteractionAllowed
1315 | kAuthorizationFlagExtendRights,
1316 NULL);
1317 if (orc == errAuthorizationSuccess)
1318 {
1319 /*
1320 * Execute with extra permissions
1321 */
1322 FILE *pSocketStrm;
1323 orc = AuthorizationExecuteWithPrivileges(AuthRef, pszExecPath, kAuthorizationFlagDefaults,
1324 (char * const *)&papszArgs[cSuArgs + 3],
1325 &pSocketStrm);
1326 if (orc == errAuthorizationSuccess)
1327 {
1328 /*
1329 * Read the output of the tool, the read will fail when it quits.
1330 */
1331 for (;;)
1332 {
1333 char achBuf[1024];
1334 size_t cbRead = fread(achBuf, 1, sizeof(achBuf), pSocketStrm);
1335 if (!cbRead)
1336 break;
1337 fwrite(achBuf, 1, cbRead, stdout);
1338 }
1339 rcExit = RTEXITCODE_SUCCESS;
1340 fclose(pSocketStrm);
1341 }
1342 else
1343 RTMsgError("AuthorizationExecuteWithPrivileges failed: %d", orc);
1344 }
1345 else if (orc == errAuthorizationCanceled)
1346 RTMsgError("Authorization canceled by the user");
1347 else
1348 RTMsgError("AuthorizationCopyRights failed: %d", orc);
1349 AuthorizationFree(AuthRef, kAuthorizationFlagDefaults);
1350 }
1351 else
1352 RTMsgError("AuthorizationCreate failed: %d", orc);
1353
1354#else
1355
1356 /*
1357 * Several of the alternatives below will require a command line.
1358 */
1359 char *pszCmdLine;
1360 int rc = RTGetOptArgvToString(&pszCmdLine, &papszArgs[cSuArgs], RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
1361 if (RT_FAILURE(rc))
1362 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptArgvToString failed: %Rrc");
1363
1364 /*
1365 * Look for various standard stuff for executing a program as root.
1366 *
1367 * N.B. When adding new arguments, please make 100% sure RelaunchElevated
1368 * allocates enough array entries.
1369 *
1370 * TODO: Feel free to contribute code for using PolicyKit directly.
1371 */
1372 bool fHaveDisplayVar = RTEnvExist("DISPLAY");
1373 int iSuArg = cSuArgs;
1374 char szExecTool[260];
1375 char szXterm[260];
1376
1377 /*
1378 * kdesudo is available on KDE3/KDE4
1379 */
1380 if (fHaveDisplayVar && FindExecTool(szExecTool, sizeof(szExecTool), "kdesudo"))
1381 {
1382 iSuArg = cSuArgs - 4;
1383 papszArgs[cSuArgs - 4] = szExecTool;
1384 papszArgs[cSuArgs - 3] = "--comment";
1385 papszArgs[cSuArgs - 2] = iCmd == CMD_INSTALL
1386 ? "VirtualBox extension pack installer"
1387 : iCmd == CMD_UNINSTALL
1388 ? "VirtualBox extension pack uninstaller"
1389 : "VirtualBox extension pack maintainer";
1390 papszArgs[cSuArgs - 1] = "--";
1391 }
1392 /*
1393 * gksu is our favorite as it is very well integrated.
1394 */
1395 else if (fHaveDisplayVar && FindExecTool(szExecTool, sizeof(szExecTool), "gksu"))
1396 {
1397#if 0 /* older gksu does not grok --description nor '--' and multiple args. */
1398 iSuArg = cSuArgs - 4;
1399 papszArgs[cSuArgs - 4] = szExecTool;
1400 papszArgs[cSuArgs - 3] = "--description";
1401 papszArgs[cSuArgs - 2] = iCmd == CMD_INSTALL
1402 ? "VirtualBox extension pack installer"
1403 : iCmd == CMD_UNINSTALL
1404 ? "VirtualBox extension pack uninstaller"
1405 : "VirtualBox extension pack maintainer";
1406 papszArgs[cSuArgs - 1] = "--";
1407#elif defined(RT_OS_SOLARIS) /* Force it not to use pfexec as it won't wait then. */
1408 iSuArg = cSuArgs - 4;
1409 papszArgs[cSuArgs - 4] = szExecTool;
1410 papszArgs[cSuArgs - 3] = "-au";
1411 papszArgs[cSuArgs - 2] = "root";
1412 papszArgs[cSuArgs - 1] = pszCmdLine;
1413 papszArgs[cSuArgs] = NULL;
1414#else
1415 iSuArg = cSuArgs - 2;
1416 papszArgs[cSuArgs - 2] = szExecTool;
1417 papszArgs[cSuArgs - 1] = pszCmdLine;
1418 papszArgs[cSuArgs] = NULL;
1419#endif
1420 }
1421 /*
1422 * pkexec may work for ssh console sessions as well if the right agents
1423 * are installed. However it is very generic and does not allow for any
1424 * custom messages. Thus it comes after gksu.
1425 */
1426 else if (FindExecTool(szExecTool, sizeof(szExecTool), "pkexec"))
1427 {
1428 iSuArg = cSuArgs - 1;
1429 papszArgs[cSuArgs - 1] = szExecTool;
1430 }
1431 /*
1432 * The ultimate fallback is running 'su -' within an xterm. We use the
1433 * title of the xterm to tell what is going on.
1434 */
1435 else if ( fHaveDisplayVar
1436 && FindExecTool(szExecTool, sizeof(szExecTool), "su")
1437 && FindExecTool(szXterm, sizeof(szXterm), "xterm"))
1438 {
1439 iSuArg = cSuArgs - 9;
1440 papszArgs[cSuArgs - 9] = szXterm;
1441 papszArgs[cSuArgs - 8] = "-T";
1442 papszArgs[cSuArgs - 7] = iCmd == CMD_INSTALL
1443 ? "VirtualBox extension pack installer - su"
1444 : iCmd == CMD_UNINSTALL
1445 ? "VirtualBox extension pack uninstaller - su"
1446 : "VirtualBox extension pack maintainer - su";
1447 papszArgs[cSuArgs - 6] = "-e";
1448 papszArgs[cSuArgs - 5] = szExecTool;
1449 papszArgs[cSuArgs - 4] = "-";
1450 papszArgs[cSuArgs - 3] = "root";
1451 papszArgs[cSuArgs - 2] = "-c";
1452 papszArgs[cSuArgs - 1] = pszCmdLine;
1453 papszArgs[cSuArgs] = NULL;
1454 }
1455 else if (fHaveDisplayVar)
1456 RTMsgError("Unable to locate 'pkexec', 'gksu' or 'su+xterm'. Try perform the operation using VBoxManage running as root");
1457 else
1458 RTMsgError("Unable to locate 'pkexec'. Try perform the operation using VBoxManage running as root");
1459 if (iSuArg != cSuArgs)
1460 {
1461 AssertRelease(iSuArg >= 0);
1462
1463 /*
1464 * Argument list constructed, execute it and wait for the exec
1465 * program to complete.
1466 */
1467 RTPROCESS hProcess;
1468 rc = RTProcCreateEx(papszArgs[iSuArg], &papszArgs[iSuArg], RTENV_DEFAULT, 0 /*fFlags*/,
1469 NULL /*phStdIn*/, NULL /*phStdOut*/, NULL /*phStdErr*/, NULL /*pszAsUser*/, NULL /*pszPassword*/,
1470 &hProcess);
1471 if (RT_SUCCESS(rc))
1472 {
1473 RTPROCSTATUS Status;
1474 rc = RTProcWait(hProcess, RTPROCWAIT_FLAGS_BLOCK, &Status);
1475 if (RT_SUCCESS(rc))
1476 {
1477 if (Status.enmReason == RTPROCEXITREASON_NORMAL)
1478 rcExit = (RTEXITCODE)Status.iStatus;
1479 else
1480 rcExit = RTEXITCODE_FAILURE;
1481 }
1482 else
1483 RTMsgError("Error while waiting for '%s': %Rrc", papszArgs[iSuArg], rc);
1484 }
1485 else
1486 RTMsgError("Failed to execute '%s': %Rrc", papszArgs[iSuArg], rc);
1487 }
1488 RTStrFree(pszCmdLine);
1489
1490#endif
1491 return rcExit;
1492}
1493
1494
1495/**
1496 * Relaunches ourselves as a elevated process using platform specific facilities.
1497 *
1498 * @returns Program exit code.
1499 * @param argc The number of arguments.
1500 * @param argv The arguments.
1501 * @param iCmd The command that is being executed.
1502 * @param pszDisplayInfoHack Display information hack. Platform specific++.
1503 */
1504static RTEXITCODE RelaunchElevated(int argc, char **argv, int iCmd, const char *pszDisplayInfoHack)
1505{
1506 /*
1507 * We need the executable name later, so get it now when it's easy to quit.
1508 */
1509 char szExecPath[RTPATH_MAX];
1510 if (!RTProcGetExecutablePath(szExecPath,sizeof(szExecPath)))
1511 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcGetExecutablePath failed");
1512
1513 /*
1514 * Create a couple of temporary files for stderr and stdout.
1515 */
1516 char szTempDir[RTPATH_MAX - sizeof("/stderr")];
1517 int rc = RTPathTemp(szTempDir, sizeof(szTempDir));
1518 if (RT_FAILURE(rc))
1519 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathTemp failed: %Rrc", rc);
1520 rc = RTPathAppend(szTempDir, sizeof(szTempDir), "VBoxExtPackHelper-XXXXXX");
1521 if (RT_FAILURE(rc))
1522 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAppend failed: %Rrc", rc);
1523 rc = RTDirCreateTemp(szTempDir);
1524 if (RT_FAILURE(rc))
1525 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDirCreateTemp failed: %Rrc", rc);
1526
1527 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
1528 char szStdOut[RTPATH_MAX];
1529 char szStdErr[RTPATH_MAX];
1530 rc = RTPathJoin(szStdOut, sizeof(szStdOut), szTempDir, "stdout");
1531 if (RT_SUCCESS(rc))
1532 rc = RTPathJoin(szStdErr, sizeof(szStdErr), szTempDir, "stderr");
1533 if (RT_SUCCESS(rc))
1534 {
1535 RTFILE hStdOut;
1536 rc = RTFileOpen(&hStdOut, szStdOut, RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_NONE
1537 | (0600 << RTFILE_O_CREATE_MODE_SHIFT));
1538 if (RT_SUCCESS(rc))
1539 {
1540 RTFILE hStdErr;
1541 rc = RTFileOpen(&hStdErr, szStdErr, RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_NONE
1542 | (0600 << RTFILE_O_CREATE_MODE_SHIFT));
1543 if (RT_SUCCESS(rc))
1544 {
1545 /*
1546 * Insert the --elevated and stdout/err names into the argument
1547 * list. Note that darwin skips the --stdout bit, so don't
1548 * change the order here.
1549 */
1550 int const cSuArgs = 12;
1551 int cArgs = argc + 5 + 1;
1552 char const **papszArgs = (char const **)RTMemTmpAllocZ((cSuArgs + cArgs + 1) * sizeof(const char *));
1553 if (papszArgs)
1554 {
1555 int iDst = cSuArgs;
1556 papszArgs[iDst++] = argv[0];
1557 papszArgs[iDst++] = "--stdout";
1558 papszArgs[iDst++] = szStdOut;
1559 papszArgs[iDst++] = "--stderr";
1560 papszArgs[iDst++] = szStdErr;
1561 papszArgs[iDst++] = "--elevated";
1562 for (int iSrc = 1; iSrc <= argc; iSrc++)
1563 papszArgs[iDst++] = argv[iSrc];
1564
1565 /*
1566 * Do the platform specific process execution (waiting included).
1567 */
1568 rcExit = RelaunchElevatedNative(szExecPath, papszArgs, cSuArgs, cArgs, iCmd, pszDisplayInfoHack);
1569
1570 /*
1571 * Copy the standard files to our standard handles.
1572 */
1573 CopyFileToStdXxx(hStdErr, g_pStdErr, true /*fComplain*/);
1574 CopyFileToStdXxx(hStdOut, g_pStdOut, false);
1575
1576 RTMemTmpFree(papszArgs);
1577 }
1578
1579 RTFileClose(hStdErr);
1580 RTFileDelete(szStdErr);
1581 }
1582 RTFileClose(hStdOut);
1583 RTFileDelete(szStdOut);
1584 }
1585 }
1586 RTDirRemove(szTempDir);
1587
1588 return rcExit;
1589}
1590
1591
1592/**
1593 * Checks if the process is elevated or not.
1594 *
1595 * @returns RTEXITCODE_SUCCESS if preconditions are fine,
1596 * otherwise error message + RTEXITCODE_FAILURE.
1597 * @param pfElevated Where to store the elevation indicator.
1598 */
1599static RTEXITCODE ElevationCheck(bool *pfElevated)
1600{
1601 *pfElevated = false;
1602
1603# if defined(RT_OS_WINDOWS)
1604 /** @todo This should probably check if UAC is diabled and if we are
1605 * Administrator first. Also needs to check for Vista+ first, probably.
1606 */
1607 DWORD cb;
1608 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1609 HANDLE hToken;
1610 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
1611 return RTMsgErrorExit(RTEXITCODE_FAILURE, "OpenProcessToken failed: %u (%#x)", GetLastError(), GetLastError());
1612
1613 /*
1614 * Check if we're member of the Administrators group. If we aren't, there
1615 * is no way to elevate ourselves to system admin.
1616 * N.B. CheckTokenMembership does not do the job here (due to attributes?).
1617 */
1618 BOOL fIsAdmin = FALSE;
1619 SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
1620 PSID pAdminGrpSid;
1621 if (AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminGrpSid))
1622 {
1623# ifdef DEBUG
1624 char *pszAdminGrpSid = NULL;
1625 ConvertSidToStringSid(pAdminGrpSid, &pszAdminGrpSid);
1626# endif
1627
1628 if ( !GetTokenInformation(hToken, TokenGroups, NULL, 0, &cb)
1629 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1630 {
1631 PTOKEN_GROUPS pTokenGroups = (PTOKEN_GROUPS)RTMemAllocZ(cb);
1632 if (GetTokenInformation(hToken, TokenGroups, pTokenGroups, cb, &cb))
1633 {
1634 for (DWORD iGrp = 0; iGrp < pTokenGroups->GroupCount; iGrp++)
1635 {
1636# ifdef DEBUG
1637 char *pszGrpSid = NULL;
1638 ConvertSidToStringSid(pTokenGroups->Groups[iGrp].Sid, &pszGrpSid);
1639# endif
1640 if (EqualSid(pAdminGrpSid, pTokenGroups->Groups[iGrp].Sid))
1641 {
1642 /* That it's listed is enough I think, ignore attributes. */
1643 fIsAdmin = TRUE;
1644 break;
1645 }
1646 }
1647 }
1648 else
1649 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "GetTokenInformation(TokenGroups,cb) failed: %u (%#x)", GetLastError(), GetLastError());
1650 RTMemFree(pTokenGroups);
1651 }
1652 else
1653 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "GetTokenInformation(TokenGroups,0) failed: %u (%#x)", GetLastError(), GetLastError());
1654
1655 FreeSid(pAdminGrpSid);
1656 }
1657 else
1658 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "AllocateAndInitializeSid failed: %u (%#x)", GetLastError(), GetLastError());
1659 if (fIsAdmin)
1660 {
1661 /*
1662 * Check the integrity level (Vista / UAC).
1663 */
1664# define MY_SECURITY_MANDATORY_HIGH_RID 0x00003000L
1665# define MY_TokenIntegrityLevel ((TOKEN_INFORMATION_CLASS)25)
1666 if ( !GetTokenInformation(hToken, MY_TokenIntegrityLevel, NULL, 0, &cb)
1667 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1668 {
1669 PSID_AND_ATTRIBUTES pSidAndAttr = (PSID_AND_ATTRIBUTES)RTMemAlloc(cb);
1670 if (GetTokenInformation(hToken, MY_TokenIntegrityLevel, pSidAndAttr, cb, &cb))
1671 {
1672 DWORD dwIntegrityLevel = *GetSidSubAuthority(pSidAndAttr->Sid, *GetSidSubAuthorityCount(pSidAndAttr->Sid) - 1U);
1673
1674 if (dwIntegrityLevel >= MY_SECURITY_MANDATORY_HIGH_RID)
1675 *pfElevated = true;
1676 }
1677 else
1678 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "GetTokenInformation failed: %u (%#x)", GetLastError(), GetLastError());
1679 RTMemFree(pSidAndAttr);
1680 }
1681 else if ( GetLastError() == ERROR_INVALID_PARAMETER
1682 || GetLastError() == ERROR_NOT_SUPPORTED)
1683 *pfElevated = true; /* Older Windows version. */
1684 else
1685 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "GetTokenInformation failed: %u (%#x)", GetLastError(), GetLastError());
1686 }
1687 else
1688 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Membership in the Administrators group is required to perform this action");
1689
1690 CloseHandle(hToken);
1691 return rcExit;
1692
1693# else
1694 /*
1695 * On Unixy systems, we check if the executable and the current user is
1696 * the same. This heuristic works fine for both hardened and development
1697 * builds.
1698 */
1699 char szExecPath[RTPATH_MAX];
1700 if (RTProcGetExecutablePath(szExecPath, sizeof(szExecPath)) == NULL)
1701 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcGetExecutablePath failed");
1702
1703 RTFSOBJINFO ObjInfo;
1704 int rc = RTPathQueryInfoEx(szExecPath, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
1705 if (RT_FAILURE(rc))
1706 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathQueryInfoEx failed");
1707
1708 *pfElevated = ObjInfo.Attr.u.Unix.uid == geteuid()
1709 || ObjInfo.Attr.u.Unix.uid == getuid();
1710 return RTEXITCODE_SUCCESS;
1711# endif
1712}
1713
1714#endif /* WITH_ELEVATION */
1715
1716int main(int argc, char **argv)
1717{
1718 /*
1719 * Initialize IPRT and check that we're correctly installed.
1720 */
1721 int rc = RTR3Init();
1722 if (RT_FAILURE(rc))
1723 return RTMsgInitFailure(rc);
1724
1725 RTERRINFOSTATIC ErrInfo;
1726 RTErrInfoInitStatic(&ErrInfo);
1727 rc = SUPR3HardenedVerifySelf(argv[0], true /*fInternal*/, &ErrInfo.Core);
1728 if (RT_FAILURE(rc))
1729 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s", ErrInfo.Core.pszMsg);
1730
1731 /*
1732 * Elevation check.
1733 */
1734 const char *pszDisplayInfoHack = NULL;
1735 RTEXITCODE rcExit;
1736#ifdef WITH_ELEVATION
1737 bool fElevated;
1738 rcExit = ElevationCheck(&fElevated);
1739 if (rcExit != RTEXITCODE_SUCCESS)
1740 return rcExit;
1741#endif
1742
1743 /*
1744 * Parse the top level arguments until we find a command.
1745 */
1746 static const RTGETOPTDEF s_aOptions[] =
1747 {
1748 { "install", CMD_INSTALL, RTGETOPT_REQ_NOTHING },
1749 { "uninstall", CMD_UNINSTALL, RTGETOPT_REQ_NOTHING },
1750 { "cleanup", CMD_CLEANUP, RTGETOPT_REQ_NOTHING },
1751#ifdef WITH_ELEVATION
1752 { "--elevated", OPT_ELEVATED, RTGETOPT_REQ_NOTHING },
1753 { "--stdout", OPT_STDOUT, RTGETOPT_REQ_STRING },
1754 { "--stderr", OPT_STDERR, RTGETOPT_REQ_STRING },
1755#endif
1756 { "--display-info-hack", OPT_DISP_INFO_HACK, RTGETOPT_REQ_STRING },
1757 };
1758 RTGETOPTSTATE GetState;
1759 rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
1760 if (RT_FAILURE(rc))
1761 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
1762 for (;;)
1763 {
1764 RTGETOPTUNION ValueUnion;
1765 int ch = RTGetOpt(&GetState, &ValueUnion);
1766 switch (ch)
1767 {
1768 case 0:
1769 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No command specified");
1770
1771 case CMD_INSTALL:
1772 case CMD_UNINSTALL:
1773 case CMD_CLEANUP:
1774 {
1775#ifdef WITH_ELEVATION
1776 if (!fElevated)
1777 return RelaunchElevated(argc, argv, ch, pszDisplayInfoHack);
1778#endif
1779 int cCmdargs = argc - GetState.iNext;
1780 char **papszCmdArgs = argv + GetState.iNext;
1781 switch (ch)
1782 {
1783 case CMD_INSTALL:
1784 rcExit = DoInstall( cCmdargs, papszCmdArgs);
1785 break;
1786 case CMD_UNINSTALL:
1787 rcExit = DoUninstall(cCmdargs, papszCmdArgs);
1788 break;
1789 case CMD_CLEANUP:
1790 rcExit = DoCleanup( cCmdargs, papszCmdArgs);
1791 break;
1792 default:
1793 AssertReleaseFailedReturn(RTEXITCODE_FAILURE);
1794 }
1795
1796 /*
1797 * Standard error should end with rcExit=RTEXITCODE_SUCCESS on
1798 * success since the exit code may otherwise get lost in the
1799 * process elevation fun.
1800 */
1801 RTStrmFlush(g_pStdOut);
1802 RTStrmFlush(g_pStdErr);
1803 switch (rcExit)
1804 {
1805 case RTEXITCODE_SUCCESS:
1806 RTStrmPrintf(g_pStdErr, "rcExit=RTEXITCODE_SUCCESS\n");
1807 break;
1808 default:
1809 RTStrmPrintf(g_pStdErr, "rcExit=%d\n", rcExit);
1810 break;
1811 }
1812 RTStrmFlush(g_pStdErr);
1813 RTStrmFlush(g_pStdOut);
1814 return rcExit;
1815 }
1816
1817#ifdef WITH_ELEVATION
1818 case OPT_ELEVATED:
1819 fElevated = true;
1820 break;
1821
1822 case OPT_STDERR:
1823 case OPT_STDOUT:
1824 {
1825 FILE *pFile = freopen(ValueUnion.psz, "r+", ch == OPT_STDOUT ? stdout : stderr);
1826 if (!pFile)
1827 {
1828 rc = RTErrConvertFromErrno(errno);
1829 return RTMsgErrorExit(RTEXITCODE_FAILURE, "freopen on '%s': %Rrc", ValueUnion.psz, rc);
1830 }
1831 break;
1832 }
1833#endif
1834
1835 case OPT_DISP_INFO_HACK:
1836 if (pszDisplayInfoHack)
1837 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--display-info-hack shall only occur once");
1838 pszDisplayInfoHack = ValueUnion.psz;
1839 break;
1840
1841 case 'h':
1842 case 'V':
1843 return DoStandardOption(ch);
1844
1845 default:
1846 return RTGetOptPrintError(ch, &ValueUnion);
1847 }
1848 /* not currently reached */
1849 }
1850 /* not reached */
1851}
1852
1853
1854#ifdef RT_OS_WINDOWS
1855extern "C" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
1856{
1857 g_hInstance = hInstance;
1858 NOREF(hPrevInstance); NOREF(nShowCmd); NOREF(lpCmdLine);
1859 return main(__argc, __argv);
1860}
1861#endif
1862
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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