Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome_elf / create_file / chrome_create_file.cc
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/ntdll_cache.h"
12 #include "sandbox/win/src/nt_internals.h"
13
14 namespace {
15
16 // From ShlObj.h in the Windows SDK.
17 #define CSIDL_LOCAL_APPDATA 0x001c
18
19 typedef BOOL (WINAPI *PathIsUNCFunction)(
20   IN LPCWSTR path);
21
22 typedef BOOL (WINAPI *PathAppendFunction)(
23   IN LPWSTR path,
24   IN LPCWSTR more);
25
26 typedef BOOL (WINAPI *PathIsPrefixFunction)(
27   IN LPCWSTR prefix,
28   IN LPCWSTR path);
29
30 typedef LPCWSTR (WINAPI *PathFindFileName)(
31   IN LPCWSTR path);
32
33 typedef HRESULT (WINAPI *SHGetFolderPathFunction)(
34   IN HWND hwnd_owner,
35   IN int folder,
36   IN HANDLE token,
37   IN DWORD flags,
38   OUT LPWSTR path);
39
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;
45
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) {
57     return true;
58   }
59
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
62   // get unloaded.
63   HMODULE shell32 = ::LoadLibrary(L"shell32.dll");
64   HMODULE shlwapi = ::LoadLibrary(L"shlwapi.dll");
65
66   if (!shlwapi || !shell32)
67     return false;
68
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"));
79
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;
82 }
83
84 }  // namespace
85
86 HANDLE WINAPI CreateFileWRedirect(
87     LPCWSTR file_name,
88     DWORD desired_access,
89     DWORD share_mode,
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,
96                            desired_access,
97                            share_mode,
98                            security_attributes,
99                            creation_disposition,
100                            flags_and_attributes,
101                            template_file);
102   }
103   return CreateFile(file_name,
104                     desired_access,
105                     share_mode,
106                     security_attributes,
107                     creation_disposition,
108                     flags_and_attributes,
109                     template_file);
110
111 }
112
113 HANDLE CreateFileNTDLL(
114     LPCWSTR file_name,
115     DWORD desired_access,
116     DWORD share_mode,
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 = {};
124   ULONG flags = 0;
125
126   // Convert from Win32 domain to to NT creation disposition values.
127   switch (creation_disposition) {
128     case CREATE_NEW:
129       creation_disposition = FILE_CREATE;
130       break;
131     case CREATE_ALWAYS:
132       creation_disposition = FILE_OVERWRITE_IF;
133       break;
134     case OPEN_EXISTING:
135       creation_disposition = FILE_OPEN;
136       break;
137     case OPEN_ALWAYS:
138       creation_disposition = FILE_OPEN_IF;
139       break;
140     case TRUNCATE_EXISTING:
141       creation_disposition = FILE_OVERWRITE;
142       break;
143     default:
144       SetLastError(ERROR_INVALID_PARAMETER);
145       return INVALID_HANDLE_VALUE;
146   }
147
148   // Translate the flags that need no validation:
149   if (!(flags_and_attributes & FILE_FLAG_OVERLAPPED))
150     flags |= FILE_SYNCHRONOUS_IO_NONALERT;
151
152   if (flags_and_attributes & FILE_FLAG_WRITE_THROUGH)
153     flags |= FILE_WRITE_THROUGH;
154
155   if (flags_and_attributes & FILE_FLAG_RANDOM_ACCESS)
156     flags |= FILE_RANDOM_ACCESS;
157
158   if (flags_and_attributes & FILE_FLAG_SEQUENTIAL_SCAN)
159     flags |= FILE_SEQUENTIAL_ONLY;
160
161   if (flags_and_attributes & FILE_FLAG_DELETE_ON_CLOSE) {
162     flags |= FILE_DELETE_ON_CLOSE;
163     desired_access |= DELETE;
164   }
165
166   if (flags_and_attributes & FILE_FLAG_BACKUP_SEMANTICS)
167     flags |= FILE_OPEN_FOR_BACKUP_INTENT;
168   else
169     flags |= FILE_NON_DIRECTORY_FILE;
170
171
172   if (flags_and_attributes & FILE_FLAG_OPEN_REPARSE_POINT)
173     flags |= FILE_OPEN_REPARSE_POINT;
174
175   if (flags_and_attributes & FILE_FLAG_OPEN_NO_RECALL)
176     flags |= FILE_OPEN_NO_RECALL;
177
178   if (!g_ntdll_lookup["NtCreateFile"] ||
179       !g_ntdll_lookup["RtlInitUnicodeString"]) {
180     return INVALID_HANDLE_VALUE;
181   }
182
183   NtCreateFileFunction create_file =
184       reinterpret_cast<NtCreateFileFunction>(g_ntdll_lookup["NtCreateFile"]);
185
186   RtlInitUnicodeStringFunction init_unicode_string =
187       reinterpret_cast<RtlInitUnicodeStringFunction>(
188           g_ntdll_lookup["RtlInitUnicodeString"]);
189
190   UNICODE_STRING path_unicode_string;
191
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;
198
199   init_unicode_string(&path_unicode_string, filename_string.c_str());
200
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
207
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;
212
213   result = create_file(&file_handle,
214                        desired_access,
215                        &path_attributes,
216                        &io_status_block,
217                        0,  // Allocation size
218                        flags_and_attributes,
219                        share_mode,
220                        creation_disposition,
221                        flags,
222                        NULL,
223                        0);
224
225   if (result != STATUS_SUCCESS) {
226     if (result == STATUS_OBJECT_NAME_COLLISION &&
227         creation_disposition == FILE_CREATE) {
228       SetLastError(ERROR_FILE_EXISTS);
229     }
230     return INVALID_HANDLE_VALUE;
231   }
232
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);
239   } else {
240     SetLastError(ERROR_SUCCESS);
241   }
242
243   return file_handle;
244 }
245
246 bool IsCanary(LPWSTR exe_path) {
247   wchar_t* found = wcsstr(exe_path, L"Google\\Chrome SxS");
248   return !!found;
249 }
250
251 bool ShouldBypass(LPCWSTR file_path) {
252   // Do not redirect in non-browser processes.
253   wchar_t* command_line = ::GetCommandLine();
254   if (command_line && wcsstr(command_line, L"--type"))
255     return false;
256
257   // If the shell functions are not present, forward the call to kernel32.
258   if (!PopulateShellFunctions())
259     return false;
260
261   // Forward all UNC filepaths to kernel32.
262   if (g_path_is_unc_func(file_path))
263     return false;
264
265   wchar_t local_appdata_path[MAX_PATH];
266
267   // Get the %LOCALAPPDATA% Path and append the location of our UserData
268   // directory to it.
269   HRESULT appdata_result = g_get_folder_func(
270       NULL, CSIDL_LOCAL_APPDATA, NULL, 0, local_appdata_path);
271
272   wchar_t buffer[MAX_PATH] = {};
273   if (!GetModuleFileNameW(NULL, buffer, MAX_PATH))
274     return false;
275
276   bool is_canary = IsCanary(buffer);
277
278   // If getting the %LOCALAPPDATA% path or appending to it failed, then forward
279   // the call to kernel32.
280   if (!SUCCEEDED(appdata_result) ||
281       !g_path_append_func(local_appdata_path, is_canary ?
282           kCanaryAppDataDirName : kAppDataDirName) ||
283       !g_path_append_func(local_appdata_path, kUserDataDirName)) {
284     return false;
285   }
286
287   LPCWSTR file_name = g_path_find_filename_func(file_path);
288
289   bool in_userdata_dir = !!g_path_is_prefix_func(local_appdata_path, file_path);
290   bool is_settings_file = wcscmp(file_name, kPreferencesFilename) == 0 ||
291       wcscmp(file_name, kLocalStateFilename) == 0;
292
293   // Check if we are trying to access the Preferences in the UserData dir. If
294   // so, then redirect the call to bypass kernel32.
295   return in_userdata_dir && is_settings_file;
296 }