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