1 // Copyright 2014 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 "chrome_elf/create_file/chrome_create_file.h"
9 #include "base/strings/string16.h"
10 #include "chrome_elf/chrome_elf_constants.h"
11 #include "chrome_elf/chrome_elf_util.h"
12 #include "chrome_elf/ntdll_cache.h"
13 #include "sandbox/win/src/interception_internal.h"
14 #include "sandbox/win/src/nt_internals.h"
18 // From ShlObj.h in the Windows SDK.
19 #define CSIDL_LOCAL_APPDATA 0x001c
21 typedef BOOL (WINAPI
*PathIsUNCFunction
)(
24 typedef BOOL (WINAPI
*PathAppendFunction
)(
28 typedef BOOL (WINAPI
*PathIsPrefixFunction
)(
32 typedef LPCWSTR (WINAPI
*PathFindFileName
)(
35 typedef HRESULT (WINAPI
*SHGetFolderPathFunction
)(
42 PathIsUNCFunction g_path_is_unc_func
;
43 PathAppendFunction g_path_append_func
;
44 PathIsPrefixFunction g_path_is_prefix_func
;
45 PathFindFileName g_path_find_filename_func
;
46 SHGetFolderPathFunction g_get_folder_func
;
48 // Record the number of calls we've redirected so far.
49 int g_redirect_count
= 0;
51 // Populates the g_*_func pointers to functions which will be used in
52 // ShouldBypass(). Chrome_elf cannot have a load-time dependency on shell32 or
53 // shlwapi as this would induce a load-time dependency on user32.dll. Instead,
54 // the addresses of the functions we need are retrieved the first time this
55 // method is called, and cached to avoid subsequent calls to GetProcAddress().
56 // It is assumed that the host process will never unload these functions.
57 // Returns true if all the functions needed are present.
58 bool PopulateShellFunctions() {
59 // Early exit if functions have already been populated.
60 if (g_path_is_unc_func
&& g_path_append_func
&&
61 g_path_is_prefix_func
&& g_get_folder_func
) {
65 // Get the addresses of the functions we need and store them for future use.
66 // These handles are intentionally leaked to ensure that these modules do not
68 HMODULE shell32
= ::LoadLibrary(L
"shell32.dll");
69 HMODULE shlwapi
= ::LoadLibrary(L
"shlwapi.dll");
71 if (!shlwapi
|| !shell32
)
74 g_path_is_unc_func
= reinterpret_cast<PathIsUNCFunction
>(
75 ::GetProcAddress(shlwapi
, "PathIsUNCW"));
76 g_path_append_func
= reinterpret_cast<PathAppendFunction
>(
77 ::GetProcAddress(shlwapi
, "PathAppendW"));
78 g_path_is_prefix_func
= reinterpret_cast<PathIsPrefixFunction
>(
79 ::GetProcAddress(shlwapi
, "PathIsPrefixW"));
80 g_path_find_filename_func
= reinterpret_cast<PathFindFileName
>(
81 ::GetProcAddress(shlwapi
, "PathFindFileNameW"));
82 g_get_folder_func
= reinterpret_cast<SHGetFolderPathFunction
>(
83 ::GetProcAddress(shell32
, "SHGetFolderPathW"));
85 return g_path_is_unc_func
&& g_path_append_func
&& g_path_is_prefix_func
&&
86 g_path_find_filename_func
&& g_get_folder_func
;
91 // Turn off optimization to make sure these calls don't get inlined.
92 #pragma optimize("", off)
93 // Wrapper method for kernel32!CreateFile, to avoid setting off caller
94 // mitigation detectors.
95 HANDLE
CreateFileWImpl(LPCWSTR file_name
,
98 LPSECURITY_ATTRIBUTES security_attributes
,
99 DWORD creation_disposition
,
100 DWORD flags_and_attributes
,
101 HANDLE template_file
) {
102 return CreateFile(file_name
,
106 creation_disposition
,
107 flags_and_attributes
,
112 HANDLE WINAPI
CreateFileWRedirect(
114 DWORD desired_access
,
116 LPSECURITY_ATTRIBUTES security_attributes
,
117 DWORD creation_disposition
,
118 DWORD flags_and_attributes
,
119 HANDLE template_file
) {
120 if (ShouldBypass(file_name
)) {
122 return CreateFileNTDLL(file_name
,
126 creation_disposition
,
127 flags_and_attributes
,
130 return CreateFileWImpl(file_name
,
134 creation_disposition
,
135 flags_and_attributes
,
138 #pragma optimize("", on)
140 int GetRedirectCount() {
141 return g_redirect_count
;
144 HANDLE
CreateFileNTDLL(
146 DWORD desired_access
,
148 LPSECURITY_ATTRIBUTES security_attributes
,
149 DWORD creation_disposition
,
150 DWORD flags_and_attributes
,
151 HANDLE template_file
) {
152 HANDLE file_handle
= INVALID_HANDLE_VALUE
;
153 NTSTATUS result
= STATUS_UNSUCCESSFUL
;
154 IO_STATUS_BLOCK io_status_block
= {};
157 // Convert from Win32 domain to to NT creation disposition values.
158 switch (creation_disposition
) {
160 creation_disposition
= FILE_CREATE
;
163 creation_disposition
= FILE_OVERWRITE_IF
;
166 creation_disposition
= FILE_OPEN
;
169 creation_disposition
= FILE_OPEN_IF
;
171 case TRUNCATE_EXISTING
:
172 creation_disposition
= FILE_OVERWRITE
;
175 SetLastError(ERROR_INVALID_PARAMETER
);
176 return INVALID_HANDLE_VALUE
;
179 // Translate the flags that need no validation:
180 if (!(flags_and_attributes
& FILE_FLAG_OVERLAPPED
))
181 flags
|= FILE_SYNCHRONOUS_IO_NONALERT
;
183 if (flags_and_attributes
& FILE_FLAG_WRITE_THROUGH
)
184 flags
|= FILE_WRITE_THROUGH
;
186 if (flags_and_attributes
& FILE_FLAG_RANDOM_ACCESS
)
187 flags
|= FILE_RANDOM_ACCESS
;
189 if (flags_and_attributes
& FILE_FLAG_SEQUENTIAL_SCAN
)
190 flags
|= FILE_SEQUENTIAL_ONLY
;
192 if (flags_and_attributes
& FILE_FLAG_DELETE_ON_CLOSE
) {
193 flags
|= FILE_DELETE_ON_CLOSE
;
194 desired_access
|= DELETE
;
197 if (flags_and_attributes
& FILE_FLAG_BACKUP_SEMANTICS
)
198 flags
|= FILE_OPEN_FOR_BACKUP_INTENT
;
200 flags
|= FILE_NON_DIRECTORY_FILE
;
203 if (flags_and_attributes
& FILE_FLAG_OPEN_REPARSE_POINT
)
204 flags
|= FILE_OPEN_REPARSE_POINT
;
206 if (flags_and_attributes
& FILE_FLAG_OPEN_NO_RECALL
)
207 flags
|= FILE_OPEN_NO_RECALL
;
209 if (!g_ntdll_lookup
["RtlInitUnicodeString"])
210 return INVALID_HANDLE_VALUE
;
212 NtCreateFileFunction create_file
;
213 char thunk_buffer
[sizeof(sandbox::ThunkData
)] = {};
215 if (g_nt_thunk_storage
.data
[0] != 0) {
216 create_file
= reinterpret_cast<NtCreateFileFunction
>(&g_nt_thunk_storage
);
217 // Copy the thunk data to a buffer on the stack for debugging purposes.
218 memcpy(&thunk_buffer
, &g_nt_thunk_storage
, sizeof(sandbox::ThunkData
));
219 } else if (g_ntdll_lookup
["NtCreateFile"]) {
221 reinterpret_cast<NtCreateFileFunction
>(g_ntdll_lookup
["NtCreateFile"]);
223 return INVALID_HANDLE_VALUE
;
226 RtlInitUnicodeStringFunction init_unicode_string
=
227 reinterpret_cast<RtlInitUnicodeStringFunction
>(
228 g_ntdll_lookup
["RtlInitUnicodeString"]);
230 UNICODE_STRING path_unicode_string
;
232 // Format the path into an NT path. Arguably this should be done with
233 // RtlDosPathNameToNtPathName_U, but afaict this is equivalent for
234 // local paths. Using this with a UNC path name will almost certainly
235 // break in interesting ways.
236 base::string16
filename_string(L
"\\??\\");
237 filename_string
+= file_name
;
239 init_unicode_string(&path_unicode_string
, filename_string
.c_str());
241 OBJECT_ATTRIBUTES path_attributes
= {};
242 InitializeObjectAttributes(&path_attributes
,
243 &path_unicode_string
,
244 OBJ_CASE_INSENSITIVE
,
245 NULL
, // No Root Directory
246 NULL
); // No Security Descriptor
248 // Set desired_access, and flags_and_attributes to match those
249 // set by kernel32!CreateFile.
250 desired_access
|= 0x100080;
251 flags_and_attributes
&= 0x2FFA7;
253 result
= create_file(&file_handle
,
257 0, // Allocation size
258 flags_and_attributes
,
260 creation_disposition
,
265 if (result
!= STATUS_SUCCESS
) {
266 if (result
== STATUS_OBJECT_NAME_COLLISION
&&
267 creation_disposition
== FILE_CREATE
) {
268 SetLastError(ERROR_FILE_EXISTS
);
270 return INVALID_HANDLE_VALUE
;
273 if (creation_disposition
== FILE_OPEN_IF
) {
274 SetLastError(io_status_block
.Information
== FILE_OPENED
?
275 ERROR_ALREADY_EXISTS
: ERROR_SUCCESS
);
276 } else if (creation_disposition
== FILE_OVERWRITE_IF
) {
277 SetLastError(io_status_block
.Information
== FILE_OVERWRITTEN
?
278 ERROR_ALREADY_EXISTS
: ERROR_SUCCESS
);
280 SetLastError(ERROR_SUCCESS
);
286 bool ShouldBypass(LPCWSTR file_path
) {
287 // Do not redirect in non-browser processes.
288 if (IsNonBrowserProcess())
291 // If the shell functions are not present, forward the call to kernel32.
292 if (!PopulateShellFunctions())
295 // Forward all UNC filepaths to kernel32.
296 if (g_path_is_unc_func(file_path
))
299 wchar_t local_appdata_path
[MAX_PATH
];
301 // Get the %LOCALAPPDATA% Path and append the location of our UserData
303 HRESULT appdata_result
= g_get_folder_func(
304 NULL
, CSIDL_LOCAL_APPDATA
, NULL
, 0, local_appdata_path
);
306 wchar_t buffer
[MAX_PATH
] = {};
307 if (!GetModuleFileNameW(NULL
, buffer
, MAX_PATH
))
310 bool is_canary
= IsCanary(buffer
);
312 // If getting the %LOCALAPPDATA% path or appending to it failed, then forward
313 // the call to kernel32.
314 if (!SUCCEEDED(appdata_result
) ||
315 !g_path_append_func(local_appdata_path
, is_canary
?
316 kCanaryAppDataDirName
: kAppDataDirName
) ||
317 !g_path_append_func(local_appdata_path
, kUserDataDirName
)) {
321 LPCWSTR file_name
= g_path_find_filename_func(file_path
);
323 bool in_userdata_dir
= !!g_path_is_prefix_func(local_appdata_path
, file_path
);
324 bool is_settings_file
= wcscmp(file_name
, kPreferencesFilename
) == 0 ||
325 wcscmp(file_name
, kLocalStateFilename
) == 0;
327 // Check if we are trying to access the Preferences in the UserData dir. If
328 // so, then redirect the call to bypass kernel32.
329 return in_userdata_dir
&& is_settings_file
;