diff --git a/src/burn/engine/engine.cpp b/src/burn/engine/engine.cpp index c372772c4..d432f732d 100644 --- a/src/burn/engine/engine.cpp +++ b/src/burn/engine/engine.cpp @@ -379,7 +379,8 @@ static HRESULT InitializeEngineState( BurnPipeConnectionInitialize(&pEngineState->embeddedConnection); // Retain whether bundle was initially run elevated. - ProcElevated(::GetCurrentProcess(), &pEngineState->internalCommand.fInitiallyElevated); + hr = ProcIsHighIntegrity(::GetCurrentProcess(), &pEngineState->internalCommand.fInitiallyElevated); + ExitOnFailure(hr, "Failed to determine if process is running elevated."); // Parse command line. hr = CoreParseCommandLine(&pEngineState->internalCommand, &pEngineState->command, &pEngineState->companionConnection, &pEngineState->embeddedConnection, &hSectionFile, &hSourceEngineFile); diff --git a/src/burn/stub/precomp.h b/src/burn/stub/precomp.h index 46239a6ca..b72cd0123 100644 --- a/src/burn/stub/precomp.h +++ b/src/burn/stub/precomp.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "engine.h" diff --git a/src/burn/stub/stub.cpp b/src/burn/stub/stub.cpp index d8cee9f12..ea5d88f0a 100644 --- a/src/burn/stub/stub.cpp +++ b/src/burn/stub/stub.cpp @@ -34,6 +34,8 @@ int WINAPI wWinMain( L"feclient.dll", // unsafely loaded by DecryptFile(). }; + AppSetDefaultProcessMitigationPolicy(POLICY_BURN_REGISTRY_PATH); + // Best effort attempt to get our file handle as soon as possible. hr = PathForCurrentProcess(&sczPath, NULL); if (SUCCEEDED(hr)) diff --git a/src/dtf/SfxCA/SfxUtil.cpp b/src/dtf/SfxCA/SfxUtil.cpp index 079f16173..4d85aab78 100644 --- a/src/dtf/SfxCA/SfxUtil.cpp +++ b/src/dtf/SfxCA/SfxUtil.cpp @@ -163,24 +163,70 @@ static HRESULT CreateGuid( return hr; } -static HRESULT ProcessElevated() +static HRESULT LogLastError(__in MSIHANDLE hSession, __in_z const wchar_t* wzMessage) { - HRESULT hr = S_OK; - HANDLE hToken = NULL; - TOKEN_ELEVATION tokenElevated = {}; - DWORD cbToken = 0; + HRESULT hr = HRESULT_FROM_WIN32(::GetLastError()); - if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken) && - ::GetTokenInformation(hToken, TokenElevation, &tokenElevated, sizeof(TOKEN_ELEVATION), &cbToken)) - { - hr = (0 != tokenElevated.TokenIsElevated) ? S_OK : S_FALSE; - } - else + Log(hSession, L"%ls. Error code 0x%08X", wzMessage, hr); + + return hr; +} + +static HRESULT HighIntegrityProcess(__in MSIHANDLE hSession, __out BOOL* pfHighIntegrity) +{ + HRESULT hr = S_OK; + HANDLE hToken = NULL; + DWORD dwTokenLength = 0; + DWORD cbToken = 0; + PTOKEN_MANDATORY_LABEL pTokenMandatoryLabel = NULL; + DWORD rid = 0; + + *pfHighIntegrity = FALSE; + + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken)) + { + hr = LogLastError(hSession, L"Failed to open process token"); + goto LExit; + } + + if (!::GetTokenInformation(hToken, TokenIntegrityLevel, NULL, 0, &dwTokenLength)) + { + DWORD er = ::GetLastError(); + if (er != ERROR_INSUFFICIENT_BUFFER) { - hr = HRESULT_FROM_WIN32(::GetLastError()); + hr = LogLastError(hSession, L"Failed to get token integrity information length"); + goto LExit; } + } - return hr; + pTokenMandatoryLabel = reinterpret_cast(::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dwTokenLength)); + if (!pTokenMandatoryLabel) + { + hr = LogLastError(hSession, L"Failed to allocate memory for token integrity information"); + goto LExit; + } + + if (!::GetTokenInformation(hToken, TokenIntegrityLevel, pTokenMandatoryLabel, dwTokenLength, &cbToken)) + { + hr = LogLastError(hSession, L"Failed to get token integrity information"); + goto LExit; + } + + rid = *::GetSidSubAuthority(pTokenMandatoryLabel->Label.Sid, *::GetSidSubAuthorityCount(pTokenMandatoryLabel->Label.Sid) - 1); + *pfHighIntegrity = (SECURITY_MANDATORY_HIGH_RID <= rid); + +LExit: + if (pTokenMandatoryLabel) + { + ::HeapFree(::GetProcessHeap(), 0, pTokenMandatoryLabel); + } + + if (hToken) + { + ::CloseHandle(hToken); + } + + return hr; } /// @@ -203,6 +249,7 @@ bool ExtractToTempDirectory(__in MSIHANDLE hSession, __in HMODULE hModule, HRESULT hr = S_OK; wchar_t szModule[MAX_PATH] = {}; wchar_t szGuid[GUID_STRING_LENGTH] = {}; + BOOL fHighIntegrity = FALSE; DWORD cchCopied = ::GetModuleFileName(hModule, szModule, MAX_PATH - 1); if (cchCopied == 0 || cchCopied == MAX_PATH - 1) @@ -224,9 +271,15 @@ bool ExtractToTempDirectory(__in MSIHANDLE hSession, __in HMODULE hModule, goto LExit; } - // Unelevated we use the user's temp directory. - hr = ProcessElevated(); - if (S_FALSE == hr) + // Non-high-integrity we use the user's temp directory. + hr = HighIntegrityProcess(hSession, &fHighIntegrity); + if (FAILED(hr)) + { + Log(hSession, L"Failed to determine if process is high integrity. Assuming high integrity. Error code 0x%x", hr); + fHighIntegrity = TRUE; + } + + if (!fHighIntegrity) { // Temp path is documented to be returned with a trailing backslash. cchCopied = ::GetTempPath(cchTempDirBuf, szTempDir); @@ -242,7 +295,7 @@ bool ExtractToTempDirectory(__in MSIHANDLE hSession, __in HMODULE hModule, goto LExit; } } - else // elevated or we couldn't check (in the latter case, assume we're elevated since it's safer to use) + else // high integrity or we couldn't check (in the latter case, assume high integrity since it's safer to use because if we're not elevated we'll fail safely). { // Windows directory will not contain a trailing backslash, so we add it next. cchCopied = ::GetWindowsDirectoryW(szTempDir, cchTempDirBuf); @@ -261,7 +314,7 @@ bool ExtractToTempDirectory(__in MSIHANDLE hSession, __in HMODULE hModule, hr = ::StringCchCat(szTempDir, cchTempDirBuf, L"\\Installer\\"); if (FAILED(hr)) { - Log(hSession, L"Failed append 'Installer' to Windows directory. Error code 0x%x", hr); + Log(hSession, L"Failed to append 'Installer' to Windows directory '%ls'. Error code 0x%x", szTempDir, hr); goto LExit; } } @@ -269,14 +322,14 @@ bool ExtractToTempDirectory(__in MSIHANDLE hSession, __in HMODULE hModule, hr = ::StringCchCat(szTempDir, cchTempDirBuf, szGuid); if (FAILED(hr)) { - Log(hSession, L"Failed append GUID to temp path. Error code 0x%x", hr); + Log(hSession, L"Failed append GUID to temp path '%ls'. Error code 0x%x", szTempDir, hr); goto LExit; } if (!::CreateDirectory(szTempDir, NULL)) { hr = HRESULT_FROM_WIN32(::GetLastError()); - Log(hSession, L"Failed to create temp directory. Error code 0x%x", hr); + Log(hSession, L"Failed to create temp directory '%ls'. Error code 0x%x", szTempDir, hr); goto LExit; } diff --git a/src/libs/dutil/WixToolset.DUtil/apputil.cpp b/src/libs/dutil/WixToolset.DUtil/apputil.cpp index c08fffc79..6c56f1a06 100644 --- a/src/libs/dutil/WixToolset.DUtil/apputil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/apputil.cpp @@ -19,10 +19,15 @@ typedef BOOL(WINAPI *LPFN_SETDEFAULTDLLDIRECTORIES)(DWORD); typedef BOOL(WINAPI *LPFN_SETDLLDIRECTORYW)(LPCWSTR); +typedef BOOL(WINAPI *LPFN_SETPROCESSMITIGATIONPOLICY)(PROCESS_MITIGATION_POLICY, PVOID, SIZE_T); static BOOL vfInitialized = FALSE; static LPFN_SETDEFAULTDLLDIRECTORIES vpfnSetDefaultDllDirectories = NULL; static LPFN_SETDLLDIRECTORYW vpfnSetDllDirectory = NULL; +static LPFN_SETPROCESSMITIGATIONPOLICY vpfnSetProcessMitigationPolicy = NULL; + +static const DWORD APP_MITIGATION_POLICY_DISABLED = 0; +static const DWORD APP_MITIGATION_POLICY_ENABLED = 1; /******************************************************************** EscapeCommandLineArgument - encodes wzArgument such that @@ -50,6 +55,7 @@ static void Initialize() vpfnSetDefaultDllDirectories = (LPFN_SETDEFAULTDLLDIRECTORIES)::GetProcAddress(hKernel32, "SetDefaultDllDirectories"); vpfnSetDllDirectory = (LPFN_SETDLLDIRECTORYW)::GetProcAddress(hKernel32, "SetDllDirectoryW"); + vpfnSetProcessMitigationPolicy = (LPFN_SETPROCESSMITIGATIONPOLICY)::GetProcAddress(hKernel32, "SetProcessMitigationPolicy"); vfInitialized = TRUE; @@ -190,6 +196,100 @@ DAPI_(void) AppInitializeUnsafe() ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); } +DAPI_(HRESULT) AppSetDefaultProcessMitigationPolicy( + __in_z LPCWSTR wzPolicyPath + ) +{ + HRESULT hr = S_OK; + HRESULT hrPolicy = S_OK; + DWORD dwPolicy = APP_MITIGATION_POLICY_DISABLED; + BOOL fApplied = FALSE; + PROCESS_MITIGATION_REDIRECTION_TRUST_POLICY redirectionTrustPolicy = { }; + PROCESS_MITIGATION_DYNAMIC_CODE_POLICY dynamicCodePolicy = { }; + PROCESS_MITIGATION_FONT_DISABLE_POLICY fontDisablePolicy = { }; + + Initialize(); + + if (!vpfnSetProcessMitigationPolicy) + { + ExitFunction1(hr = S_FALSE); + } + + hrPolicy = PolcReadNumber(wzPolicyPath, L"RedirectionGuard", APP_MITIGATION_POLICY_ENABLED, &dwPolicy); + if (FAILED(hrPolicy)) + { + TraceError(hrPolicy, "Failed to read mitigation policy setting: RedirectionGuard."); + dwPolicy = APP_MITIGATION_POLICY_ENABLED; + } + + if (APP_MITIGATION_POLICY_ENABLED == dwPolicy) + { + redirectionTrustPolicy.EnforceRedirectionTrust = 1; + + if (!vpfnSetProcessMitigationPolicy(ProcessRedirectionTrustPolicy, &redirectionTrustPolicy, sizeof(redirectionTrustPolicy))) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + TraceError(hr, "Failed to set RedirectionGuard mitigation policy."); + } + else + { + fApplied = TRUE; + } + } + + hrPolicy = PolcReadNumber(wzPolicyPath, L"DynamicCode", APP_MITIGATION_POLICY_DISABLED, &dwPolicy); + if (FAILED(hrPolicy)) + { + TraceError(hrPolicy, "Failed to read mitigation policy setting: DynamicCode."); + dwPolicy = APP_MITIGATION_POLICY_DISABLED; + } + + if (APP_MITIGATION_POLICY_ENABLED == dwPolicy) + { + dynamicCodePolicy.ProhibitDynamicCode = 1; + + if (!vpfnSetProcessMitigationPolicy(ProcessDynamicCodePolicy, &dynamicCodePolicy, sizeof(dynamicCodePolicy))) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + TraceError(hr, "Failed to set DynamicCode mitigation policy."); + } + else + { + fApplied = TRUE; + } + } + + hrPolicy = PolcReadNumber(wzPolicyPath, L"FontDisable", APP_MITIGATION_POLICY_DISABLED, &dwPolicy); + if (FAILED(hrPolicy)) + { + TraceError(hrPolicy, "Failed to read mitigation policy setting: FontDisable."); + dwPolicy = APP_MITIGATION_POLICY_DISABLED; + } + + if (APP_MITIGATION_POLICY_ENABLED == dwPolicy) + { + fontDisablePolicy.DisableNonSystemFonts = 1; + + if (!vpfnSetProcessMitigationPolicy(ProcessFontDisablePolicy, &fontDisablePolicy, sizeof(fontDisablePolicy))) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + TraceError(hr, "Failed to set FontDisable mitigation policy."); + } + else + { + fApplied = TRUE; + } + } + +LExit: + if (SUCCEEDED(hr) && !fApplied) + { + hr = S_FALSE; + } + + return hr; +} + DAPI_(HRESULT) AppAppendCommandLineArgument( __deref_inout_z LPWSTR* psczCommandLine, __in_z LPCWSTR wzArgument diff --git a/src/libs/dutil/WixToolset.DUtil/inc/apputil.h b/src/libs/dutil/WixToolset.DUtil/inc/apputil.h index e2812ee48..711f28c03 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/apputil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/apputil.h @@ -33,6 +33,16 @@ AppInitializeUnsafe - initializes without the full standard safety ********************************************************************/ void DAPI AppInitializeUnsafe(); +/******************************************************************** +AppSetDefaultProcessMitigationPolicy - enables default process + mitigations, with per-mitigation policy overrides. + +NOTE: Best effort. S_FALSE indicates not supported or disabled. +********************************************************************/ +HRESULT DAPI AppSetDefaultProcessMitigationPolicy( + __in_z LPCWSTR wzPolicyPath + ); + /******************************************************************** AppParseCommandLine - parses the command line using CommandLineToArgvW. The caller must free the value of pArgv on success diff --git a/src/libs/dutil/WixToolset.DUtil/inc/procutil.h b/src/libs/dutil/WixToolset.DUtil/inc/procutil.h index e7e917053..974003eb4 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/procutil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/procutil.h @@ -28,7 +28,10 @@ HRESULT DAPI ProcGetTokenInformation( __in TOKEN_INFORMATION_CLASS tokenInformationClass, __out LPVOID* ppvTokenInformation ); - +HRESULT DAPI ProcIsHighIntegrity( + __in HANDLE hProcess, + __out BOOL* pfHighIntegrity + ); HRESULT DAPI ProcHasPrivilege( __in HANDLE hProcess, __in LPCWSTR wzPrivilegeName, diff --git a/src/libs/dutil/WixToolset.DUtil/procutil.cpp b/src/libs/dutil/WixToolset.DUtil/procutil.cpp index 6cd3214ca..7109551be 100644 --- a/src/libs/dutil/WixToolset.DUtil/procutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/procutil.cpp @@ -34,7 +34,6 @@ static BOOL CALLBACK CloseWindowEnumCallback( __in LPARAM lParam ); - extern "C" HRESULT DAPI ProcElevated( __in HANDLE hProcess, __out BOOL* pfElevated @@ -141,6 +140,30 @@ extern "C" HRESULT DAPI ProcGetTokenInformation( return hr; } +extern "C" HRESULT DAPI ProcIsHighIntegrity( + __in HANDLE hProcess, + __out BOOL* pfHighIntegrity + ) +{ + HRESULT hr = S_OK; + TOKEN_MANDATORY_LABEL* pTokenMandatoryLabel = NULL; + DWORD integrityRid = 0; + + *pfHighIntegrity = FALSE; + + hr = ProcGetTokenInformation(hProcess, TokenIntegrityLevel, reinterpret_cast(&pTokenMandatoryLabel)); + ProcExitOnFailure(hr, "Failed to get token mandatory label."); + + integrityRid = *::GetSidSubAuthority(pTokenMandatoryLabel->Label.Sid, *::GetSidSubAuthorityCount(pTokenMandatoryLabel->Label.Sid) - 1); + + *pfHighIntegrity = (SECURITY_MANDATORY_HIGH_RID <= integrityRid); + +LExit: + ReleaseMem(pTokenMandatoryLabel); + + return hr; +} + extern "C" HRESULT DAPI ProcHasPrivilege( __in HANDLE hProcess, __in LPCWSTR wzPrivilegeName, diff --git a/src/libs/dutil/WixToolset.DUtil/rmutil.cpp b/src/libs/dutil/WixToolset.DUtil/rmutil.cpp index 95c8c8a4b..a73ffde17 100644 --- a/src/libs/dutil/WixToolset.DUtil/rmutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/rmutil.cpp @@ -168,41 +168,40 @@ extern "C" HRESULT DAPI RmuAddProcessById( DWORD cbPrevPriv = 0; DWORD er = ERROR_SUCCESS; BOOL fAdjustedPrivileges = FALSE; - BOOL fElevated = FALSE; - ProcElevated(::GetCurrentProcess(), &fElevated); - - // Must be elevated to adjust process privileges - if (fElevated) { - // Adding SeDebugPrivilege in the event that the process targeted by ::OpenProcess() is in a another user context. - if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) - { - RmExitWithLastError(hr, "Failed to get process token."); - } + // Best-effort attempt to enable SeDebugPrivilege in case the target process is in another user context. + if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) + { priv.PrivilegeCount = 1; priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - if (!::LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", &priv.Privileges[0].Luid)) - { - RmExitWithLastError(hr, "Failed to get debug privilege LUID."); - } - - cbPrevPriv = sizeof(TOKEN_PRIVILEGES); - pPrevPriv = static_cast(MemAlloc(cbPrevPriv, TRUE)); - RmExitOnNull(pPrevPriv, hr, E_OUTOFMEMORY, "Failed to allocate memory for empty previous privileges."); - - if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv)) + if (::LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", &priv.Privileges[0].Luid)) { - LPVOID pv = MemReAlloc(pPrevPriv, cbPrevPriv, TRUE); - RmExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for previous privileges."); - pPrevPriv = static_cast(pv); - - if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv)) + cbPrevPriv = sizeof(TOKEN_PRIVILEGES); + pPrevPriv = static_cast(MemAlloc(cbPrevPriv, TRUE)); + if (pPrevPriv) { - RmExitWithLastError(hr, "Failed to get debug privilege LUID."); + fAdjustedPrivileges = ::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv); + er = ::GetLastError(); // AdjustTokenPrivileges may succeed but still return ERROR_NOT_ALL_ASSIGNED. + + if (!fAdjustedPrivileges && ERROR_INSUFFICIENT_BUFFER == er) + { + LPVOID pv = MemReAlloc(pPrevPriv, cbPrevPriv, TRUE); + pPrevPriv = static_cast(pv); + + if (pPrevPriv) + { + fAdjustedPrivileges = ::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv); + er = ::GetLastError(); // AdjustTokenPrivileges may succeed but still return ERROR_NOT_ALL_ASSIGNED. + } + } + + // We actually only adjusted privileges if the privilege was assigned AND *succeeded*. + if (ERROR_SUCCESS != er) + { + fAdjustedPrivileges = FALSE; + } } } - - fAdjustedPrivileges = TRUE; } hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId);