Home | History | Annotate | Download | only in src
      1 // Copyright (c) 2011 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 "sandbox/win/src/win_utils.h"
      6 
      7 #include <map>
      8 
      9 #include "base/memory/scoped_ptr.h"
     10 #include "base/win/pe_image.h"
     11 #include "sandbox/win/src/internal_types.h"
     12 #include "sandbox/win/src/nt_internals.h"
     13 #include "sandbox/win/src/sandbox_nt_util.h"
     14 
     15 namespace {
     16 
     17 // Holds the information about a known registry key.
     18 struct KnownReservedKey {
     19   const wchar_t* name;
     20   HKEY key;
     21 };
     22 
     23 // Contains all the known registry key by name and by handle.
     24 const KnownReservedKey kKnownKey[] = {
     25     { L"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT },
     26     { L"HKEY_CURRENT_USER", HKEY_CURRENT_USER },
     27     { L"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE},
     28     { L"HKEY_USERS", HKEY_USERS},
     29     { L"HKEY_PERFORMANCE_DATA", HKEY_PERFORMANCE_DATA},
     30     { L"HKEY_PERFORMANCE_TEXT", HKEY_PERFORMANCE_TEXT},
     31     { L"HKEY_PERFORMANCE_NLSTEXT", HKEY_PERFORMANCE_NLSTEXT},
     32     { L"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG},
     33     { L"HKEY_DYN_DATA", HKEY_DYN_DATA}
     34 };
     35 
     36 // Returns true if the provided path points to a pipe.
     37 bool IsPipe(const base::string16& path) {
     38   size_t start = 0;
     39   if (0 == path.compare(0, sandbox::kNTPrefixLen, sandbox::kNTPrefix))
     40     start = sandbox::kNTPrefixLen;
     41 
     42   const wchar_t kPipe[] = L"pipe\\";
     43   return (0 == path.compare(start, arraysize(kPipe) - 1, kPipe));
     44 }
     45 
     46 }  // namespace
     47 
     48 namespace sandbox {
     49 
     50 HKEY GetReservedKeyFromName(const base::string16& name) {
     51   for (size_t i = 0; i < arraysize(kKnownKey); ++i) {
     52     if (name == kKnownKey[i].name)
     53       return kKnownKey[i].key;
     54   }
     55 
     56   return NULL;
     57 }
     58 
     59 bool ResolveRegistryName(base::string16 name, base::string16* resolved_name) {
     60   for (size_t i = 0; i < arraysize(kKnownKey); ++i) {
     61     if (name.find(kKnownKey[i].name) == 0) {
     62       HKEY key;
     63       DWORD disposition;
     64       if (ERROR_SUCCESS != ::RegCreateKeyEx(kKnownKey[i].key, L"", 0, NULL, 0,
     65                                             MAXIMUM_ALLOWED, NULL, &key,
     66                                             &disposition))
     67         return false;
     68 
     69       bool result = GetPathFromHandle(key, resolved_name);
     70       ::RegCloseKey(key);
     71 
     72       if (!result)
     73         return false;
     74 
     75       *resolved_name += name.substr(wcslen(kKnownKey[i].name));
     76       return true;
     77     }
     78   }
     79 
     80   return false;
     81 }
     82 
     83 DWORD IsReparsePoint(const base::string16& full_path, bool* result) {
     84   base::string16 path = full_path;
     85 
     86   // Remove the nt prefix.
     87   if (0 == path.compare(0, kNTPrefixLen, kNTPrefix))
     88     path = path.substr(kNTPrefixLen);
     89 
     90   // Check if it's a pipe. We can't query the attributes of a pipe.
     91   if (IsPipe(path)) {
     92     *result = FALSE;
     93     return ERROR_SUCCESS;
     94   }
     95 
     96   base::string16::size_type last_pos = base::string16::npos;
     97 
     98   do {
     99     path = path.substr(0, last_pos);
    100 
    101     DWORD attributes = ::GetFileAttributes(path.c_str());
    102     if (INVALID_FILE_ATTRIBUTES == attributes) {
    103       DWORD error = ::GetLastError();
    104       if (error != ERROR_FILE_NOT_FOUND &&
    105           error != ERROR_PATH_NOT_FOUND &&
    106           error != ERROR_INVALID_NAME) {
    107         // Unexpected error.
    108         NOTREACHED_NT();
    109         return error;
    110       }
    111     } else if (FILE_ATTRIBUTE_REPARSE_POINT & attributes) {
    112       // This is a reparse point.
    113       *result = true;
    114       return ERROR_SUCCESS;
    115     }
    116 
    117     last_pos = path.rfind(L'\\');
    118   } while (last_pos > 2);  // Skip root dir.
    119 
    120   *result = false;
    121   return ERROR_SUCCESS;
    122 }
    123 
    124 // We get a |full_path| of the form \??\c:\some\foo\bar, and the name that
    125 // we'll get from |handle| will be \device\harddiskvolume1\some\foo\bar.
    126 bool SameObject(HANDLE handle, const wchar_t* full_path) {
    127   base::string16 path(full_path);
    128   DCHECK_NT(!path.empty());
    129 
    130   // Check if it's a pipe.
    131   if (IsPipe(path))
    132     return true;
    133 
    134   base::string16 actual_path;
    135   if (!GetPathFromHandle(handle, &actual_path))
    136     return false;
    137 
    138   // This may end with a backslash.
    139   const wchar_t kBackslash = '\\';
    140   if (path[path.length() - 1] == kBackslash)
    141     path = path.substr(0, path.length() - 1);
    142 
    143   // Perfect match (case-insesitive check).
    144   if (0 == _wcsicmp(actual_path.c_str(), path.c_str()))
    145     return true;
    146 
    147   // Look for the drive letter.
    148   size_t colon_pos = path.find(L':');
    149   if (colon_pos == 0 || colon_pos == base::string16::npos)
    150     return false;
    151 
    152   // Only one character for the drive.
    153   if (colon_pos > 1 && path[colon_pos - 2] != kBackslash)
    154     return false;
    155 
    156   // We only need 3 chars, but let's alloc a buffer for four.
    157   wchar_t drive[4] = {0};
    158   wchar_t vol_name[MAX_PATH];
    159   memcpy(drive, &path[colon_pos - 1], 2 * sizeof(*drive));
    160 
    161   // We'll get a double null terminated string.
    162   DWORD vol_length = ::QueryDosDeviceW(drive, vol_name, MAX_PATH);
    163   if (vol_length < 2 || vol_length == MAX_PATH)
    164     return false;
    165 
    166   // Ignore the nulls at the end.
    167   vol_length = static_cast<DWORD>(wcslen(vol_name));
    168 
    169   // The two paths should be the same length.
    170   if (vol_length + path.size() - (colon_pos + 1) != actual_path.size())
    171     return false;
    172 
    173   // Check up to the drive letter.
    174   if (0 != _wcsnicmp(actual_path.c_str(), vol_name, vol_length))
    175     return false;
    176 
    177   // Check the path after the drive letter.
    178   if (0 != _wcsicmp(&actual_path[vol_length], &path[colon_pos + 1]))
    179     return false;
    180 
    181   return true;
    182 }
    183 
    184 bool ConvertToLongPath(const base::string16& short_path,
    185                        base::string16* long_path) {
    186   // Check if the path is a NT path.
    187   bool is_nt_path = false;
    188   base::string16 path = short_path;
    189   if (0 == path.compare(0, kNTPrefixLen, kNTPrefix)) {
    190     path = path.substr(kNTPrefixLen);
    191     is_nt_path = true;
    192   }
    193 
    194   DWORD size = MAX_PATH;
    195   scoped_ptr<wchar_t[]> long_path_buf(new wchar_t[size]);
    196 
    197   DWORD return_value = ::GetLongPathName(path.c_str(), long_path_buf.get(),
    198                                          size);
    199   while (return_value >= size) {
    200     size *= 2;
    201     long_path_buf.reset(new wchar_t[size]);
    202     return_value = ::GetLongPathName(path.c_str(), long_path_buf.get(), size);
    203   }
    204 
    205   DWORD last_error = ::GetLastError();
    206   if (0 == return_value && (ERROR_FILE_NOT_FOUND == last_error ||
    207                             ERROR_PATH_NOT_FOUND == last_error ||
    208                             ERROR_INVALID_NAME == last_error)) {
    209     // The file does not exist, but maybe a sub path needs to be expanded.
    210     base::string16::size_type last_slash = path.rfind(L'\\');
    211     if (base::string16::npos == last_slash)
    212       return false;
    213 
    214     base::string16 begin = path.substr(0, last_slash);
    215     base::string16 end = path.substr(last_slash);
    216     if (!ConvertToLongPath(begin, &begin))
    217       return false;
    218 
    219     // Ok, it worked. Let's reset the return value.
    220     path = begin + end;
    221     return_value = 1;
    222   } else if (0 != return_value) {
    223     path = long_path_buf.get();
    224   }
    225 
    226   if (return_value != 0) {
    227     if (is_nt_path) {
    228       *long_path = kNTPrefix;
    229       *long_path += path;
    230     } else {
    231       *long_path = path;
    232     }
    233 
    234     return true;
    235   }
    236 
    237   return false;
    238 }
    239 
    240 bool GetPathFromHandle(HANDLE handle, base::string16* path) {
    241   NtQueryObjectFunction NtQueryObject = NULL;
    242   ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject);
    243 
    244   OBJECT_NAME_INFORMATION initial_buffer;
    245   OBJECT_NAME_INFORMATION* name = &initial_buffer;
    246   ULONG size = sizeof(initial_buffer);
    247   // Query the name information a first time to get the size of the name.
    248   NTSTATUS status = NtQueryObject(handle, ObjectNameInformation, name, size,
    249                                   &size);
    250 
    251   scoped_ptr<OBJECT_NAME_INFORMATION> name_ptr;
    252   if (size) {
    253     name = reinterpret_cast<OBJECT_NAME_INFORMATION*>(new BYTE[size]);
    254     name_ptr.reset(name);
    255 
    256     // Query the name information a second time to get the name of the
    257     // object referenced by the handle.
    258     status = NtQueryObject(handle, ObjectNameInformation, name, size, &size);
    259   }
    260 
    261   if (STATUS_SUCCESS != status)
    262     return false;
    263 
    264   path->assign(name->ObjectName.Buffer, name->ObjectName.Length /
    265                                         sizeof(name->ObjectName.Buffer[0]));
    266   return true;
    267 }
    268 
    269 bool GetNtPathFromWin32Path(const base::string16& path,
    270                             base::string16* nt_path) {
    271   HANDLE file = ::CreateFileW(path.c_str(), 0,
    272     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
    273     OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
    274   if (file == INVALID_HANDLE_VALUE)
    275     return false;
    276   bool rv = GetPathFromHandle(file, nt_path);
    277   ::CloseHandle(file);
    278   return rv;
    279 }
    280 
    281 bool WriteProtectedChildMemory(HANDLE child_process, void* address,
    282                                const void* buffer, size_t length) {
    283   // First, remove the protections.
    284   DWORD old_protection;
    285   if (!::VirtualProtectEx(child_process, address, length,
    286                           PAGE_WRITECOPY, &old_protection))
    287     return false;
    288 
    289   SIZE_T written;
    290   bool ok = ::WriteProcessMemory(child_process, address, buffer, length,
    291                                  &written) && (length == written);
    292 
    293   // Always attempt to restore the original protection.
    294   if (!::VirtualProtectEx(child_process, address, length,
    295                           old_protection, &old_protection))
    296     return false;
    297 
    298   return ok;
    299 }
    300 
    301 };  // namespace sandbox
    302 
    303 void ResolveNTFunctionPtr(const char* name, void* ptr) {
    304   static volatile HMODULE ntdll = NULL;
    305 
    306   if (!ntdll) {
    307     HMODULE ntdll_local = ::GetModuleHandle(sandbox::kNtdllName);
    308     // Use PEImage to sanity-check that we have a valid ntdll handle.
    309     base::win::PEImage ntdll_peimage(ntdll_local);
    310     CHECK_NT(ntdll_peimage.VerifyMagic());
    311     // Race-safe way to set static ntdll.
    312     ::InterlockedCompareExchangePointer(
    313         reinterpret_cast<PVOID volatile*>(&ntdll), ntdll_local, NULL);
    314 
    315   }
    316 
    317   CHECK_NT(ntdll);
    318   FARPROC* function_ptr = reinterpret_cast<FARPROC*>(ptr);
    319   *function_ptr = ::GetProcAddress(ntdll, name);
    320   CHECK_NT(*function_ptr);
    321 }
    322