1 // Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "sandbox/win/sandbox_poc/sandbox.h"
6 
7 #include <windows.h>
8 #include <tchar.h>
9 #include <shellapi.h>
10 
11 #include <string>
12 
13 #include "base/check.h"
14 #include "sandbox/win/sandbox_poc/main_ui_window.h"
15 #include "sandbox/win/src/sandbox.h"
16 #include "sandbox/win/src/sandbox_factory.h"
17 
18 // Prototype allowed for functions to be called in the POC
19 typedef void(__cdecl *lpfnInit)(HANDLE);
20 
ParseCommandLine(wchar_t * command_line,std::string * dll_name,std::string * entry_point,std::wstring * log_file)21 bool ParseCommandLine(wchar_t* command_line,
22                       std::string* dll_name,
23                       std::string* entry_point,
24                       std::wstring* log_file) {
25   DCHECK(dll_name);
26   DCHECK(entry_point);
27   DCHECK(log_file);
28   if (!dll_name || !entry_point || !log_file)
29     return false;
30 
31   LPWSTR* arg_list;
32   int arg_count;
33 
34   // We expect the command line to contain: EntryPointName "DLLPath" "LogPath"
35   // NOTE: Double quotes are required, even if long path name not used
36   // NOTE: LogPath can be blank, but still requires the double quotes
37   arg_list = CommandLineToArgvW(command_line, &arg_count);
38   if (NULL == arg_list || arg_count < 4) {
39      return false;
40   }
41 
42   std::wstring entry_point_wide = arg_list[1];
43   std::wstring dll_name_wide = arg_list[2];
44   *entry_point = std::string(entry_point_wide.begin(), entry_point_wide.end());
45   *dll_name    = std::string(dll_name_wide.begin(), dll_name_wide.end());
46   *log_file    = arg_list[3];
47 
48   // Free memory allocated for CommandLineToArgvW arguments.
49   LocalFree(arg_list);
50 
51   return true;
52 }
53 
_tWinMain(HINSTANCE instance,HINSTANCE,wchar_t * command_line,int show_command)54 int APIENTRY _tWinMain(HINSTANCE instance, HINSTANCE, wchar_t* command_line,
55                        int show_command) {
56   sandbox::BrokerServices* broker_service =
57       sandbox::SandboxFactory::GetBrokerServices();
58   sandbox::ResultCode result;
59 
60   // This application starts as the broker; an application with a UI that
61   // spawns an instance of itself (called a 'target') inside the sandbox.
62   // Before spawning a hidden instance of itself, the application will have
63   // asked the user which DLL the spawned instance should load and passes
64   // that as command line argument to the spawned instance.
65   //
66   // We check here to see if we can retrieve a pointer to the BrokerServices,
67   // which is not possible if we are running inside the sandbox under a
68   // restricted token so it also tells us which mode we are in. If we can
69   // retrieve the pointer, then we are the broker, otherwise we are the target
70   // that the broker launched.
71   if (NULL != broker_service) {
72     // Yes, we are the broker so we need to initialize and show the UI
73     if (0 != (result = broker_service->Init())) {
74       ::MessageBox(NULL, L"Failed to initialize the BrokerServices object",
75                    L"Error during initialization", MB_ICONERROR);
76       return 1;
77     }
78 
79     wchar_t exe_name[MAX_PATH];
80     if (0 == GetModuleFileName(NULL, exe_name, MAX_PATH - 1)) {
81       ::MessageBox(NULL, L"Failed to get name of current EXE",
82                    L"Error during initialization", MB_ICONERROR);
83       return 1;
84     }
85 
86     // The CreateMainWindowAndLoop() call will not return until the user closes
87     // the application window (or selects File\Exit).
88     MainUIWindow window;
89     window.CreateMainWindowAndLoop(instance,
90                                    exe_name,
91                                    show_command,
92                                    broker_service);
93 
94 
95     // Cannot exit until we have cleaned up after all the targets we have
96     // created
97     broker_service->WaitForAllTargets();
98   } else {
99     // This is an instance that has been spawned inside the sandbox by the
100     // broker, so we need to parse the command line to figure out which DLL to
101     // load and what entry point to call
102     sandbox::TargetServices* target_service
103         = sandbox::SandboxFactory::GetTargetServices();
104 
105     if (NULL == target_service) {
106       // TODO(finnur): write the failure to the log file
107       // We cannot display messageboxes inside the sandbox unless access to
108       // the desktop handle has been granted to us, and we don't have a
109       // console window to write to. Therefore we need to have the broker
110       // grant us access to a handle to a logfile and write the error that
111       // occurred into the log before continuing
112       return -1;
113     }
114 
115     // Debugging the spawned application can be tricky, because DebugBreak()
116     // and _asm int 3 cause the app to terminate (due to a flag in the job
117     // object), MessageBoxes() will not be displayed unless we have been granted
118     // that privilege and the target finishes its business so quickly we cannot
119     // attach to it quickly enough. Therefore, you can uncomment the
120     // following line and attach (w. msdev or windbg) as the target is sleeping
121 
122     // Sleep(10000);
123 
124     if (sandbox::SBOX_ALL_OK != (result = target_service->Init())) {
125       // TODO(finnur): write the initialization error to the log file
126       return -2;
127     }
128 
129     // Parse the command line to find out what we need to call
130     std::string dll_name, entry_point;
131     std::wstring log_file;
132     if (!ParseCommandLine(GetCommandLineW(),
133                           &dll_name,
134                           &entry_point,
135                           &log_file)) {
136       // TODO(finnur): write the failure to the log file
137       return -3;
138     }
139 
140     // Open the pipe to transfert the log output
141     HANDLE pipe = ::CreateFile(log_file.c_str(),
142                                GENERIC_WRITE,
143                                FILE_SHARE_READ | FILE_SHARE_WRITE,
144                                NULL,  // Default security attributes.
145                                CREATE_ALWAYS,
146                                FILE_ATTRIBUTE_NORMAL,
147                                NULL);  // No template
148 
149     if (INVALID_HANDLE_VALUE == pipe) {
150       return -4;
151     }
152 
153     // We now know what we should load, so load it
154     HMODULE dll_module = ::LoadLibraryA(dll_name.c_str());
155     if (dll_module == NULL) {
156       // TODO(finnur): write the failure to the log file
157       CloseHandle(pipe);
158       return -5;
159     }
160 
161     // Initialization is finished, so we can enter lock-down mode
162     target_service->LowerToken();
163 
164     lpfnInit init_function =
165         (lpfnInit) ::GetProcAddress(dll_module, entry_point.c_str());
166 
167     if (!init_function) {
168       // TODO(finnur): write the failure to the log file
169       ::FreeLibrary(dll_module);
170       CloseHandle(pipe);
171       return -6;
172     }
173 
174     // Transfer control to the entry point in the DLL requested
175     init_function(pipe);
176 
177     CloseHandle(pipe);
178     Sleep(1000);  // Give a change to the debug output to arrive before the
179                   // end of the process
180 
181     ::FreeLibrary(dll_module);
182   }
183 
184   return 0;
185 }
186