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