From cdba28de1ee229369b254c62bc58cf2f001899a3 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 3 Aug 2021 18:06:54 -0500 Subject: Add argument and policy setting to set Burn's base working directory. Fixes #5856 --- src/burn/engine/cache.cpp | 65 +++++++++++++++--- src/burn/engine/core.cpp | 151 ++++++++++++++++++++++++++++++++++++++++-- src/burn/engine/core.h | 7 ++ src/burn/engine/elevation.cpp | 14 ++++ src/burn/engine/engine.cpp | 1 + src/burn/engine/exeengine.cpp | 9 +++ src/burn/engine/package.h | 1 + src/burn/engine/plan.cpp | 2 + src/burn/engine/plan.h | 1 + 9 files changed, 235 insertions(+), 16 deletions(-) (limited to 'src/burn') diff --git a/src/burn/engine/cache.cpp b/src/burn/engine/cache.cpp index 54328091..0c5266a0 100644 --- a/src/burn/engine/cache.cpp +++ b/src/burn/engine/cache.cpp @@ -14,6 +14,10 @@ static HRESULT CacheVerifyPayloadSignature( __in_z LPCWSTR wzUnverifiedPayloadPath, __in HANDLE hFile ); +static HRESULT CalculateBaseWorkingFolder( + __in BURN_ENGINE_COMMAND* pInternalCommand, + __inout_z LPWSTR* psczBaseWorkingFolder + ); static HRESULT CalculateWorkingFolder( __in BURN_CACHE* pCache, __in BURN_ENGINE_COMMAND* pInternalCommand @@ -1337,28 +1341,71 @@ extern "C" void CacheUninitialize( // Internal functions. -static HRESULT CalculateWorkingFolder( - __in BURN_CACHE* pCache, - __in BURN_ENGINE_COMMAND* pInternalCommand +static HRESULT CalculateBaseWorkingFolder( + __in BURN_ENGINE_COMMAND* pInternalCommand, + __inout_z LPWSTR* psczBaseWorkingFolder ) { HRESULT hr = S_OK; - RPC_STATUS rs = RPC_S_OK; - LPWSTR sczTempPath = NULL; - UUID guid = {}; - WCHAR wzGuid[39]; + ReleaseNullStr(*psczBaseWorkingFolder); + + // The value from the command line takes precedence. + if (pInternalCommand->sczWorkingDirectory) + { + hr = PathExpand(psczBaseWorkingFolder, pInternalCommand->sczWorkingDirectory, PATH_EXPAND_FULLPATH); + ExitOnFailure(hr, "Failed to expand engine working directory from command-line: '%ls'", pInternalCommand->sczWorkingDirectory); + + ExitFunction(); + } + + // The base working folder can be specified through policy, + // but only use it if elevated because it should be secured against non-admin users. if (pInternalCommand->fInitiallyElevated) { - hr = PathGetSystemTempPath(&sczTempPath); + hr = PolcReadString(POLICY_BURN_REGISTRY_PATH, L"EngineWorkingDirectory", NULL, psczBaseWorkingFolder); + ExitOnFailure(hr, "Failed to read EngineWorkingDirectory policy directory."); + + if (*psczBaseWorkingFolder) + { + // PolcReadString is supposed to automatically expand REG_EXPAND_SZ values. + ExitFunction(); + } + } + + // Default to the temp path specified in environment variables, but need to use system temp path for security reasons if running elevated. + if (pInternalCommand->fInitiallyElevated) + { + hr = PathGetSystemTempPath(psczBaseWorkingFolder); ExitOnFailure(hr, "Failed to get system temp folder path for working folder."); } else { - hr = PathGetTempPath(&sczTempPath); + hr = PathGetTempPath(psczBaseWorkingFolder); ExitOnFailure(hr, "Failed to get temp folder path for working folder."); } +LExit: + return hr; +} + +static HRESULT CalculateWorkingFolder( + __in BURN_CACHE* pCache, + __in BURN_ENGINE_COMMAND* pInternalCommand + ) +{ + HRESULT hr = S_OK; + RPC_STATUS rs = RPC_S_OK; + LPWSTR sczTempPath = NULL; + UUID guid = {}; + WCHAR wzGuid[39]; + + hr = CalculateBaseWorkingFolder(pInternalCommand, &sczTempPath); + ExitOnFailure(hr, "Failed to get base engine working directory."); + + hr = PathBackslashTerminate(&sczTempPath); + ExitOnFailure(hr, "Failed to backslashify base engine working directory."); + rs = ::UuidCreate(&guid); hr = HRESULT_FROM_RPC(rs); ExitOnFailure(hr, "Failed to create working folder guid."); diff --git a/src/burn/engine/core.cpp b/src/burn/engine/core.cpp index 3e45cdfc..e8c51187 100644 --- a/src/burn/engine/core.cpp +++ b/src/burn/engine/core.cpp @@ -22,6 +22,23 @@ static HRESULT CoreRecreateCommandLine( __in BOOTSTRAPPER_RELATION_TYPE relationType, __in BOOL fPassthrough ); +static HRESULT AppendEscapedArgumentToCommandLine( + __in_z LPCWSTR wzEscapedArgument, + __deref_inout_z LPWSTR* psczCommandLine, + __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine + ); +static HRESULT EscapeAndAppendArgumentToCommandLineFormatted( + __deref_inout_z LPWSTR* psczCommandLine, + __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine, + __in __format_string LPCWSTR wzFormat, + ... + ); +static HRESULT EscapeAndAppendArgumentToCommandLineFormattedArgs( + __deref_inout_z LPWSTR* psczCommandLine, + __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ); static HRESULT AppendLayoutToCommandLine( __in BOOTSTRAPPER_ACTION action, __in_z LPCWSTR wzLayoutDirectory, @@ -207,11 +224,12 @@ extern "C" HRESULT CoreInitializeConstants( for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) { BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; - + if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) // TODO: Don't assume exePackages with burn protocol are bundles. { // Pass along any ancestors and ourself to prevent infinite loops. pPackage->Exe.wzAncestors = pRegistration->sczBundlePackageAncestors; + pPackage->Exe.wzEngineWorkingDirectory = pInternalCommand->sczWorkingDirectory; } } @@ -1001,6 +1019,9 @@ static HRESULT CoreRecreateCommandLine( ExitOnFailure(hr, "Failed to append ancestors to command-line."); } + hr = CoreAppendEngineWorkingDirectoryToCommandLine(pInternalCommand->sczWorkingDirectory, psczCommandLine, NULL); + ExitOnFailure(hr, "Failed to append the custom working directory to command-line."); + if (wzRelationTypeCommandLine) { hr = StrAllocConcatFormatted(psczCommandLine, L" /%ls", wzRelationTypeCommandLine); @@ -1060,7 +1081,7 @@ extern "C" HRESULT CoreCreateCleanRoomCommandLine( hr = StrAllocConcatFormatted(psczCommandLine, L" /%ls", wzLogParameter); ExitOnFailure(hr, "Failed to append logging switch."); - hr = PathCommandLineAppend(psczCommandLine, pInternalCommand->sczLogFile); + hr = AppAppendCommandLineArgument(psczCommandLine, pInternalCommand->sczLogFile); ExitOnFailure(hr, "Failed to append custom log path."); } @@ -1091,7 +1112,7 @@ extern "C" HRESULT CoreCreateCleanRoomCommandLine( hr = StrAllocConcat(psczCommandLine, L" /originalsource", 0); ExitOnFailure(hr, "Failed to append /originalsource."); - hr = PathCommandLineAppend(psczCommandLine, pInternalCommand->sczOriginalSource); + hr = AppAppendCommandLineArgument(psczCommandLine, pInternalCommand->sczOriginalSource); ExitOnFailure(hr, "Failed to append original source."); } @@ -1256,6 +1277,28 @@ LExit: return hr; } +extern "C" HRESULT CoreAppendEngineWorkingDirectoryToCommandLine( + __in_z_opt LPCWSTR wzEngineWorkingDirectory, + __deref_inout_z LPWSTR* psczCommandLine, + __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine + ) +{ + HRESULT hr = S_OK; + LPWSTR sczArgument = NULL; + + if (wzEngineWorkingDirectory) + { + hr = EscapeAndAppendArgumentToCommandLineFormatted(psczCommandLine, psczObfuscatedCommandLine, L"-%ls=%ls", BURN_COMMANDLINE_SWITCH_WORKING_DIRECTORY, wzEngineWorkingDirectory); + ExitOnFailure(hr, "Failed to append the custom working directory to the command line."); + } + +LExit: + ReleaseStr(sczArgument); + + return hr; +} + + extern "C" void CoreCleanup( __in BURN_ENGINE_STATE* pEngineState ) @@ -1678,6 +1721,27 @@ extern "C" HRESULT CoreParseCommandLine( ExitOnFailure(hr, "Failed to allocate the list of ancestors."); } } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_WORKING_DIRECTORY), BURN_COMMANDLINE_SWITCH_WORKING_DIRECTORY, lstrlenW(BURN_COMMANDLINE_SWITCH_WORKING_DIRECTORY))) + { + // Get a pointer to the next character after the switch. + LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_WORKING_DIRECTORY)]; + if (L'=' != wzParam[0]) + { + fInvalidCommandLine = TRUE; + TraceLog(E_INVALIDARG, "Invalid switch: %ls", argv[i]); + } + else if (L'\0' == wzParam[1]) + { + // Need to grab the current directory here since this is passed on to other processes. + hr = DirGetCurrent(&pInternalCommand->sczWorkingDirectory); + ExitOnFailure(hr, "Failed to get current directory for custom working directory."); + } + else + { + hr = StrAllocString(&pInternalCommand->sczWorkingDirectory, wzParam + 1, 0); + ExitOnFailure(hr, "Failed to allocate the custom working directory."); + } + } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED), BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED))) { LPCWSTR wzParam = &argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED)]; @@ -1807,6 +1871,79 @@ LExit: // internal helper functions +static HRESULT AppendEscapedArgumentToCommandLine( + __in_z LPCWSTR wzEscapedArgument, + __deref_inout_z LPWSTR* psczCommandLine, + __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine + ) +{ + HRESULT hr = S_OK; + + // If there is already data in the command line, + // append a space before appending the argument. + if (*psczCommandLine && **psczCommandLine) + { + hr = StrAllocConcatSecure(psczCommandLine, L" ", 0); + ExitOnFailure(hr, "Failed to append space to command line with existing data."); + } + + hr = StrAllocConcatSecure(psczCommandLine, wzEscapedArgument, 0); + ExitOnFailure(hr, "Failed to append escaped command line argument."); + + if (psczObfuscatedCommandLine) + { + if (*psczObfuscatedCommandLine && **psczObfuscatedCommandLine) + { + hr = StrAllocConcat(psczObfuscatedCommandLine, L" ", 0); + ExitOnFailure(hr, "Failed to append space to obfuscated command line with existing data."); + } + + hr = StrAllocConcat(psczObfuscatedCommandLine, wzEscapedArgument, 0); + ExitOnFailure(hr, "Failed to append escaped argument to obfuscated command line."); + } + +LExit: + return hr; +} + +static HRESULT EscapeAndAppendArgumentToCommandLineFormatted( + __deref_inout_z LPWSTR* psczCommandLine, + __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine, + __in __format_string LPCWSTR wzFormat, + ... + ) +{ + HRESULT hr = S_OK; + va_list args; + + va_start(args, wzFormat); + hr = EscapeAndAppendArgumentToCommandLineFormattedArgs(psczCommandLine, psczObfuscatedCommandLine, wzFormat, args); + va_end(args); + + return hr; +} + +static HRESULT EscapeAndAppendArgumentToCommandLineFormattedArgs( + __deref_inout_z LPWSTR* psczCommandLine, + __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ) +{ + HRESULT hr = S_OK; + LPWSTR sczArgument = NULL; + + hr = AppEscapeCommandLineArgumentFormattedArgs(&sczArgument, wzFormat, args); + ExitOnFailure(hr, "Failed to escape the argument for the command line."); + + hr = AppendEscapedArgumentToCommandLine(sczArgument, psczCommandLine, psczObfuscatedCommandLine); + +LExit: + ReleaseStr(sczArgument); + + return hr; +} + static HRESULT AppendLayoutToCommandLine( __in BOOTSTRAPPER_ACTION action, __in_z LPCWSTR wzLayoutDirectory, @@ -1822,7 +1959,7 @@ static HRESULT AppendLayoutToCommandLine( if (wzLayoutDirectory) { - hr = PathCommandLineAppend(psczCommandLine, wzLayoutDirectory); + hr = AppAppendCommandLineArgument(psczCommandLine, wzLayoutDirectory); ExitOnFailure(hr, "Failed to append layout directory."); } } @@ -1883,16 +2020,16 @@ static HRESULT GetSanitizedCommandLine( } // Remember command-line switch to pass off to BA. - PathCommandLineAppend(&pCommand->wzCommandLine, argv[i]); + AppAppendCommandLineArgument(&pCommand->wzCommandLine, argv[i]); } if (fHidden) { - PathCommandLineAppend(psczSanitizedCommandLine, sczSanitizedArgument); + AppAppendCommandLineArgument(psczSanitizedCommandLine, sczSanitizedArgument); } else { - PathCommandLineAppend(psczSanitizedCommandLine, argv[i]); + AppAppendCommandLineArgument(psczSanitizedCommandLine, argv[i]); } } diff --git a/src/burn/engine/core.h b/src/burn/engine/core.h index f3328738..5361a5c0 100644 --- a/src/burn/engine/core.h +++ b/src/burn/engine/core.h @@ -14,6 +14,7 @@ const LPCWSTR BURN_POLICY_REGISTRY_PATH = L"WiX\\Burn"; const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT = L"parent"; const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT_NONE = L"parent:none"; const LPCWSTR BURN_COMMANDLINE_SWITCH_CLEAN_ROOM = L"burn.clean.room"; +const LPCWSTR BURN_COMMANDLINE_SWITCH_WORKING_DIRECTORY = L"burn.working.directory"; const LPCWSTR BURN_COMMANDLINE_SWITCH_ELEVATED = L"burn.elevated"; const LPCWSTR BURN_COMMANDLINE_SWITCH_EMBEDDED = L"burn.embedded"; const LPCWSTR BURN_COMMANDLINE_SWITCH_RUNONCE = L"burn.runonce"; @@ -99,6 +100,7 @@ typedef struct _BURN_ENGINE_COMMAND LPWSTR sczSourceProcessPath; LPWSTR sczOriginalSource; + LPWSTR sczWorkingDirectory; DWORD dwLoggingAttributes; LPWSTR sczLogFile; @@ -254,6 +256,11 @@ HRESULT CoreAppendSplashScreenWindowToCommandLine( __in_opt HWND hwndSplashScreen, __deref_inout_z LPWSTR* psczCommandLine ); +HRESULT CoreAppendEngineWorkingDirectoryToCommandLine( + __in_z_opt LPCWSTR wzEngineWorkingDirectory, + __deref_inout_z LPWSTR* psczCommandLine, + __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine + ); void CoreCleanup( __in BURN_ENGINE_STATE* pEngineState ); diff --git a/src/burn/engine/elevation.cpp b/src/burn/engine/elevation.cpp index c229fa58..0e1cf0b7 100644 --- a/src/burn/engine/elevation.cpp +++ b/src/burn/engine/elevation.cpp @@ -852,6 +852,9 @@ extern "C" HRESULT ElevationExecuteExePackage( hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczAncestors); ExitOnFailure(hr, "Failed to write the list of ancestors to the message buffer."); + hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczEngineWorkingDirectory); + ExitOnFailure(hr, "Failed to write the custom working directory to the message buffer."); + hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData); ExitOnFailure(hr, "Failed to write variables."); @@ -2476,6 +2479,7 @@ static HRESULT OnExecuteExePackage( BURN_EXECUTE_ACTION executeAction = { }; LPWSTR sczIgnoreDependencies = NULL; LPWSTR sczAncestors = NULL; + LPWSTR sczEngineWorkingDirectory = NULL; BOOTSTRAPPER_APPLY_RESTART exeRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; executeAction.type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE; @@ -2496,6 +2500,9 @@ static HRESULT OnExecuteExePackage( hr = BuffReadString(pbData, cbData, &iData, &sczAncestors); ExitOnFailure(hr, "Failed to read the list of ancestors."); + hr = BuffReadString(pbData, cbData, &iData, &sczEngineWorkingDirectory); + ExitOnFailure(hr, "Failed to read the custom working directory."); + hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); ExitOnFailure(hr, "Failed to read variables."); @@ -2520,11 +2527,18 @@ static HRESULT OnExecuteExePackage( ExitOnFailure(hr, "Failed to allocate the list of ancestors."); } + if (sczEngineWorkingDirectory && *sczEngineWorkingDirectory) + { + hr = StrAllocString(&executeAction.exePackage.sczEngineWorkingDirectory, sczEngineWorkingDirectory, 0); + ExitOnFailure(hr, "Failed to allocate the custom working directory."); + } + // Execute EXE package. hr = ExeEngineExecutePackage(&executeAction, pCache, pVariables, static_cast(dwRollback), GenericExecuteMessageHandler, hPipe, &exeRestart); ExitOnFailure(hr, "Failed to execute EXE package."); LExit: + ReleaseStr(sczEngineWorkingDirectory); ReleaseStr(sczAncestors); ReleaseStr(sczIgnoreDependencies); ReleaseStr(sczPackage); diff --git a/src/burn/engine/engine.cpp b/src/burn/engine/engine.cpp index 0ce2de6d..d5dc0545 100644 --- a/src/burn/engine/engine.cpp +++ b/src/burn/engine/engine.cpp @@ -401,6 +401,7 @@ static void UninitializeEngineState( ReleaseStr(pEngineState->internalCommand.sczLogFile); ReleaseStr(pEngineState->internalCommand.sczOriginalSource); ReleaseStr(pEngineState->internalCommand.sczSourceProcessPath); + ReleaseStr(pEngineState->internalCommand.sczWorkingDirectory); ReleaseStr(pEngineState->log.sczExtension); ReleaseStr(pEngineState->log.sczPrefix); diff --git a/src/burn/engine/exeengine.cpp b/src/burn/engine/exeengine.cpp index 9eea4960..67da3bdd 100644 --- a/src/burn/engine/exeengine.cpp +++ b/src/burn/engine/exeengine.cpp @@ -308,6 +308,12 @@ extern "C" HRESULT ExeEnginePlanAddPackage( ExitOnFailure(hr, "Failed to allocate the list of ancestors."); } + if (pPackage->Exe.wzEngineWorkingDirectory) + { + hr = StrAllocString(&pAction->exePackage.sczEngineWorkingDirectory, pPackage->Exe.wzEngineWorkingDirectory, 0); + ExitOnFailure(hr, "Failed to allocate the custom working directory."); + } + LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, NULL); // ignore errors. } @@ -488,6 +494,9 @@ extern "C" HRESULT ExeEngineExecutePackage( if (BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol) { + hr = CoreAppendEngineWorkingDirectoryToCommandLine(pExecuteAction->exePackage.sczEngineWorkingDirectory, &sczCommand, &sczCommandObfuscated); + ExitOnFailure(hr, "Failed to append the custom working directory to the exepackage command line."); + hr = CoreAppendFileHandleSelfToCommandLine(sczExecutablePath, &hExecutableFile, &sczCommand, &sczCommandObfuscated); ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF); } diff --git a/src/burn/engine/package.h b/src/burn/engine/package.h index 94f27c23..f14064db 100644 --- a/src/burn/engine/package.h +++ b/src/burn/engine/package.h @@ -267,6 +267,7 @@ typedef struct _BURN_PACKAGE LPWSTR sczUninstallArguments; LPWSTR sczIgnoreDependencies; LPCWSTR wzAncestors; // points directly into engine state. + LPCWSTR wzEngineWorkingDirectory; // points directly into engine state. BOOL fPseudoBundle; diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp index f77e8e2a..04da2a9d 100644 --- a/src/burn/engine/plan.cpp +++ b/src/burn/engine/plan.cpp @@ -266,6 +266,7 @@ extern "C" void PlanUninitializeExecuteAction( case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: ReleaseStr(pExecuteAction->exePackage.sczIgnoreDependencies); ReleaseStr(pExecuteAction->exePackage.sczAncestors); + ReleaseStr(pExecuteAction->exePackage.sczEngineWorkingDirectory); break; case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: @@ -1282,6 +1283,7 @@ extern "C" HRESULT PlanRelatedBundlesBegin( // Pass along any ancestors and ourself to prevent infinite loops. pRelatedBundle->package.Exe.wzAncestors = pRegistration->sczBundlePackageAncestors; + pRelatedBundle->package.Exe.wzEngineWorkingDirectory = pPlan->pInternalCommand->sczWorkingDirectory; hr = PlanDefaultRelatedBundleRequestState(relationType, pRelatedBundle->relationType, pPlan->action, pRegistration->pVersion, pRelatedBundle->pVersion, &pRelatedBundle->package.requested); ExitOnFailure(hr, "Failed to get default request state for related bundle."); diff --git a/src/burn/engine/plan.h b/src/burn/engine/plan.h index 224f3806..b148c75b 100644 --- a/src/burn/engine/plan.h +++ b/src/burn/engine/plan.h @@ -164,6 +164,7 @@ typedef struct _BURN_EXECUTE_ACTION BOOTSTRAPPER_ACTION_STATE action; LPWSTR sczIgnoreDependencies; LPWSTR sczAncestors; + LPWSTR sczEngineWorkingDirectory; } exePackage; struct { -- cgit v1.2.3-55-g6feb