• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 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/src/process_thread_dispatcher.h"
6 
7 #include "base/basictypes.h"
8 #include "base/logging.h"
9 #include "sandbox/win/src/crosscall_client.h"
10 #include "sandbox/win/src/interception.h"
11 #include "sandbox/win/src/interceptors.h"
12 #include "sandbox/win/src/ipc_tags.h"
13 #include "sandbox/win/src/policy_broker.h"
14 #include "sandbox/win/src/policy_params.h"
15 #include "sandbox/win/src/process_thread_interception.h"
16 #include "sandbox/win/src/process_thread_policy.h"
17 #include "sandbox/win/src/sandbox.h"
18 
19 namespace {
20 
21 // Extracts the application name from a command line.
22 //
23 // The application name is the first element of the command line. If
24 // there is no quotes, the first element is delimited by the first space.
25 // If there are quotes, the first element is delimited by the quotes.
26 //
27 // The create process call is smarter than us. It tries really hard to launch
28 // the process even if the command line is wrong. For example:
29 // "c:\program files\test param" will first try to launch c:\program.exe then
30 // c:\program files\test.exe. We don't do that, we stop after at the first
31 // space when there is no quotes.
GetPathFromCmdLine(const base::string16 & cmd_line)32 base::string16 GetPathFromCmdLine(const base::string16 &cmd_line) {
33   base::string16 exe_name;
34   // Check if it starts with '"'.
35   if (cmd_line[0] == L'\"') {
36     // Find the position of the second '"', this terminates the path.
37     base::string16::size_type pos = cmd_line.find(L'\"', 1);
38     if (base::string16::npos == pos)
39       return cmd_line;
40     exe_name = cmd_line.substr(1, pos - 1);
41   } else {
42     // There is no '"', that means that the appname is terminated at the
43     // first space.
44     base::string16::size_type pos = cmd_line.find(L' ');
45     if (base::string16::npos == pos) {
46       // There is no space, the cmd_line contains only the app_name
47       exe_name = cmd_line;
48     } else {
49       exe_name = cmd_line.substr(0, pos);
50     }
51   }
52 
53   return exe_name;
54 }
55 
56 // Returns true is the path in parameter is relative. False if it's
57 // absolute.
IsPathRelative(const base::string16 & path)58 bool IsPathRelative(const base::string16 &path) {
59   // A path is Relative if it's not a UNC path beginnning with \\ or a
60   // path beginning with a drive. (i.e. X:\)
61   if (path.find(L"\\\\") == 0 || path.find(L":\\") == 1)
62     return false;
63   return true;
64 }
65 
66 // Converts a relative path to an absolute path.
ConvertToAbsolutePath(const base::string16 & child_current_directory,bool use_env_path,base::string16 * path)67 bool ConvertToAbsolutePath(const base::string16& child_current_directory,
68                            bool use_env_path, base::string16 *path) {
69   wchar_t file_buffer[MAX_PATH];
70   wchar_t *file_part = NULL;
71 
72   // Here we should start by looking at the path where the child application was
73   // started. We don't have this information yet.
74   DWORD result = 0;
75   if (use_env_path) {
76     // Try with the complete path
77     result = ::SearchPath(NULL, path->c_str(), NULL, MAX_PATH, file_buffer,
78                           &file_part);
79   }
80 
81   if (0 == result) {
82     // Try with the current directory of the child
83     result = ::SearchPath(child_current_directory.c_str(), path->c_str(), NULL,
84                           MAX_PATH, file_buffer, &file_part);
85   }
86 
87   if (0 == result || result >= MAX_PATH)
88     return false;
89 
90   *path = file_buffer;
91   return true;
92 }
93 
94 }  // namespace
95 namespace sandbox {
96 
ThreadProcessDispatcher(PolicyBase * policy_base)97 ThreadProcessDispatcher::ThreadProcessDispatcher(PolicyBase* policy_base)
98     : policy_base_(policy_base) {
99   static const IPCCall open_thread = {
100     {IPC_NTOPENTHREAD_TAG, ULONG_TYPE, ULONG_TYPE},
101     reinterpret_cast<CallbackGeneric>(
102         &ThreadProcessDispatcher::NtOpenThread)
103   };
104 
105   static const IPCCall open_process = {
106     {IPC_NTOPENPROCESS_TAG, ULONG_TYPE, ULONG_TYPE},
107     reinterpret_cast<CallbackGeneric>(
108         &ThreadProcessDispatcher::NtOpenProcess)
109   };
110 
111   static const IPCCall process_token = {
112     {IPC_NTOPENPROCESSTOKEN_TAG, VOIDPTR_TYPE, ULONG_TYPE},
113     reinterpret_cast<CallbackGeneric>(
114         &ThreadProcessDispatcher::NtOpenProcessToken)
115   };
116 
117   static const IPCCall process_tokenex = {
118     {IPC_NTOPENPROCESSTOKENEX_TAG, VOIDPTR_TYPE, ULONG_TYPE, ULONG_TYPE},
119     reinterpret_cast<CallbackGeneric>(
120         &ThreadProcessDispatcher::NtOpenProcessTokenEx)
121   };
122 
123   static const IPCCall create_params = {
124     {IPC_CREATEPROCESSW_TAG, WCHAR_TYPE, WCHAR_TYPE, WCHAR_TYPE, INOUTPTR_TYPE},
125     reinterpret_cast<CallbackGeneric>(
126         &ThreadProcessDispatcher::CreateProcessW)
127   };
128 
129   ipc_calls_.push_back(open_thread);
130   ipc_calls_.push_back(open_process);
131   ipc_calls_.push_back(process_token);
132   ipc_calls_.push_back(process_tokenex);
133   ipc_calls_.push_back(create_params);
134 }
135 
SetupService(InterceptionManager * manager,int service)136 bool ThreadProcessDispatcher::SetupService(InterceptionManager* manager,
137                                            int service) {
138   switch (service) {
139     case IPC_NTOPENTHREAD_TAG:
140     case IPC_NTOPENPROCESS_TAG:
141     case IPC_NTOPENPROCESSTOKEN_TAG:
142     case IPC_NTOPENPROCESSTOKENEX_TAG:
143       // There is no explicit policy for these services.
144       NOTREACHED();
145       return false;
146 
147     case IPC_CREATEPROCESSW_TAG:
148       return INTERCEPT_EAT(manager, kKerneldllName, CreateProcessW,
149                            CREATE_PROCESSW_ID, 44) &&
150              INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessA,
151                            CREATE_PROCESSA_ID, 44);
152 
153     default:
154       return false;
155   }
156 }
157 
NtOpenThread(IPCInfo * ipc,DWORD desired_access,DWORD thread_id)158 bool ThreadProcessDispatcher::NtOpenThread(IPCInfo* ipc, DWORD desired_access,
159                                            DWORD thread_id) {
160   HANDLE handle;
161   NTSTATUS ret = ProcessPolicy::OpenThreadAction(*ipc->client_info,
162                                                  desired_access, thread_id,
163                                                  &handle);
164   ipc->return_info.nt_status = ret;
165   ipc->return_info.handle = handle;
166   return true;
167 }
168 
NtOpenProcess(IPCInfo * ipc,DWORD desired_access,DWORD process_id)169 bool ThreadProcessDispatcher::NtOpenProcess(IPCInfo* ipc, DWORD desired_access,
170                                             DWORD process_id) {
171   HANDLE handle;
172   NTSTATUS ret = ProcessPolicy::OpenProcessAction(*ipc->client_info,
173                                                   desired_access, process_id,
174                                                   &handle);
175   ipc->return_info.nt_status = ret;
176   ipc->return_info.handle = handle;
177   return true;
178 }
179 
NtOpenProcessToken(IPCInfo * ipc,HANDLE process,DWORD desired_access)180 bool ThreadProcessDispatcher::NtOpenProcessToken(IPCInfo* ipc, HANDLE process,
181                                                  DWORD desired_access) {
182   HANDLE handle;
183   NTSTATUS ret = ProcessPolicy::OpenProcessTokenAction(*ipc->client_info,
184                                                        process, desired_access,
185                                                        &handle);
186   ipc->return_info.nt_status = ret;
187   ipc->return_info.handle = handle;
188   return true;
189 }
190 
NtOpenProcessTokenEx(IPCInfo * ipc,HANDLE process,DWORD desired_access,DWORD attributes)191 bool ThreadProcessDispatcher::NtOpenProcessTokenEx(IPCInfo* ipc, HANDLE process,
192                                                    DWORD desired_access,
193                                                    DWORD attributes) {
194   HANDLE handle;
195   NTSTATUS ret = ProcessPolicy::OpenProcessTokenExAction(*ipc->client_info,
196                                                          process,
197                                                          desired_access,
198                                                          attributes, &handle);
199   ipc->return_info.nt_status = ret;
200   ipc->return_info.handle = handle;
201   return true;
202 }
203 
CreateProcessW(IPCInfo * ipc,base::string16 * name,base::string16 * cmd_line,base::string16 * cur_dir,CountedBuffer * info)204 bool ThreadProcessDispatcher::CreateProcessW(IPCInfo* ipc, base::string16* name,
205                                              base::string16* cmd_line,
206                                              base::string16* cur_dir,
207                                              CountedBuffer* info) {
208   if (sizeof(PROCESS_INFORMATION) != info->Size())
209     return false;
210 
211   // Check if there is an application name.
212   base::string16 exe_name;
213   if (!name->empty())
214     exe_name = *name;
215   else
216     exe_name = GetPathFromCmdLine(*cmd_line);
217 
218   if (IsPathRelative(exe_name)) {
219     if (!ConvertToAbsolutePath(*cur_dir, name->empty(), &exe_name)) {
220       // Cannot find the path. Maybe the file does not exist.
221       ipc->return_info.win32_result = ERROR_FILE_NOT_FOUND;
222       return true;
223     }
224   }
225 
226   const wchar_t* const_exe_name = exe_name.c_str();
227   CountedParameterSet<NameBased> params;
228   params[NameBased::NAME] = ParamPickerMake(const_exe_name);
229 
230   EvalResult eval = policy_base_->EvalPolicy(IPC_CREATEPROCESSW_TAG,
231                                              params.GetBase());
232 
233   PROCESS_INFORMATION* proc_info =
234       reinterpret_cast<PROCESS_INFORMATION*>(info->Buffer());
235   // Here we force the app_name to be the one we used for the policy lookup.
236   // If our logic was wrong, at least we wont allow create a random process.
237   DWORD ret = ProcessPolicy::CreateProcessWAction(eval, *ipc->client_info,
238                                                   exe_name, *cmd_line,
239                                                   proc_info);
240 
241   ipc->return_info.win32_result = ret;
242   return true;
243 }
244 
245 }  // namespace sandbox
246