diff options
Diffstat (limited to 'sandbox/src/process_thread_dispatcher.cc')
-rw-r--r-- | sandbox/src/process_thread_dispatcher.cc | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/sandbox/src/process_thread_dispatcher.cc b/sandbox/src/process_thread_dispatcher.cc new file mode 100644 index 0000000..c66ef2f --- /dev/null +++ b/sandbox/src/process_thread_dispatcher.cc @@ -0,0 +1,270 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "sandbox/src/process_thread_dispatcher.h" + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/win_util.h" +#include "sandbox/src/crosscall_client.h" +#include "sandbox/src/interception.h" +#include "sandbox/src/ipc_tags.h" +#include "sandbox/src/policy_broker.h" +#include "sandbox/src/policy_params.h" +#include "sandbox/src/process_thread_interception.h" +#include "sandbox/src/process_thread_policy.h" +#include "sandbox/src/sandbox.h" + +namespace { + +// Extracts the application name from a command line. +// +// The application name is the first element of the command line. If +// there is no quotes, the first element is delimited by the first space. +// If there are quotes, the first element is delimited by the quotes. +// +// The create process call is smarter than us. It tries really hard to launch +// the process even if the command line is wrong. For example: +// "c:\program files\test param" will first try to launch c:\program.exe then +// c:\program files\test.exe. We don't do that, we stop after at the first +// space when there is no quotes. +std::wstring GetPathFromCmdLine(const std::wstring &cmd_line) { + std::wstring exe_name; + // Check if it starts with '"'. + if (cmd_line[0] == L'\"') { + // Find the position of the second '"', this terminates the path. + std::wstring::size_type pos = cmd_line.find(L'\"', 1); + if (std::wstring::npos == pos) + return cmd_line; + exe_name = cmd_line.substr(1, pos - 1); + } else { + // There is no '"', that means that the appname is terminated at the + // first space. + std::wstring::size_type pos = cmd_line.find(L' '); + if (std::wstring::npos == pos) { + // There is no space, the cmd_line contains only the app_name + exe_name = cmd_line; + } else { + exe_name = cmd_line.substr(0, pos); + } + } + + return exe_name; +} + +// Returns true is the path in parameter is relative. False if it's +// absolute. +bool IsPathRelative(const std::wstring &path) { + // A path is Relative if it's not a UNC path beginnning with \\ or a + // path beginning with a drive. (i.e. X:\) + if (path.find(L"\\\\") == 0 || path.find(L":\\") == 1) + return false; + return true; +} + +// Converts a relative path to an absolute path. +bool ConvertToAbsolutePath(const std::wstring child_current_directory, + bool use_env_path, std::wstring *path) { + wchar_t file_buffer[MAX_PATH]; + wchar_t *file_part = NULL; + + // Here we should start by looking at the path where the child application was + // started. We don't have this information yet. + DWORD result = 0; + if (use_env_path) { + // Try with the complete path + result = ::SearchPath(NULL, path->c_str(), NULL, MAX_PATH, file_buffer, + &file_part); + } + + if (0 == result) { + // Try with the current directory of the child + result = ::SearchPath(child_current_directory.c_str(), path->c_str(), NULL, + MAX_PATH, file_buffer, &file_part); + } + + if (0 == result || result >= MAX_PATH) + return false; + + *path = file_buffer; + return true; +} + +} // namespace +namespace sandbox { + +ThreadProcessDispatcher::ThreadProcessDispatcher(PolicyBase* policy_base) + : policy_base_(policy_base) { + static const IPCCall open_thread = { + {IPC_NTOPENTHREAD_TAG, ULONG_TYPE, ULONG_TYPE}, + reinterpret_cast<CallbackGeneric>( + &ThreadProcessDispatcher::NtOpenThread) + }; + + static const IPCCall open_process = { + {IPC_NTOPENPROCESS_TAG, ULONG_TYPE, ULONG_TYPE}, + reinterpret_cast<CallbackGeneric>( + &ThreadProcessDispatcher::NtOpenProcess) + }; + + static const IPCCall process_token = { + {IPC_NTOPENPROCESSTOKEN_TAG, ULONG_TYPE, ULONG_TYPE}, + reinterpret_cast<CallbackGeneric>( + &ThreadProcessDispatcher::NtOpenProcessToken) + }; + + static const IPCCall process_tokenex = { + {IPC_NTOPENPROCESSTOKENEX_TAG, ULONG_TYPE, ULONG_TYPE, ULONG_TYPE}, + reinterpret_cast<CallbackGeneric>( + &ThreadProcessDispatcher::NtOpenProcessTokenEx) + }; + + static const IPCCall create_params = { + {IPC_CREATEPROCESSW_TAG, WCHAR_TYPE, WCHAR_TYPE, WCHAR_TYPE, INOUTPTR_TYPE}, + reinterpret_cast<CallbackGeneric>( + &ThreadProcessDispatcher::CreateProcessW) + }; + + ipc_calls_.push_back(open_thread); + ipc_calls_.push_back(open_process); + ipc_calls_.push_back(process_token); + ipc_calls_.push_back(process_tokenex); + ipc_calls_.push_back(create_params); +} + +bool ThreadProcessDispatcher::SetupService(InterceptionManager* manager, + int service) { + switch (service) { + case IPC_NTOPENTHREAD_TAG: + case IPC_NTOPENPROCESS_TAG: + case IPC_NTOPENPROCESSTOKEN_TAG: + case IPC_NTOPENPROCESSTOKENEX_TAG: + // There is no explicit policy for these services. + NOTREACHED(); + return false; + + case IPC_CREATEPROCESSW_TAG: + return INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessW, + L"_TargetCreateProcessW@44") && + INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessA, + L"_TargetCreateProcessA@44"); + + default: + return false; + } +} + +bool ThreadProcessDispatcher::NtOpenThread(IPCInfo* ipc, DWORD desired_access, + DWORD thread_id) { + HANDLE handle; + NTSTATUS ret = ProcessPolicy::OpenThreadAction(*ipc->client_info, + desired_access, thread_id, + &handle); + ipc->return_info.nt_status = ret; + ipc->return_info.handle = handle; + return true; +} + +bool ThreadProcessDispatcher::NtOpenProcess(IPCInfo* ipc, DWORD desired_access, + DWORD process_id) { + HANDLE handle; + NTSTATUS ret = ProcessPolicy::OpenProcessAction(*ipc->client_info, + desired_access, process_id, + &handle); + ipc->return_info.nt_status = ret; + ipc->return_info.handle = handle; + return true; +} + +bool ThreadProcessDispatcher::NtOpenProcessToken(IPCInfo* ipc, DWORD process, + DWORD desired_access) { + HANDLE handle; + NTSTATUS ret = ProcessPolicy::OpenProcessTokenAction(*ipc->client_info, + process, desired_access, + &handle); + ipc->return_info.nt_status = ret; + ipc->return_info.handle = handle; + return true; +} + +bool ThreadProcessDispatcher::NtOpenProcessTokenEx(IPCInfo* ipc, DWORD process, + DWORD desired_access, + DWORD attributes) { + HANDLE handle; + NTSTATUS ret = ProcessPolicy::OpenProcessTokenExAction(*ipc->client_info, + process, + desired_access, + attributes, &handle); + ipc->return_info.nt_status = ret; + ipc->return_info.handle = handle; + return true; +} + +bool ThreadProcessDispatcher::CreateProcessW(IPCInfo* ipc, std::wstring* name, + std::wstring* cmd_line, + std::wstring* cur_dir, + CountedBuffer* info) { + if (sizeof(PROCESS_INFORMATION) != info->Size()) + return false; + + // Check if there is an application name. + std::wstring exe_name; + if (!name->empty()) + exe_name = *name; + else + exe_name = GetPathFromCmdLine(*cmd_line); + + if (IsPathRelative(exe_name)) { + if (!ConvertToAbsolutePath(*cur_dir, name->empty(), &exe_name)) { + // Cannot find the path. Maybe the file does not exist. + ipc->return_info.win32_result = ERROR_FILE_NOT_FOUND; + return true; + } + } + + const wchar_t* const_exe_name = exe_name.c_str(); + CountedParameterSet<NameBased> params; + params[NameBased::NAME] = ParamPickerMake(const_exe_name); + + EvalResult eval = policy_base_->EvalPolicy(IPC_CREATEPROCESSW_TAG, + params.GetBase()); + + PROCESS_INFORMATION* proc_info = + reinterpret_cast<PROCESS_INFORMATION*>(info->Buffer()); + // Here we force the app_name to be the one we used for the policy lookup. + // If our logic was wrong, at least we wont allow create a random process. + DWORD ret = ProcessPolicy::CreateProcessWAction(eval, *ipc->client_info, + exe_name, *cmd_line, + proc_info); + + ipc->return_info.win32_result = ret; + return true; +} + +} // namespace sandbox |