Home | History | Annotate | Download | only in create_file
      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