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 203