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.
5 #include "sandbox/win/src/process_thread_dispatcher.h"
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"
21 // Extracts the application name from a command line.
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.
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.
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
)
40 exe_name
= cmd_line
.substr(1, pos
- 1);
42 // There is no '"', that means that the appname is terminated at the
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
49 exe_name
= cmd_line
.substr(0, pos
);
56 // Returns true is the path in parameter is relative. False if it's
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)
66 // Converts a relative path to an absolute 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
;
72 // Here we should start by looking at the path where the child application was
73 // started. We don't have this information yet.
76 // Try with the complete path
77 result
= ::SearchPath(NULL
, path
->c_str(), NULL
, MAX_PATH
, file_buffer
,
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
);
87 if (0 == result
|| result
>= MAX_PATH
)
97 ThreadProcessDispatcher::ThreadProcessDispatcher(PolicyBase
* policy_base
)
98 : policy_base_(policy_base
) {
99 static const IPCCall open_thread
= {
100 {IPC_NTOPENTHREAD_TAG
, {UINT32_TYPE
, UINT32_TYPE
}},
101 reinterpret_cast<CallbackGeneric
>(
102 &ThreadProcessDispatcher::NtOpenThread
)};
104 static const IPCCall open_process
= {
105 {IPC_NTOPENPROCESS_TAG
, {UINT32_TYPE
, UINT32_TYPE
}},
106 reinterpret_cast<CallbackGeneric
>(
107 &ThreadProcessDispatcher::NtOpenProcess
)};
109 static const IPCCall process_token
= {
110 {IPC_NTOPENPROCESSTOKEN_TAG
, {VOIDPTR_TYPE
, UINT32_TYPE
}},
111 reinterpret_cast<CallbackGeneric
>(
112 &ThreadProcessDispatcher::NtOpenProcessToken
)};
114 static const IPCCall process_tokenex
= {
115 {IPC_NTOPENPROCESSTOKENEX_TAG
, {VOIDPTR_TYPE
, UINT32_TYPE
, UINT32_TYPE
}},
116 reinterpret_cast<CallbackGeneric
>(
117 &ThreadProcessDispatcher::NtOpenProcessTokenEx
)};
119 static const IPCCall create_params
= {
120 {IPC_CREATEPROCESSW_TAG
,
121 {WCHAR_TYPE
, WCHAR_TYPE
, WCHAR_TYPE
, INOUTPTR_TYPE
}},
122 reinterpret_cast<CallbackGeneric
>(
123 &ThreadProcessDispatcher::CreateProcessW
)};
125 ipc_calls_
.push_back(open_thread
);
126 ipc_calls_
.push_back(open_process
);
127 ipc_calls_
.push_back(process_token
);
128 ipc_calls_
.push_back(process_tokenex
);
129 ipc_calls_
.push_back(create_params
);
132 bool ThreadProcessDispatcher::SetupService(InterceptionManager
* manager
,
135 case IPC_NTOPENTHREAD_TAG
:
136 case IPC_NTOPENPROCESS_TAG
:
137 case IPC_NTOPENPROCESSTOKEN_TAG
:
138 case IPC_NTOPENPROCESSTOKENEX_TAG
:
139 // There is no explicit policy for these services.
143 case IPC_CREATEPROCESSW_TAG
:
144 return INTERCEPT_EAT(manager
, kKerneldllName
, CreateProcessW
,
145 CREATE_PROCESSW_ID
, 44) &&
146 INTERCEPT_EAT(manager
, L
"kernel32.dll", CreateProcessA
,
147 CREATE_PROCESSA_ID
, 44);
154 bool ThreadProcessDispatcher::NtOpenThread(IPCInfo
* ipc
,
155 uint32 desired_access
,
158 NTSTATUS ret
= ProcessPolicy::OpenThreadAction(*ipc
->client_info
,
159 desired_access
, thread_id
,
161 ipc
->return_info
.nt_status
= ret
;
162 ipc
->return_info
.handle
= handle
;
166 bool ThreadProcessDispatcher::NtOpenProcess(IPCInfo
* ipc
,
167 uint32 desired_access
,
170 NTSTATUS ret
= ProcessPolicy::OpenProcessAction(*ipc
->client_info
,
171 desired_access
, process_id
,
173 ipc
->return_info
.nt_status
= ret
;
174 ipc
->return_info
.handle
= handle
;
178 bool ThreadProcessDispatcher::NtOpenProcessToken(IPCInfo
* ipc
,
180 uint32 desired_access
) {
182 NTSTATUS ret
= ProcessPolicy::OpenProcessTokenAction(*ipc
->client_info
,
183 process
, desired_access
,
185 ipc
->return_info
.nt_status
= ret
;
186 ipc
->return_info
.handle
= handle
;
190 bool ThreadProcessDispatcher::NtOpenProcessTokenEx(IPCInfo
* ipc
,
192 uint32 desired_access
,
195 NTSTATUS ret
= ProcessPolicy::OpenProcessTokenExAction(*ipc
->client_info
,
198 attributes
, &handle
);
199 ipc
->return_info
.nt_status
= ret
;
200 ipc
->return_info
.handle
= handle
;
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())
211 // Check if there is an application name.
212 base::string16 exe_name
;
216 exe_name
= GetPathFromCmdLine(*cmd_line
);
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
;
226 const wchar_t* const_exe_name
= exe_name
.c_str();
227 CountedParameterSet
<NameBased
> params
;
228 params
[NameBased::NAME
] = ParamPickerMake(const_exe_name
);
230 EvalResult eval
= policy_base_
->EvalPolicy(IPC_CREATEPROCESSW_TAG
,
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
,
241 ipc
->return_info
.win32_result
= ret
;
245 } // namespace sandbox