OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "sandbox/win/src/target_process.h" | 5 #include "sandbox/win/src/target_process.h" |
6 | 6 |
7 #include "base/basictypes.h" | 7 #include "base/basictypes.h" |
8 #include "base/macros.h" | 8 #include "base/macros.h" |
9 #include "base/memory/scoped_ptr.h" | 9 #include "base/memory/scoped_ptr.h" |
10 #include "base/win/pe_image.h" | 10 #include "base/win/pe_image.h" |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
58 return exe; | 58 return exe; |
59 } | 59 } |
60 PIMAGE_NT_HEADERS nt_header = pe.GetNTHeaders(); | 60 PIMAGE_NT_HEADERS nt_header = pe.GetNTHeaders(); |
61 char* base = reinterpret_cast<char*>(entry_point) - | 61 char* base = reinterpret_cast<char*>(entry_point) - |
62 nt_header->OptionalHeader.AddressOfEntryPoint; | 62 nt_header->OptionalHeader.AddressOfEntryPoint; |
63 | 63 |
64 ::FreeLibrary(exe); | 64 ::FreeLibrary(exe); |
65 return base; | 65 return base; |
66 } | 66 } |
67 | 67 |
68 | |
69 TargetProcess::TargetProcess(base::win::ScopedHandle initial_token, | 68 TargetProcess::TargetProcess(base::win::ScopedHandle initial_token, |
70 base::win::ScopedHandle lockdown_token, | 69 base::win::ScopedHandle lockdown_token, |
71 HANDLE job, ThreadProvider* thread_pool) | 70 base::win::ScopedHandle lowbox_token, |
72 // This object owns everything initialized here except thread_pool and | 71 HANDLE job, |
73 // the job_ handle. The Job handle is closed by BrokerServices and results | 72 ThreadProvider* thread_pool) |
74 // eventually in a call to our dtor. | 73 // This object owns everything initialized here except thread_pool and |
| 74 // the job_ handle. The Job handle is closed by BrokerServices and results |
| 75 // eventually in a call to our dtor. |
75 : lockdown_token_(lockdown_token.Pass()), | 76 : lockdown_token_(lockdown_token.Pass()), |
76 initial_token_(initial_token.Pass()), | 77 initial_token_(initial_token.Pass()), |
| 78 lowbox_token_(lowbox_token.Pass()), |
77 job_(job), | 79 job_(job), |
78 thread_pool_(thread_pool), | 80 thread_pool_(thread_pool), |
79 base_address_(NULL) { | 81 base_address_(NULL) {} |
80 } | |
81 | 82 |
82 TargetProcess::~TargetProcess() { | 83 TargetProcess::~TargetProcess() { |
83 DWORD exit_code = 0; | 84 DWORD exit_code = 0; |
84 // Give a chance to the process to die. In most cases the JOB_KILL_ON_CLOSE | 85 // Give a chance to the process to die. In most cases the JOB_KILL_ON_CLOSE |
85 // will take effect only when the context changes. As far as the testing went, | 86 // will take effect only when the context changes. As far as the testing went, |
86 // this wait was enough to switch context and kill the processes in the job. | 87 // this wait was enough to switch context and kill the processes in the job. |
87 // If this process is already dead, the function will return without waiting. | 88 // If this process is already dead, the function will return without waiting. |
88 // TODO(nsylvain): If the process is still alive at the end, we should kill | 89 // TODO(nsylvain): If the process is still alive at the end, we should kill |
89 // it. http://b/893891 | 90 // it. http://b/893891 |
90 // For now, this wait is there only to do a best effort to prevent some leaks | 91 // For now, this wait is there only to do a best effort to prevent some leaks |
(...skipping 18 matching lines...) Expand all Loading... |
109 // ipc_server_ references our process handle, so make sure the former is shut | 110 // ipc_server_ references our process handle, so make sure the former is shut |
110 // down before the latter is closed (by ScopedProcessInformation). | 111 // down before the latter is closed (by ScopedProcessInformation). |
111 ipc_server_.reset(); | 112 ipc_server_.reset(); |
112 } | 113 } |
113 | 114 |
114 // Creates the target (child) process suspended and assigns it to the job | 115 // Creates the target (child) process suspended and assigns it to the job |
115 // object. | 116 // object. |
116 DWORD TargetProcess::Create(const wchar_t* exe_path, | 117 DWORD TargetProcess::Create(const wchar_t* exe_path, |
117 const wchar_t* command_line, | 118 const wchar_t* command_line, |
118 bool inherit_handles, | 119 bool inherit_handles, |
119 bool set_lockdown_token_after_create, | |
120 const base::win::StartupInformation& startup_info, | 120 const base::win::StartupInformation& startup_info, |
121 base::win::ScopedProcessInformation* target_info) { | 121 base::win::ScopedProcessInformation* target_info) { |
122 if (set_lockdown_token_after_create && | 122 if (lowbox_token_.IsValid() && |
123 base::win::GetVersion() < base::win::VERSION_WIN8) { | 123 base::win::GetVersion() < base::win::VERSION_WIN8) { |
124 // We don't allow set_lockdown_token_after_create below Windows 8. | 124 // We don't allow lowbox_token below Windows 8. |
125 return ERROR_INVALID_PARAMETER; | 125 return ERROR_INVALID_PARAMETER; |
126 } | 126 } |
127 | 127 |
128 exe_name_.reset(_wcsdup(exe_path)); | 128 exe_name_.reset(_wcsdup(exe_path)); |
129 | 129 |
130 // the command line needs to be writable by CreateProcess(). | 130 // the command line needs to be writable by CreateProcess(). |
131 scoped_ptr<wchar_t, base::FreeDeleter> cmd_line(_wcsdup(command_line)); | 131 scoped_ptr<wchar_t, base::FreeDeleter> cmd_line(_wcsdup(command_line)); |
132 | 132 |
133 // Start the target process suspended. | 133 // Start the target process suspended. |
134 DWORD flags = | 134 DWORD flags = |
135 CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS; | 135 CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS; |
136 | 136 |
137 if (startup_info.has_extended_startup_info()) | 137 if (startup_info.has_extended_startup_info()) |
138 flags |= EXTENDED_STARTUPINFO_PRESENT; | 138 flags |= EXTENDED_STARTUPINFO_PRESENT; |
139 | 139 |
140 if (job_ && base::win::GetVersion() < base::win::VERSION_WIN8) { | 140 if (job_ && base::win::GetVersion() < base::win::VERSION_WIN8) { |
141 // Windows 8 implements nested jobs, but for older systems we need to | 141 // Windows 8 implements nested jobs, but for older systems we need to |
142 // break out of any job we're in to enforce our restrictions. | 142 // break out of any job we're in to enforce our restrictions. |
143 flags |= CREATE_BREAKAWAY_FROM_JOB; | 143 flags |= CREATE_BREAKAWAY_FROM_JOB; |
144 } | 144 } |
145 | 145 |
146 base::win::ScopedHandle scoped_lockdown_token(lockdown_token_.Take()); | |
147 PROCESS_INFORMATION temp_process_info = {}; | 146 PROCESS_INFORMATION temp_process_info = {}; |
148 if (set_lockdown_token_after_create) { | 147 if (!::CreateProcessAsUserW(lockdown_token_.Get(), exe_path, cmd_line.get(), |
149 // First create process with a default token and then replace it later, | 148 NULL, // No security attribute. |
150 // after setting primary thread token. This is required for setting | 149 NULL, // No thread attribute. |
151 // an AppContainer token along with an impersonation token. | 150 inherit_handles, flags, |
152 if (!::CreateProcess(exe_path, | 151 NULL, // Use the environment of the caller. |
153 cmd_line.get(), | 152 NULL, // Use current directory of the caller. |
154 NULL, // No security attribute. | 153 startup_info.startup_info(), |
155 NULL, // No thread attribute. | 154 &temp_process_info)) { |
156 inherit_handles, | 155 return ::GetLastError(); |
157 flags, | |
158 NULL, // Use the environment of the caller. | |
159 NULL, // Use current directory of the caller. | |
160 startup_info.startup_info(), | |
161 &temp_process_info)) { | |
162 return ::GetLastError(); | |
163 } | |
164 } else { | |
165 if (!::CreateProcessAsUserW(scoped_lockdown_token.Get(), | |
166 exe_path, | |
167 cmd_line.get(), | |
168 NULL, // No security attribute. | |
169 NULL, // No thread attribute. | |
170 inherit_handles, | |
171 flags, | |
172 NULL, // Use the environment of the caller. | |
173 NULL, // Use current directory of the caller. | |
174 startup_info.startup_info(), | |
175 &temp_process_info)) { | |
176 return ::GetLastError(); | |
177 } | |
178 } | 156 } |
179 base::win::ScopedProcessInformation process_info(temp_process_info); | 157 base::win::ScopedProcessInformation process_info(temp_process_info); |
180 | 158 |
181 DWORD win_result = ERROR_SUCCESS; | 159 DWORD win_result = ERROR_SUCCESS; |
182 | 160 |
183 if (job_) { | 161 if (job_) { |
184 // Assign the suspended target to the windows job object. | 162 // Assign the suspended target to the windows job object. |
185 if (!::AssignProcessToJobObject(job_, process_info.process_handle())) { | 163 if (!::AssignProcessToJobObject(job_, process_info.process_handle())) { |
186 win_result = ::GetLastError(); | 164 win_result = ::GetLastError(); |
187 ::TerminateProcess(process_info.process_handle(), 0); | 165 ::TerminateProcess(process_info.process_handle(), 0); |
188 return win_result; | 166 return win_result; |
189 } | 167 } |
190 } | 168 } |
191 | 169 |
192 if (initial_token_.IsValid()) { | 170 if (initial_token_.IsValid()) { |
193 // Change the token of the main thread of the new process for the | 171 // Change the token of the main thread of the new process for the |
194 // impersonation token with more rights. This allows the target to start; | 172 // impersonation token with more rights. This allows the target to start; |
195 // otherwise it will crash too early for us to help. | 173 // otherwise it will crash too early for us to help. |
196 HANDLE temp_thread = process_info.thread_handle(); | 174 HANDLE temp_thread = process_info.thread_handle(); |
197 if (!::SetThreadToken(&temp_thread, initial_token_.Get())) { | 175 if (!::SetThreadToken(&temp_thread, initial_token_.Get())) { |
198 win_result = ::GetLastError(); | 176 win_result = ::GetLastError(); |
199 // It might be a security breach if we let the target run outside the job | 177 // It might be a security breach if we let the target run outside the job |
200 // so kill it before it causes damage. | 178 // so kill it before it causes damage. |
201 ::TerminateProcess(process_info.process_handle(), 0); | 179 ::TerminateProcess(process_info.process_handle(), 0); |
202 return win_result; | 180 return win_result; |
203 } | 181 } |
204 initial_token_.Close(); | 182 initial_token_.Close(); |
205 } | 183 } |
206 | 184 |
207 if (set_lockdown_token_after_create) { | |
208 PROCESS_ACCESS_TOKEN process_access_token; | |
209 process_access_token.thread = process_info.thread_handle(); | |
210 process_access_token.token = scoped_lockdown_token.Get(); | |
211 | |
212 NtSetInformationProcess SetInformationProcess = NULL; | |
213 ResolveNTFunctionPtr("NtSetInformationProcess", &SetInformationProcess); | |
214 | |
215 NTSTATUS status = SetInformationProcess( | |
216 process_info.process_handle(), | |
217 static_cast<PROCESS_INFORMATION_CLASS>(NtProcessInformationAccessToken), | |
218 &process_access_token, | |
219 sizeof(process_access_token)); | |
220 if (!NT_SUCCESS(status)) { | |
221 win_result = ::GetLastError(); | |
222 ::TerminateProcess(process_info.process_handle(), 0); // exit code | |
223 return win_result; | |
224 } | |
225 } | |
226 | |
227 CONTEXT context; | 185 CONTEXT context; |
228 context.ContextFlags = CONTEXT_ALL; | 186 context.ContextFlags = CONTEXT_ALL; |
229 if (!::GetThreadContext(process_info.thread_handle(), &context)) { | 187 if (!::GetThreadContext(process_info.thread_handle(), &context)) { |
230 win_result = ::GetLastError(); | 188 win_result = ::GetLastError(); |
231 ::TerminateProcess(process_info.process_handle(), 0); | 189 ::TerminateProcess(process_info.process_handle(), 0); |
232 return win_result; | 190 return win_result; |
233 } | 191 } |
234 | 192 |
235 #if defined(_WIN64) | 193 #if defined(_WIN64) |
236 void* entry_point = reinterpret_cast<void*>(context.Rcx); | 194 void* entry_point = reinterpret_cast<void*>(context.Rcx); |
237 #else | 195 #else |
238 #pragma warning(push) | 196 #pragma warning(push) |
239 #pragma warning(disable: 4312) | 197 #pragma warning(disable: 4312) |
240 // This cast generates a warning because it is 32 bit specific. | 198 // This cast generates a warning because it is 32 bit specific. |
241 void* entry_point = reinterpret_cast<void*>(context.Eax); | 199 void* entry_point = reinterpret_cast<void*>(context.Eax); |
242 #pragma warning(pop) | 200 #pragma warning(pop) |
243 #endif // _WIN64 | 201 #endif // _WIN64 |
244 | 202 |
245 if (!target_info->DuplicateFrom(process_info)) { | 203 if (!target_info->DuplicateFrom(process_info)) { |
246 win_result = ::GetLastError(); // This may or may not be correct. | 204 win_result = ::GetLastError(); // This may or may not be correct. |
247 ::TerminateProcess(process_info.process_handle(), 0); | 205 ::TerminateProcess(process_info.process_handle(), 0); |
248 return win_result; | 206 return win_result; |
249 } | 207 } |
250 | 208 |
| 209 if (lowbox_token_.IsValid()) { |
| 210 PROCESS_ACCESS_TOKEN process_access_token; |
| 211 process_access_token.thread = process_info.thread_handle(); |
| 212 process_access_token.token = lowbox_token_.Get(); |
| 213 |
| 214 NtSetInformationProcess SetInformationProcess = NULL; |
| 215 ResolveNTFunctionPtr("NtSetInformationProcess", &SetInformationProcess); |
| 216 |
| 217 NTSTATUS status = SetInformationProcess( |
| 218 process_info.process_handle(), |
| 219 static_cast<PROCESS_INFORMATION_CLASS>(NtProcessInformationAccessToken), |
| 220 &process_access_token, sizeof(process_access_token)); |
| 221 if (!NT_SUCCESS(status)) { |
| 222 win_result = ERROR_INVALID_TOKEN; |
| 223 ::TerminateProcess(process_info.process_handle(), 0); // exit code |
| 224 return win_result; |
| 225 } |
| 226 } |
| 227 |
251 base_address_ = GetBaseAddress(exe_path, entry_point); | 228 base_address_ = GetBaseAddress(exe_path, entry_point); |
252 sandbox_process_info_.Set(process_info.Take()); | 229 sandbox_process_info_.Set(process_info.Take()); |
253 return win_result; | 230 return win_result; |
254 } | 231 } |
255 | 232 |
256 ResultCode TargetProcess::TransferVariable(const char* name, void* address, | 233 ResultCode TargetProcess::TransferVariable(const char* name, void* address, |
257 size_t size) { | 234 size_t size) { |
258 if (!sandbox_process_info_.IsValid()) | 235 if (!sandbox_process_info_.IsValid()) |
259 return SBOX_ERROR_UNEXPECTED_CALL; | 236 return SBOX_ERROR_UNEXPECTED_CALL; |
260 | 237 |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
367 } | 344 } |
368 | 345 |
369 void TargetProcess::Terminate() { | 346 void TargetProcess::Terminate() { |
370 if (!sandbox_process_info_.IsValid()) | 347 if (!sandbox_process_info_.IsValid()) |
371 return; | 348 return; |
372 | 349 |
373 ::TerminateProcess(sandbox_process_info_.process_handle(), 0); | 350 ::TerminateProcess(sandbox_process_info_.process_handle(), 0); |
374 } | 351 } |
375 | 352 |
376 TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address) { | 353 TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address) { |
377 TargetProcess* target = new TargetProcess(base::win::ScopedHandle(), | 354 TargetProcess* target = |
378 base::win::ScopedHandle(), | 355 new TargetProcess(base::win::ScopedHandle(), base::win::ScopedHandle(), |
379 NULL, NULL); | 356 base::win::ScopedHandle(), NULL, NULL); |
380 PROCESS_INFORMATION process_info = {}; | 357 PROCESS_INFORMATION process_info = {}; |
381 process_info.hProcess = process; | 358 process_info.hProcess = process; |
382 target->sandbox_process_info_.Set(process_info); | 359 target->sandbox_process_info_.Set(process_info); |
383 target->base_address_ = base_address; | 360 target->base_address_ = base_address; |
384 return target; | 361 return target; |
385 } | 362 } |
386 | 363 |
387 } // namespace sandbox | 364 } // namespace sandbox |
OLD | NEW |