• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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