summaryrefslogtreecommitdiffstats
path: root/sandbox/src/process_thread_dispatcher.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/src/process_thread_dispatcher.cc')
-rw-r--r--sandbox/src/process_thread_dispatcher.cc270
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