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 <stddef.h>
8 
9 #include <memory>
10 
11 #include "base/logging.h"
12 #include "base/memory/free_deleter.h"
13 #include "base/win/windows_version.h"
14 #include "sandbox/win/src/interceptors.h"
15 #include "sandbox/win/src/internal_types.h"
16 #include "sandbox/win/src/nt_internals.h"
17 #include "sandbox/win/src/process_thread_interception.h"
18 #include "sandbox/win/src/win_utils.h"
19 
20 namespace {
21 
22 template <typename T>
RoundUpToWordSize(T v)23 T RoundUpToWordSize(T v) {
24   if (size_t mod = v % sizeof(size_t))
25     v += sizeof(size_t) - mod;
26   return v;
27 }
28 
29 template <typename T>
RoundUpToWordSize(T * v)30 T* RoundUpToWordSize(T* v) {
31   return reinterpret_cast<T*>(RoundUpToWordSize(reinterpret_cast<size_t>(v)));
32 }
33 
34 }  // namespace
35 
36 namespace sandbox {
37 
38 // Memory buffer mapped from the parent, with the list of handles.
39 SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close;
40 
HandleCloser()41 HandleCloser::HandleCloser() {}
42 
~HandleCloser()43 HandleCloser::~HandleCloser() {}
44 
AddHandle(const wchar_t * handle_type,const wchar_t * handle_name)45 ResultCode HandleCloser::AddHandle(const wchar_t* handle_type,
46                                    const wchar_t* handle_name) {
47   if (!handle_type)
48     return SBOX_ERROR_BAD_PARAMS;
49 
50   std::wstring resolved_name;
51   if (handle_name) {
52     resolved_name = handle_name;
53     if (handle_type == std::wstring(L"Key"))
54       if (!ResolveRegistryName(resolved_name, &resolved_name))
55         return SBOX_ERROR_BAD_PARAMS;
56   }
57 
58   HandleMap::iterator names = handles_to_close_.find(handle_type);
59   if (names == handles_to_close_.end()) {  // We have no entries for this type.
60     std::pair<HandleMap::iterator, bool> result = handles_to_close_.insert(
61         HandleMap::value_type(handle_type, HandleMap::mapped_type()));
62     names = result.first;
63     if (handle_name)
64       names->second.insert(resolved_name);
65   } else if (!handle_name) {  // Now we need to close all handles of this type.
66     names->second.clear();
67   } else if (!names->second.empty()) {  // Add another name for this type.
68     names->second.insert(resolved_name);
69   }  // If we're already closing all handles of type then we're done.
70 
71   return SBOX_ALL_OK;
72 }
73 
GetBufferSize()74 size_t HandleCloser::GetBufferSize() {
75   size_t bytes_total = offsetof(HandleCloserInfo, handle_entries);
76 
77   for (HandleMap::iterator i = handles_to_close_.begin();
78        i != handles_to_close_.end(); ++i) {
79     size_t bytes_entry = offsetof(HandleListEntry, handle_type) +
80                          (i->first.size() + 1) * sizeof(wchar_t);
81     for (HandleMap::mapped_type::iterator j = i->second.begin();
82          j != i->second.end(); ++j) {
83       bytes_entry += ((*j).size() + 1) * sizeof(wchar_t);
84     }
85 
86     // Round up to the nearest multiple of word size.
87     bytes_entry = RoundUpToWordSize(bytes_entry);
88     bytes_total += bytes_entry;
89   }
90 
91   return bytes_total;
92 }
93 
InitializeTargetHandles(TargetProcess * target)94 bool HandleCloser::InitializeTargetHandles(TargetProcess* target) {
95   // Do nothing on an empty list (global pointer already initialized to
96   // nullptr).
97   if (handles_to_close_.empty())
98     return true;
99 
100   size_t bytes_needed = GetBufferSize();
101   std::unique_ptr<size_t[]> local_buffer(
102       new size_t[bytes_needed / sizeof(size_t)]);
103 
104   if (!SetupHandleList(local_buffer.get(), bytes_needed))
105     return false;
106 
107   void* remote_data;
108   if (!CopyToChildMemory(target->Process(), local_buffer.get(), bytes_needed,
109                          &remote_data))
110     return false;
111 
112   g_handles_to_close = reinterpret_cast<HandleCloserInfo*>(remote_data);
113 
114   ResultCode rc = target->TransferVariable(
115       "g_handles_to_close", &g_handles_to_close, sizeof(g_handles_to_close));
116 
117   return (SBOX_ALL_OK == rc);
118 }
119 
SetupHandleList(void * buffer,size_t buffer_bytes)120 bool HandleCloser::SetupHandleList(void* buffer, size_t buffer_bytes) {
121   ::ZeroMemory(buffer, buffer_bytes);
122   HandleCloserInfo* handle_info = reinterpret_cast<HandleCloserInfo*>(buffer);
123   handle_info->record_bytes = buffer_bytes;
124   handle_info->num_handle_types = handles_to_close_.size();
125 
126   wchar_t* output = reinterpret_cast<wchar_t*>(&handle_info->handle_entries[0]);
127   wchar_t* end = reinterpret_cast<wchar_t*>(reinterpret_cast<char*>(buffer) +
128                                             buffer_bytes);
129   for (HandleMap::iterator i = handles_to_close_.begin();
130        i != handles_to_close_.end(); ++i) {
131     if (output >= end)
132       return false;
133     HandleListEntry* list_entry = reinterpret_cast<HandleListEntry*>(output);
134     output = &list_entry->handle_type[0];
135 
136     // Copy the typename and set the offset and count.
137     i->first.copy(output, i->first.size());
138     *(output += i->first.size()) = L'\0';
139     output++;
140     list_entry->offset_to_names =
141         reinterpret_cast<char*>(output) - reinterpret_cast<char*>(list_entry);
142     list_entry->name_count = i->second.size();
143 
144     // Copy the handle names.
145     for (HandleMap::mapped_type::iterator j = i->second.begin();
146          j != i->second.end(); ++j) {
147       output = std::copy((*j).begin(), (*j).end(), output) + 1;
148     }
149 
150     // Round up to the nearest multiple of sizeof(size_t).
151     output = RoundUpToWordSize(output);
152     list_entry->record_bytes =
153         reinterpret_cast<char*>(output) - reinterpret_cast<char*>(list_entry);
154   }
155 
156   DCHECK_EQ(reinterpret_cast<size_t>(output), reinterpret_cast<size_t>(end));
157   return output <= end;
158 }
159 
GetHandleName(HANDLE handle,std::wstring * handle_name)160 bool GetHandleName(HANDLE handle, std::wstring* handle_name) {
161   static NtQueryObject QueryObject = nullptr;
162   if (!QueryObject)
163     ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
164 
165   ULONG size = MAX_PATH;
166   std::unique_ptr<UNICODE_STRING, base::FreeDeleter> name;
167   NTSTATUS result;
168 
169   do {
170     name.reset(static_cast<UNICODE_STRING*>(malloc(size)));
171     DCHECK(name.get());
172     result =
173         QueryObject(handle, ObjectNameInformation, name.get(), size, &size);
174   } while (result == STATUS_INFO_LENGTH_MISMATCH ||
175            result == STATUS_BUFFER_OVERFLOW);
176 
177   if (NT_SUCCESS(result) && name->Buffer && name->Length)
178     handle_name->assign(name->Buffer, name->Length / sizeof(wchar_t));
179   else
180     handle_name->clear();
181 
182   return NT_SUCCESS(result);
183 }
184 
185 }  // namespace sandbox
186