1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/sandbox/win/src/process_thread_dispatcher.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,245 @@ 1.4 +// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +#include "sandbox/win/src/process_thread_dispatcher.h" 1.9 + 1.10 +#include "base/basictypes.h" 1.11 +#include "base/logging.h" 1.12 +#include "sandbox/win/src/crosscall_client.h" 1.13 +#include "sandbox/win/src/interception.h" 1.14 +#include "sandbox/win/src/interceptors.h" 1.15 +#include "sandbox/win/src/ipc_tags.h" 1.16 +#include "sandbox/win/src/policy_broker.h" 1.17 +#include "sandbox/win/src/policy_params.h" 1.18 +#include "sandbox/win/src/process_thread_interception.h" 1.19 +#include "sandbox/win/src/process_thread_policy.h" 1.20 +#include "sandbox/win/src/sandbox.h" 1.21 + 1.22 +namespace { 1.23 + 1.24 +// Extracts the application name from a command line. 1.25 +// 1.26 +// The application name is the first element of the command line. If 1.27 +// there is no quotes, the first element is delimited by the first space. 1.28 +// If there are quotes, the first element is delimited by the quotes. 1.29 +// 1.30 +// The create process call is smarter than us. It tries really hard to launch 1.31 +// the process even if the command line is wrong. For example: 1.32 +// "c:\program files\test param" will first try to launch c:\program.exe then 1.33 +// c:\program files\test.exe. We don't do that, we stop after at the first 1.34 +// space when there is no quotes. 1.35 +std::wstring GetPathFromCmdLine(const std::wstring &cmd_line) { 1.36 + std::wstring exe_name; 1.37 + // Check if it starts with '"'. 1.38 + if (cmd_line[0] == L'\"') { 1.39 + // Find the position of the second '"', this terminates the path. 1.40 + std::wstring::size_type pos = cmd_line.find(L'\"', 1); 1.41 + if (std::wstring::npos == pos) 1.42 + return cmd_line; 1.43 + exe_name = cmd_line.substr(1, pos - 1); 1.44 + } else { 1.45 + // There is no '"', that means that the appname is terminated at the 1.46 + // first space. 1.47 + std::wstring::size_type pos = cmd_line.find(L' '); 1.48 + if (std::wstring::npos == pos) { 1.49 + // There is no space, the cmd_line contains only the app_name 1.50 + exe_name = cmd_line; 1.51 + } else { 1.52 + exe_name = cmd_line.substr(0, pos); 1.53 + } 1.54 + } 1.55 + 1.56 + return exe_name; 1.57 +} 1.58 + 1.59 +// Returns true is the path in parameter is relative. False if it's 1.60 +// absolute. 1.61 +bool IsPathRelative(const std::wstring &path) { 1.62 + // A path is Relative if it's not a UNC path beginnning with \\ or a 1.63 + // path beginning with a drive. (i.e. X:\) 1.64 + if (path.find(L"\\\\") == 0 || path.find(L":\\") == 1) 1.65 + return false; 1.66 + return true; 1.67 +} 1.68 + 1.69 +// Converts a relative path to an absolute path. 1.70 +bool ConvertToAbsolutePath(const std::wstring& child_current_directory, 1.71 + bool use_env_path, std::wstring *path) { 1.72 + wchar_t file_buffer[MAX_PATH]; 1.73 + wchar_t *file_part = NULL; 1.74 + 1.75 + // Here we should start by looking at the path where the child application was 1.76 + // started. We don't have this information yet. 1.77 + DWORD result = 0; 1.78 + if (use_env_path) { 1.79 + // Try with the complete path 1.80 + result = ::SearchPath(NULL, path->c_str(), NULL, MAX_PATH, file_buffer, 1.81 + &file_part); 1.82 + } 1.83 + 1.84 + if (0 == result) { 1.85 + // Try with the current directory of the child 1.86 + result = ::SearchPath(child_current_directory.c_str(), path->c_str(), NULL, 1.87 + MAX_PATH, file_buffer, &file_part); 1.88 + } 1.89 + 1.90 + if (0 == result || result >= MAX_PATH) 1.91 + return false; 1.92 + 1.93 + *path = file_buffer; 1.94 + return true; 1.95 +} 1.96 + 1.97 +} // namespace 1.98 +namespace sandbox { 1.99 + 1.100 +ThreadProcessDispatcher::ThreadProcessDispatcher(PolicyBase* policy_base) 1.101 + : policy_base_(policy_base) { 1.102 + static const IPCCall open_thread = { 1.103 + {IPC_NTOPENTHREAD_TAG, ULONG_TYPE, ULONG_TYPE}, 1.104 + reinterpret_cast<CallbackGeneric>( 1.105 + &ThreadProcessDispatcher::NtOpenThread) 1.106 + }; 1.107 + 1.108 + static const IPCCall open_process = { 1.109 + {IPC_NTOPENPROCESS_TAG, ULONG_TYPE, ULONG_TYPE}, 1.110 + reinterpret_cast<CallbackGeneric>( 1.111 + &ThreadProcessDispatcher::NtOpenProcess) 1.112 + }; 1.113 + 1.114 + static const IPCCall process_token = { 1.115 + {IPC_NTOPENPROCESSTOKEN_TAG, VOIDPTR_TYPE, ULONG_TYPE}, 1.116 + reinterpret_cast<CallbackGeneric>( 1.117 + &ThreadProcessDispatcher::NtOpenProcessToken) 1.118 + }; 1.119 + 1.120 + static const IPCCall process_tokenex = { 1.121 + {IPC_NTOPENPROCESSTOKENEX_TAG, VOIDPTR_TYPE, ULONG_TYPE, ULONG_TYPE}, 1.122 + reinterpret_cast<CallbackGeneric>( 1.123 + &ThreadProcessDispatcher::NtOpenProcessTokenEx) 1.124 + }; 1.125 + 1.126 + static const IPCCall create_params = { 1.127 + {IPC_CREATEPROCESSW_TAG, WCHAR_TYPE, WCHAR_TYPE, WCHAR_TYPE, INOUTPTR_TYPE}, 1.128 + reinterpret_cast<CallbackGeneric>( 1.129 + &ThreadProcessDispatcher::CreateProcessW) 1.130 + }; 1.131 + 1.132 + ipc_calls_.push_back(open_thread); 1.133 + ipc_calls_.push_back(open_process); 1.134 + ipc_calls_.push_back(process_token); 1.135 + ipc_calls_.push_back(process_tokenex); 1.136 + ipc_calls_.push_back(create_params); 1.137 +} 1.138 + 1.139 +bool ThreadProcessDispatcher::SetupService(InterceptionManager* manager, 1.140 + int service) { 1.141 + switch (service) { 1.142 + case IPC_NTOPENTHREAD_TAG: 1.143 + case IPC_NTOPENPROCESS_TAG: 1.144 + case IPC_NTOPENPROCESSTOKEN_TAG: 1.145 + case IPC_NTOPENPROCESSTOKENEX_TAG: 1.146 + // There is no explicit policy for these services. 1.147 + NOTREACHED(); 1.148 + return false; 1.149 + 1.150 + case IPC_CREATEPROCESSW_TAG: 1.151 + return INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessW, 1.152 + CREATE_PROCESSW_ID, 44) && 1.153 + INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessA, 1.154 + CREATE_PROCESSA_ID, 44); 1.155 + 1.156 + default: 1.157 + return false; 1.158 + } 1.159 +} 1.160 + 1.161 +bool ThreadProcessDispatcher::NtOpenThread(IPCInfo* ipc, DWORD desired_access, 1.162 + DWORD thread_id) { 1.163 + HANDLE handle; 1.164 + NTSTATUS ret = ProcessPolicy::OpenThreadAction(*ipc->client_info, 1.165 + desired_access, thread_id, 1.166 + &handle); 1.167 + ipc->return_info.nt_status = ret; 1.168 + ipc->return_info.handle = handle; 1.169 + return true; 1.170 +} 1.171 + 1.172 +bool ThreadProcessDispatcher::NtOpenProcess(IPCInfo* ipc, DWORD desired_access, 1.173 + DWORD process_id) { 1.174 + HANDLE handle; 1.175 + NTSTATUS ret = ProcessPolicy::OpenProcessAction(*ipc->client_info, 1.176 + desired_access, process_id, 1.177 + &handle); 1.178 + ipc->return_info.nt_status = ret; 1.179 + ipc->return_info.handle = handle; 1.180 + return true; 1.181 +} 1.182 + 1.183 +bool ThreadProcessDispatcher::NtOpenProcessToken(IPCInfo* ipc, HANDLE process, 1.184 + DWORD desired_access) { 1.185 + HANDLE handle; 1.186 + NTSTATUS ret = ProcessPolicy::OpenProcessTokenAction(*ipc->client_info, 1.187 + process, desired_access, 1.188 + &handle); 1.189 + ipc->return_info.nt_status = ret; 1.190 + ipc->return_info.handle = handle; 1.191 + return true; 1.192 +} 1.193 + 1.194 +bool ThreadProcessDispatcher::NtOpenProcessTokenEx(IPCInfo* ipc, HANDLE process, 1.195 + DWORD desired_access, 1.196 + DWORD attributes) { 1.197 + HANDLE handle; 1.198 + NTSTATUS ret = ProcessPolicy::OpenProcessTokenExAction(*ipc->client_info, 1.199 + process, 1.200 + desired_access, 1.201 + attributes, &handle); 1.202 + ipc->return_info.nt_status = ret; 1.203 + ipc->return_info.handle = handle; 1.204 + return true; 1.205 +} 1.206 + 1.207 +bool ThreadProcessDispatcher::CreateProcessW(IPCInfo* ipc, std::wstring* name, 1.208 + std::wstring* cmd_line, 1.209 + std::wstring* cur_dir, 1.210 + CountedBuffer* info) { 1.211 + if (sizeof(PROCESS_INFORMATION) != info->Size()) 1.212 + return false; 1.213 + 1.214 + // Check if there is an application name. 1.215 + std::wstring exe_name; 1.216 + if (!name->empty()) 1.217 + exe_name = *name; 1.218 + else 1.219 + exe_name = GetPathFromCmdLine(*cmd_line); 1.220 + 1.221 + if (IsPathRelative(exe_name)) { 1.222 + if (!ConvertToAbsolutePath(*cur_dir, name->empty(), &exe_name)) { 1.223 + // Cannot find the path. Maybe the file does not exist. 1.224 + ipc->return_info.win32_result = ERROR_FILE_NOT_FOUND; 1.225 + return true; 1.226 + } 1.227 + } 1.228 + 1.229 + const wchar_t* const_exe_name = exe_name.c_str(); 1.230 + CountedParameterSet<NameBased> params; 1.231 + params[NameBased::NAME] = ParamPickerMake(const_exe_name); 1.232 + 1.233 + EvalResult eval = policy_base_->EvalPolicy(IPC_CREATEPROCESSW_TAG, 1.234 + params.GetBase()); 1.235 + 1.236 + PROCESS_INFORMATION* proc_info = 1.237 + reinterpret_cast<PROCESS_INFORMATION*>(info->Buffer()); 1.238 + // Here we force the app_name to be the one we used for the policy lookup. 1.239 + // If our logic was wrong, at least we wont allow create a random process. 1.240 + DWORD ret = ProcessPolicy::CreateProcessWAction(eval, *ipc->client_info, 1.241 + exe_name, *cmd_line, 1.242 + proc_info); 1.243 + 1.244 + ipc->return_info.win32_result = ret; 1.245 + return true; 1.246 +} 1.247 + 1.248 +} // namespace sandbox