summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjschuh@chromium.org <jschuh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-19 22:21:26 +0000
committerjschuh@chromium.org <jschuh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-19 22:21:26 +0000
commitd9b5d1768be62ec8584fae70d15d0b902a790abc (patch)
tree7bceb03f01ee7839dc04b3553527ee1be1284086
parente7007a7941e75d985b9b78ef5a80b9743f890809 (diff)
downloadchromium_src-d9b5d1768be62ec8584fae70d15d0b902a790abc.zip
chromium_src-d9b5d1768be62ec8584fae70d15d0b902a790abc.tar.gz
chromium_src-d9b5d1768be62ec8584fae70d15d0b902a790abc.tar.bz2
Add a sandbox API to allow closing open handles at lockdown.
BUG=58069 BUG=74242 TEST=sbox_integration_tests --gtest_filter=HandleCloserTests.* Review URL: http://codereview.chromium.org/7253054 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@93113 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--content/common/sandbox_policy.cc38
-rw-r--r--sandbox/sandbox.gyp5
-rw-r--r--sandbox/src/handle_closer.cc164
-rw-r--r--sandbox/src/handle_closer.h72
-rw-r--r--sandbox/src/handle_closer_agent.cc124
-rw-r--r--sandbox/src/handle_closer_agent.h37
-rw-r--r--sandbox/src/handle_closer_test.cc125
-rw-r--r--sandbox/src/sandbox_policy.h8
-rw-r--r--sandbox/src/sandbox_policy_base.cc9
-rw-r--r--sandbox/src/sandbox_policy_base.h24
-rw-r--r--sandbox/src/sandbox_types.h3
-rw-r--r--sandbox/src/target_services.cc20
12 files changed, 620 insertions, 9 deletions
diff --git a/content/common/sandbox_policy.cc b/content/common/sandbox_policy.cc
index dd0473e..2d01bde 100644
--- a/content/common/sandbox_policy.cc
+++ b/content/common/sandbox_policy.cc
@@ -194,6 +194,41 @@ void AddDllEvictionPolicy(sandbox::TargetPolicy* policy) {
BlacklistAddOneDll(kTroublesomeDlls[ix], policy);
}
+// Returns the object path prepended with the current logon session.
+string16 PrependWindowsSessionPath(const char16* object) {
+ // Cache this because it can't change after process creation.
+ static string16* session_prefix = NULL;
+ if (!session_prefix) {
+ HANDLE token;
+ DWORD session_id;
+ DWORD session_id_length;
+
+ CHECK(::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token));
+ CHECK(::GetTokenInformation(token, TokenSessionId, &session_id,
+ sizeof(session_id), &session_id_length));
+ CloseHandle(token);
+
+ session_prefix = new string16(base::StringPrintf(L"\\Sessions\\%d",
+ session_id));
+ }
+
+ return *session_prefix + object;
+}
+
+// Closes handles that are opened at process creation and initialization.
+void AddBaseHandleClosePolicy(sandbox::TargetPolicy* policy) {
+ // Being able to manipulate anything BaseNamedObjects is bad.
+ policy->AddKernelObjectToClose(L"Directory", PrependWindowsSessionPath(
+ L"\\BaseNamedObjects").data());
+ policy->AddKernelObjectToClose(L"Section", PrependWindowsSessionPath(
+ L"\\BaseNamedObjects\\windows_shell_global_counters").data());
+}
+
+void AddStrictHandleClosePolicy(sandbox::TargetPolicy* policy) {
+ // This is loaded when rand_s is seeded, but not needed again.
+ policy->AddKernelObjectToClose(L"File", L"\\Device\\KsecDD");
+}
+
// Adds the generic policy rules to a sandbox TargetPolicy.
bool AddGenericPolicy(sandbox::TargetPolicy* policy) {
sandbox::ResultCode result;
@@ -279,6 +314,7 @@ void AddPolicyForRenderer(sandbox::TargetPolicy* policy) {
}
AddDllEvictionPolicy(policy);
+ AddBaseHandleClosePolicy(policy);
}
// The Pepper process as locked-down as a renderer execpt that it can
@@ -414,6 +450,8 @@ base::ProcessHandle StartProcessWithAccess(CommandLine* cmd_line,
return 0;
} else {
AddPolicyForRenderer(policy);
+ if (type == ChildProcessInfo::RENDER_PROCESS)
+ AddStrictHandleClosePolicy(policy);
if (type_str != switches::kRendererProcess) {
// Hack for Google Desktop crash. Trick GD into not injecting its DLL into
diff --git a/sandbox/sandbox.gyp b/sandbox/sandbox.gyp
index a9ea6f4..6832c8a 100644
--- a/sandbox/sandbox.gyp
+++ b/sandbox/sandbox.gyp
@@ -33,6 +33,10 @@
'src/filesystem_interception.h',
'src/filesystem_policy.cc',
'src/filesystem_policy.h',
+ 'src/handle_closer.cc',
+ 'src/handle_closer.h',
+ 'src/handle_closer_agent.cc',
+ 'src/handle_closer_agent.h',
'src/handle_table.cc',
'src/handle_table.h',
'src/interception.cc',
@@ -283,6 +287,7 @@
'src/dep_test.cc',
'src/file_policy_test.cc',
'tests/integration_tests/integration_tests_test.cc',
+ 'src/handle_closer_test.cc',
'src/integrity_level_test.cc',
'src/ipc_ping_test.cc',
'src/named_pipe_policy_test.cc',
diff --git a/sandbox/src/handle_closer.cc b/sandbox/src/handle_closer.cc
new file mode 100644
index 0000000..ee201de
--- /dev/null
+++ b/sandbox/src/handle_closer.cc
@@ -0,0 +1,164 @@
+// Copyright (c) 2011 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 "sandbox/src/handle_closer.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/src/nt_internals.h"
+#include "sandbox/src/win_utils.h"
+
+namespace sandbox {
+
+// Memory buffer mapped from the parent, with the list of handles.
+SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close;
+
+HandleCloser::HandleCloser() {}
+
+ResultCode HandleCloser::AddHandle(const char16* handle_type,
+ const char16* handle_name) {
+ if (!handle_type)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ HandleMap::iterator names = handles_to_close_.find(handle_type);
+ if (names == handles_to_close_.end()) { // We have no entries for this type.
+ std::pair<HandleMap::iterator, bool> result = handles_to_close_.insert(
+ HandleMap::value_type(handle_type, HandleMap::mapped_type()));
+ names = result.first;
+ if (handle_name)
+ names->second.insert(handle_name);
+ } else if (!handle_name) { // Now we need to close all handles of this type.
+ names->second.clear();
+ } else if (!names->second.empty()) { // Add another name for this type.
+ names->second.insert(handle_name);
+ } // If we're already closing all handles of type then we're done.
+
+ return SBOX_ALL_OK;
+}
+
+size_t HandleCloser::GetBufferSize() {
+ size_t bytes_total = offsetof(HandleCloserInfo, handle_entries);
+
+ for (HandleMap::iterator i = handles_to_close_.begin();
+ i != handles_to_close_.end(); ++i) {
+ size_t bytes_entry = offsetof(HandleListEntry, handle_type) +
+ (i->first.size() + 1) * sizeof(char16);
+ for (HandleMap::mapped_type::iterator j = i->second.begin();
+ j != i->second.end(); ++j) {
+ bytes_entry += ((*j).size() + 1) * sizeof(char16);
+ }
+
+ // Round up to the nearest multiple of sizeof(size_t).
+ if (bytes_entry % sizeof(size_t))
+ bytes_entry = (bytes_entry & ~(sizeof(size_t) - 1)) + sizeof(size_t);
+
+ bytes_total += bytes_entry;
+ }
+
+ return bytes_total;
+}
+
+bool HandleCloser::InitializeTargetHandles(TargetProcess* target) {
+ // Do nothing on an empty list (global pointer already initialized to NULL).
+ if (handles_to_close_.empty())
+ return true;
+
+ size_t bytes_needed = GetBufferSize();
+ scoped_array<size_t> local_buffer(
+ new size_t[bytes_needed / sizeof(size_t)]);
+
+ if (!SetupHandleList(local_buffer.get(), bytes_needed))
+ return false;
+
+ HANDLE child = target->Process();
+
+ // Allocate memory in the target process without specifying the address
+ void* remote_data = ::VirtualAllocEx(child, NULL, bytes_needed,
+ MEM_COMMIT, PAGE_READWRITE);
+ if (NULL == remote_data)
+ return false;
+
+ // Copy the handle buffer over.
+ SIZE_T bytes_written;
+ BOOL result = ::WriteProcessMemory(child, remote_data, local_buffer.get(),
+ bytes_needed, &bytes_written);
+ if (!result || bytes_written != bytes_needed) {
+ ::VirtualFreeEx(child, remote_data, 0, MEM_RELEASE);
+ return false;
+ }
+
+ g_handles_to_close = reinterpret_cast<HandleCloserInfo*>(remote_data);
+
+ ResultCode rc = target->TransferVariable("g_handles_to_close",
+ &g_handles_to_close,
+ sizeof(g_handles_to_close));
+
+ return (SBOX_ALL_OK == rc);
+}
+
+bool HandleCloser::SetupHandleList(void* buffer, size_t buffer_bytes) {
+ ::ZeroMemory(buffer, buffer_bytes);
+ HandleCloserInfo* handle_info = reinterpret_cast<HandleCloserInfo*>(buffer);
+ handle_info->record_bytes = buffer_bytes;
+ handle_info->num_handle_types = handles_to_close_.size();
+
+ char16* output = reinterpret_cast<char16*>(&handle_info->handle_entries[0]);
+ char16* end = reinterpret_cast<char16*>(
+ reinterpret_cast<char*>(buffer) + buffer_bytes);
+ for (HandleMap::iterator i = handles_to_close_.begin();
+ i != handles_to_close_.end(); ++i) {
+ if (output >= end)
+ return false;
+ HandleListEntry* list_entry = reinterpret_cast<HandleListEntry*>(output);
+ output = &list_entry->handle_type[0];
+
+ // Copy the typename and set the offset and count.
+ i->first._Copy_s(output, i->first.size(), i->first.size());
+ *(output += i->first.size()) = L'\0';
+ output++;
+ list_entry->offset_to_names = reinterpret_cast<char*>(output) -
+ reinterpret_cast<char*>(list_entry);
+ list_entry->name_count = i->second.size();
+
+ // Copy the handle names.
+ for (HandleMap::mapped_type::iterator j = i->second.begin();
+ j != i->second.end(); ++j) {
+ output = std::copy((*j).begin(), (*j).end(), output) + 1;
+ }
+
+ // Round up to the nearest multiple of sizeof(size_t).
+ output += (reinterpret_cast<size_t>(output) % sizeof(size_t)) /
+ sizeof(char16);
+ list_entry->record_bytes = reinterpret_cast<char*>(output) -
+ reinterpret_cast<char*>(list_entry);
+ }
+
+ DCHECK(output == end);
+ return output <= end;
+}
+
+bool GetHandleName(HANDLE handle, string16* handle_name) {
+ static NtQueryObject QueryObject = NULL;
+ if (!QueryObject)
+ ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
+
+ ULONG size = MAX_PATH;
+ scoped_ptr<UNICODE_STRING> name;
+ NTSTATUS result;
+
+ do {
+ name.reset(reinterpret_cast<UNICODE_STRING*>(new BYTE[size]));
+ result = QueryObject(handle, ObjectNameInformation, name.get(),
+ size, &size);
+ } while (result == STATUS_INFO_LENGTH_MISMATCH);
+
+ if (NT_SUCCESS(result) && name->Buffer && name->Length)
+ handle_name->assign(name->Buffer, name->Length / sizeof(wchar_t));
+ else
+ handle_name->clear();
+
+ return NT_SUCCESS(result);
+}
+
+} // namespace sandbox
diff --git a/sandbox/src/handle_closer.h b/sandbox/src/handle_closer.h
new file mode 100644
index 0000000..7252968
--- /dev/null
+++ b/sandbox/src/handle_closer.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2011 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.
+
+#ifndef SANDBOX_SRC_HANDLE_CLOSER_H_
+#define SANDBOX_SRC_HANDLE_CLOSER_H_
+
+#include <map>
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/string16.h"
+#include "sandbox/src/sandbox_types.h"
+#include "sandbox/src/target_process.h"
+
+namespace sandbox {
+
+// This is a map of handle-types to names that we need to close in the
+// target process. A null set means we need to close all handles of the
+// given type.
+typedef std::map<const string16, std::set<const string16> > HandleMap;
+
+// Type and set of corresponding handle names to close.
+struct HandleListEntry {
+ size_t record_bytes; // Rounded to sizeof(size_t) bytes.
+ size_t offset_to_names; // Nul terminated strings of name_count names.
+ size_t name_count;
+ char16 handle_type[1];
+};
+
+// Global parameters and a pointer to the list of entries.
+struct HandleCloserInfo {
+ size_t record_bytes; // Rounded to sizeof(size_t) bytes.
+ size_t num_handle_types;
+ struct HandleListEntry handle_entries[1];
+};
+
+SANDBOX_INTERCEPT HandleCloserInfo* g_handle_closer_info;
+
+// Adds handles to close after lockdown.
+class HandleCloser {
+ public:
+ HandleCloser();
+
+ // Adds a handle that will be closed in the target process after lockdown.
+ // A NULL value for handle_name indicates all handles of the specified type.
+ // An empty string for handle_name indicates the handle is unnamed.
+ ResultCode HandleCloser::AddHandle(const char16* handle_type,
+ const char16* handle_name);
+
+ // Serializes and copies the closer table into the target process.
+ bool InitializeTargetHandles(TargetProcess* target);
+
+ private:
+ // Calculates the memory needed to copy the serialized handles list (rounded
+ // to the nearest machine-word size).
+ size_t GetBufferSize();
+
+ // Serializes the handle list into the target process.
+ bool SetupHandleList(void* buffer, size_t buffer_bytes);
+
+ HandleMap handles_to_close_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleCloser);
+};
+
+// Returns the object manager's name associated with a handle
+bool GetHandleName(HANDLE handle, string16* handle_name);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_CLOSER_H_
diff --git a/sandbox/src/handle_closer_agent.cc b/sandbox/src/handle_closer_agent.cc
new file mode 100644
index 0000000..b640c08
--- /dev/null
+++ b/sandbox/src/handle_closer_agent.cc
@@ -0,0 +1,124 @@
+// Copyright (c) 2011 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 "sandbox/src/handle_closer_agent.h"
+
+#include "base/logging.h"
+#include "sandbox/src/nt_internals.h"
+#include "sandbox/src/win_utils.h"
+
+namespace sandbox {
+
+// Memory buffer mapped from the parent, with the list of handles.
+SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close = NULL;
+
+bool HandleCloserAgent::NeedsHandlesClosed() {
+ return g_handles_to_close != NULL;
+}
+
+// Reads g_handles_to_close and creates the lookup map.
+void HandleCloserAgent::InitializeHandlesToClose() {
+ CHECK(g_handles_to_close != NULL);
+
+ // Grab the header.
+ HandleListEntry* entry = g_handles_to_close->handle_entries;
+ for (size_t i = 0; i < g_handles_to_close->num_handle_types; ++i) {
+ // Set the type name.
+ char16* input = entry->handle_type;
+ HandleMap::mapped_type& handle_names = handles_to_close_[input];
+ input = reinterpret_cast<char16*>(reinterpret_cast<char*>(entry)
+ + entry->offset_to_names);
+ // Grab all the handle names.
+ for (size_t j = 0; j < entry->name_count; ++j) {
+ std::pair<HandleMap::mapped_type::iterator, bool> name
+ = handle_names.insert(input);
+ CHECK(name.second);
+ input += name.first->size() + 1;
+ }
+
+ // Move on to the next entry.
+ entry = reinterpret_cast<HandleListEntry*>(reinterpret_cast<char*>(entry)
+ + entry->record_bytes);
+
+ DCHECK(reinterpret_cast<char16*>(entry) >= input);
+ DCHECK(reinterpret_cast<char16*>(entry) - input <
+ sizeof(size_t) / sizeof(char16));
+ }
+
+ // Clean up the memory we copied over.
+ ::VirtualFree(g_handles_to_close, 0, MEM_RELEASE);
+ g_handles_to_close = NULL;
+}
+
+bool HandleCloserAgent::CloseHandles() {
+ DWORD handle_count = UINT_MAX;
+ const int kInvalidHandleThreshold = 100;
+ const size_t kHandleOffset = sizeof(HANDLE);
+
+ if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count))
+ return false;
+
+ static NtQueryObject QueryObject = NULL;
+ if (!QueryObject)
+ ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
+
+ // Set up buffers for the type info and the name.
+ std::vector<BYTE> type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION) +
+ 32 * sizeof(wchar_t));
+ OBJECT_TYPE_INFORMATION* type_info =
+ reinterpret_cast<OBJECT_TYPE_INFORMATION*>(&(type_info_buffer[0]));
+ string16 handle_name;
+ HANDLE handle = NULL;
+ int invalid_count = 0;
+
+ // Keep incrementing until we hit the number of handles reported by
+ // GetProcessHandleCount(). If we hit a very long sequence of invalid
+ // handles we assume that we've run past the end of the table.
+ while (handle_count && invalid_count < kInvalidHandleThreshold) {
+ reinterpret_cast<size_t&>(handle) += kHandleOffset;
+ NTSTATUS rc;
+
+ // Get the type name, reusing the buffer.
+ ULONG size = static_cast<ULONG>(type_info_buffer.size());
+ rc = QueryObject(handle, ObjectTypeInformation, type_info, size, &size);
+ while (rc == STATUS_INFO_LENGTH_MISMATCH) {
+ type_info_buffer.resize(size + sizeof(wchar_t));
+ type_info = reinterpret_cast<OBJECT_TYPE_INFORMATION*>(
+ &(type_info_buffer[0]));
+ rc = QueryObject(handle, ObjectTypeInformation, type_info, size, &size);
+ // Leave padding for the nul terminator.
+ if (NT_SUCCESS(0) && size == type_info_buffer.size())
+ rc = STATUS_INFO_LENGTH_MISMATCH;
+ }
+ if (!NT_SUCCESS(rc)) {
+ ++invalid_count;
+ continue;
+ }
+
+ --handle_count;
+ type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0';
+
+ // Check if we're looking for this type of handle.
+ HandleMap::iterator result =
+ handles_to_close_.find(type_info->Name.Buffer);
+ if (result != handles_to_close_.end()) {
+ HandleMap::mapped_type& names = result->second;
+ // Empty set means close all handles of this type; otherwise check name.
+ if (!names.empty()) {
+ // Move on to the next handle if this name doesn't match.
+ if (!GetHandleName(handle, &handle_name) || !names.count(handle_name))
+ continue;
+ }
+
+ if (!::SetHandleInformation(handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0))
+ return false;
+ if (!::CloseHandle(handle))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace sandbox
diff --git a/sandbox/src/handle_closer_agent.h b/sandbox/src/handle_closer_agent.h
new file mode 100644
index 0000000..c74987c
--- /dev/null
+++ b/sandbox/src/handle_closer_agent.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2011 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.
+
+#ifndef SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_
+#define SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_
+
+#include "base/basictypes.h"
+#include "base/string16.h"
+#include "sandbox/src/handle_closer.h"
+#include "sandbox/src/sandbox_types.h"
+
+namespace sandbox {
+
+// Target process code to close the handle list copied over from the broker.
+class HandleCloserAgent {
+ public:
+ HandleCloserAgent() {}
+
+ // Reads the serialized list from the broker and creates the lookup map.
+ void InitializeHandlesToClose();
+
+ // Closes any handles matching those in the lookup map.
+ bool CloseHandles();
+
+ // True if we have handles waiting to be closed
+ static bool NeedsHandlesClosed();
+
+ private:
+ HandleMap handles_to_close_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleCloserAgent);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_
diff --git a/sandbox/src/handle_closer_test.cc b/sandbox/src/handle_closer_test.cc
new file mode 100644
index 0000000..4e2bf99
--- /dev/null
+++ b/sandbox/src/handle_closer_test.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2011 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/stringprintf.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/src/handle_closer_agent.h"
+#include "sandbox/src/sandbox.h"
+#include "sandbox/src/sandbox_factory.h"
+#include "sandbox/src/target_services.h"
+#include "sandbox/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Returns a handle to a unique marker file that can be retrieved between runs.
+HANDLE GetMarkerFile() {
+ wchar_t path_buffer[MAX_PATH + 1];
+ CHECK(::GetTempPath(MAX_PATH, path_buffer));
+ string16 marker_path = path_buffer;
+ marker_path += L"\\sbox_marker_";
+
+ // Generate a unique value from the exe's size and timestamp.
+ CHECK(::GetModuleFileName(NULL, path_buffer, MAX_PATH));
+ base::win::ScopedHandle module(::CreateFile(path_buffer,
+ FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
+ CHECK(module.IsValid());
+ FILETIME timestamp;
+ CHECK(::GetFileTime(module, &timestamp, NULL, NULL));
+ marker_path += base::StringPrintf(L"%08x%08x%08x",
+ ::GetFileSize(module, NULL),
+ timestamp.dwLowDateTime,
+ timestamp.dwHighDateTime);
+
+ // Make the file delete-on-close so cleanup is automatic.
+ return CreateFile(marker_path.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
+}
+
+} // namespace
+
+namespace sandbox {
+
+// Checks for the presence of a file (in object path form).
+// Format: CheckForFileHandle \path\to\file (Y|N)
+// - Y or N depending if the file should exist or not.
+SBOX_TESTS_COMMAND int CheckForFileHandle(int argc, wchar_t **argv) {
+ if (argc != 2)
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+ bool should_find = argv[1][0] == L'Y';
+ if (argv[1][1] != L'\0' || !should_find && argv[1][0] != L'N')
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+
+ static int state = BEFORE_INIT;
+ switch (state++) {
+ case BEFORE_INIT:
+ // Create a unique marker file that is open while the test is running.
+ // The handle leaks, but it will be closed by the test or on exit.
+ EXPECT_NE(GetMarkerFile(), INVALID_HANDLE_VALUE);
+ return SBOX_TEST_SUCCEEDED;
+
+ case AFTER_REVERT: {
+ // Brute force the handle table to find what we're looking for.
+ DWORD handle_count = UINT_MAX;
+ const int kInvalidHandleThreshold = 100;
+ const size_t kHandleOffset = sizeof(HANDLE);
+ HANDLE handle = NULL;
+ int invalid_count = 0;
+ string16 handle_name;
+
+ if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count))
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+
+ while (handle_count && invalid_count < kInvalidHandleThreshold) {
+ reinterpret_cast<size_t&>(handle) += kHandleOffset;
+ if (GetHandleName(handle, &handle_name)) {
+ if (handle_name == argv[0])
+ return should_find ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED;
+ --handle_count;
+ } else {
+ ++invalid_count;
+ }
+ }
+
+ return should_find ? SBOX_TEST_FAILED : SBOX_TEST_SUCCEEDED;
+ }
+ }
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+TEST(HandleCloserTest, CheckForMarkerFile) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ base::win::ScopedHandle marker(GetMarkerFile());
+ EXPECT_TRUE(marker.IsValid());
+ string16 handle_name;
+ EXPECT_TRUE(sandbox::GetHandleName(marker, &handle_name));
+ string16 command = string16(L"CheckForFileHandle ") + handle_name + L" Y";
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command.c_str())) <<
+ "Could not find: " << handle_name;
+}
+
+TEST(HandleCloserTest, CloseMarkerFile) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ base::win::ScopedHandle marker(GetMarkerFile());
+ EXPECT_TRUE(marker.IsValid());
+ string16 handle_name;
+ EXPECT_TRUE(sandbox::GetHandleName(marker, &handle_name));
+ policy->AddKernelObjectToClose(L"File", handle_name.c_str());
+ string16 command = string16(L"CheckForFileHandle ") + handle_name + L" N";
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command.c_str())) <<
+ "Failed closing: " << handle_name;
+}
+
+} // namespace sandbox
diff --git a/sandbox/src/sandbox_policy.h b/sandbox/src/sandbox_policy.h
index 716cefa..4f21158 100644
--- a/sandbox/src/sandbox_policy.h
+++ b/sandbox/src/sandbox_policy.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2011 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.
@@ -172,6 +172,12 @@ class TargetPolicy {
// a chance to initialize itself. Typically, dlls that cause the target
// to crash go here.
virtual ResultCode AddDllToUnload(const wchar_t* dll_name) = 0;
+
+ // Adds a handle that will be closed in the target process after lockdown.
+ // A NULL value for handle_name indicates all handles of the specified type.
+ // An empty string for handle_name indicates the handle is unnamed.
+ virtual ResultCode AddKernelObjectToClose(const wchar_t* handle_type,
+ const wchar_t* handle_name) = 0;
};
} // namespace sandbox
diff --git a/sandbox/src/sandbox_policy_base.cc b/sandbox/src/sandbox_policy_base.cc
index ec9ffac..8e25b71 100644
--- a/sandbox/src/sandbox_policy_base.cc
+++ b/sandbox/src/sandbox_policy_base.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2011 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.
@@ -227,6 +227,9 @@ bool PolicyBase::AddTarget(TargetProcess* target) {
if (!SetupAllInterceptions(target))
return false;
+ if (!SetupHandleCloser(target))
+ return false;
+
// Initialize the sandbox infrastructure for the target.
if (ERROR_SUCCESS != target->Init(this, policy_, kIPCMemSize, kPolMemSize))
return false;
@@ -448,4 +451,8 @@ bool PolicyBase::SetupAllInterceptions(TargetProcess* target) {
return SetupNtdllImports(target);
}
+bool PolicyBase::SetupHandleCloser(TargetProcess* target) {
+ return handle_closer_.InitializeTargetHandles(target);
+}
+
} // namespace sandbox
diff --git a/sandbox/src/sandbox_policy_base.h b/sandbox/src/sandbox_policy_base.h
index 6294ed8..b9afc6e 100644
--- a/sandbox/src/sandbox_policy_base.h
+++ b/sandbox/src/sandbox_policy_base.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2011 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.
@@ -7,15 +7,17 @@
#include <windows.h>
#include <list>
+#include <vector>
#include "base/basictypes.h"
+#include "base/string16.h"
+#include "sandbox/src/crosscall_server.h"
+#include "sandbox/src/handle_closer.h"
#include "sandbox/src/ipc_tags.h"
+#include "sandbox/src/policy_engine_opcodes.h"
+#include "sandbox/src/policy_engine_params.h"
#include "sandbox/src/sandbox_policy.h"
#include "sandbox/src/win_utils.h"
-#include "sandbox/src/crosscall_server.h"
-
-#include "sandbox/src/policy_engine_params.h"
-#include "sandbox/src/policy_engine_opcodes.h"
namespace sandbox {
@@ -100,6 +102,11 @@ class PolicyBase : public Dispatcher, public TargetPolicy {
return SBOX_ALL_OK;
}
+ virtual ResultCode AddKernelObjectToClose(const char16* handle_type,
+ const char16* handle_name) {
+ return handle_closer_.AddHandle(handle_type, handle_name);
+ }
+
// Creates a Job object with the level specified in a previous call to
// SetJobLevel(). Returns the standard windows of ::GetLastError().
DWORD MakeJobObject(HANDLE* job);
@@ -134,6 +141,9 @@ class PolicyBase : public Dispatcher, public TargetPolicy {
// Sets up interceptions for a new target.
bool SetupAllInterceptions(TargetProcess* target);
+ // Sets up the handle closer for a new target.
+ bool SetupHandleCloser(TargetProcess* target);
+
// This lock synchronizes operations on the targets_ collection.
CRITICAL_SECTION lock_;
// Maintains the list of target process associated with this policy.
@@ -163,6 +173,10 @@ class PolicyBase : public Dispatcher, public TargetPolicy {
bool relaxed_interceptions_;
// The list of dlls to unload in the target process.
std::vector<std::wstring> blacklisted_dlls_;
+ // This is a map of handle-types to names that we need to close in the
+ // target process. A null set means we need to close all handles of the
+ // given type.
+ HandleCloser handle_closer_;
static HDESK alternate_desktop_handle_;
static HWINSTA alternate_winstation_handle_;
diff --git a/sandbox/src/sandbox_types.h b/sandbox/src/sandbox_types.h
index 898f107..dee1838 100644
--- a/sandbox/src/sandbox_types.h
+++ b/sandbox/src/sandbox_types.h
@@ -47,7 +47,8 @@ enum TerminationCodes {
SBOX_FATAL_INTEGRITY = 7006, // Could not set the integrity level.
SBOX_FATAL_DROPTOKEN = 7007, // Could not lower the token.
SBOX_FATAL_FLUSHANDLES = 7008, // Failed to flush registry handles.
- SBOX_FATAL_CACHEDISABLE = 7009 // Failed to forbid HCKU caching.
+ SBOX_FATAL_CACHEDISABLE = 7009, // Failed to forbid HCKU caching.
+ SBOX_FATAL_CLOSEHANDLES = 7010 // Failed to close pending handles.
};
class TargetServices;
diff --git a/sandbox/src/target_services.cc b/sandbox/src/target_services.cc
index 72f6d4c..9b91a1c 100644
--- a/sandbox/src/target_services.cc
+++ b/sandbox/src/target_services.cc
@@ -1,11 +1,14 @@
-// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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 "sandbox/src/target_services.h"
+#include <process.h>
+
#include "base/basictypes.h"
#include "sandbox/src/crosscall_client.h"
+#include "sandbox/src/handle_closer_agent.h"
#include "sandbox/src/ipc_tags.h"
#include "sandbox/src/restricted_token_utils.h"
#include "sandbox/src/sandbox.h"
@@ -38,6 +41,19 @@ bool FlushCachedRegHandles() {
FlushRegKey(HKEY_USERS));
}
+// Checks if we have handle entries pending and runs the closer.
+bool CloseOpenHandles() {
+ if (sandbox::HandleCloserAgent::NeedsHandlesClosed()) {
+ sandbox::HandleCloserAgent handle_closer;
+
+ handle_closer.InitializeHandlesToClose();
+ if (!handle_closer.CloseHandles())
+ return false;
+ }
+
+ return true;
+}
+
} // namespace
namespace sandbox {
@@ -67,6 +83,8 @@ void TargetServicesBase::LowerToken() {
::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_FLUSHANDLES);
if (ERROR_SUCCESS != ::RegDisablePredefinedCache())
::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CACHEDISABLE);
+ if (!CloseOpenHandles())
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CLOSEHANDLES);
}
ProcessState* TargetServicesBase::GetState() {