|
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 |
|
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 |
|
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 |
|
35 HandleCloser::HandleCloser() {} |
|
36 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |