summaryrefslogtreecommitdiffstats
path: root/security/sandbox/chromium/sandbox/win/src/policy_target_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'security/sandbox/chromium/sandbox/win/src/policy_target_test.cc')
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_target_test.cc486
1 files changed, 486 insertions, 0 deletions
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_target_test.cc b/security/sandbox/chromium/sandbox/win/src/policy_target_test.cc
new file mode 100644
index 0000000000..4ee03e19f6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_target_test.cc
@@ -0,0 +1,486 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/memory/writable_shared_memory_region.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/target_services.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include "base/win/win_util.h"
+#endif
+
+namespace sandbox {
+
+#define BINDNTDLL(name) \
+ name##Function name = reinterpret_cast<name##Function>( \
+ ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name))
+
+// Reverts to self and verify that SetInformationToken was faked. Returns
+// SBOX_TEST_SUCCEEDED if faked and SBOX_TEST_FAILED if not faked.
+SBOX_TESTS_COMMAND int PolicyTargetTest_token(int argc, wchar_t** argv) {
+ HANDLE thread_token;
+ // Get the thread token, using impersonation.
+ if (!::OpenThreadToken(GetCurrentThread(),
+ TOKEN_IMPERSONATE | TOKEN_DUPLICATE, false,
+ &thread_token))
+ return ::GetLastError();
+
+ ::RevertToSelf();
+ ::CloseHandle(thread_token);
+
+ int ret = SBOX_TEST_FAILED;
+ if (::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
+ false, &thread_token)) {
+ ret = SBOX_TEST_SUCCEEDED;
+ ::CloseHandle(thread_token);
+ }
+ return ret;
+}
+
+// Stores the high privilege token on a static variable, change impersonation
+// again to that one and verify that we are not interfering anymore with
+// RevertToSelf.
+SBOX_TESTS_COMMAND int PolicyTargetTest_steal(int argc, wchar_t** argv) {
+ static HANDLE thread_token;
+ if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) {
+ if (!::OpenThreadToken(GetCurrentThread(),
+ TOKEN_IMPERSONATE | TOKEN_DUPLICATE, false,
+ &thread_token))
+ return ::GetLastError();
+ } else {
+ if (!::SetThreadToken(nullptr, thread_token))
+ return ::GetLastError();
+
+ // See if we fake the call again.
+ int ret = PolicyTargetTest_token(argc, argv);
+ ::CloseHandle(thread_token);
+ return ret;
+ }
+ return 0;
+}
+
+// Opens the thread token with and without impersonation.
+SBOX_TESTS_COMMAND int PolicyTargetTest_token2(int argc, wchar_t** argv) {
+ HANDLE thread_token;
+ // Get the thread token, using impersonation.
+ if (!::OpenThreadToken(GetCurrentThread(),
+ TOKEN_IMPERSONATE | TOKEN_DUPLICATE, false,
+ &thread_token))
+ return ::GetLastError();
+ ::CloseHandle(thread_token);
+
+ // Get the thread token, without impersonation.
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
+ true, &thread_token))
+ return ::GetLastError();
+ ::CloseHandle(thread_token);
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Opens the thread token with and without impersonation, using
+// NtOpenThreadTokenEX.
+SBOX_TESTS_COMMAND int PolicyTargetTest_token3(int argc, wchar_t** argv) {
+ BINDNTDLL(NtOpenThreadTokenEx);
+ if (!NtOpenThreadTokenEx)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ HANDLE thread_token;
+ // Get the thread token, using impersonation.
+ NTSTATUS status = NtOpenThreadTokenEx(GetCurrentThread(),
+ TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
+ false, 0, &thread_token);
+ if (status == STATUS_NO_TOKEN)
+ return ERROR_NO_TOKEN;
+ if (!NT_SUCCESS(status))
+ return SBOX_TEST_FAILED;
+
+ ::CloseHandle(thread_token);
+
+ // Get the thread token, without impersonation.
+ status = NtOpenThreadTokenEx(GetCurrentThread(),
+ TOKEN_IMPERSONATE | TOKEN_DUPLICATE, true, 0,
+ &thread_token);
+ if (!NT_SUCCESS(status))
+ return SBOX_TEST_FAILED;
+
+ ::CloseHandle(thread_token);
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Tests that we can open the current thread.
+SBOX_TESTS_COMMAND int PolicyTargetTest_thread(int argc, wchar_t** argv) {
+ DWORD thread_id = ::GetCurrentThreadId();
+ HANDLE thread = ::OpenThread(SYNCHRONIZE, false, thread_id);
+ if (!thread)
+ return ::GetLastError();
+ if (!::CloseHandle(thread))
+ return ::GetLastError();
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// New thread entry point: do nothing.
+DWORD WINAPI PolicyTargetTest_thread_main(void* param) {
+ ::Sleep(INFINITE);
+ return 0;
+}
+
+// Tests that we can create a new thread, and open it.
+SBOX_TESTS_COMMAND int PolicyTargetTest_thread2(int argc, wchar_t** argv) {
+ // Use default values to create a new thread.
+ DWORD thread_id;
+ HANDLE thread = ::CreateThread(nullptr, 0, &PolicyTargetTest_thread_main, 0,
+ 0, &thread_id);
+ if (!thread)
+ return ::GetLastError();
+ if (!::CloseHandle(thread))
+ return ::GetLastError();
+
+ thread = ::OpenThread(SYNCHRONIZE, false, thread_id);
+ if (!thread)
+ return ::GetLastError();
+
+ if (!::CloseHandle(thread))
+ return ::GetLastError();
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Tests that we can call CreateProcess.
+SBOX_TESTS_COMMAND int PolicyTargetTest_process(int argc, wchar_t** argv) {
+ // Use default values to create a new process.
+ STARTUPINFO startup_info = {0};
+ startup_info.cb = sizeof(startup_info);
+ PROCESS_INFORMATION temp_process_info = {};
+ // Note: CreateProcessW() can write to its lpCommandLine, don't pass a
+ // raw string literal.
+ std::wstring writable_cmdline_str(L"foo.exe");
+ if (!::CreateProcessW(L"foo.exe", &writable_cmdline_str[0], nullptr, nullptr,
+ false, 0, nullptr, nullptr, &startup_info,
+ &temp_process_info))
+ return SBOX_TEST_SUCCEEDED;
+ base::win::ScopedProcessInformation process_info(temp_process_info);
+ return SBOX_TEST_FAILED;
+}
+
+TEST(PolicyTargetTest, SetInformationThread) {
+ TestRunner runner;
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token"));
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token"));
+
+ runner.SetTestState(EVERY_STATE);
+ EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"PolicyTargetTest_steal"));
+}
+
+TEST(PolicyTargetTest, OpenThreadToken) {
+ TestRunner runner;
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token2"));
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token2"));
+}
+
+TEST(PolicyTargetTest, OpenThreadTokenEx) {
+ TestRunner runner;
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token3"));
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token3"));
+}
+
+TEST(PolicyTargetTest, OpenThread) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread"))
+ << "Opens the current thread";
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread2"))
+ << "Creates a new thread and opens it";
+}
+
+TEST(PolicyTargetTest, OpenProcess) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_process"))
+ << "Opens a process";
+}
+
+TEST(PolicyTargetTest, PolicyBaseNoJobLifetime) {
+ TestRunner runner(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
+ runner.SetReleasePolicyInRun(true);
+ // TargetPolicy and its SharedMemIPCServer should continue to exist until
+ // the child process dies.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread"))
+ << "Opens the current thread";
+}
+
+// Launches the app in the sandbox and ask it to wait in an
+// infinite loop. Waits for 2 seconds and then check if the
+// desktop associated with the app thread is not the same as the
+// current desktop.
+TEST(PolicyTargetTest, DesktopPolicy) {
+ BrokerServices* broker = GetBroker();
+
+ // Precreate the desktop.
+ scoped_refptr<TargetPolicy> temp_policy = broker->CreatePolicy();
+ temp_policy->CreateAlternateDesktop(false);
+ temp_policy = nullptr;
+
+ ASSERT_TRUE(broker);
+
+ // Get the path to the sandboxed app.
+ wchar_t prog_name[MAX_PATH];
+ GetModuleFileNameW(nullptr, prog_name, MAX_PATH);
+
+ std::wstring arguments(L"\"");
+ arguments += prog_name;
+ arguments += L"\" -child 0 wait"; // Don't care about the "state" argument.
+
+ // Launch the app.
+ ResultCode result = SBOX_ALL_OK;
+ ResultCode warning_result = SBOX_ALL_OK;
+ DWORD last_error = ERROR_SUCCESS;
+ base::win::ScopedProcessInformation target;
+
+ scoped_refptr<TargetPolicy> policy = broker->CreatePolicy();
+ policy->SetAlternateDesktop(false);
+ policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
+ PROCESS_INFORMATION temp_process_info = {};
+ result =
+ broker->SpawnTarget(prog_name, arguments.c_str(), policy, &warning_result,
+ &last_error, &temp_process_info);
+ std::wstring desktop_name = policy->GetAlternateDesktop();
+ policy = nullptr;
+
+ EXPECT_EQ(SBOX_ALL_OK, result);
+ if (result == SBOX_ALL_OK)
+ target.Set(temp_process_info);
+
+ EXPECT_EQ(1u, ::ResumeThread(target.thread_handle()));
+
+ EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT),
+ ::WaitForSingleObject(target.process_handle(), 2000));
+
+ EXPECT_NE(::GetThreadDesktop(target.thread_id()),
+ ::GetThreadDesktop(::GetCurrentThreadId()));
+
+ HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, false, DESKTOP_ENUMERATE);
+ EXPECT_TRUE(desk);
+ EXPECT_TRUE(::CloseDesktop(desk));
+ EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0));
+
+ ::WaitForSingleObject(target.process_handle(), INFINITE);
+
+ // Close the desktop handle.
+ temp_policy = broker->CreatePolicy();
+ temp_policy->DestroyAlternateDesktop();
+ temp_policy = nullptr;
+
+ // Make sure the desktop does not exist anymore.
+ desk = ::OpenDesktop(desktop_name.c_str(), 0, false, DESKTOP_ENUMERATE);
+ EXPECT_FALSE(desk);
+}
+
+// Launches the app in the sandbox and ask it to wait in an
+// infinite loop. Waits for 2 seconds and then check if the
+// winstation associated with the app thread is not the same as the
+// current desktop.
+TEST(PolicyTargetTest, WinstaPolicy) {
+ BrokerServices* broker = GetBroker();
+
+ // Precreate the desktop.
+ scoped_refptr<TargetPolicy> temp_policy = broker->CreatePolicy();
+ temp_policy->CreateAlternateDesktop(true);
+ temp_policy = nullptr;
+
+ ASSERT_TRUE(broker);
+
+ // Get the path to the sandboxed app.
+ wchar_t prog_name[MAX_PATH];
+ GetModuleFileNameW(nullptr, prog_name, MAX_PATH);
+
+ std::wstring arguments(L"\"");
+ arguments += prog_name;
+ arguments += L"\" -child 0 wait"; // Don't care about the "state" argument.
+
+ // Launch the app.
+ ResultCode result = SBOX_ALL_OK;
+ ResultCode warning_result = SBOX_ALL_OK;
+ base::win::ScopedProcessInformation target;
+
+ scoped_refptr<TargetPolicy> policy = broker->CreatePolicy();
+ policy->SetAlternateDesktop(true);
+ policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
+ PROCESS_INFORMATION temp_process_info = {};
+ DWORD last_error = ERROR_SUCCESS;
+ result =
+ broker->SpawnTarget(prog_name, arguments.c_str(), policy, &warning_result,
+ &last_error, &temp_process_info);
+ std::wstring desktop_name = policy->GetAlternateDesktop();
+ policy = nullptr;
+
+ EXPECT_EQ(SBOX_ALL_OK, result);
+ if (result == SBOX_ALL_OK)
+ target.Set(temp_process_info);
+
+ EXPECT_EQ(1u, ::ResumeThread(target.thread_handle()));
+
+ EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT),
+ ::WaitForSingleObject(target.process_handle(), 2000));
+
+ EXPECT_NE(::GetThreadDesktop(target.thread_id()),
+ ::GetThreadDesktop(::GetCurrentThreadId()));
+
+ ASSERT_FALSE(desktop_name.empty());
+
+ // Make sure there is a backslash, for the window station name.
+ EXPECT_NE(desktop_name.find_first_of(L'\\'), std::wstring::npos);
+
+ // Isolate the desktop name.
+ desktop_name = desktop_name.substr(desktop_name.find_first_of(L'\\') + 1);
+
+ HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, false, DESKTOP_ENUMERATE);
+ // This should fail if the desktop is really on another window station.
+ EXPECT_FALSE(desk);
+ EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0));
+
+ ::WaitForSingleObject(target.process_handle(), INFINITE);
+
+ // Close the desktop handle.
+ temp_policy = broker->CreatePolicy();
+ temp_policy->DestroyAlternateDesktop();
+ temp_policy = nullptr;
+}
+
+// Creates multiple policies, with alternate desktops on both local and
+// alternate winstations.
+TEST(PolicyTargetTest, BothLocalAndAlternateWinstationDesktop) {
+ BrokerServices* broker = GetBroker();
+
+ scoped_refptr<TargetPolicy> policy1 = broker->CreatePolicy();
+ scoped_refptr<TargetPolicy> policy2 = broker->CreatePolicy();
+ scoped_refptr<TargetPolicy> policy3 = broker->CreatePolicy();
+
+ ResultCode result;
+ result = policy1->SetAlternateDesktop(false);
+ EXPECT_EQ(SBOX_ALL_OK, result);
+ result = policy2->SetAlternateDesktop(true);
+ EXPECT_EQ(SBOX_ALL_OK, result);
+ result = policy3->SetAlternateDesktop(false);
+ EXPECT_EQ(SBOX_ALL_OK, result);
+
+ std::wstring policy1_desktop_name = policy1->GetAlternateDesktop();
+ std::wstring policy2_desktop_name = policy2->GetAlternateDesktop();
+
+ // Extract only the "desktop name" portion of
+ // "{winstation name}\\{desktop name}"
+ EXPECT_NE(policy1_desktop_name.substr(
+ policy1_desktop_name.find_first_of(L'\\') + 1),
+ policy2_desktop_name.substr(
+ policy2_desktop_name.find_first_of(L'\\') + 1));
+
+ policy1->DestroyAlternateDesktop();
+ policy2->DestroyAlternateDesktop();
+ policy3->DestroyAlternateDesktop();
+}
+
+// Launches the app in the sandbox and share a handle with it. The app should
+// be able to use the handle.
+TEST(PolicyTargetTest, ShareHandleTest) {
+ BrokerServices* broker = GetBroker();
+ ASSERT_TRUE(broker);
+
+ base::StringPiece contents = "Hello World";
+ base::WritableSharedMemoryRegion writable_region =
+ base::WritableSharedMemoryRegion::Create(contents.size());
+ ASSERT_TRUE(writable_region.IsValid());
+ base::WritableSharedMemoryMapping writable_mapping = writable_region.Map();
+ ASSERT_TRUE(writable_mapping.IsValid());
+ memcpy(writable_mapping.memory(), contents.data(), contents.size());
+
+ // Get the path to the sandboxed app.
+ wchar_t prog_name[MAX_PATH];
+ GetModuleFileNameW(nullptr, prog_name, MAX_PATH);
+
+ base::ReadOnlySharedMemoryRegion read_only_region =
+ base::WritableSharedMemoryRegion::ConvertToReadOnly(
+ std::move(writable_region));
+ ASSERT_TRUE(read_only_region.IsValid());
+
+ scoped_refptr<TargetPolicy> policy = broker->CreatePolicy();
+ policy->AddHandleToShare(read_only_region.GetPlatformHandle());
+
+ std::wstring arguments(L"\"");
+ arguments += prog_name;
+ arguments += L"\" -child 0 shared_memory_handle ";
+ arguments += base::AsWString(base::NumberToString16(
+ base::win::HandleToUint32(read_only_region.GetPlatformHandle())));
+
+ // Launch the app.
+ ResultCode result = SBOX_ALL_OK;
+ ResultCode warning_result = SBOX_ALL_OK;
+ base::win::ScopedProcessInformation target;
+
+ policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
+ PROCESS_INFORMATION temp_process_info = {};
+ DWORD last_error = ERROR_SUCCESS;
+ result =
+ broker->SpawnTarget(prog_name, arguments.c_str(), policy, &warning_result,
+ &last_error, &temp_process_info);
+ policy = nullptr;
+
+ EXPECT_EQ(SBOX_ALL_OK, result);
+ if (result == SBOX_ALL_OK)
+ target.Set(temp_process_info);
+
+ EXPECT_EQ(1u, ::ResumeThread(target.thread_handle()));
+
+ EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT),
+ ::WaitForSingleObject(target.process_handle(), 2000));
+
+ EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0));
+
+ ::WaitForSingleObject(target.process_handle(), INFINITE);
+}
+
+// Dummy target that just reports that's it spawned correctly.
+SBOX_TESTS_COMMAND int PolicyTargetTest_SetEffectiveToken(int argc,
+ wchar_t** argv) {
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Test whether after using SetEffectiveToken spawning a target works as
+// expected.
+TEST(PolicyTargetTest, SetEffectiveToken) {
+ TestRunner runner;
+ HANDLE token;
+
+ // Get current process token.
+ EXPECT_TRUE(
+ ::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &token));
+
+ // Setup token guard.
+ base::win::ScopedHandle token_guard(token);
+
+ // Set token and run target.
+ runner.GetPolicy()->SetEffectiveToken(token_guard.Get());
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"PolicyTargetTest_SetEffectiveToken"));
+}
+
+} // namespace sandbox