Skip to content

Commit

Permalink
Add a sandbox API to allow closing open handles at lockdown.
Browse files Browse the repository at this point in the history
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@92887 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
jschuh@chromium.org committed Jul 18, 2011
1 parent 0a34b6a commit 404fbfc
Show file tree
Hide file tree
Showing 12 changed files with 590 additions and 9 deletions.
38 changes: 38 additions & 0 deletions content/common/sandbox_policy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions sandbox/sandbox.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down
164 changes: 164 additions & 0 deletions sandbox/src/handle_closer.cc
Original file line number Diff line number Diff line change
@@ -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
72 changes: 72 additions & 0 deletions sandbox/src/handle_closer.h
Original file line number Diff line number Diff line change
@@ -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_
Loading

0 comments on commit 404fbfc

Please sign in to comment.