Roll src/third_party/WebKit e0eac24:489c548 (svn 193311:193320)
[chromium-blink-merge.git] / sandbox / win / src / process_thread_dispatcher.cc
blobca17d4920a461d0c99983528f202c45d5b5e575a
1 // Copyright (c) 2013 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.
5 #include "sandbox/win/src/process_thread_dispatcher.h"
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"
19 namespace {
21 // Extracts the application name from a command line.
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.
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 base::string16 GetPathFromCmdLine(const base::string16 &cmd_line) {
33 base::string16 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 base::string16::size_type pos = cmd_line.find(L'\"', 1);
38 if (base::string16::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 base::string16::size_type pos = cmd_line.find(L' ');
45 if (base::string16::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);
53 return exe_name;
56 // Returns true is the path in parameter is relative. False if it's
57 // absolute.
58 bool IsPathRelative(const base::string16 &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;
66 // Converts a relative path to an absolute path.
67 bool ConvertToAbsolutePath(const base::string16& child_current_directory,
68 bool use_env_path, base::string16 *path) {
69 wchar_t file_buffer[MAX_PATH];
70 wchar_t *file_part = NULL;
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);
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);
87 if (0 == result || result >= MAX_PATH)
88 return false;
90 *path = file_buffer;
91 return true;
94 } // namespace
95 namespace sandbox {
97 ThreadProcessDispatcher::ThreadProcessDispatcher(PolicyBase* policy_base)
98 : policy_base_(policy_base) {
99 static const IPCCall open_thread = {
100 {IPC_NTOPENTHREAD_TAG, UINT32_TYPE, UINT32_TYPE},
101 reinterpret_cast<CallbackGeneric>(
102 &ThreadProcessDispatcher::NtOpenThread)
105 static const IPCCall open_process = {
106 {IPC_NTOPENPROCESS_TAG, UINT32_TYPE, UINT32_TYPE},
107 reinterpret_cast<CallbackGeneric>(
108 &ThreadProcessDispatcher::NtOpenProcess)
111 static const IPCCall process_token = {
112 {IPC_NTOPENPROCESSTOKEN_TAG, VOIDPTR_TYPE, UINT32_TYPE},
113 reinterpret_cast<CallbackGeneric>(
114 &ThreadProcessDispatcher::NtOpenProcessToken)
117 static const IPCCall process_tokenex = {
118 {IPC_NTOPENPROCESSTOKENEX_TAG, VOIDPTR_TYPE, UINT32_TYPE, UINT32_TYPE},
119 reinterpret_cast<CallbackGeneric>(
120 &ThreadProcessDispatcher::NtOpenProcessTokenEx)
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)
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);
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;
147 case IPC_CREATEPROCESSW_TAG:
148 return INTERCEPT_EAT(manager, kKerneldllName, CreateProcessW,
149 CREATE_PROCESSW_ID, 44) &&
150 INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessA,
151 CREATE_PROCESSA_ID, 44);
153 default:
154 return false;
158 bool ThreadProcessDispatcher::NtOpenThread(IPCInfo* ipc,
159 uint32 desired_access,
160 uint32 thread_id) {
161 HANDLE handle;
162 NTSTATUS ret = ProcessPolicy::OpenThreadAction(*ipc->client_info,
163 desired_access, thread_id,
164 &handle);
165 ipc->return_info.nt_status = ret;
166 ipc->return_info.handle = handle;
167 return true;
170 bool ThreadProcessDispatcher::NtOpenProcess(IPCInfo* ipc,
171 uint32 desired_access,
172 uint32 process_id) {
173 HANDLE handle;
174 NTSTATUS ret = ProcessPolicy::OpenProcessAction(*ipc->client_info,
175 desired_access, process_id,
176 &handle);
177 ipc->return_info.nt_status = ret;
178 ipc->return_info.handle = handle;
179 return true;
182 bool ThreadProcessDispatcher::NtOpenProcessToken(IPCInfo* ipc,
183 HANDLE process,
184 uint32 desired_access) {
185 HANDLE handle;
186 NTSTATUS ret = ProcessPolicy::OpenProcessTokenAction(*ipc->client_info,
187 process, desired_access,
188 &handle);
189 ipc->return_info.nt_status = ret;
190 ipc->return_info.handle = handle;
191 return true;
194 bool ThreadProcessDispatcher::NtOpenProcessTokenEx(IPCInfo* ipc,
195 HANDLE process,
196 uint32 desired_access,
197 uint32 attributes) {
198 HANDLE handle;
199 NTSTATUS ret = ProcessPolicy::OpenProcessTokenExAction(*ipc->client_info,
200 process,
201 desired_access,
202 attributes, &handle);
203 ipc->return_info.nt_status = ret;
204 ipc->return_info.handle = handle;
205 return true;
208 bool ThreadProcessDispatcher::CreateProcessW(IPCInfo* ipc, base::string16* name,
209 base::string16* cmd_line,
210 base::string16* cur_dir,
211 CountedBuffer* info) {
212 if (sizeof(PROCESS_INFORMATION) != info->Size())
213 return false;
215 // Check if there is an application name.
216 base::string16 exe_name;
217 if (!name->empty())
218 exe_name = *name;
219 else
220 exe_name = GetPathFromCmdLine(*cmd_line);
222 if (IsPathRelative(exe_name)) {
223 if (!ConvertToAbsolutePath(*cur_dir, name->empty(), &exe_name)) {
224 // Cannot find the path. Maybe the file does not exist.
225 ipc->return_info.win32_result = ERROR_FILE_NOT_FOUND;
226 return true;
230 const wchar_t* const_exe_name = exe_name.c_str();
231 CountedParameterSet<NameBased> params;
232 params[NameBased::NAME] = ParamPickerMake(const_exe_name);
234 EvalResult eval = policy_base_->EvalPolicy(IPC_CREATEPROCESSW_TAG,
235 params.GetBase());
237 PROCESS_INFORMATION* proc_info =
238 reinterpret_cast<PROCESS_INFORMATION*>(info->Buffer());
239 // Here we force the app_name to be the one we used for the policy lookup.
240 // If our logic was wrong, at least we wont allow create a random process.
241 DWORD ret = ProcessPolicy::CreateProcessWAction(eval, *ipc->client_info,
242 exe_name, *cmd_line,
243 proc_info);
245 ipc->return_info.win32_result = ret;
246 return true;
249 } // namespace sandbox