Changeset 104162 in vbox for trunk/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.cpp
- Timestamp:
- Apr 4, 2024 5:05:37 PM (10 months ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.cpp
r103889 r104162 38 38 #include <iprt/win/windows.h> 39 39 40 #include <aclapi.h> 40 41 #include <msi.h> 41 42 #include <msiquery.h> … … 46 47 #include <cfgmgr32.h> 47 48 #include <devguid.h> 49 #include <sddl.h> /* For ConvertSidToStringSidW. */ 48 50 49 51 #include <iprt/win/objbase.h> … … 55 57 #include <iprt/assert.h> 56 58 #include <iprt/alloca.h> 59 #include <iprt/dir.h> 60 #include <iprt/err.h> 61 #include <iprt/file.h> 57 62 #include <iprt/mem.h> 58 63 #include <iprt/path.h> /* RTPATH_MAX, RTPATH_IS_SLASH */ 59 64 #include <iprt/string.h> /* RT_ZERO */ 65 #include <iprt/stream.h> 66 #include <iprt/thread.h> 60 67 #include <iprt/utf16.h> 61 68 … … 79 86 80 87 88 /********************************************************************************************************************************* 89 * Internal structures * 90 *********************************************************************************************************************************/ 91 /** 92 * Structure for keeping a target's directory security context. 93 */ 94 typedef struct TGTDIRSECCTX 95 { 96 /** Initialized status. */ 97 bool fInitialized; 98 /** Handle of the target's parent directory. 99 * 100 * Kept open while the context is around and initialized. */ 101 RTDIR hParentDir; 102 /** Absolute (resolved) path of the target directory. */ 103 char szTargetDirAbs[RTPATH_MAX]; 104 /** Access mask which is forbidden for an ACE of type ACCESS_ALLOWED_ACE_TYPE. */ 105 uint32_t fAccessMaskForbidden; 106 /** Array of well-known SIDs which are forbidden. */ 107 PSID *paWellKnownSidsForbidden; 108 /** Number of entries in \a paWellKnownSidsForbidden. */ 109 size_t cWellKnownSidsForbidden; 110 } TGTDIRSECCTX; 111 /** Pointer to a target's directory security context. */ 112 typedef TGTDIRSECCTX *PTGTDIRSECCTX; 113 114 115 /********************************************************************************************************************************* 116 * Prototypes * 117 *********************************************************************************************************************************/ 118 static void destroyTargetDirSecurityCtx(PTGTDIRSECCTX pCtx); 119 120 121 /********************************************************************************************************************************* 122 * Globals * 123 *********************************************************************************************************************************/ 124 static uint32_t g_cRef = 0; 125 /** Our target directory security context. 126 * 127 * Has to be global in order to keep it around as long as the DLL is being loaded. */ 128 static TGTDIRSECCTX g_TargetDirSecCtx = { 0 }; 129 81 130 82 131 /** … … 87 136 RT_NOREF(hInst, uReason, pReserved); 88 137 138 #ifdef DEBUG 139 WCHAR wszMsg[128]; 140 RTUtf16Printf(wszMsg, RT_ELEMENTS(wszMsg), "DllMain: hInst=%#x, uReason=%u (PID %u), g_cRef=%RU32\n", 141 hInst, uReason, GetCurrentProcessId(), g_cRef); 142 OutputDebugStringW(wszMsg); 143 #endif 144 145 switch (uReason) 146 { 147 case DLL_PROCESS_ATTACH: 148 { 149 g_cRef++; 89 150 #if 0 90 /* 91 * This is a trick for allowing the debugger to be attached, don't know if 92 * there is an official way to do that, but this is a pretty efficient. 93 * 94 * Monitor the debug output in DbgView and be ready to start windbg when 95 * the message below appear. This will happen 3-4 times during install, 96 * and 2-3 times during uninstall. 97 * 98 * Note! The DIFxApp.DLL will automatically trigger breakpoints when a 99 * debugger is attached. Just continue on these. 100 */ 101 if (uReason == DLL_PROCESS_ATTACH) 102 { 103 WCHAR wszMsg[128]; 104 RTUtf16Printf(wszMsg, RT_ELEMENTS(wszMsg), "Waiting for debugger to attach: windbg -g -G -p %u\n", GetCurrentProcessId()); 105 for (unsigned i = 0; i < 128 && !IsDebuggerPresent(); i++) 106 { 107 OutputDebugStringW(wszMsg); 108 Sleep(1001); 109 } 110 Sleep(1002); 111 __debugbreak(); 112 } 151 /* 152 * This is a trick for allowing the debugger to be attached, don't know if 153 * there is an official way to do that, but this is a pretty efficient. 154 * 155 * Monitor the debug output in DbgView and be ready to start windbg when 156 * the message below appear. This will happen 3-4 times during install, 157 * and 2-3 times during uninstall. 158 * 159 * Note! The DIFxApp.DLL will automatically trigger breakpoints when a 160 * debugger is attached. Just continue on these. 161 */ 162 RTUtf16Printf(wszMsg, RT_ELEMENTS(wszMsg), "Waiting for debugger to attach: windbg -g -G -p %u\n", GetCurrentProcessId()); 163 for (unsigned i = 0; i < 128 && !IsDebuggerPresent(); i++) 164 { 165 OutputDebugStringW(wszMsg); 166 Sleep(1001); 167 } 168 Sleep(1002); 169 __debugbreak(); 113 170 #endif 171 break; 172 } 173 174 case DLL_PROCESS_DETACH: 175 { 176 g_cRef--; 177 break; 178 } 179 180 default: 181 break; 182 } 114 183 115 184 return TRUE; … … 117 186 118 187 /** 119 * Format a nd add message to the MSI log.188 * Format a log message and print it to whatever is there (i.e. to the MSI log). 120 189 * 121 190 * UTF-16 strings are formatted using '%ls' (lowercase). 122 191 * ANSI strings are formatted using '%s' (uppercase). 192 * 193 * @returns VBox status code. 194 * @param hInstall MSI installer handle. Optional and can be NULL. 195 * @param pszFmt Format string. 196 * @param ... Variable arguments for format string. 123 197 */ 124 static UINT logStringF(MSIHANDLE hInstall, const char *pszFmt, ...) 125 { 198 static int logStringF(MSIHANDLE hInstall, const char *pszFmt, ...) 199 { 200 RTUTF16 wszVa[RTPATH_MAX + 256]; 201 va_list va; 202 va_start(va, pszFmt); 203 ssize_t cwc = RTUtf16PrintfV(wszVa, RT_ELEMENTS(wszVa), pszFmt, va); 204 va_end(va); 205 206 RTUTF16 wszMsg[RTPATH_MAX + 256]; 207 cwc = RTUtf16Printf(wszMsg, sizeof(wszMsg), "VBoxInstallHelper: %ls", wszVa); 208 if (cwc <= 0) 209 return VERR_BUFFER_OVERFLOW; 210 211 #ifdef DEBUG 212 OutputDebugStringW(wszMsg); 213 #endif 214 #ifdef TESTCASE 215 RTPrintf("%ls\n", wszMsg); 216 #endif 126 217 PMSIHANDLE hMSI = MsiCreateRecord(2 /* cParms */); 127 218 if (hMSI) 128 219 { 129 wchar_t wszBuf[RTPATH_MAX + 256]; 130 va_list va; 131 va_start(va, pszFmt); 132 ssize_t cwc = RTUtf16PrintfV(wszBuf, RT_ELEMENTS(wszBuf), pszFmt, va); 133 va_end(va); 134 135 MsiRecordSetStringW(hMSI, 0, wszBuf); 220 MsiRecordSetStringW(hMSI, 0, wszMsg); 136 221 MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_INFO), hMSI); 137 138 222 MsiCloseHandle(hMSI); 139 return cwc < RT_ELEMENTS(wszBuf) ? ERROR_SUCCESS : ERROR_BUFFER_OVERFLOW;140 } 141 return ERROR_ACCESS_DENIED;223 } 224 225 return cwc < RT_ELEMENTS(wszVa) ? VINF_SUCCESS : VERR_BUFFER_OVERFLOW; 142 226 } 143 227 … … 160 244 #endif 161 245 return ERROR_SUCCESS; 246 } 247 248 /** 249 * Initializes a target security context. 250 * 251 * @returns VBox status code. 252 * @param pCtx Target directory security context to initialize. 253 * @param hModule Windows installer module handle. 254 * @param pszPath Target directory path to use. 255 */ 256 static int initTargetDirSecurityCtx(PTGTDIRSECCTX pCtx, MSIHANDLE hModule, const char *pszPath) 257 { 258 if (pCtx->fInitialized) 259 return VINF_SUCCESS; 260 261 #ifdef DEBUG 262 logStringF(hModule, "initTargetDirSecurityCtx: pszPath=%s\n", pszPath); 263 #endif 264 265 char szPathTemp[RTPATH_MAX]; 266 int vrc = RTStrCopy(szPathTemp, sizeof(szPathTemp), pszPath); 267 if (RT_FAILURE(vrc)) 268 return vrc; 269 270 /* Try to find a parent path which exists. */ 271 char szPathParentAbs[RTPATH_MAX] = { 0 }; 272 for (int i = 0; i < 256; i++) /* Failsafe counter. */ 273 { 274 RTPathStripTrailingSlash(szPathTemp); 275 RTPathStripFilename(szPathTemp); 276 vrc = RTPathReal(szPathTemp, szPathParentAbs, sizeof(szPathParentAbs)); 277 if (RT_SUCCESS(vrc)) 278 break; 279 } 280 281 if (RT_FAILURE(vrc)) 282 { 283 logStringF(hModule, "initTargetDirSecurityCtx: No existing / valid parent directory found (%Rrc), giving up\n", vrc); 284 return vrc; 285 } 286 287 RTDIR hParentDir; 288 vrc = RTDirOpen(&hParentDir, szPathParentAbs); 289 if (RT_FAILURE(vrc)) 290 { 291 logStringF(hModule, "initTargetDirSecurityCtx: Locking parent directory '%s' failed with %Rrc\n", szPathParentAbs, vrc); 292 return vrc; 293 } 294 295 #ifdef DEBUG 296 logStringF(hModule, "initTargetDirSecurityCtx: Locked parent directory '%s'\n", szPathParentAbs); 297 #endif 298 299 char szPathTargetAbs[RTPATH_MAX]; 300 vrc = RTPathReal(pszPath, szPathTargetAbs, sizeof(szPathTargetAbs)); 301 if (RT_FAILURE(vrc)) 302 vrc = RTStrCopy(szPathTargetAbs, sizeof(szPathTargetAbs), pszPath); 303 if (RT_FAILURE(vrc)) 304 { 305 logStringF(hModule, "initTargetDirSecurityCtx: Failed to resolve absolute target path (%Rrc)\n", vrc); 306 return vrc; 307 } 308 309 #ifdef DEBUG 310 logStringF(hModule, "initTargetDirSecurityCtx: szPathTargetAbs=%s, szPathParentAbs=%s\n", szPathTargetAbs, szPathParentAbs); 311 #endif 312 313 /* Target directory validation. */ 314 if ( !RTStrCmp(szPathTargetAbs, szPathParentAbs) /* Don't allow installation into root directories. */ 315 || RTStrStr(szPathTargetAbs, "..")) 316 { 317 logStringF(hModule, "initTargetDirSecurityCtx: Directory '%s' invalid", szPathTargetAbs); 318 vrc = VERR_INVALID_NAME; 319 } 320 321 if (RT_SUCCESS(vrc)) 322 { 323 RTFSOBJINFO fsObjInfo; 324 vrc = RTPathQueryInfo(szPathParentAbs, &fsObjInfo, RTFSOBJATTRADD_NOTHING); 325 if (RT_SUCCESS(vrc)) 326 { 327 if (RTFS_IS_DIRECTORY(fsObjInfo.Attr.fMode)) /* No symlinks or other fun stuff. */ 328 { 329 static WELL_KNOWN_SID_TYPE aForbiddenWellKnownSids[] = 330 { 331 WinNullSid, 332 WinWorldSid, 333 WinAuthenticatedUserSid, 334 WinBuiltinUsersSid, 335 WinBuiltinGuestsSid, 336 WinBuiltinPowerUsersSid 337 }; 338 339 pCtx->paWellKnownSidsForbidden = (PSID *)RTMemAlloc(sizeof(PSID) * RT_ELEMENTS(aForbiddenWellKnownSids)); 340 AssertPtrReturn(pCtx->paWellKnownSidsForbidden, VERR_NO_MEMORY); 341 342 size_t i = 0; 343 for(; i < RT_ELEMENTS(aForbiddenWellKnownSids); i++) 344 { 345 pCtx->paWellKnownSidsForbidden[i] = RTMemAlloc(SECURITY_MAX_SID_SIZE); 346 AssertPtrBreakStmt(pCtx->paWellKnownSidsForbidden, vrc = VERR_NO_MEMORY); 347 DWORD cbSid = SECURITY_MAX_SID_SIZE; 348 if (!CreateWellKnownSid(aForbiddenWellKnownSids[i], NULL, pCtx->paWellKnownSidsForbidden[i], &cbSid)) 349 { 350 vrc = RTErrConvertFromWin32(GetLastError()); 351 logStringF(hModule, "initTargetDirSecurityCtx: Creating SID (index %zu) failed with %Rrc\n", i, vrc); 352 break; 353 } 354 } 355 356 if (RT_SUCCESS(vrc)) 357 { 358 vrc = RTStrCopy(pCtx->szTargetDirAbs, sizeof(pCtx->szTargetDirAbs), szPathTargetAbs); 359 if (RT_SUCCESS(vrc)) 360 { 361 pCtx->fInitialized = true; 362 pCtx->hParentDir = hParentDir; 363 pCtx->cWellKnownSidsForbidden = i; 364 pCtx->fAccessMaskForbidden = FILE_WRITE_DATA 365 | FILE_APPEND_DATA 366 | FILE_WRITE_ATTRIBUTES 367 | FILE_WRITE_EA; 368 369 RTFILE fh; 370 RTFileOpen(&fh, "c:\\temp\\targetdir.ctx", RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE | RTFILE_O_WRITE); 371 RTFileClose(fh); 372 373 return VINF_SUCCESS; 374 } 375 } 376 } 377 else 378 vrc = VERR_INVALID_NAME; 379 } 380 } 381 382 RTDirClose(hParentDir); 383 384 while (pCtx->cWellKnownSidsForbidden--) 385 { 386 RTMemFree(pCtx->paWellKnownSidsForbidden[pCtx->cWellKnownSidsForbidden]); 387 pCtx->paWellKnownSidsForbidden[pCtx->cWellKnownSidsForbidden] = NULL; 388 } 389 390 logStringF(hModule, "initTargetDirSecurityCtx: Initialization failed failed with %Rrc\n", vrc); 391 return vrc; 392 } 393 394 /** 395 * Destroys a target security context. 396 * 397 * @returns VBox status code. 398 * @param pCtx Target directory security context to destroy. 399 */ 400 static void destroyTargetDirSecurityCtx(PTGTDIRSECCTX pCtx) 401 { 402 if ( !pCtx 403 || !pCtx->fInitialized) 404 return; 405 406 if (pCtx->hParentDir != NIL_RTDIR) 407 { 408 RTDirClose(pCtx->hParentDir); 409 pCtx->hParentDir = NIL_RTDIR; 410 } 411 RT_ZERO(pCtx->szTargetDirAbs); 412 413 for (size_t i = 0; i < pCtx->cWellKnownSidsForbidden; i++) 414 RTMemFree(pCtx->paWellKnownSidsForbidden[i]); 415 pCtx->cWellKnownSidsForbidden = 0; 416 417 RTMemFree(pCtx->paWellKnownSidsForbidden); 418 pCtx->paWellKnownSidsForbidden = NULL; 419 420 RTFileDelete("c:\\temp\\targetdir.ctx"); 421 422 logStringF(NULL, "destroyTargetDirSecurityCtx\n"); 423 } 424 425 #ifdef DEBUG 426 /** 427 * Returns a stingified version of an ACE type. 428 * 429 * @returns Stingified version of an ACE type. 430 * @param uType ACE type. 431 */ 432 inline const char *dbgAceTypeToString(uint8_t uType) 433 { 434 switch (uType) 435 { 436 RT_CASE_RET_STR(ACCESS_ALLOWED_ACE_TYPE); 437 RT_CASE_RET_STR(ACCESS_DENIED_ACE_TYPE); 438 RT_CASE_RET_STR(SYSTEM_AUDIT_ACE_TYPE); 439 RT_CASE_RET_STR(SYSTEM_ALARM_ACE_TYPE); 440 default: break; 441 } 442 443 return "<Invalid>"; 444 } 445 446 /** 447 * Returns an allocated string for a SID containing the user/domain name. 448 * 449 * @returns Allocated string (UTF-8). Must be free'd using RTStrFree(). 450 * @param pSid SID to return allocated string for. 451 */ 452 inline char *dbgSidToNameA(const PSID pSid) 453 { 454 char *pszName = NULL; 455 int vrc = VINF_SUCCESS; 456 457 LPWSTR pwszSid = NULL; 458 if (ConvertSidToStringSid(pSid, &pwszSid)) 459 { 460 SID_NAME_USE SidNameUse; 461 462 WCHAR wszUser[MAX_PATH]; 463 DWORD cbUser = sizeof(wszUser); 464 WCHAR wszDomain[MAX_PATH]; 465 DWORD cbDomain = sizeof(wszDomain); 466 if (LookupAccountSid(NULL, pSid, wszUser, &cbUser, wszDomain, &cbDomain, &SidNameUse)) 467 { 468 RTUTF16 wszName[RTPATH_MAX]; 469 if (RTUtf16Printf(wszName, RT_ELEMENTS(wszName), "%ls%s%ls (%ls)", 470 wszUser, wszDomain[0] == L'\0' ? "" : "\\", wszDomain, pwszSid)) 471 { 472 vrc = RTUtf16ToUtf8(wszName, &pszName); 473 } 474 else 475 vrc = VERR_NO_MEMORY; 476 } 477 else 478 vrc = RTStrAPrintf(&pszName, "<Lookup Error>"); 479 480 LocalFree(pwszSid); 481 } 482 else 483 vrc = VERR_NOT_FOUND; 484 485 return RT_SUCCESS(vrc) ? pszName : "<Invalid>"; 486 } 487 #endif /* DEBUG */ 488 489 /** 490 * Checks a single target path whether it's safe to use or not. 491 * 492 * We check if the given path is owned by "NT Service\TrustedInstaller" and therefore assume that it's safe to use. 493 * 494 * @returns VBox status code. On error the path should be considered unsafe. 495 * @retval VERR_INVALID_NAME if the given path is considered unsafe. 496 * @retval VINF_SUCCESS if the given path is found to be safe to use. 497 * @param hModule Windows installer module handle. 498 * @param pszPath Path to check. 499 */ 500 static int checkTargetDirOne(MSIHANDLE hModule, PTGTDIRSECCTX pCtx, const char *pszPath) 501 { 502 logStringF(hModule, "checkTargetDirOne: Checking '%s' ...", pszPath); 503 504 PRTUTF16 pwszPath; 505 int vrc = RTStrToUtf16(pszPath, &pwszPath); 506 if (RT_FAILURE(vrc)) 507 return vrc; 508 509 PACL pDacl = NULL; 510 PSECURITY_DESCRIPTOR pSecurityDescriptor = { 0 }; 511 DWORD dwErr = GetNamedSecurityInfo(pwszPath, SE_FILE_OBJECT, GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, 512 NULL, NULL, NULL, NULL, &pSecurityDescriptor); 513 if (dwErr == ERROR_SUCCESS) 514 { 515 BOOL fDaclPresent = FALSE; 516 BOOL fDaclDefaultedIgnored = FALSE; 517 if (GetSecurityDescriptorDacl(pSecurityDescriptor, &fDaclPresent, 518 &pDacl, &fDaclDefaultedIgnored)) 519 { 520 if ( !fDaclPresent 521 || !pDacl) 522 { 523 /* Bail out early if the DACL isn't provided or is missing. */ 524 vrc = VERR_INVALID_NAME; 525 } 526 else 527 { 528 ACL_SIZE_INFORMATION aclSizeInfo; 529 RT_ZERO(aclSizeInfo); 530 if (GetAclInformation(pDacl, &aclSizeInfo, sizeof(aclSizeInfo), AclSizeInformation)) 531 { 532 for(DWORD idxACE = 0; idxACE < aclSizeInfo.AceCount; idxACE++) 533 { 534 ACE_HEADER *pAceHdr = NULL; 535 if (GetAce(pDacl, idxACE, (LPVOID *)&pAceHdr)) 536 { 537 #ifdef DEBUG 538 logStringF(hModule, "checkTargetDirOne: ACE type=%s, flags=%#x, size=%#x", 539 dbgAceTypeToString(pAceHdr->AceType), pAceHdr->AceFlags, pAceHdr->AceSize); 540 #endif 541 /* Note: We print the ACEs in canonoical order. */ 542 switch (pAceHdr->AceType) 543 { 544 case ACCESS_ALLOWED_ACE_TYPE: /* We're only interested in the ALLOW ACE. */ 545 { 546 ACCESS_ALLOWED_ACE const *pAce = (ACCESS_ALLOWED_ACE *)pAceHdr; 547 PSID const pSid = (PSID)&pAce->SidStart; 548 #ifdef DEBUG 549 char *pszSid = dbgSidToNameA(pSid); 550 logStringF(hModule, "checkTargetDirOne:\t%s fMask=%#x", pszSid, pAce->Mask); 551 RTStrFree(pszSid); 552 #endif 553 /* We check the flags here first for performance reasons. */ 554 if ((pAce->Mask & pCtx->fAccessMaskForbidden) == pCtx->fAccessMaskForbidden) 555 { 556 for (size_t idxSID = 0; idxSID < pCtx->cWellKnownSidsForbidden; idxSID++) 557 { 558 PSID const pSidForbidden = pCtx->paWellKnownSidsForbidden[idxSID]; 559 bool const fForbidden = EqualSid(pSid, pSidForbidden); 560 #ifdef DEBUG 561 char *pszName = dbgSidToNameA(pSidForbidden); 562 logStringF(hModule, "checkTargetDirOne:\t%s : %s", 563 fForbidden ? "** FORBIDDEN **" : "ALLOWED ", pszName); 564 RTStrFree(pszName); 565 #endif /* DEBUG */ 566 if (fForbidden) 567 { 568 vrc = VERR_INVALID_NAME; 569 break; 570 } 571 } 572 } 573 574 break; 575 } 576 577 case ACCESS_DENIED_ACE_TYPE: /* We're only interested in the ALLOW ACE. */ 578 { 579 ACCESS_DENIED_ACE const *pAce = (ACCESS_DENIED_ACE *)pAceHdr; 580 #ifdef DEBUG 581 LPWSTR pwszSid = NULL; 582 ConvertSidToStringSid((PSID)&pAce->SidStart, &pwszSid); 583 584 logStringF(hModule, "checkTargetDirOne:\t%ls fMask=%#x (generic %#x specific %#x)", 585 pwszSid ? pwszSid : L"<Allocation Error>", pAce->Mask); 586 #endif /* DEBUG */ 587 LocalFree(pwszSid); 588 589 /* Ignore everything else. */ 590 break; 591 } 592 } 593 } 594 else 595 dwErr = GetLastError(); 596 597 /* No point in checking further if we failed somewhere above. */ 598 if (RT_FAILURE(vrc)) 599 break; 600 601 } /* for ACE */ 602 } 603 else 604 dwErr = GetLastError(); 605 } 606 } 607 else 608 dwErr = GetLastError(); 609 610 LocalFree(pSecurityDescriptor); 611 } 612 else 613 dwErr = GetLastError(); 614 615 if (RT_SUCCESS(vrc)) 616 vrc = RTErrConvertFromWin32(dwErr); 617 618 #ifdef DEBUG 619 logStringF(hModule, "checkTargetDirOne: Returning %Rrc", vrc); 620 #endif 621 622 if ( RT_FAILURE(vrc) 623 && vrc != VERR_INVALID_NAME) 624 logStringF(hModule, "checkTargetDirOne: Failed with %Rrc (%#x)", vrc, dwErr); 625 626 return vrc; 627 } 628 629 /** 630 * Checks whether the path in the public property INSTALLDIR has the correct ACL permissions and returns whether 631 * it's valid or not. 632 * 633 * Called from the MSI installer as a custom action. 634 * 635 * @returns Success status (acccording to MSI custom actions). 636 * @retval ERROR_SUCCESS if checking the target directory turned out to be valid. 637 * @retval ERROR_NO_NET_OR_BAD_PATH is the target directory is invalid. 638 * @param hModule Windows installer module handle. 639 * 640 * @note Sets private property VBox_Target_Dir_Is_Valid to "1" (true) if the given target path is valid, 641 * or "0" (false) if it is not. An empty target directory is considered to be valid (i.e. INSTALLDIR not set yet). 642 * 643 * @sa @bugref{10616} 644 */ 645 UINT __stdcall CheckTargetDir(MSIHANDLE hModule) 646 { 647 char *pszTargetDir; 648 649 int vrc = VBoxGetMsiPropUtf8(hModule, "INSTALLDIR", &pszTargetDir); 650 if (RT_SUCCESS(vrc)) 651 { 652 logStringF(hModule, "CheckTargetDir: Checking target directory '%s' ...", pszTargetDir); 653 654 if (!RTStrNLen(pszTargetDir, RTPATH_MAX)) 655 { 656 logStringF(hModule, "CheckTargetDir: No INSTALLDIR set (yet), skipping ..."); 657 VBoxSetMsiProp(hModule, L"VBox_Target_Dir_Is_Valid", L"1"); 658 } 659 else 660 { 661 union 662 { 663 RTPATHPARSED Parsed; 664 uint8_t ab[RTPATH_MAX]; 665 } u; 666 667 vrc = RTPathParse(pszTargetDir, &u.Parsed, sizeof(u), RTPATH_STR_F_STYLE_DOS); 668 if (RT_SUCCESS(vrc)) 669 { 670 if (u.Parsed.fProps & RTPATH_PROP_DOTDOT_REFS) 671 vrc = VERR_INVALID_PARAMETER; 672 if (RT_SUCCESS(vrc)) 673 { 674 vrc = initTargetDirSecurityCtx(&g_TargetDirSecCtx, hModule, pszTargetDir); 675 if (RT_SUCCESS(vrc)) 676 { 677 uint16_t idxComp = u.Parsed.cComps; 678 char szPathToCheck[RTPATH_MAX]; 679 while (idxComp > 1) /* We traverse backwards from INSTALLDIR and leave out the root (e.g. C:\"). */ 680 { 681 u.Parsed.cComps = idxComp; 682 vrc = RTPathParsedReassemble(pszTargetDir, &u.Parsed, RTPATH_STR_F_STYLE_DOS, 683 szPathToCheck, sizeof(szPathToCheck)); 684 if (RT_FAILURE(vrc)) 685 break; 686 if (RTDirExists(szPathToCheck)) 687 { 688 vrc = checkTargetDirOne(hModule, &g_TargetDirSecCtx, szPathToCheck); 689 if (RT_FAILURE(vrc)) 690 break; 691 } 692 else 693 logStringF(hModule, "CheckTargetDir: Path '%s' does not exist (yet)", szPathToCheck); 694 idxComp--; 695 } 696 697 destroyTargetDirSecurityCtx(&g_TargetDirSecCtx); 698 } 699 else 700 logStringF(hModule, "CheckTargetDir: initTargetDirSecurityCtx failed with %Rrc\n", vrc); 701 702 if (RT_SUCCESS(vrc)) 703 VBoxSetMsiProp(hModule, L"VBox_Target_Dir_Is_Valid", L"1"); 704 } 705 } 706 else 707 logStringF(hModule, "CheckTargetDir: Parsing path failed with %Rrc", vrc); 708 } 709 710 RTStrFree(pszTargetDir); 711 } 712 713 if (RT_FAILURE(vrc)) /* On failure (or when in doubt), mark the installation directory as invalid. */ 714 { 715 logStringF(hModule, "CheckTargetDir: Checking failed with %Rrc", vrc); 716 VBoxSetMsiProp(hModule, L"VBox_Target_Dir_Is_Valid", L"0"); 717 } 718 719 /* Return back outcome to the MSI engine. */ 720 return RT_SUCCESS(vrc) ? ERROR_SUCCESS : ERROR_NO_NET_OR_BAD_PATH; 162 721 } 163 722
Note:
See TracChangeset
for help on using the changeset viewer.