1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "sandbox/win/src/handle_closer.h"
6
7 #include "base/logging.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/win/windows_version.h"
10 #include "sandbox/win/src/interceptors.h"
11 #include "sandbox/win/src/internal_types.h"
12 #include "sandbox/win/src/nt_internals.h"
13 #include "sandbox/win/src/process_thread_interception.h"
14 #include "sandbox/win/src/win_utils.h"
15
16 namespace {
17
RoundUpToWordSize(T v)18 template<typename T> T RoundUpToWordSize(T v) {
19 if (size_t mod = v % sizeof(size_t))
20 v += sizeof(size_t) - mod;
21 return v;
22 }
23
RoundUpToWordSize(T * v)24 template<typename T> T* RoundUpToWordSize(T* v) {
25 return reinterpret_cast<T*>(RoundUpToWordSize(reinterpret_cast<size_t>(v)));
26 }
27
28 } // namespace
29
30 namespace sandbox {
31
32 // Memory buffer mapped from the parent, with the list of handles.
33 SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close;
34
HandleCloser()35 HandleCloser::HandleCloser() {}
36
AddHandle(const char16 * handle_type,const char16 * handle_name)37 ResultCode HandleCloser::AddHandle(const char16* handle_type,
38 const char16* handle_name) {
39 if (!handle_type)
40 return SBOX_ERROR_BAD_PARAMS;
41
42 HandleMap::iterator names = handles_to_close_.find(handle_type);
43 if (names == handles_to_close_.end()) { // We have no entries for this type.
44 std::pair<HandleMap::iterator, bool> result = handles_to_close_.insert(
45 HandleMap::value_type(handle_type, HandleMap::mapped_type()));
46 names = result.first;
47 if (handle_name)
48 names->second.insert(handle_name);
49 } else if (!handle_name) { // Now we need to close all handles of this type.
50 names->second.clear();
51 } else if (!names->second.empty()) { // Add another name for this type.
52 names->second.insert(handle_name);
53 } // If we're already closing all handles of type then we're done.
54
55 return SBOX_ALL_OK;
56 }
57
GetBufferSize()58 size_t HandleCloser::GetBufferSize() {
59 size_t bytes_total = offsetof(HandleCloserInfo, handle_entries);
60
61 for (HandleMap::iterator i = handles_to_close_.begin();
62 i != handles_to_close_.end(); ++i) {
63 size_t bytes_entry = offsetof(HandleListEntry, handle_type) +
64 (i->first.size() + 1) * sizeof(char16);
65 for (HandleMap::mapped_type::iterator j = i->second.begin();
66 j != i->second.end(); ++j) {
67 bytes_entry += ((*j).size() + 1) * sizeof(char16);
68 }
69
70 // Round up to the nearest multiple of word size.
71 bytes_entry = RoundUpToWordSize(bytes_entry);
72 bytes_total += bytes_entry;
73 }
74
75 return bytes_total;
76 }
77
InitializeTargetHandles(TargetProcess * target)78 bool HandleCloser::InitializeTargetHandles(TargetProcess* target) {
79 // Do nothing on an empty list (global pointer already initialized to NULL).
80 if (handles_to_close_.empty())
81 return true;
82
83 size_t bytes_needed = GetBufferSize();
84 scoped_ptr<size_t[]> local_buffer(
85 new size_t[bytes_needed / sizeof(size_t)]);
86
87 if (!SetupHandleList(local_buffer.get(), bytes_needed))
88 return false;
89
90 HANDLE child = target->Process();
91
92 // Allocate memory in the target process without specifying the address
93 void* remote_data = ::VirtualAllocEx(child, NULL, bytes_needed,
94 MEM_COMMIT, PAGE_READWRITE);
95 if (NULL == remote_data)
96 return false;
97
98 // Copy the handle buffer over.
99 SIZE_T bytes_written;
100 BOOL result = ::WriteProcessMemory(child, remote_data, local_buffer.get(),
101 bytes_needed, &bytes_written);
102 if (!result || bytes_written != bytes_needed) {
103 ::VirtualFreeEx(child, remote_data, 0, MEM_RELEASE);
104 return false;
105 }
106
107 g_handles_to_close = reinterpret_cast<HandleCloserInfo*>(remote_data);
108
109 ResultCode rc = target->TransferVariable("g_handles_to_close",
110 &g_handles_to_close,
111 sizeof(g_handles_to_close));
112
113 return (SBOX_ALL_OK == rc);
114 }
115
SetupHandleList(void * buffer,size_t buffer_bytes)116 bool HandleCloser::SetupHandleList(void* buffer, size_t buffer_bytes) {
117 ::ZeroMemory(buffer, buffer_bytes);
118 HandleCloserInfo* handle_info = reinterpret_cast<HandleCloserInfo*>(buffer);
119 handle_info->record_bytes = buffer_bytes;
120 handle_info->num_handle_types = handles_to_close_.size();
121
122 char16* output = reinterpret_cast<char16*>(&handle_info->handle_entries[0]);
123 char16* end = reinterpret_cast<char16*>(
124 reinterpret_cast<char*>(buffer) + buffer_bytes);
125 for (HandleMap::iterator i = handles_to_close_.begin();
126 i != handles_to_close_.end(); ++i) {
127 if (output >= end)
128 return false;
129 HandleListEntry* list_entry = reinterpret_cast<HandleListEntry*>(output);
130 output = &list_entry->handle_type[0];
131
132 // Copy the typename and set the offset and count.
133 i->first._Copy_s(output, i->first.size(), i->first.size());
134 *(output += i->first.size()) = L'\0';
135 output++;
136 list_entry->offset_to_names = reinterpret_cast<char*>(output) -
137 reinterpret_cast<char*>(list_entry);
138 list_entry->name_count = i->second.size();
139
140 // Copy the handle names.
141 for (HandleMap::mapped_type::iterator j = i->second.begin();
142 j != i->second.end(); ++j) {
143 output = std::copy((*j).begin(), (*j).end(), output) + 1;
144 }
145
146 // Round up to the nearest multiple of sizeof(size_t).
147 output = RoundUpToWordSize(output);
148 list_entry->record_bytes = reinterpret_cast<char*>(output) -
149 reinterpret_cast<char*>(list_entry);
150 }
151
152 DCHECK_EQ(reinterpret_cast<size_t>(output), reinterpret_cast<size_t>(end));
153 return output <= end;
154 }
155
SetupHandleInterceptions(InterceptionManager * manager)156 bool HandleCloser::SetupHandleInterceptions(InterceptionManager* manager) {
157 // We need to intercept CreateThread if we're closing ALPC port clients.
158 HandleMap::iterator names = handles_to_close_.find(L"ALPC Port");
159 if (base::win::GetVersion() >= base::win::VERSION_VISTA &&
160 names != handles_to_close_.end() &&
161 (names->second.empty() || names->second.size() == 0)) {
162 if (!INTERCEPT_EAT(manager, kKerneldllName, CreateThread,
163 CREATE_THREAD_ID, 28)) {
164 return false;
165 }
166 if (!INTERCEPT_EAT(manager, kKerneldllName, GetUserDefaultLCID,
167 GET_USER_DEFAULT_LCID_ID, 4)) {
168 return false;
169 }
170
171 return true;
172 }
173
174 return true;
175 }
176
GetHandleName(HANDLE handle,string16 * handle_name)177 bool GetHandleName(HANDLE handle, string16* handle_name) {
178 static NtQueryObject QueryObject = NULL;
179 if (!QueryObject)
180 ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
181
182 ULONG size = MAX_PATH;
183 scoped_ptr<UNICODE_STRING, base::FreeDeleter> name;
184 NTSTATUS result;
185
186 do {
187 name.reset(static_cast<UNICODE_STRING*>(malloc(size)));
188 DCHECK(name.get());
189 result = QueryObject(handle, ObjectNameInformation, name.get(),
190 size, &size);
191 } while (result == STATUS_INFO_LENGTH_MISMATCH ||
192 result == STATUS_BUFFER_OVERFLOW);
193
194 if (NT_SUCCESS(result) && name->Buffer && name->Length)
195 handle_name->assign(name->Buffer, name->Length / sizeof(wchar_t));
196 else
197 handle_name->clear();
198
199 return NT_SUCCESS(result);
200 }
201
202 } // namespace sandbox
203