security/sandbox/win/src/process_thread_dispatcher.cc

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 // Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
michael@0 2 // Use of this source code is governed by a BSD-style license that can be
michael@0 3 // found in the LICENSE file.
michael@0 4
michael@0 5 #include "sandbox/win/src/process_thread_dispatcher.h"
michael@0 6
michael@0 7 #include "base/basictypes.h"
michael@0 8 #include "base/logging.h"
michael@0 9 #include "sandbox/win/src/crosscall_client.h"
michael@0 10 #include "sandbox/win/src/interception.h"
michael@0 11 #include "sandbox/win/src/interceptors.h"
michael@0 12 #include "sandbox/win/src/ipc_tags.h"
michael@0 13 #include "sandbox/win/src/policy_broker.h"
michael@0 14 #include "sandbox/win/src/policy_params.h"
michael@0 15 #include "sandbox/win/src/process_thread_interception.h"
michael@0 16 #include "sandbox/win/src/process_thread_policy.h"
michael@0 17 #include "sandbox/win/src/sandbox.h"
michael@0 18
michael@0 19 namespace {
michael@0 20
michael@0 21 // Extracts the application name from a command line.
michael@0 22 //
michael@0 23 // The application name is the first element of the command line. If
michael@0 24 // there is no quotes, the first element is delimited by the first space.
michael@0 25 // If there are quotes, the first element is delimited by the quotes.
michael@0 26 //
michael@0 27 // The create process call is smarter than us. It tries really hard to launch
michael@0 28 // the process even if the command line is wrong. For example:
michael@0 29 // "c:\program files\test param" will first try to launch c:\program.exe then
michael@0 30 // c:\program files\test.exe. We don't do that, we stop after at the first
michael@0 31 // space when there is no quotes.
michael@0 32 std::wstring GetPathFromCmdLine(const std::wstring &cmd_line) {
michael@0 33 std::wstring exe_name;
michael@0 34 // Check if it starts with '"'.
michael@0 35 if (cmd_line[0] == L'\"') {
michael@0 36 // Find the position of the second '"', this terminates the path.
michael@0 37 std::wstring::size_type pos = cmd_line.find(L'\"', 1);
michael@0 38 if (std::wstring::npos == pos)
michael@0 39 return cmd_line;
michael@0 40 exe_name = cmd_line.substr(1, pos - 1);
michael@0 41 } else {
michael@0 42 // There is no '"', that means that the appname is terminated at the
michael@0 43 // first space.
michael@0 44 std::wstring::size_type pos = cmd_line.find(L' ');
michael@0 45 if (std::wstring::npos == pos) {
michael@0 46 // There is no space, the cmd_line contains only the app_name
michael@0 47 exe_name = cmd_line;
michael@0 48 } else {
michael@0 49 exe_name = cmd_line.substr(0, pos);
michael@0 50 }
michael@0 51 }
michael@0 52
michael@0 53 return exe_name;
michael@0 54 }
michael@0 55
michael@0 56 // Returns true is the path in parameter is relative. False if it's
michael@0 57 // absolute.
michael@0 58 bool IsPathRelative(const std::wstring &path) {
michael@0 59 // A path is Relative if it's not a UNC path beginnning with \\ or a
michael@0 60 // path beginning with a drive. (i.e. X:\)
michael@0 61 if (path.find(L"\\\\") == 0 || path.find(L":\\") == 1)
michael@0 62 return false;
michael@0 63 return true;
michael@0 64 }
michael@0 65
michael@0 66 // Converts a relative path to an absolute path.
michael@0 67 bool ConvertToAbsolutePath(const std::wstring& child_current_directory,
michael@0 68 bool use_env_path, std::wstring *path) {
michael@0 69 wchar_t file_buffer[MAX_PATH];
michael@0 70 wchar_t *file_part = NULL;
michael@0 71
michael@0 72 // Here we should start by looking at the path where the child application was
michael@0 73 // started. We don't have this information yet.
michael@0 74 DWORD result = 0;
michael@0 75 if (use_env_path) {
michael@0 76 // Try with the complete path
michael@0 77 result = ::SearchPath(NULL, path->c_str(), NULL, MAX_PATH, file_buffer,
michael@0 78 &file_part);
michael@0 79 }
michael@0 80
michael@0 81 if (0 == result) {
michael@0 82 // Try with the current directory of the child
michael@0 83 result = ::SearchPath(child_current_directory.c_str(), path->c_str(), NULL,
michael@0 84 MAX_PATH, file_buffer, &file_part);
michael@0 85 }
michael@0 86
michael@0 87 if (0 == result || result >= MAX_PATH)
michael@0 88 return false;
michael@0 89
michael@0 90 *path = file_buffer;
michael@0 91 return true;
michael@0 92 }
michael@0 93
michael@0 94 } // namespace
michael@0 95 namespace sandbox {
michael@0 96
michael@0 97 ThreadProcessDispatcher::ThreadProcessDispatcher(PolicyBase* policy_base)
michael@0 98 : policy_base_(policy_base) {
michael@0 99 static const IPCCall open_thread = {
michael@0 100 {IPC_NTOPENTHREAD_TAG, ULONG_TYPE, ULONG_TYPE},
michael@0 101 reinterpret_cast<CallbackGeneric>(
michael@0 102 &ThreadProcessDispatcher::NtOpenThread)
michael@0 103 };
michael@0 104
michael@0 105 static const IPCCall open_process = {
michael@0 106 {IPC_NTOPENPROCESS_TAG, ULONG_TYPE, ULONG_TYPE},
michael@0 107 reinterpret_cast<CallbackGeneric>(
michael@0 108 &ThreadProcessDispatcher::NtOpenProcess)
michael@0 109 };
michael@0 110
michael@0 111 static const IPCCall process_token = {
michael@0 112 {IPC_NTOPENPROCESSTOKEN_TAG, VOIDPTR_TYPE, ULONG_TYPE},
michael@0 113 reinterpret_cast<CallbackGeneric>(
michael@0 114 &ThreadProcessDispatcher::NtOpenProcessToken)
michael@0 115 };
michael@0 116
michael@0 117 static const IPCCall process_tokenex = {
michael@0 118 {IPC_NTOPENPROCESSTOKENEX_TAG, VOIDPTR_TYPE, ULONG_TYPE, ULONG_TYPE},
michael@0 119 reinterpret_cast<CallbackGeneric>(
michael@0 120 &ThreadProcessDispatcher::NtOpenProcessTokenEx)
michael@0 121 };
michael@0 122
michael@0 123 static const IPCCall create_params = {
michael@0 124 {IPC_CREATEPROCESSW_TAG, WCHAR_TYPE, WCHAR_TYPE, WCHAR_TYPE, INOUTPTR_TYPE},
michael@0 125 reinterpret_cast<CallbackGeneric>(
michael@0 126 &ThreadProcessDispatcher::CreateProcessW)
michael@0 127 };
michael@0 128
michael@0 129 ipc_calls_.push_back(open_thread);
michael@0 130 ipc_calls_.push_back(open_process);
michael@0 131 ipc_calls_.push_back(process_token);
michael@0 132 ipc_calls_.push_back(process_tokenex);
michael@0 133 ipc_calls_.push_back(create_params);
michael@0 134 }
michael@0 135
michael@0 136 bool ThreadProcessDispatcher::SetupService(InterceptionManager* manager,
michael@0 137 int service) {
michael@0 138 switch (service) {
michael@0 139 case IPC_NTOPENTHREAD_TAG:
michael@0 140 case IPC_NTOPENPROCESS_TAG:
michael@0 141 case IPC_NTOPENPROCESSTOKEN_TAG:
michael@0 142 case IPC_NTOPENPROCESSTOKENEX_TAG:
michael@0 143 // There is no explicit policy for these services.
michael@0 144 NOTREACHED();
michael@0 145 return false;
michael@0 146
michael@0 147 case IPC_CREATEPROCESSW_TAG:
michael@0 148 return INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessW,
michael@0 149 CREATE_PROCESSW_ID, 44) &&
michael@0 150 INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessA,
michael@0 151 CREATE_PROCESSA_ID, 44);
michael@0 152
michael@0 153 default:
michael@0 154 return false;
michael@0 155 }
michael@0 156 }
michael@0 157
michael@0 158 bool ThreadProcessDispatcher::NtOpenThread(IPCInfo* ipc, DWORD desired_access,
michael@0 159 DWORD thread_id) {
michael@0 160 HANDLE handle;
michael@0 161 NTSTATUS ret = ProcessPolicy::OpenThreadAction(*ipc->client_info,
michael@0 162 desired_access, thread_id,
michael@0 163 &handle);
michael@0 164 ipc->return_info.nt_status = ret;
michael@0 165 ipc->return_info.handle = handle;
michael@0 166 return true;
michael@0 167 }
michael@0 168
michael@0 169 bool ThreadProcessDispatcher::NtOpenProcess(IPCInfo* ipc, DWORD desired_access,
michael@0 170 DWORD process_id) {
michael@0 171 HANDLE handle;
michael@0 172 NTSTATUS ret = ProcessPolicy::OpenProcessAction(*ipc->client_info,
michael@0 173 desired_access, process_id,
michael@0 174 &handle);
michael@0 175 ipc->return_info.nt_status = ret;
michael@0 176 ipc->return_info.handle = handle;
michael@0 177 return true;
michael@0 178 }
michael@0 179
michael@0 180 bool ThreadProcessDispatcher::NtOpenProcessToken(IPCInfo* ipc, HANDLE process,
michael@0 181 DWORD desired_access) {
michael@0 182 HANDLE handle;
michael@0 183 NTSTATUS ret = ProcessPolicy::OpenProcessTokenAction(*ipc->client_info,
michael@0 184 process, desired_access,
michael@0 185 &handle);
michael@0 186 ipc->return_info.nt_status = ret;
michael@0 187 ipc->return_info.handle = handle;
michael@0 188 return true;
michael@0 189 }
michael@0 190
michael@0 191 bool ThreadProcessDispatcher::NtOpenProcessTokenEx(IPCInfo* ipc, HANDLE process,
michael@0 192 DWORD desired_access,
michael@0 193 DWORD attributes) {
michael@0 194 HANDLE handle;
michael@0 195 NTSTATUS ret = ProcessPolicy::OpenProcessTokenExAction(*ipc->client_info,
michael@0 196 process,
michael@0 197 desired_access,
michael@0 198 attributes, &handle);
michael@0 199 ipc->return_info.nt_status = ret;
michael@0 200 ipc->return_info.handle = handle;
michael@0 201 return true;
michael@0 202 }
michael@0 203
michael@0 204 bool ThreadProcessDispatcher::CreateProcessW(IPCInfo* ipc, std::wstring* name,
michael@0 205 std::wstring* cmd_line,
michael@0 206 std::wstring* cur_dir,
michael@0 207 CountedBuffer* info) {
michael@0 208 if (sizeof(PROCESS_INFORMATION) != info->Size())
michael@0 209 return false;
michael@0 210
michael@0 211 // Check if there is an application name.
michael@0 212 std::wstring exe_name;
michael@0 213 if (!name->empty())
michael@0 214 exe_name = *name;
michael@0 215 else
michael@0 216 exe_name = GetPathFromCmdLine(*cmd_line);
michael@0 217
michael@0 218 if (IsPathRelative(exe_name)) {
michael@0 219 if (!ConvertToAbsolutePath(*cur_dir, name->empty(), &exe_name)) {
michael@0 220 // Cannot find the path. Maybe the file does not exist.
michael@0 221 ipc->return_info.win32_result = ERROR_FILE_NOT_FOUND;
michael@0 222 return true;
michael@0 223 }
michael@0 224 }
michael@0 225
michael@0 226 const wchar_t* const_exe_name = exe_name.c_str();
michael@0 227 CountedParameterSet<NameBased> params;
michael@0 228 params[NameBased::NAME] = ParamPickerMake(const_exe_name);
michael@0 229
michael@0 230 EvalResult eval = policy_base_->EvalPolicy(IPC_CREATEPROCESSW_TAG,
michael@0 231 params.GetBase());
michael@0 232
michael@0 233 PROCESS_INFORMATION* proc_info =
michael@0 234 reinterpret_cast<PROCESS_INFORMATION*>(info->Buffer());
michael@0 235 // Here we force the app_name to be the one we used for the policy lookup.
michael@0 236 // If our logic was wrong, at least we wont allow create a random process.
michael@0 237 DWORD ret = ProcessPolicy::CreateProcessWAction(eval, *ipc->client_info,
michael@0 238 exe_name, *cmd_line,
michael@0 239 proc_info);
michael@0 240
michael@0 241 ipc->return_info.win32_result = ret;
michael@0 242 return true;
michael@0 243 }
michael@0 244
michael@0 245 } // namespace sandbox

mercurial