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