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/ntdll_cache.h"
12 #include "sandbox/win/src/nt_internals.h"
16 // From ShlObj.h in the Windows SDK.
17 #define CSIDL_LOCAL_APPDATA 0x001c
19 typedef BOOL (WINAPI
*PathIsUNCFunction
)(
22 typedef BOOL (WINAPI
*PathAppendFunction
)(
26 typedef BOOL (WINAPI
*PathIsPrefixFunction
)(
30 typedef LPCWSTR (WINAPI
*PathFindFileName
)(
33 typedef HRESULT (WINAPI
*SHGetFolderPathFunction
)(
40 PathIsUNCFunction g_path_is_unc_func
;
41 PathAppendFunction g_path_append_func
;
42 PathIsPrefixFunction g_path_is_prefix_func
;
43 PathFindFileName g_path_find_filename_func
;
44 SHGetFolderPathFunction g_get_folder_func
;
46 // Populates the g_*_func pointers to functions which will be used in
47 // ShouldBypass(). Chrome_elf cannot have a load-time dependency on shell32 or
48 // shlwapi as this would induce a load-time dependency on user32.dll. Instead,
49 // the addresses of the functions we need are retrieved the first time this
50 // method is called, and cached to avoid subsequent calls to GetProcAddress().
51 // It is assumed that the host process will never unload these functions.
52 // Returns true if all the functions needed are present.
53 bool PopulateShellFunctions() {
54 // Early exit if functions have already been populated.
55 if (g_path_is_unc_func
&& g_path_append_func
&&
56 g_path_is_prefix_func
&& g_get_folder_func
) {
60 // Get the addresses of the functions we need and store them for future use.
61 // These handles are intentionally leaked to ensure that these modules do not
63 HMODULE shell32
= ::LoadLibrary(L
"shell32.dll");
64 HMODULE shlwapi
= ::LoadLibrary(L
"shlwapi.dll");
66 if (!shlwapi
|| !shell32
)
69 g_path_is_unc_func
= reinterpret_cast<PathIsUNCFunction
>(
70 ::GetProcAddress(shlwapi
, "PathIsUNCW"));
71 g_path_append_func
= reinterpret_cast<PathAppendFunction
>(
72 ::GetProcAddress(shlwapi
, "PathAppendW"));
73 g_path_is_prefix_func
= reinterpret_cast<PathIsPrefixFunction
>(
74 ::GetProcAddress(shlwapi
, "PathIsPrefixW"));
75 g_path_find_filename_func
= reinterpret_cast<PathFindFileName
>(
76 ::GetProcAddress(shlwapi
, "PathFindFileNameW"));
77 g_get_folder_func
= reinterpret_cast<SHGetFolderPathFunction
>(
78 ::GetProcAddress(shell32
, "SHGetFolderPathW"));
80 return g_path_is_unc_func
&& g_path_append_func
&& g_path_is_prefix_func
&&
81 g_path_find_filename_func
&& g_get_folder_func
;
86 HANDLE WINAPI
CreateFileWRedirect(
90 LPSECURITY_ATTRIBUTES security_attributes
,
91 DWORD creation_disposition
,
92 DWORD flags_and_attributes
,
93 HANDLE template_file
) {
94 if (ShouldBypass(file_name
)) {
95 return CreateFileNTDLL(file_name
,
100 flags_and_attributes
,
103 return CreateFile(file_name
,
107 creation_disposition
,
108 flags_and_attributes
,
113 HANDLE
CreateFileNTDLL(
115 DWORD desired_access
,
117 LPSECURITY_ATTRIBUTES security_attributes
,
118 DWORD creation_disposition
,
119 DWORD flags_and_attributes
,
120 HANDLE template_file
) {
121 HANDLE file_handle
= INVALID_HANDLE_VALUE
;
122 NTSTATUS result
= STATUS_UNSUCCESSFUL
;
123 IO_STATUS_BLOCK io_status_block
= {};
126 // Convert from Win32 domain to to NT creation disposition values.
127 switch (creation_disposition
) {
129 creation_disposition
= FILE_CREATE
;
132 creation_disposition
= FILE_OVERWRITE_IF
;
135 creation_disposition
= FILE_OPEN
;
138 creation_disposition
= FILE_OPEN_IF
;
140 case TRUNCATE_EXISTING
:
141 creation_disposition
= FILE_OVERWRITE
;
144 SetLastError(ERROR_INVALID_PARAMETER
);
145 return INVALID_HANDLE_VALUE
;
148 // Translate the flags that need no validation:
149 if (!(flags_and_attributes
& FILE_FLAG_OVERLAPPED
))
150 flags
|= FILE_SYNCHRONOUS_IO_NONALERT
;
152 if (flags_and_attributes
& FILE_FLAG_WRITE_THROUGH
)
153 flags
|= FILE_WRITE_THROUGH
;
155 if (flags_and_attributes
& FILE_FLAG_RANDOM_ACCESS
)
156 flags
|= FILE_RANDOM_ACCESS
;
158 if (flags_and_attributes
& FILE_FLAG_SEQUENTIAL_SCAN
)
159 flags
|= FILE_SEQUENTIAL_ONLY
;
161 if (flags_and_attributes
& FILE_FLAG_DELETE_ON_CLOSE
) {
162 flags
|= FILE_DELETE_ON_CLOSE
;
163 desired_access
|= DELETE
;
166 if (flags_and_attributes
& FILE_FLAG_BACKUP_SEMANTICS
)
167 flags
|= FILE_OPEN_FOR_BACKUP_INTENT
;
169 flags
|= FILE_NON_DIRECTORY_FILE
;
172 if (flags_and_attributes
& FILE_FLAG_OPEN_REPARSE_POINT
)
173 flags
|= FILE_OPEN_REPARSE_POINT
;
175 if (flags_and_attributes
& FILE_FLAG_OPEN_NO_RECALL
)
176 flags
|= FILE_OPEN_NO_RECALL
;
178 if (!g_ntdll_lookup
["NtCreateFile"] ||
179 !g_ntdll_lookup
["RtlInitUnicodeString"]) {
180 return INVALID_HANDLE_VALUE
;
183 NtCreateFileFunction create_file
=
184 reinterpret_cast<NtCreateFileFunction
>(g_ntdll_lookup
["NtCreateFile"]);
186 RtlInitUnicodeStringFunction init_unicode_string
=
187 reinterpret_cast<RtlInitUnicodeStringFunction
>(
188 g_ntdll_lookup
["RtlInitUnicodeString"]);
190 UNICODE_STRING path_unicode_string
;
192 // Format the path into an NT path. Arguably this should be done with
193 // RtlDosPathNameToNtPathName_U, but afaict this is equivalent for
194 // local paths. Using this with a UNC path name will almost certainly
195 // break in interesting ways.
196 base::string16
filename_string(L
"\\??\\");
197 filename_string
+= file_name
;
199 init_unicode_string(&path_unicode_string
, filename_string
.c_str());
201 OBJECT_ATTRIBUTES path_attributes
= {};
202 InitializeObjectAttributes(&path_attributes
,
203 &path_unicode_string
,
204 OBJ_CASE_INSENSITIVE
,
205 NULL
, // No Root Directory
206 NULL
); // No Security Descriptor
208 // Set desired_access, and flags_and_attributes to match those
209 // set by kernel32!CreateFile.
210 desired_access
|= 0x100080;
211 flags_and_attributes
&= 0x2FFA7;
213 result
= create_file(&file_handle
,
217 0, // Allocation size
218 flags_and_attributes
,
220 creation_disposition
,
225 if (result
!= STATUS_SUCCESS
) {
226 if (result
== STATUS_OBJECT_NAME_COLLISION
&&
227 creation_disposition
== FILE_CREATE
) {
228 SetLastError(ERROR_FILE_EXISTS
);
230 return INVALID_HANDLE_VALUE
;
233 if (creation_disposition
== FILE_OPEN_IF
) {
234 SetLastError(io_status_block
.Information
== FILE_OPENED
?
235 ERROR_ALREADY_EXISTS
: ERROR_SUCCESS
);
236 } else if (creation_disposition
== FILE_OVERWRITE_IF
) {
237 SetLastError(io_status_block
.Information
== FILE_OVERWRITTEN
?
238 ERROR_ALREADY_EXISTS
: ERROR_SUCCESS
);
240 SetLastError(ERROR_SUCCESS
);
246 bool ShouldBypass(LPCWSTR file_path
) {
247 // If the shell functions are not present, forward the call to kernel32.
248 if (!PopulateShellFunctions())
251 // Forward all UNC filepaths to kernel32.
252 if (g_path_is_unc_func(file_path
))
255 wchar_t local_appdata_path
[MAX_PATH
];
257 // Get the %LOCALAPPDATA% Path and append the location of our UserData
259 HRESULT appdata_result
= g_get_folder_func(
260 NULL
, CSIDL_LOCAL_APPDATA
, NULL
, 0, local_appdata_path
);
262 // If getting the %LOCALAPPDATA% path or appending to it failed, then forward
263 // the call to kernel32.
264 if (!SUCCEEDED(appdata_result
) ||
265 !g_path_append_func(local_appdata_path
, kAppDataDirName
) ||
266 !g_path_append_func(local_appdata_path
, kUserDataDirName
)) {
270 LPCWSTR file_name
= g_path_find_filename_func(file_path
);
272 bool in_userdata_dir
= !!g_path_is_prefix_func(local_appdata_path
, file_path
);
273 bool is_settings_file
= wcscmp(file_name
, kPreferencesFilename
) == 0 ||
274 wcscmp(file_name
, kLocalStateFilename
) == 0;
276 // Check if we are trying to access the Preferences in the UserData dir. If
277 // so, then redirect the call to bypass kernel32.
278 return in_userdata_dir
&& is_settings_file
;