Index: chrome_elf/create_file/chrome_create_file.cc |
diff --git a/chrome_elf/create_file/chrome_create_file.cc b/chrome_elf/create_file/chrome_create_file.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a39cd3e128123d74f030940415ed2261ef66142f |
--- /dev/null |
+++ b/chrome_elf/create_file/chrome_create_file.cc |
@@ -0,0 +1,279 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome_elf/create_file/chrome_create_file.h" |
+ |
+#include <string> |
+ |
+#include "base/strings/string16.h" |
+#include "chrome_elf/chrome_elf_constants.h" |
+#include "chrome_elf/ntdll_cache.h" |
+#include "sandbox/win/src/nt_internals.h" |
+ |
+namespace { |
+ |
+// From ShlObj.h in the Windows SDK. |
+#define CSIDL_LOCAL_APPDATA 0x001c |
+ |
+typedef BOOL (WINAPI *PathIsUNCFunction)( |
+ IN LPCWSTR path); |
+ |
+typedef BOOL (WINAPI *PathAppendFunction)( |
+ IN LPWSTR path, |
+ IN LPCWSTR more); |
+ |
+typedef BOOL (WINAPI *PathIsPrefixFunction)( |
+ IN LPCWSTR prefix, |
+ IN LPCWSTR path); |
+ |
+typedef LPCWSTR (WINAPI *PathFindFileName)( |
+ IN LPCWSTR path); |
+ |
+typedef HRESULT (WINAPI *SHGetFolderPathFunction)( |
+ IN HWND hwnd_owner, |
+ IN int folder, |
+ IN HANDLE token, |
+ IN DWORD flags, |
+ OUT LPWSTR path); |
+ |
+PathIsUNCFunction g_path_is_unc_func; |
+PathAppendFunction g_path_append_func; |
+PathIsPrefixFunction g_path_is_prefix_func; |
+PathFindFileName g_path_find_filename_func; |
+SHGetFolderPathFunction g_get_folder_func; |
+ |
+// Populates the g_*_func pointers to functions which will be used in |
+// ShouldBypass(). Chrome_elf cannot have a load-time dependency on shell32 or |
+// shlwapi as this would induce a load-time dependency on user32.dll. Instead, |
+// the addresses of the functions we need are retrieved the first time this |
+// method is called, and cached to avoid subsequent calls to GetProcAddress(). |
+// It is assumed that the host process will never unload these functions. |
+// Returns true if all the functions needed are present. |
+bool PopulateShellFunctions() { |
+ // Early exit if functions have already been populated. |
+ if (g_path_is_unc_func && g_path_append_func && |
+ g_path_is_prefix_func && g_get_folder_func) { |
+ return true; |
+ } |
+ |
+ // Get the addresses of the functions we need and store them for future use. |
+ // These handles are intentionally leaked to ensure that these modules do not |
+ // get unloaded. |
+ HMODULE shell32 = ::LoadLibrary(L"shell32.dll"); |
+ HMODULE shlwapi = ::LoadLibrary(L"shlwapi.dll"); |
+ |
+ if (!shlwapi || !shell32) |
+ return false; |
+ |
+ g_path_is_unc_func = reinterpret_cast<PathIsUNCFunction>( |
+ ::GetProcAddress(shlwapi, "PathIsUNCW")); |
+ g_path_append_func = reinterpret_cast<PathAppendFunction>( |
+ ::GetProcAddress(shlwapi, "PathAppendW")); |
+ g_path_is_prefix_func = reinterpret_cast<PathIsPrefixFunction>( |
+ ::GetProcAddress(shlwapi, "PathIsPrefixW")); |
+ g_path_find_filename_func = reinterpret_cast<PathFindFileName>( |
+ ::GetProcAddress(shlwapi, "PathFindFileNameW")); |
+ g_get_folder_func = reinterpret_cast<SHGetFolderPathFunction>( |
+ ::GetProcAddress(shell32, "SHGetFolderPathW")); |
+ |
+ return g_path_is_unc_func && g_path_append_func && g_path_is_prefix_func && |
+ g_path_find_filename_func && g_get_folder_func; |
+} |
+ |
+} // namespace |
+ |
+HANDLE WINAPI CreateFileWRedirect( |
+ LPCWSTR file_name, |
+ DWORD desired_access, |
+ DWORD share_mode, |
+ LPSECURITY_ATTRIBUTES security_attributes, |
+ DWORD creation_disposition, |
+ DWORD flags_and_attributes, |
+ HANDLE template_file) { |
+ if (ShouldBypass(file_name)) { |
+ return CreateFileNTDLL(file_name, |
+ desired_access, |
+ share_mode, |
+ security_attributes, |
+ creation_disposition, |
+ flags_and_attributes, |
+ template_file); |
+ } |
+ return CreateFile(file_name, |
+ desired_access, |
+ share_mode, |
+ security_attributes, |
+ creation_disposition, |
+ flags_and_attributes, |
+ template_file); |
+ |
+} |
+ |
+HANDLE CreateFileNTDLL( |
+ LPCWSTR file_name, |
+ DWORD desired_access, |
+ DWORD share_mode, |
+ LPSECURITY_ATTRIBUTES security_attributes, |
+ DWORD creation_disposition, |
+ DWORD flags_and_attributes, |
+ HANDLE template_file) { |
+ HANDLE file_handle = INVALID_HANDLE_VALUE; |
+ NTSTATUS result = STATUS_UNSUCCESSFUL; |
+ IO_STATUS_BLOCK io_status_block = {}; |
+ ULONG flags = 0; |
+ |
+ // Convert from Win32 domain to to NT creation disposition values. |
+ switch (creation_disposition) { |
+ case CREATE_NEW: |
+ creation_disposition = FILE_CREATE; |
+ break; |
+ case CREATE_ALWAYS: |
+ creation_disposition = FILE_OVERWRITE_IF; |
+ break; |
+ case OPEN_EXISTING: |
+ creation_disposition = FILE_OPEN; |
+ break; |
+ case OPEN_ALWAYS: |
+ creation_disposition = FILE_OPEN_IF; |
+ break; |
+ case TRUNCATE_EXISTING: |
+ creation_disposition = FILE_OVERWRITE; |
+ break; |
+ default: |
+ SetLastError(ERROR_INVALID_PARAMETER); |
+ return INVALID_HANDLE_VALUE; |
+ } |
+ |
+ // Translate the flags that need no validation: |
+ if (!(flags_and_attributes & FILE_FLAG_OVERLAPPED)) |
+ flags |= FILE_SYNCHRONOUS_IO_NONALERT; |
+ |
+ if (flags_and_attributes & FILE_FLAG_WRITE_THROUGH) |
+ flags |= FILE_WRITE_THROUGH; |
+ |
+ if (flags_and_attributes & FILE_FLAG_RANDOM_ACCESS) |
+ flags |= FILE_RANDOM_ACCESS; |
+ |
+ if (flags_and_attributes & FILE_FLAG_SEQUENTIAL_SCAN) |
+ flags |= FILE_SEQUENTIAL_ONLY; |
+ |
+ if (flags_and_attributes & FILE_FLAG_DELETE_ON_CLOSE) { |
+ flags |= FILE_DELETE_ON_CLOSE; |
+ desired_access |= DELETE; |
+ } |
+ |
+ if (flags_and_attributes & FILE_FLAG_BACKUP_SEMANTICS) |
+ flags |= FILE_OPEN_FOR_BACKUP_INTENT; |
+ else |
+ flags |= FILE_NON_DIRECTORY_FILE; |
+ |
+ |
+ if (flags_and_attributes & FILE_FLAG_OPEN_REPARSE_POINT) |
+ flags |= FILE_OPEN_REPARSE_POINT; |
+ |
+ if (flags_and_attributes & FILE_FLAG_OPEN_NO_RECALL) |
+ flags |= FILE_OPEN_NO_RECALL; |
+ |
+ if (!g_ntdll_lookup["NtCreateFile"] || |
+ !g_ntdll_lookup["RtlInitUnicodeString"]) { |
+ return INVALID_HANDLE_VALUE; |
+ } |
+ |
+ NtCreateFileFunction create_file = |
+ reinterpret_cast<NtCreateFileFunction>(g_ntdll_lookup["NtCreateFile"]); |
+ |
+ RtlInitUnicodeStringFunction init_unicode_string = |
+ reinterpret_cast<RtlInitUnicodeStringFunction>( |
+ g_ntdll_lookup["RtlInitUnicodeString"]); |
+ |
+ UNICODE_STRING path_unicode_string; |
+ |
+ // Format the path into an NT path. Arguably this should be done with |
+ // RtlDosPathNameToNtPathName_U, but afaict this is equivalent for |
+ // local paths. Using this with a UNC path name will almost certainly |
+ // break in interesting ways. |
+ base::string16 filename_string(L"\\??\\"); |
+ filename_string += file_name; |
+ |
+ init_unicode_string(&path_unicode_string, filename_string.c_str()); |
+ |
+ OBJECT_ATTRIBUTES path_attributes = {}; |
+ InitializeObjectAttributes(&path_attributes, |
+ &path_unicode_string, |
+ OBJ_CASE_INSENSITIVE, |
+ NULL, // No Root Directory |
+ NULL); // No Security Descriptor |
+ |
+ // Set desired_access, and flags_and_attributes to match those |
+ // set by kernel32!CreateFile. |
+ desired_access |= 0x100080; |
+ flags_and_attributes &= 0x2FFA7; |
+ |
+ result = create_file(&file_handle, |
+ desired_access, |
+ &path_attributes, |
+ &io_status_block, |
+ 0, // Allocation size |
+ flags_and_attributes, |
+ share_mode, |
+ creation_disposition, |
+ flags, |
+ NULL, |
+ 0); |
+ |
+ if (result != STATUS_SUCCESS) { |
+ if (result == STATUS_OBJECT_NAME_COLLISION && |
+ creation_disposition == FILE_CREATE) { |
+ SetLastError(ERROR_FILE_EXISTS); |
+ } |
+ return INVALID_HANDLE_VALUE; |
+ } |
+ |
+ if (creation_disposition == FILE_OPEN_IF) { |
+ SetLastError(io_status_block.Information == FILE_OPENED ? |
+ ERROR_ALREADY_EXISTS : ERROR_SUCCESS); |
+ } else if (creation_disposition == FILE_OVERWRITE_IF) { |
+ SetLastError(io_status_block.Information == FILE_OVERWRITTEN ? |
+ ERROR_ALREADY_EXISTS : ERROR_SUCCESS); |
+ } else { |
+ SetLastError(ERROR_SUCCESS); |
+ } |
+ |
+ return file_handle; |
+} |
+ |
+bool ShouldBypass(LPCWSTR file_path) { |
+ // If the shell functions are not present, forward the call to kernel32. |
+ if (!PopulateShellFunctions()) |
+ return false; |
+ |
+ // Forward all UNC filepaths to kernel32. |
+ if (g_path_is_unc_func(file_path)) |
+ return false; |
+ |
+ wchar_t local_appdata_path[MAX_PATH]; |
+ |
+ // Get the %LOCALAPPDATA% Path and append the location of our UserData |
+ // directory to it. |
+ HRESULT appdata_result = g_get_folder_func( |
+ NULL, CSIDL_LOCAL_APPDATA, NULL, 0, local_appdata_path); |
+ |
+ // If getting the %LOCALAPPDATA% path or appending to it failed, then forward |
+ // the call to kernel32. |
+ if (!SUCCEEDED(appdata_result) || |
+ !g_path_append_func(local_appdata_path, kAppDataDirName) || |
+ !g_path_append_func(local_appdata_path, kUserDataDirName)) { |
+ return false; |
+ } |
+ |
+ LPCWSTR file_name = g_path_find_filename_func(file_path); |
+ |
+ bool in_userdata_dir = !!g_path_is_prefix_func(local_appdata_path, file_path); |
+ bool is_settings_file = wcscmp(file_name, kPreferencesFilename) == 0 || |
+ wcscmp(file_name, kLocalStateFilename) == 0; |
+ |
+ // Check if we are trying to access the Preferences in the UserData dir. If |
+ // so, then redirect the call to bypass kernel32. |
+ return in_userdata_dir && is_settings_file; |
+} |