340 lines
11 KiB
C++
340 lines
11 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "precomp.hpp"
|
|
#include "IntegrityTest.hpp"
|
|
|
|
// WinRT namespaces
|
|
|
|
using namespace Platform;
|
|
using namespace Microsoft::OneCoreUap::Test::AppModel;
|
|
|
|
static PCWSTR c_pwszLowIntegrity = L"Low Integrity";
|
|
static PCWSTR c_pwszMedIntegrity = L"Medium Integrity";
|
|
static PCWSTR c_pwszHighIntegrity = L"High Integrity";
|
|
static PCWSTR c_pwszSysIntegrity = L"System Integrity";
|
|
static PCWSTR c_pwszUnkIntegrity = L"UNKNOWN INTEGRITY";
|
|
|
|
static void s_ExpandAnyEnvStrings(_Inout_ std::unique_ptr<wchar_t[]>& cmdline)
|
|
{
|
|
const DWORD cchNeeded = ExpandEnvironmentStringsW(cmdline.get(), nullptr, 0);
|
|
THROW_LAST_ERROR_IF(0 == cchNeeded);
|
|
|
|
std::unique_ptr<wchar_t[]> cmdlineExpanded = std::make_unique<wchar_t[]>(cchNeeded);
|
|
|
|
THROW_LAST_ERROR_IF(0 == ExpandEnvironmentStringsW(cmdline.get(), cmdlineExpanded.get(), cchNeeded));
|
|
|
|
cmdline.swap(cmdlineExpanded);
|
|
}
|
|
|
|
static void s_RunViaCreateProcess(_In_ PCWSTR pwszExePath)
|
|
{
|
|
STARTUPINFOW si = { 0 };
|
|
si.cb = sizeof(si);
|
|
si.wShowWindow = SW_SHOWNORMAL;
|
|
|
|
wil::unique_process_information pi;
|
|
|
|
// We will need a mutable string to give to CreateProcessW.
|
|
const size_t cchNeeded = wcslen(pwszExePath) + 1;
|
|
std::unique_ptr<wchar_t[]> cmdlineMutable = std::make_unique<wchar_t[]>(cchNeeded);
|
|
THROW_IF_FAILED(StringCchCopyW(cmdlineMutable.get(), cchNeeded, pwszExePath));
|
|
|
|
// Replace the environment vars with their actual paths
|
|
s_ExpandAnyEnvStrings(cmdlineMutable);
|
|
|
|
LOG_OUTPUT(L"Launching '%s'", cmdlineMutable.get());
|
|
|
|
THROW_IF_WIN32_BOOL_FALSE(CreateProcessW(nullptr,
|
|
cmdlineMutable.get(),
|
|
nullptr,
|
|
nullptr,
|
|
TRUE,
|
|
0,
|
|
nullptr,
|
|
nullptr,
|
|
&si,
|
|
&pi));
|
|
|
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
}
|
|
|
|
static void s_SetConIntegrityLow()
|
|
{
|
|
// This is absolute paths because OneCoreUAPTest wouldn't accept relative paths here.
|
|
// We're trying to call this:
|
|
// C:\\windows\\system32\\icacls.exe C:\\data\\test\\bin\\conintegrity.exe /setintegritylevel low
|
|
|
|
// First assemble with WinRT strings including the Test Deployment Directory C:\data\test\bin which can vary.
|
|
auto commandLine = ref new String(L"%WINDIR%\\system32\\icacls.exe ") +
|
|
TAEFHelper::GetTestDeploymentDirectory() + ref new String(L"conintegrity.exe /setintegritylevel low");
|
|
|
|
// Now call our helper to munge the environment strings then run it and wait for exit.
|
|
s_RunViaCreateProcess(commandLine->Data());
|
|
}
|
|
|
|
bool ModuleSetup()
|
|
{
|
|
IntegrityTest::s_LogMyIntegrityLevel(L"ModSetup");
|
|
|
|
THROW_IF_FAILED(::WinRTHelper_Register());
|
|
THROW_IF_FAILED(Windows::Foundation::Initialize(WINRT_INIT_MULTITHREADED));
|
|
|
|
TestHelper::Initialize();
|
|
|
|
// Set ConIntegrity.exe to low integrity with ICACLS.
|
|
// We have to do this from SYSTEM context.
|
|
s_SetConIntegrityLow();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ModuleCleanup()
|
|
{
|
|
IntegrityTest::s_LogMyIntegrityLevel(L"ModCleanup");
|
|
|
|
TestHelper::Uninitialize();
|
|
Windows::Foundation::Uninitialize();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IntegrityTest::ClassSetup()
|
|
{
|
|
IntegrityTest::s_LogMyIntegrityLevel(L"ClassSetup");
|
|
|
|
THROW_IF_FAILED(Windows::Foundation::Initialize(WINRT_INIT_MULTITHREADED));
|
|
|
|
// Get Appx location
|
|
auto testDeploymentDir = TAEFHelper::GetTestDeploymentDirectory();
|
|
LOG_OUTPUT(L"Test Deployment Dir: \"%s\"", testDeploymentDir->Data());
|
|
|
|
// Deploy App
|
|
|
|
#if defined(_X86_)
|
|
auto vclibPackage = DeploymentHelper::AddPackageIfNotPresent(testDeploymentDir + ref new String(L"Microsoft.VCLibs.x86.14.00.appx"));
|
|
#elif defined(_AMD64_)
|
|
auto vclibPackage = DeploymentHelper::AddPackageIfNotPresent(testDeploymentDir + ref new String(L"Microsoft.VCLibs.x64.14.00.appx"));
|
|
#elif defined(_ARM_)
|
|
auto vclibPackage = DeploymentHelper::AddPackageIfNotPresent(testDeploymentDir + ref new String(L"Microsoft.VCLibs.arm.14.00.appx"));
|
|
#elif defined(_ARM64_)
|
|
auto vclibPackage = DeploymentHelper::AddPackageIfNotPresent(testDeploymentDir + ref new String(L"Microsoft.VCLibs.arm64.14.00.appx"));
|
|
#else
|
|
#error Unknown architecture for test.
|
|
#endif
|
|
|
|
auto appPackage = DeploymentHelper::AddPackage(testDeploymentDir + ref new String(L"ConsoleIntegrityUWP.appx"));
|
|
VERIFY_ARE_EQUAL(appPackage->Size, 1u);
|
|
|
|
// Get App's AUMID
|
|
auto appAumids = appPackage->GetAt(0)->AUMIDs;
|
|
VERIFY_IS_NOT_NULL(appAumids);
|
|
VERIFY_ARE_EQUAL(appAumids->Size, 1u);
|
|
|
|
// save off aumid
|
|
this->_appAumid = appAumids->GetAt(0);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IntegrityTest::ClassCleanup()
|
|
{
|
|
s_LogMyIntegrityLevel(L"ClassCleanup");
|
|
|
|
Windows::Foundation::Uninitialize();
|
|
return true;
|
|
}
|
|
|
|
void IntegrityTest::_RunWin32ConIntegrityLowHelper()
|
|
{
|
|
s_RunViaCreateProcess(L"conintegrity.exe");
|
|
}
|
|
|
|
void IntegrityTest::_RunUWPConIntegrityAppHelper()
|
|
{
|
|
// We need to start the execution alias from the current user's location.
|
|
// We can't assume it will find it in the PATH.
|
|
std::wstring cmdline(L"%localappdata%\\microsoft\\windowsapps\\conintegrityuwp.exe");
|
|
|
|
s_RunViaCreateProcess(cmdline.c_str());
|
|
}
|
|
|
|
void IntegrityTest::_RunUWPConIntegrityViaTile()
|
|
{
|
|
LOG_OUTPUT(L"Launching %s", _appAumid->Data());
|
|
|
|
auto viewDescriptor = NavigationHelper::LaunchApplication(_appAumid);
|
|
|
|
LOG_OUTPUT(L" AUMID: \"%s\"", viewDescriptor->AUMID->Data());
|
|
LOG_OUTPUT(L" Args: \"%s\"", viewDescriptor->Args->Data());
|
|
LOG_OUTPUT(L" Tile Id: \"%s\"", viewDescriptor->TileId->Data());
|
|
LOG_OUTPUT(L" View Id: %u", viewDescriptor->ViewId);
|
|
LOG_OUTPUT(L" Process Id: %u, 0x%x", viewDescriptor->ProcessId, viewDescriptor->ProcessId);
|
|
LOG_OUTPUT(L" Host Id: 0x%016llx", viewDescriptor->HostId);
|
|
LOG_OUTPUT(L" PSM Key: \"%s\"", viewDescriptor->PSMKey->Data());
|
|
|
|
// There's not really a wait for exit here, so just sleep.
|
|
::Sleep(5000);
|
|
|
|
// Terminate
|
|
LOG_OUTPUT(L"Terminating");
|
|
NavigationHelper::CloseView(viewDescriptor->ViewId);
|
|
}
|
|
|
|
// These are shorthands for the function calls, their True/False return code, and then the GetLastError
|
|
// They are serialized into an extremely short string to deal with potentially small console buffers
|
|
// on OneCore-derived Windows SKUs.
|
|
// Example: RCOW;1;0 = ReadConsoleOutputW returning TRUE and a GetLastError() of 0.
|
|
// Please see conintegrity.exe and conintegrityuwp.exe for how they are formed.
|
|
static PCWSTR _rgpwszExpectedSuccess[] = {
|
|
L"RCOW;1;0",
|
|
L"RCOA;1;0",
|
|
L"RCOCW;1;0",
|
|
L"RCOCA;1;0",
|
|
L"RCOAttr;1;0",
|
|
L"WCIA;1;0",
|
|
L"WCIW;1;0"
|
|
};
|
|
|
|
static PCWSTR _rgpwszExpectedFail[] = {
|
|
L"RCOW;0;5",
|
|
L"RCOA;0;5",
|
|
L"RCOCW;0;5",
|
|
L"RCOCA;0;5",
|
|
L"RCOAttr;0;5",
|
|
L"WCIA;0;5",
|
|
L"WCIW;0;5"
|
|
};
|
|
|
|
void IntegrityTest::_TestValidationHelper(const bool fIsBlockExpected,
|
|
_In_ PCWSTR pwszIntegrityExpected)
|
|
{
|
|
CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 };
|
|
csbiex.cbSize = sizeof(csbiex);
|
|
GetConsoleScreenBufferInfoEx(GetStdHandle(STD_OUTPUT_HANDLE),
|
|
&csbiex);
|
|
|
|
LOG_OUTPUT(L"Buffer Size X:%d Y:%d", csbiex.dwSize.X, csbiex.dwSize.Y);
|
|
|
|
size_t cch = csbiex.dwSize.X;
|
|
wistd::unique_ptr<wchar_t[]> stringData = wil::make_unique_nothrow<wchar_t[]>(cch);
|
|
THROW_IF_NULL_ALLOC(stringData);
|
|
|
|
COORD coordRead = { 0 };
|
|
for (coordRead.Y = 0; coordRead.Y < 8; coordRead.Y++)
|
|
{
|
|
ZeroMemory(stringData.get(), sizeof(wchar_t) * cch);
|
|
|
|
DWORD dwRead = 0;
|
|
|
|
ReadConsoleOutputCharacterW(GetStdHandle(STD_OUTPUT_HANDLE),
|
|
stringData.get(),
|
|
(DWORD)cch,
|
|
coordRead,
|
|
&dwRead);
|
|
|
|
WEX::Common::String strExpected;
|
|
WEX::Common::String strActual;
|
|
|
|
// At position 0, check the integrity.
|
|
if (coordRead.Y == 0)
|
|
{
|
|
strExpected = pwszIntegrityExpected;
|
|
}
|
|
else
|
|
{
|
|
// For the rest, check whether the API call worked.
|
|
if (fIsBlockExpected)
|
|
{
|
|
strExpected = _rgpwszExpectedFail[coordRead.Y - 1];
|
|
}
|
|
else
|
|
{
|
|
strExpected = _rgpwszExpectedSuccess[coordRead.Y - 1];
|
|
}
|
|
}
|
|
stringData[strExpected.GetLength()] = L'\0';
|
|
strActual = stringData.get();
|
|
VERIFY_ARE_EQUAL(strExpected, strActual);
|
|
|
|
LOG_OUTPUT(stringData.get());
|
|
}
|
|
}
|
|
|
|
void IntegrityTest::TestLaunchLowILFromHigh()
|
|
{
|
|
s_LogMyIntegrityLevel(L"TestBody");
|
|
_RunWin32ConIntegrityLowHelper();
|
|
|
|
PCWSTR pwszIntegrityExpected = s_GetMyIntegrityLevel();
|
|
bool fIsBlockExpected = false;
|
|
|
|
// If I'm High, expect low.
|
|
// Otherwise if I'm System, expect system.
|
|
if (0 == wcscmp(pwszIntegrityExpected, c_pwszHighIntegrity))
|
|
{
|
|
pwszIntegrityExpected = c_pwszLowIntegrity;
|
|
fIsBlockExpected = true;
|
|
}
|
|
|
|
_TestValidationHelper(fIsBlockExpected, pwszIntegrityExpected);
|
|
}
|
|
|
|
void IntegrityTest::TestLaunchLowILFromMedium()
|
|
{
|
|
s_LogMyIntegrityLevel(L"TestBody");
|
|
_RunWin32ConIntegrityLowHelper();
|
|
_TestValidationHelper(true, c_pwszLowIntegrity);
|
|
}
|
|
|
|
void IntegrityTest::TestLaunchAppFromHigh()
|
|
{
|
|
s_LogMyIntegrityLevel(L"TestBody");
|
|
_RunUWPConIntegrityAppHelper();
|
|
_TestValidationHelper(true, c_pwszLowIntegrity);
|
|
}
|
|
|
|
void IntegrityTest::TestLaunchAppFromMedium()
|
|
{
|
|
s_LogMyIntegrityLevel(L"TestBody");
|
|
_RunUWPConIntegrityAppHelper();
|
|
_TestValidationHelper(true, c_pwszLowIntegrity);
|
|
}
|
|
|
|
void IntegrityTest::TestLaunchAppAlone()
|
|
{
|
|
s_LogMyIntegrityLevel(L"TestBody");
|
|
_RunUWPConIntegrityViaTile();
|
|
}
|
|
|
|
PCWSTR IntegrityTest::s_GetMyIntegrityLevel()
|
|
{
|
|
DWORD dwIntegrityLevel = 0;
|
|
|
|
// Get the Integrity level.
|
|
wistd::unique_ptr<TOKEN_MANDATORY_LABEL> tokenLabel;
|
|
THROW_IF_FAILED(wil::GetTokenInformationNoThrow(tokenLabel, GetCurrentProcessToken()));
|
|
|
|
dwIntegrityLevel = *GetSidSubAuthority(tokenLabel->Label.Sid,
|
|
(DWORD)(UCHAR)(*GetSidSubAuthorityCount(tokenLabel->Label.Sid) - 1));
|
|
|
|
switch (dwIntegrityLevel)
|
|
{
|
|
case SECURITY_MANDATORY_LOW_RID:
|
|
return c_pwszLowIntegrity;
|
|
case SECURITY_MANDATORY_MEDIUM_RID:
|
|
return c_pwszMedIntegrity;
|
|
case SECURITY_MANDATORY_HIGH_RID:
|
|
return c_pwszHighIntegrity;
|
|
case SECURITY_MANDATORY_SYSTEM_RID:
|
|
return c_pwszSysIntegrity;
|
|
default:
|
|
return c_pwszUnkIntegrity;
|
|
}
|
|
}
|
|
|
|
void IntegrityTest::s_LogMyIntegrityLevel(PCWSTR WhoAmI)
|
|
{
|
|
LOG_OUTPUT(L"%s: %s", WhoAmI, s_GetMyIntegrityLevel());
|
|
}
|