| // Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "sandbox/win/sandbox_poc/sandbox.h" |
| |
| #include <windows.h> |
| #include <tchar.h> |
| #include <shellapi.h> |
| |
| #include <string> |
| |
| #include "base/logging.h" |
| #include "sandbox/win/sandbox_poc/main_ui_window.h" |
| #include "sandbox/win/src/sandbox.h" |
| #include "sandbox/win/src/sandbox_factory.h" |
| |
| // Prototype allowed for functions to be called in the POC |
| typedef void(__cdecl *lpfnInit)(HANDLE); |
| |
| bool ParseCommandLine(wchar_t * command_line, |
| std::string * dll_name, |
| std::string * entry_point, |
| base::string16 * log_file) { |
| DCHECK(dll_name); |
| DCHECK(entry_point); |
| DCHECK(log_file); |
| if (!dll_name || !entry_point || !log_file) |
| return false; |
| |
| LPWSTR* arg_list; |
| int arg_count; |
| |
| // We expect the command line to contain: EntryPointName "DLLPath" "LogPath" |
| // NOTE: Double quotes are required, even if long path name not used |
| // NOTE: LogPath can be blank, but still requires the double quotes |
| arg_list = CommandLineToArgvW(command_line, &arg_count); |
| if (NULL == arg_list || arg_count < 4) { |
| return false; |
| } |
| |
| base::string16 entry_point_wide = arg_list[1]; |
| base::string16 dll_name_wide = arg_list[2]; |
| *entry_point = std::string(entry_point_wide.begin(), entry_point_wide.end()); |
| *dll_name = std::string(dll_name_wide.begin(), dll_name_wide.end()); |
| *log_file = arg_list[3]; |
| |
| // Free memory allocated for CommandLineToArgvW arguments. |
| LocalFree(arg_list); |
| |
| return true; |
| } |
| |
| int APIENTRY _tWinMain(HINSTANCE instance, HINSTANCE, wchar_t* command_line, |
| int show_command) { |
| sandbox::BrokerServices* broker_service = |
| sandbox::SandboxFactory::GetBrokerServices(); |
| sandbox::ResultCode result; |
| |
| // This application starts as the broker; an application with a UI that |
| // spawns an instance of itself (called a 'target') inside the sandbox. |
| // Before spawning a hidden instance of itself, the application will have |
| // asked the user which DLL the spawned instance should load and passes |
| // that as command line argument to the spawned instance. |
| // |
| // We check here to see if we can retrieve a pointer to the BrokerServices, |
| // which is not possible if we are running inside the sandbox under a |
| // restricted token so it also tells us which mode we are in. If we can |
| // retrieve the pointer, then we are the broker, otherwise we are the target |
| // that the broker launched. |
| if (NULL != broker_service) { |
| // Yes, we are the broker so we need to initialize and show the UI |
| if (0 != (result = broker_service->Init())) { |
| ::MessageBox(NULL, L"Failed to initialize the BrokerServices object", |
| L"Error during initialization", MB_ICONERROR); |
| return 1; |
| } |
| |
| wchar_t exe_name[MAX_PATH]; |
| if (0 == GetModuleFileName(NULL, exe_name, MAX_PATH - 1)) { |
| ::MessageBox(NULL, L"Failed to get name of current EXE", |
| L"Error during initialization", MB_ICONERROR); |
| return 1; |
| } |
| |
| // The CreateMainWindowAndLoop() call will not return until the user closes |
| // the application window (or selects File\Exit). |
| MainUIWindow window; |
| window.CreateMainWindowAndLoop(instance, |
| exe_name, |
| show_command, |
| broker_service); |
| |
| |
| // Cannot exit until we have cleaned up after all the targets we have |
| // created |
| broker_service->WaitForAllTargets(); |
| } else { |
| // This is an instance that has been spawned inside the sandbox by the |
| // broker, so we need to parse the command line to figure out which DLL to |
| // load and what entry point to call |
| sandbox::TargetServices* target_service |
| = sandbox::SandboxFactory::GetTargetServices(); |
| |
| if (NULL == target_service) { |
| // TODO(finnur): write the failure to the log file |
| // We cannot display messageboxes inside the sandbox unless access to |
| // the desktop handle has been granted to us, and we don't have a |
| // console window to write to. Therefore we need to have the broker |
| // grant us access to a handle to a logfile and write the error that |
| // occurred into the log before continuing |
| return -1; |
| } |
| |
| // Debugging the spawned application can be tricky, because DebugBreak() |
| // and _asm int 3 cause the app to terminate (due to a flag in the job |
| // object), MessageBoxes() will not be displayed unless we have been granted |
| // that privilege and the target finishes its business so quickly we cannot |
| // attach to it quickly enough. Therefore, you can uncomment the |
| // following line and attach (w. msdev or windbg) as the target is sleeping |
| |
| // Sleep(10000); |
| |
| if (sandbox::SBOX_ALL_OK != (result = target_service->Init())) { |
| // TODO(finnur): write the initialization error to the log file |
| return -2; |
| } |
| |
| // Parse the command line to find out what we need to call |
| std::string dll_name, entry_point; |
| base::string16 log_file; |
| if (!ParseCommandLine(GetCommandLineW(), |
| &dll_name, |
| &entry_point, |
| &log_file)) { |
| // TODO(finnur): write the failure to the log file |
| return -3; |
| } |
| |
| // Open the pipe to transfert the log output |
| HANDLE pipe = ::CreateFile(log_file.c_str(), |
| GENERIC_WRITE, |
| FILE_SHARE_READ | FILE_SHARE_WRITE, |
| NULL, // Default security attributes. |
| CREATE_ALWAYS, |
| FILE_ATTRIBUTE_NORMAL, |
| NULL); // No template |
| |
| if (INVALID_HANDLE_VALUE == pipe) { |
| return -4; |
| } |
| |
| // We now know what we should load, so load it |
| HMODULE dll_module = ::LoadLibraryA(dll_name.c_str()); |
| if (dll_module == NULL) { |
| // TODO(finnur): write the failure to the log file |
| return -5; |
| } |
| |
| // Initialization is finished, so we can enter lock-down mode |
| target_service->LowerToken(); |
| |
| lpfnInit init_function = |
| (lpfnInit) ::GetProcAddress(dll_module, entry_point.c_str()); |
| |
| if (!init_function) { |
| // TODO(finnur): write the failure to the log file |
| ::FreeLibrary(dll_module); |
| CloseHandle(pipe); |
| return -6; |
| } |
| |
| // Transfer control to the entry point in the DLL requested |
| init_function(pipe); |
| |
| CloseHandle(pipe); |
| Sleep(1000); // Give a change to the debug output to arrive before the |
| // end of the process |
| |
| ::FreeLibrary(dll_module); |
| } |
| |
| return 0; |
| } |