michael@0: // Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "sandbox/win/src/process_thread_dispatcher.h" michael@0: michael@0: #include "base/basictypes.h" michael@0: #include "base/logging.h" michael@0: #include "sandbox/win/src/crosscall_client.h" michael@0: #include "sandbox/win/src/interception.h" michael@0: #include "sandbox/win/src/interceptors.h" michael@0: #include "sandbox/win/src/ipc_tags.h" michael@0: #include "sandbox/win/src/policy_broker.h" michael@0: #include "sandbox/win/src/policy_params.h" michael@0: #include "sandbox/win/src/process_thread_interception.h" michael@0: #include "sandbox/win/src/process_thread_policy.h" michael@0: #include "sandbox/win/src/sandbox.h" michael@0: michael@0: namespace { michael@0: michael@0: // Extracts the application name from a command line. michael@0: // michael@0: // The application name is the first element of the command line. If michael@0: // there is no quotes, the first element is delimited by the first space. michael@0: // If there are quotes, the first element is delimited by the quotes. michael@0: // michael@0: // The create process call is smarter than us. It tries really hard to launch michael@0: // the process even if the command line is wrong. For example: michael@0: // "c:\program files\test param" will first try to launch c:\program.exe then michael@0: // c:\program files\test.exe. We don't do that, we stop after at the first michael@0: // space when there is no quotes. michael@0: std::wstring GetPathFromCmdLine(const std::wstring &cmd_line) { michael@0: std::wstring exe_name; michael@0: // Check if it starts with '"'. michael@0: if (cmd_line[0] == L'\"') { michael@0: // Find the position of the second '"', this terminates the path. michael@0: std::wstring::size_type pos = cmd_line.find(L'\"', 1); michael@0: if (std::wstring::npos == pos) michael@0: return cmd_line; michael@0: exe_name = cmd_line.substr(1, pos - 1); michael@0: } else { michael@0: // There is no '"', that means that the appname is terminated at the michael@0: // first space. michael@0: std::wstring::size_type pos = cmd_line.find(L' '); michael@0: if (std::wstring::npos == pos) { michael@0: // There is no space, the cmd_line contains only the app_name michael@0: exe_name = cmd_line; michael@0: } else { michael@0: exe_name = cmd_line.substr(0, pos); michael@0: } michael@0: } michael@0: michael@0: return exe_name; michael@0: } michael@0: michael@0: // Returns true is the path in parameter is relative. False if it's michael@0: // absolute. michael@0: bool IsPathRelative(const std::wstring &path) { michael@0: // A path is Relative if it's not a UNC path beginnning with \\ or a michael@0: // path beginning with a drive. (i.e. X:\) michael@0: if (path.find(L"\\\\") == 0 || path.find(L":\\") == 1) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: // Converts a relative path to an absolute path. michael@0: bool ConvertToAbsolutePath(const std::wstring& child_current_directory, michael@0: bool use_env_path, std::wstring *path) { michael@0: wchar_t file_buffer[MAX_PATH]; michael@0: wchar_t *file_part = NULL; michael@0: michael@0: // Here we should start by looking at the path where the child application was michael@0: // started. We don't have this information yet. michael@0: DWORD result = 0; michael@0: if (use_env_path) { michael@0: // Try with the complete path michael@0: result = ::SearchPath(NULL, path->c_str(), NULL, MAX_PATH, file_buffer, michael@0: &file_part); michael@0: } michael@0: michael@0: if (0 == result) { michael@0: // Try with the current directory of the child michael@0: result = ::SearchPath(child_current_directory.c_str(), path->c_str(), NULL, michael@0: MAX_PATH, file_buffer, &file_part); michael@0: } michael@0: michael@0: if (0 == result || result >= MAX_PATH) michael@0: return false; michael@0: michael@0: *path = file_buffer; michael@0: return true; michael@0: } michael@0: michael@0: } // namespace michael@0: namespace sandbox { michael@0: michael@0: ThreadProcessDispatcher::ThreadProcessDispatcher(PolicyBase* policy_base) michael@0: : policy_base_(policy_base) { michael@0: static const IPCCall open_thread = { michael@0: {IPC_NTOPENTHREAD_TAG, ULONG_TYPE, ULONG_TYPE}, michael@0: reinterpret_cast( michael@0: &ThreadProcessDispatcher::NtOpenThread) michael@0: }; michael@0: michael@0: static const IPCCall open_process = { michael@0: {IPC_NTOPENPROCESS_TAG, ULONG_TYPE, ULONG_TYPE}, michael@0: reinterpret_cast( michael@0: &ThreadProcessDispatcher::NtOpenProcess) michael@0: }; michael@0: michael@0: static const IPCCall process_token = { michael@0: {IPC_NTOPENPROCESSTOKEN_TAG, VOIDPTR_TYPE, ULONG_TYPE}, michael@0: reinterpret_cast( michael@0: &ThreadProcessDispatcher::NtOpenProcessToken) michael@0: }; michael@0: michael@0: static const IPCCall process_tokenex = { michael@0: {IPC_NTOPENPROCESSTOKENEX_TAG, VOIDPTR_TYPE, ULONG_TYPE, ULONG_TYPE}, michael@0: reinterpret_cast( michael@0: &ThreadProcessDispatcher::NtOpenProcessTokenEx) michael@0: }; michael@0: michael@0: static const IPCCall create_params = { michael@0: {IPC_CREATEPROCESSW_TAG, WCHAR_TYPE, WCHAR_TYPE, WCHAR_TYPE, INOUTPTR_TYPE}, michael@0: reinterpret_cast( michael@0: &ThreadProcessDispatcher::CreateProcessW) michael@0: }; michael@0: michael@0: ipc_calls_.push_back(open_thread); michael@0: ipc_calls_.push_back(open_process); michael@0: ipc_calls_.push_back(process_token); michael@0: ipc_calls_.push_back(process_tokenex); michael@0: ipc_calls_.push_back(create_params); michael@0: } michael@0: michael@0: bool ThreadProcessDispatcher::SetupService(InterceptionManager* manager, michael@0: int service) { michael@0: switch (service) { michael@0: case IPC_NTOPENTHREAD_TAG: michael@0: case IPC_NTOPENPROCESS_TAG: michael@0: case IPC_NTOPENPROCESSTOKEN_TAG: michael@0: case IPC_NTOPENPROCESSTOKENEX_TAG: michael@0: // There is no explicit policy for these services. michael@0: NOTREACHED(); michael@0: return false; michael@0: michael@0: case IPC_CREATEPROCESSW_TAG: michael@0: return INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessW, michael@0: CREATE_PROCESSW_ID, 44) && michael@0: INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessA, michael@0: CREATE_PROCESSA_ID, 44); michael@0: michael@0: default: michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: bool ThreadProcessDispatcher::NtOpenThread(IPCInfo* ipc, DWORD desired_access, michael@0: DWORD thread_id) { michael@0: HANDLE handle; michael@0: NTSTATUS ret = ProcessPolicy::OpenThreadAction(*ipc->client_info, michael@0: desired_access, thread_id, michael@0: &handle); michael@0: ipc->return_info.nt_status = ret; michael@0: ipc->return_info.handle = handle; michael@0: return true; michael@0: } michael@0: michael@0: bool ThreadProcessDispatcher::NtOpenProcess(IPCInfo* ipc, DWORD desired_access, michael@0: DWORD process_id) { michael@0: HANDLE handle; michael@0: NTSTATUS ret = ProcessPolicy::OpenProcessAction(*ipc->client_info, michael@0: desired_access, process_id, michael@0: &handle); michael@0: ipc->return_info.nt_status = ret; michael@0: ipc->return_info.handle = handle; michael@0: return true; michael@0: } michael@0: michael@0: bool ThreadProcessDispatcher::NtOpenProcessToken(IPCInfo* ipc, HANDLE process, michael@0: DWORD desired_access) { michael@0: HANDLE handle; michael@0: NTSTATUS ret = ProcessPolicy::OpenProcessTokenAction(*ipc->client_info, michael@0: process, desired_access, michael@0: &handle); michael@0: ipc->return_info.nt_status = ret; michael@0: ipc->return_info.handle = handle; michael@0: return true; michael@0: } michael@0: michael@0: bool ThreadProcessDispatcher::NtOpenProcessTokenEx(IPCInfo* ipc, HANDLE process, michael@0: DWORD desired_access, michael@0: DWORD attributes) { michael@0: HANDLE handle; michael@0: NTSTATUS ret = ProcessPolicy::OpenProcessTokenExAction(*ipc->client_info, michael@0: process, michael@0: desired_access, michael@0: attributes, &handle); michael@0: ipc->return_info.nt_status = ret; michael@0: ipc->return_info.handle = handle; michael@0: return true; michael@0: } michael@0: michael@0: bool ThreadProcessDispatcher::CreateProcessW(IPCInfo* ipc, std::wstring* name, michael@0: std::wstring* cmd_line, michael@0: std::wstring* cur_dir, michael@0: CountedBuffer* info) { michael@0: if (sizeof(PROCESS_INFORMATION) != info->Size()) michael@0: return false; michael@0: michael@0: // Check if there is an application name. michael@0: std::wstring exe_name; michael@0: if (!name->empty()) michael@0: exe_name = *name; michael@0: else michael@0: exe_name = GetPathFromCmdLine(*cmd_line); michael@0: michael@0: if (IsPathRelative(exe_name)) { michael@0: if (!ConvertToAbsolutePath(*cur_dir, name->empty(), &exe_name)) { michael@0: // Cannot find the path. Maybe the file does not exist. michael@0: ipc->return_info.win32_result = ERROR_FILE_NOT_FOUND; michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: const wchar_t* const_exe_name = exe_name.c_str(); michael@0: CountedParameterSet params; michael@0: params[NameBased::NAME] = ParamPickerMake(const_exe_name); michael@0: michael@0: EvalResult eval = policy_base_->EvalPolicy(IPC_CREATEPROCESSW_TAG, michael@0: params.GetBase()); michael@0: michael@0: PROCESS_INFORMATION* proc_info = michael@0: reinterpret_cast(info->Buffer()); michael@0: // Here we force the app_name to be the one we used for the policy lookup. michael@0: // If our logic was wrong, at least we wont allow create a random process. michael@0: DWORD ret = ProcessPolicy::CreateProcessWAction(eval, *ipc->client_info, michael@0: exe_name, *cmd_line, michael@0: proc_info); michael@0: michael@0: ipc->return_info.win32_result = ret; michael@0: return true; michael@0: } michael@0: michael@0: } // namespace sandbox