chrome_create_file.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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/nt_internals.h" 14 15namespace { 16 17// From ShlObj.h in the Windows SDK. 18#define CSIDL_LOCAL_APPDATA 0x001c 19 20typedef BOOL (WINAPI *PathIsUNCFunction)( 21 IN LPCWSTR path); 22 23typedef BOOL (WINAPI *PathAppendFunction)( 24 IN LPWSTR path, 25 IN LPCWSTR more); 26 27typedef BOOL (WINAPI *PathIsPrefixFunction)( 28 IN LPCWSTR prefix, 29 IN LPCWSTR path); 30 31typedef LPCWSTR (WINAPI *PathFindFileName)( 32 IN LPCWSTR path); 33 34typedef HRESULT (WINAPI *SHGetFolderPathFunction)( 35 IN HWND hwnd_owner, 36 IN int folder, 37 IN HANDLE token, 38 IN DWORD flags, 39 OUT LPWSTR path); 40 41PathIsUNCFunction g_path_is_unc_func; 42PathAppendFunction g_path_append_func; 43PathIsPrefixFunction g_path_is_prefix_func; 44PathFindFileName g_path_find_filename_func; 45SHGetFolderPathFunction g_get_folder_func; 46 47// Record the number of calls we've redirected so far. 48int g_redirect_count = 0; 49 50// Populates the g_*_func pointers to functions which will be used in 51// ShouldBypass(). Chrome_elf cannot have a load-time dependency on shell32 or 52// shlwapi as this would induce a load-time dependency on user32.dll. Instead, 53// the addresses of the functions we need are retrieved the first time this 54// method is called, and cached to avoid subsequent calls to GetProcAddress(). 55// It is assumed that the host process will never unload these functions. 56// Returns true if all the functions needed are present. 57bool PopulateShellFunctions() { 58 // Early exit if functions have already been populated. 59 if (g_path_is_unc_func && g_path_append_func && 60 g_path_is_prefix_func && g_get_folder_func) { 61 return true; 62 } 63 64 // Get the addresses of the functions we need and store them for future use. 65 // These handles are intentionally leaked to ensure that these modules do not 66 // get unloaded. 67 HMODULE shell32 = ::LoadLibrary(L"shell32.dll"); 68 HMODULE shlwapi = ::LoadLibrary(L"shlwapi.dll"); 69 70 if (!shlwapi || !shell32) 71 return false; 72 73 g_path_is_unc_func = reinterpret_cast<PathIsUNCFunction>( 74 ::GetProcAddress(shlwapi, "PathIsUNCW")); 75 g_path_append_func = reinterpret_cast<PathAppendFunction>( 76 ::GetProcAddress(shlwapi, "PathAppendW")); 77 g_path_is_prefix_func = reinterpret_cast<PathIsPrefixFunction>( 78 ::GetProcAddress(shlwapi, "PathIsPrefixW")); 79 g_path_find_filename_func = reinterpret_cast<PathFindFileName>( 80 ::GetProcAddress(shlwapi, "PathFindFileNameW")); 81 g_get_folder_func = reinterpret_cast<SHGetFolderPathFunction>( 82 ::GetProcAddress(shell32, "SHGetFolderPathW")); 83 84 return g_path_is_unc_func && g_path_append_func && g_path_is_prefix_func && 85 g_path_find_filename_func && g_get_folder_func; 86} 87 88} // namespace 89 90// Turn off optimization to make sure these calls don't get inlined. 91#pragma optimize("", off) 92// Wrapper method for kernel32!CreateFile, to avoid setting off caller 93// mitigation detectors. 94HANDLE CreateFileWImpl(LPCWSTR file_name, 95 DWORD desired_access, 96 DWORD share_mode, 97 LPSECURITY_ATTRIBUTES security_attributes, 98 DWORD creation_disposition, 99 DWORD flags_and_attributes, 100 HANDLE template_file) { 101 return CreateFile(file_name, 102 desired_access, 103 share_mode, 104 security_attributes, 105 creation_disposition, 106 flags_and_attributes, 107 template_file); 108 109} 110 111HANDLE WINAPI CreateFileWRedirect( 112 LPCWSTR file_name, 113 DWORD desired_access, 114 DWORD share_mode, 115 LPSECURITY_ATTRIBUTES security_attributes, 116 DWORD creation_disposition, 117 DWORD flags_and_attributes, 118 HANDLE template_file) { 119 if (ShouldBypass(file_name)) { 120 ++g_redirect_count; 121 return CreateFileNTDLL(file_name, 122 desired_access, 123 share_mode, 124 security_attributes, 125 creation_disposition, 126 flags_and_attributes, 127 template_file); 128 } 129 return CreateFileWImpl(file_name, 130 desired_access, 131 share_mode, 132 security_attributes, 133 creation_disposition, 134 flags_and_attributes, 135 template_file); 136} 137#pragma optimize("", on) 138 139int GetRedirectCount() { 140 return g_redirect_count; 141} 142 143HANDLE CreateFileNTDLL( 144 LPCWSTR file_name, 145 DWORD desired_access, 146 DWORD share_mode, 147 LPSECURITY_ATTRIBUTES security_attributes, 148 DWORD creation_disposition, 149 DWORD flags_and_attributes, 150 HANDLE template_file) { 151 HANDLE file_handle = INVALID_HANDLE_VALUE; 152 NTSTATUS result = STATUS_UNSUCCESSFUL; 153 IO_STATUS_BLOCK io_status_block = {}; 154 ULONG flags = 0; 155 156 // Convert from Win32 domain to to NT creation disposition values. 157 switch (creation_disposition) { 158 case CREATE_NEW: 159 creation_disposition = FILE_CREATE; 160 break; 161 case CREATE_ALWAYS: 162 creation_disposition = FILE_OVERWRITE_IF; 163 break; 164 case OPEN_EXISTING: 165 creation_disposition = FILE_OPEN; 166 break; 167 case OPEN_ALWAYS: 168 creation_disposition = FILE_OPEN_IF; 169 break; 170 case TRUNCATE_EXISTING: 171 creation_disposition = FILE_OVERWRITE; 172 break; 173 default: 174 SetLastError(ERROR_INVALID_PARAMETER); 175 return INVALID_HANDLE_VALUE; 176 } 177 178 // Translate the flags that need no validation: 179 if (!(flags_and_attributes & FILE_FLAG_OVERLAPPED)) 180 flags |= FILE_SYNCHRONOUS_IO_NONALERT; 181 182 if (flags_and_attributes & FILE_FLAG_WRITE_THROUGH) 183 flags |= FILE_WRITE_THROUGH; 184 185 if (flags_and_attributes & FILE_FLAG_RANDOM_ACCESS) 186 flags |= FILE_RANDOM_ACCESS; 187 188 if (flags_and_attributes & FILE_FLAG_SEQUENTIAL_SCAN) 189 flags |= FILE_SEQUENTIAL_ONLY; 190 191 if (flags_and_attributes & FILE_FLAG_DELETE_ON_CLOSE) { 192 flags |= FILE_DELETE_ON_CLOSE; 193 desired_access |= DELETE; 194 } 195 196 if (flags_and_attributes & FILE_FLAG_BACKUP_SEMANTICS) 197 flags |= FILE_OPEN_FOR_BACKUP_INTENT; 198 else 199 flags |= FILE_NON_DIRECTORY_FILE; 200 201 202 if (flags_and_attributes & FILE_FLAG_OPEN_REPARSE_POINT) 203 flags |= FILE_OPEN_REPARSE_POINT; 204 205 if (flags_and_attributes & FILE_FLAG_OPEN_NO_RECALL) 206 flags |= FILE_OPEN_NO_RECALL; 207 208 if (!g_ntdll_lookup["NtCreateFile"] || 209 !g_ntdll_lookup["RtlInitUnicodeString"]) { 210 return INVALID_HANDLE_VALUE; 211 } 212 213 NtCreateFileFunction create_file = 214 reinterpret_cast<NtCreateFileFunction>(g_ntdll_lookup["NtCreateFile"]); 215 216 RtlInitUnicodeStringFunction init_unicode_string = 217 reinterpret_cast<RtlInitUnicodeStringFunction>( 218 g_ntdll_lookup["RtlInitUnicodeString"]); 219 220 UNICODE_STRING path_unicode_string; 221 222 // Format the path into an NT path. Arguably this should be done with 223 // RtlDosPathNameToNtPathName_U, but afaict this is equivalent for 224 // local paths. Using this with a UNC path name will almost certainly 225 // break in interesting ways. 226 base::string16 filename_string(L"\\??\\"); 227 filename_string += file_name; 228 229 init_unicode_string(&path_unicode_string, filename_string.c_str()); 230 231 OBJECT_ATTRIBUTES path_attributes = {}; 232 InitializeObjectAttributes(&path_attributes, 233 &path_unicode_string, 234 OBJ_CASE_INSENSITIVE, 235 NULL, // No Root Directory 236 NULL); // No Security Descriptor 237 238 // Set desired_access, and flags_and_attributes to match those 239 // set by kernel32!CreateFile. 240 desired_access |= 0x100080; 241 flags_and_attributes &= 0x2FFA7; 242 243 result = create_file(&file_handle, 244 desired_access, 245 &path_attributes, 246 &io_status_block, 247 0, // Allocation size 248 flags_and_attributes, 249 share_mode, 250 creation_disposition, 251 flags, 252 NULL, 253 0); 254 255 if (result != STATUS_SUCCESS) { 256 if (result == STATUS_OBJECT_NAME_COLLISION && 257 creation_disposition == FILE_CREATE) { 258 SetLastError(ERROR_FILE_EXISTS); 259 } 260 return INVALID_HANDLE_VALUE; 261 } 262 263 if (creation_disposition == FILE_OPEN_IF) { 264 SetLastError(io_status_block.Information == FILE_OPENED ? 265 ERROR_ALREADY_EXISTS : ERROR_SUCCESS); 266 } else if (creation_disposition == FILE_OVERWRITE_IF) { 267 SetLastError(io_status_block.Information == FILE_OVERWRITTEN ? 268 ERROR_ALREADY_EXISTS : ERROR_SUCCESS); 269 } else { 270 SetLastError(ERROR_SUCCESS); 271 } 272 273 return file_handle; 274} 275 276bool ShouldBypass(LPCWSTR file_path) { 277 // Do not redirect in non-browser processes. 278 if (IsNonBrowserProcess()) 279 return false; 280 281 // If the shell functions are not present, forward the call to kernel32. 282 if (!PopulateShellFunctions()) 283 return false; 284 285 // Forward all UNC filepaths to kernel32. 286 if (g_path_is_unc_func(file_path)) 287 return false; 288 289 wchar_t local_appdata_path[MAX_PATH]; 290 291 // Get the %LOCALAPPDATA% Path and append the location of our UserData 292 // directory to it. 293 HRESULT appdata_result = g_get_folder_func( 294 NULL, CSIDL_LOCAL_APPDATA, NULL, 0, local_appdata_path); 295 296 wchar_t buffer[MAX_PATH] = {}; 297 if (!GetModuleFileNameW(NULL, buffer, MAX_PATH)) 298 return false; 299 300 bool is_canary = IsCanary(buffer); 301 302 // If getting the %LOCALAPPDATA% path or appending to it failed, then forward 303 // the call to kernel32. 304 if (!SUCCEEDED(appdata_result) || 305 !g_path_append_func(local_appdata_path, is_canary ? 306 kCanaryAppDataDirName : kAppDataDirName) || 307 !g_path_append_func(local_appdata_path, kUserDataDirName)) { 308 return false; 309 } 310 311 LPCWSTR file_name = g_path_find_filename_func(file_path); 312 313 bool in_userdata_dir = !!g_path_is_prefix_func(local_appdata_path, file_path); 314 bool is_settings_file = wcscmp(file_name, kPreferencesFilename) == 0 || 315 wcscmp(file_name, kLocalStateFilename) == 0; 316 317 // Check if we are trying to access the Preferences in the UserData dir. If 318 // so, then redirect the call to bypass kernel32. 319 return in_userdata_dir && is_settings_file; 320} 321