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. 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. 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 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 140 int GetRedirectCount() { 141 return g_redirect_count; 142 } 143 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 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