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 + src/libs/dutil/WixToolset.DUtil/apputil.cpp | 231 ++++++++++++++++++++++++- src/libs/dutil/WixToolset.DUtil/inc/apputil.h | 41 ++++- src/libs/dutil/WixToolset.DUtil/inc/pathutil.h | 10 -- src/libs/dutil/WixToolset.DUtil/pathutil.cpp | 102 ----------- src/libs/dutil/WixToolset.DUtil/precomp.h | 1 + 14 files changed, 503 insertions(+), 133 deletions(-) (limited to 'src') 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 { diff --git a/src/libs/dutil/WixToolset.DUtil/apputil.cpp b/src/libs/dutil/WixToolset.DUtil/apputil.cpp index 589a09dd..7e0bbc7b 100644 --- a/src/libs/dutil/WixToolset.DUtil/apputil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/apputil.cpp @@ -20,7 +20,18 @@ const DWORD PRIVATE_LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800; typedef BOOL(WINAPI *LPFN_SETDEFAULTDLLDIRECTORIES)(DWORD); typedef BOOL(WINAPI *LPFN_SETDLLDIRECTORYW)(LPCWSTR); -extern "C" void DAPI AppFreeCommandLineArgs( +/******************************************************************** +EscapeCommandLineArgument - encodes wzArgument such that + ::CommandLineToArgv() will parse it back unaltered. If no escaping + was required, *psczEscaped is NULL. + +********************************************************************/ +static HRESULT EscapeCommandLineArgument( + __in_z LPCWSTR wzArgument, + __out_z LPWSTR* psczEscaped + ); + +DAPI_(void) AppFreeCommandLineArgs( __in LPWSTR* argv ) { @@ -34,7 +45,7 @@ AppInitialize - initializes the standard safety precautions for an installation application. ********************************************************************/ -extern "C" void DAPI AppInitialize( +DAPI_(void) AppInitialize( __in_ecount(cSafelyLoadSystemDlls) LPCWSTR rgsczSafelyLoadSystemDlls[], __in DWORD cSafelyLoadSystemDlls ) @@ -85,12 +96,12 @@ extern "C" void DAPI AppInitialize( } } -extern "C" void DAPI AppInitializeUnsafe() +DAPI_(void) AppInitializeUnsafe() { ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); } -extern "C" DAPI_(HRESULT) AppParseCommandLine( +DAPI_(HRESULT) AppParseCommandLine( __in LPCWSTR wzCommandLine, __in int* pArgc, __in LPWSTR** pArgv @@ -122,3 +133,215 @@ LExit: return hr; } + +DAPI_(HRESULT) AppAppendCommandLineArgument( + __deref_inout_z LPWSTR* psczCommandLine, + __in_z LPCWSTR wzArgument + ) +{ + HRESULT hr = S_OK; + LPWSTR sczQuotedArg = NULL; + + hr = EscapeCommandLineArgument(wzArgument, &sczQuotedArg); + AppExitOnFailure(hr, "Failed to escape command line argument."); + + // If there is already data in the command line, + // append a space before appending the argument. + if (*psczCommandLine && **psczCommandLine) + { + hr = StrAllocConcatSecure(psczCommandLine, L" ", 0); + AppExitOnFailure(hr, "Failed to append space to command line with existing data."); + } + + hr = StrAllocConcatSecure(psczCommandLine, sczQuotedArg ? sczQuotedArg : wzArgument, 0); + AppExitOnFailure(hr, "Failed to copy command line argument."); + +LExit: + ReleaseStr(sczQuotedArg); + + return hr; +} + +DAPIV_(HRESULT) AppAppendCommandLineArgumentFormatted( + __deref_inout_z LPWSTR* psczCommandLine, + __in __format_string LPCWSTR wzFormat, + ... + ) +{ + HRESULT hr = S_OK; + va_list args; + + va_start(args, wzFormat); + hr = AppAppendCommandLineArgumentFormattedArgs(psczCommandLine, wzFormat, args); + va_end(args); + + return hr; +} + +DAPI_(HRESULT) AppAppendCommandLineArgumentFormattedArgs( + __deref_inout_z LPWSTR* psczCommandLine, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ) +{ + HRESULT hr = S_OK; + LPWSTR sczQuotedArg = NULL; + + hr = AppEscapeCommandLineArgumentFormattedArgs(&sczQuotedArg, wzFormat, args); + AppExitOnFailure(hr, "Failed to escape command line argument."); + + // If there is already data in the command line, + // append a space before appending the argument. + if (*psczCommandLine && **psczCommandLine) + { + hr = StrAllocConcatSecure(psczCommandLine, L" ", 0); + AppExitOnFailure(hr, "Failed to append space to command line with existing data."); + } + + hr = StrAllocConcatSecure(psczCommandLine, sczQuotedArg, 0); + AppExitOnFailure(hr, "Failed to copy command line argument."); + +LExit: + ReleaseStr(sczQuotedArg); + + return hr; +} + +DAPIV_(HRESULT) AppEscapeCommandLineArgumentFormatted( + __deref_inout_z LPWSTR* psczEscapedArgument, + __in __format_string LPCWSTR wzFormat, + ... + ) +{ + HRESULT hr = S_OK; + va_list args; + + va_start(args, wzFormat); + hr = AppEscapeCommandLineArgumentFormattedArgs(psczEscapedArgument, wzFormat, args); + va_end(args); + + return hr; +} + +DAPI_(HRESULT) AppEscapeCommandLineArgumentFormattedArgs( + __deref_inout_z LPWSTR* psczEscapedArgument, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ) +{ + HRESULT hr = S_OK; + LPWSTR sczFormattedArg = NULL; + LPWSTR sczQuotedArg = NULL; + + hr = StrAllocFormattedArgsSecure(&sczFormattedArg, wzFormat, args); + AppExitOnFailure(hr, "Failed to format command line argument."); + + hr = EscapeCommandLineArgument(sczFormattedArg, &sczQuotedArg); + AppExitOnFailure(hr, "Failed to escape command line argument."); + + if (sczQuotedArg) + { + *psczEscapedArgument = sczQuotedArg; + sczQuotedArg = NULL; + } + else + { + *psczEscapedArgument = sczFormattedArg; + sczFormattedArg = NULL; + } + +LExit: + ReleaseStr(sczFormattedArg); + ReleaseStr(sczQuotedArg); + + return hr; +} + +static HRESULT EscapeCommandLineArgument( + __in_z LPCWSTR wzArgument, + __out_z LPWSTR* psczEscaped + ) +{ + HRESULT hr = S_OK; + BOOL fRequiresQuoting = FALSE; + SIZE_T cMaxEscapedSize = 0; + + *psczEscaped = NULL; + + // Loop through the argument determining if it needs to be quoted and what the maximum + // size would be if there are escape characters required. + for (LPCWSTR pwz = wzArgument; *pwz; ++pwz) + { + // Arguments with whitespace need quoting. + if (L' ' == *pwz || L'\t' == *pwz || L'\n' == *pwz || L'\v' == *pwz) + { + fRequiresQuoting = TRUE; + } + else if (L'"' == *pwz) // quotes need quoting and sometimes escaping. + { + fRequiresQuoting = TRUE; + ++cMaxEscapedSize; + } + else if (L'\\' == *pwz) // some backslashes need escaping, so we'll count them all to make sure there is room. + { + ++cMaxEscapedSize; + } + + ++cMaxEscapedSize; + } + + // If we found anything in the argument that requires our argument to be quoted + if (fRequiresQuoting) + { + hr = StrAlloc(psczEscaped, cMaxEscapedSize + 3); // plus three for the start and end quote plus null terminator. + AppExitOnFailure(hr, "Failed to allocate argument to be quoted."); + + LPCWSTR pwz = wzArgument; + LPWSTR pwzQuoted = *psczEscaped; + + *pwzQuoted = L'"'; + ++pwzQuoted; + while (*pwz) + { + DWORD dwBackslashes = 0; + while (L'\\' == *pwz) + { + ++dwBackslashes; + ++pwz; + } + + // Escape all backslashes at the end of the string. + if (!*pwz) + { + dwBackslashes *= 2; + } + else if (L'"' == *pwz) // escape all backslashes before the quote and escape the quote itself. + { + dwBackslashes = dwBackslashes * 2 + 1; + } + // the backslashes don't have to be escaped. + + // Add the appropriate number of backslashes + for (DWORD i = 0; i < dwBackslashes; ++i) + { + *pwzQuoted = L'\\'; + ++pwzQuoted; + } + + // If there is a character, add it after all the escaped backslashes + if (*pwz) + { + *pwzQuoted = *pwz; + ++pwz; + ++pwzQuoted; + } + } + + *pwzQuoted = L'"'; + ++pwzQuoted; + *pwzQuoted = L'\0'; // ensure the arg is null terminated. + } + +LExit: + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/inc/apputil.h b/src/libs/dutil/WixToolset.DUtil/inc/apputil.h index 1a1e14f7..11280102 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/apputil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/apputil.h @@ -34,12 +34,51 @@ AppParseCommandLine - parses the command line using CommandLineToArgvW. by calling AppFreeCommandLineArgs. ********************************************************************/ -DAPI_(HRESULT) AppParseCommandLine( +HRESULT DAPI AppParseCommandLine( __in LPCWSTR wzCommandLine, __in int* argc, __in LPWSTR** pArgv ); +/******************************************************************* + AppAppendCommandLineArgument - appends a command line argument on to a + string such that ::CommandLineToArgv() will shred them correctly + (i.e. quote arguments with spaces in them). +********************************************************************/ +HRESULT DAPI AppAppendCommandLineArgument( + __deref_inout_z LPWSTR* psczCommandLine, + __in_z LPCWSTR wzArgument + ); + +HRESULT DAPIV AppAppendCommandLineArgumentFormatted( + __deref_inout_z LPWSTR* psczCommandLine, + __in __format_string LPCWSTR wzFormat, + ... + ); + +HRESULT DAPI AppAppendCommandLineArgumentFormattedArgs( + __deref_inout_z LPWSTR* psczCommandLine, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ); + +/******************************************************************** +AppEscapeCommandLineArgumentFormatted - formats a string and then + escapes it such that ::CommandLineToArgv() will parse it back unaltered. + +********************************************************************/ +HRESULT DAPIV AppEscapeCommandLineArgumentFormatted( + __deref_inout_z LPWSTR* psczEscapedArgument, + __in __format_string LPCWSTR wzFormat, + ... + ); + +HRESULT DAPI AppEscapeCommandLineArgumentFormattedArgs( + __deref_inout_z LPWSTR* psczEscapedArgument, + __in __format_string LPCWSTR wzFormat, + __in va_list args + ); + #ifdef __cplusplus } #endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h index 0ae9f437..00a468ce 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h @@ -13,16 +13,6 @@ typedef enum PATH_EXPAND } PATH_EXPAND; -/******************************************************************* - PathCommandLineAppend - appends a command line argument on to a - string such that ::CommandLineToArgv() will shred them correctly - (i.e. quote arguments with spaces in them). -********************************************************************/ -DAPI_(HRESULT) PathCommandLineAppend( - __deref_inout_z LPWSTR* psczCommandLine, - __in_z LPCWSTR wzArgument - ); - /******************************************************************* PathFile - returns a pointer to the file part of the path. ********************************************************************/ diff --git a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp index 5fad519b..7bac8ac3 100644 --- a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp @@ -21,108 +21,6 @@ #define PATH_GOOD_ENOUGH 64 -DAPI_(HRESULT) PathCommandLineAppend( - __deref_inout_z LPWSTR* psczCommandLine, - __in_z LPCWSTR wzArgument - ) -{ - HRESULT hr = S_OK; - LPWSTR sczQuotedArg = NULL; - BOOL fRequiresQuoting = FALSE; - DWORD dwMaxEscapedSize = 0; - - // Loop through the argument determining if it needs to be quoted and what the maximum - // size would be if there are escape characters required. - for (LPCWSTR pwz = wzArgument; *pwz; ++pwz) - { - // Arguments with whitespace need quoting. - if (L' ' == *pwz || L'\t' == *pwz || L'\n' == *pwz || L'\v' == *pwz) - { - fRequiresQuoting = TRUE; - } - else if (L'"' == *pwz) // quotes need quoting and sometimes escaping. - { - fRequiresQuoting = TRUE; - ++dwMaxEscapedSize; - } - else if (L'\\' == *pwz) // some backslashes need escaping, so we'll count them all to make sure there is room. - { - ++dwMaxEscapedSize; - } - - ++dwMaxEscapedSize; - } - - // If we found anything in the argument that requires our argument to be quoted - if (fRequiresQuoting) - { - hr = StrAlloc(&sczQuotedArg, dwMaxEscapedSize + 3); // plus three for the start and end quote plus null terminator. - PathExitOnFailure(hr, "Failed to allocate argument to be quoted."); - - LPCWSTR pwz = wzArgument; - LPWSTR pwzQuoted = sczQuotedArg; - - *pwzQuoted = L'"'; - ++pwzQuoted; - while (*pwz) - { - DWORD dwBackslashes = 0; - while (L'\\' == *pwz) - { - ++dwBackslashes; - ++pwz; - } - - // Escape all backslashes at the end of the string. - if (!*pwz) - { - dwBackslashes *= 2; - } - else if (L'"' == *pwz) // escape all backslashes before the quote and escape the quote itself. - { - dwBackslashes = dwBackslashes * 2 + 1; - } - // the backslashes don't have to be escaped. - - // Add the appropriate number of backslashes - for (DWORD i = 0; i < dwBackslashes; ++i) - { - *pwzQuoted = L'\\'; - ++pwzQuoted; - } - - // If there is a character, add it after all the escaped backslashes - if (*pwz) - { - *pwzQuoted = *pwz; - ++pwz; - ++pwzQuoted; - } - } - - *pwzQuoted = L'"'; - ++pwzQuoted; - *pwzQuoted = L'\0'; // ensure the arg is null terminated. - } - - // If there is already data in the command line, append a space before appending the - // argument. - if (*psczCommandLine && **psczCommandLine) - { - hr = StrAllocConcat(psczCommandLine, L" ", 0); - PathExitOnFailure(hr, "Failed to append space to command line with existing data."); - } - - hr = StrAllocConcat(psczCommandLine, sczQuotedArg ? sczQuotedArg : wzArgument, 0); - PathExitOnFailure(hr, "Failed to copy command line argument."); - -LExit: - ReleaseStr(sczQuotedArg); - - return hr; -} - - DAPI_(LPWSTR) PathFile( __in_z LPCWSTR wzPath ) diff --git a/src/libs/dutil/WixToolset.DUtil/precomp.h b/src/libs/dutil/WixToolset.DUtil/precomp.h index f8f3b944..46d29f21 100644 --- a/src/libs/dutil/WixToolset.DUtil/precomp.h +++ b/src/libs/dutil/WixToolset.DUtil/precomp.h @@ -45,6 +45,7 @@ #include "dutil.h" #include "verutil.h" #include "aclutil.h" +#include "apputil.h" #include "atomutil.h" #include "buffutil.h" #include "butil.h" -- cgit v1.2.3-55-g6feb