|
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 |