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.
4
5 #include "chrome_elf/create_file/chrome_create_file.h"
6
7 #include <string>
8
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"
15
16 namespace {
17
18 // From ShlObj.h in the Windows SDK.
19 #define CSIDL_LOCAL_APPDATA 0x001c
20
21 typedef BOOL (WINAPI *PathIsUNCFunction)(
22 IN LPCWSTR path);
23
24 typedef BOOL (WINAPI *PathAppendFunction)(
25 IN LPWSTR path,
26 IN LPCWSTR more);
27
28 typedef BOOL (WINAPI *PathIsPrefixFunction)(
29 IN LPCWSTR prefix,
30 IN LPCWSTR path);
31
32 typedef LPCWSTR (WINAPI *PathFindFileName)(
33 IN LPCWSTR path);
34
35 typedef HRESULT (WINAPI *SHGetFolderPathFunction)(
36 IN HWND hwnd_owner,
37 IN int folder,
38 IN HANDLE token,
39 IN DWORD flags,
40 OUT LPWSTR path);
41
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;
47
48 // Record the number of calls we've redirected so far.
49 int g_redirect_count = 0;
50
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.
PopulateShellFunctions()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) {
62 return true;
63 }
64
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
67 // get unloaded.
68 HMODULE shell32 = ::LoadLibrary(L"shell32.dll");
69 HMODULE shlwapi = ::LoadLibrary(L"shlwapi.dll");
70
71 if (!shlwapi || !shell32)
72 return false;
73
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"));
84
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;
87 }
88
89 } // namespace
90
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.
CreateFileWImpl(LPCWSTR file_name,DWORD desired_access,DWORD share_mode,LPSECURITY_ATTRIBUTES security_attributes,DWORD creation_disposition,DWORD flags_and_attributes,HANDLE template_file)95 HANDLE CreateFileWImpl(LPCWSTR file_name,
96 DWORD desired_access,
97 DWORD share_mode,
98 LPSECURITY_ATTRIBUTES security_attributes,
99 DWORD creation_disposition,
100 DWORD flags_and_attributes,
101 HANDLE template_file) {
102 return CreateFile(file_name,
103 desired_access,
104 share_mode,
105 security_attributes,
106 creation_disposition,
107 flags_and_attributes,
108 template_file);
109
110 }
111
CreateFileWRedirect(LPCWSTR file_name,DWORD desired_access,DWORD share_mode,LPSECURITY_ATTRIBUTES security_attributes,DWORD creation_disposition,DWORD flags_and_attributes,HANDLE template_file)112 HANDLE WINAPI CreateFileWRedirect(
113 LPCWSTR file_name,
114 DWORD desired_access,
115 DWORD share_mode,
116 LPSECURITY_ATTRIBUTES security_attributes,
117 DWORD creation_disposition,
118 DWORD flags_and_attributes,
119 HANDLE template_file) {
120 if (ShouldBypass(file_name)) {
121 ++g_redirect_count;
122 return CreateFileNTDLL(file_name,
123 desired_access,
124 share_mode,
125 security_attributes,
126 creation_disposition,
127 flags_and_attributes,
128 template_file);
129 }
130 return CreateFileWImpl(file_name,
131 desired_access,
132 share_mode,
133 security_attributes,
134 creation_disposition,
135 flags_and_attributes,
136 template_file);
137 }
138 #pragma optimize("", on)
139
GetRedirectCount()140 int GetRedirectCount() {
141 return g_redirect_count;
142 }
143
CreateFileNTDLL(LPCWSTR file_name,DWORD desired_access,DWORD share_mode,LPSECURITY_ATTRIBUTES security_attributes,DWORD creation_disposition,DWORD flags_and_attributes,HANDLE template_file)144 HANDLE CreateFileNTDLL(
145 LPCWSTR file_name,
146 DWORD desired_access,
147 DWORD share_mode,
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 = {};
155 ULONG flags = 0;
156
157 // Convert from Win32 domain to to NT creation disposition values.
158 switch (creation_disposition) {
159 case CREATE_NEW:
160 creation_disposition = FILE_CREATE;
161 break;
162 case CREATE_ALWAYS:
163 creation_disposition = FILE_OVERWRITE_IF;
164 break;
165 case OPEN_EXISTING:
166 creation_disposition = FILE_OPEN;
167 break;
168 case OPEN_ALWAYS:
169 creation_disposition = FILE_OPEN_IF;
170 break;
171 case TRUNCATE_EXISTING:
172 creation_disposition = FILE_OVERWRITE;
173 break;
174 default:
175 SetLastError(ERROR_INVALID_PARAMETER);
176 return INVALID_HANDLE_VALUE;
177 }
178
179 // Translate the flags that need no validation:
180 if (!(flags_and_attributes & FILE_FLAG_OVERLAPPED))
181 flags |= FILE_SYNCHRONOUS_IO_NONALERT;
182
183 if (flags_and_attributes & FILE_FLAG_WRITE_THROUGH)
184 flags |= FILE_WRITE_THROUGH;
185
186 if (flags_and_attributes & FILE_FLAG_RANDOM_ACCESS)
187 flags |= FILE_RANDOM_ACCESS;
188
189 if (flags_and_attributes & FILE_FLAG_SEQUENTIAL_SCAN)
190 flags |= FILE_SEQUENTIAL_ONLY;
191
192 if (flags_and_attributes & FILE_FLAG_DELETE_ON_CLOSE) {
193 flags |= FILE_DELETE_ON_CLOSE;
194 desired_access |= DELETE;
195 }
196
197 if (flags_and_attributes & FILE_FLAG_BACKUP_SEMANTICS)
198 flags |= FILE_OPEN_FOR_BACKUP_INTENT;
199 else
200 flags |= FILE_NON_DIRECTORY_FILE;
201
202
203 if (flags_and_attributes & FILE_FLAG_OPEN_REPARSE_POINT)
204 flags |= FILE_OPEN_REPARSE_POINT;
205
206 if (flags_and_attributes & FILE_FLAG_OPEN_NO_RECALL)
207 flags |= FILE_OPEN_NO_RECALL;
208
209 if (!g_ntdll_lookup["RtlInitUnicodeString"])
210 return INVALID_HANDLE_VALUE;
211
212 NtCreateFileFunction create_file;
213 char thunk_buffer[sizeof(sandbox::ThunkData)] = {};
214
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"]) {
220 create_file =
221 reinterpret_cast<NtCreateFileFunction>(g_ntdll_lookup["NtCreateFile"]);
222 } else {
223 return INVALID_HANDLE_VALUE;
224 }
225
226 RtlInitUnicodeStringFunction init_unicode_string =
227 reinterpret_cast<RtlInitUnicodeStringFunction>(
228 g_ntdll_lookup["RtlInitUnicodeString"]);
229
230 UNICODE_STRING path_unicode_string;
231
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;
238
239 init_unicode_string(&path_unicode_string, filename_string.c_str());
240
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
247
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;
252
253 result = create_file(&file_handle,
254 desired_access,
255 &path_attributes,
256 &io_status_block,
257 0, // Allocation size
258 flags_and_attributes,
259 share_mode,
260 creation_disposition,
261 flags,
262 NULL,
263 0);
264
265 if (result != STATUS_SUCCESS) {
266 if (result == STATUS_OBJECT_NAME_COLLISION &&
267 creation_disposition == FILE_CREATE) {
268 SetLastError(ERROR_FILE_EXISTS);
269 }
270 return INVALID_HANDLE_VALUE;
271 }
272
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);
279 } else {
280 SetLastError(ERROR_SUCCESS);
281 }
282
283 return file_handle;
284 }
285
ShouldBypass(LPCWSTR file_path)286 bool ShouldBypass(LPCWSTR file_path) {
287 // Do not redirect in non-browser processes.
288 if (IsNonBrowserProcess())
289 return false;
290
291 // If the shell functions are not present, forward the call to kernel32.
292 if (!PopulateShellFunctions())
293 return false;
294
295 // Forward all UNC filepaths to kernel32.
296 if (g_path_is_unc_func(file_path))
297 return false;
298
299 wchar_t local_appdata_path[MAX_PATH];
300
301 // Get the %LOCALAPPDATA% Path and append the location of our UserData
302 // directory to it.
303 HRESULT appdata_result = g_get_folder_func(
304 NULL, CSIDL_LOCAL_APPDATA, NULL, 0, local_appdata_path);
305
306 wchar_t buffer[MAX_PATH] = {};
307 if (!GetModuleFileNameW(NULL, buffer, MAX_PATH))
308 return false;
309
310 bool is_canary = IsCanary(buffer);
311
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)) {
318 return false;
319 }
320
321 LPCWSTR file_name = g_path_find_filename_func(file_path);
322
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;
326
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;
330 }
331