security/sandbox/win/src/win_utils.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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.
     5 #include "sandbox/win/src/win_utils.h"
     7 #include <map>
     9 #include "base/logging.h"
    10 #include "base/memory/scoped_ptr.h"
    11 #include "sandbox/win/src/internal_types.h"
    12 #include "sandbox/win/src/nt_internals.h"
    14 namespace {
    16 // Holds the information about a known registry key.
    17 struct KnownReservedKey {
    18   const wchar_t* name;
    19   HKEY key;
    20 };
    22 // Contains all the known registry key by name and by handle.
    23 const KnownReservedKey kKnownKey[] = {
    24     { L"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT },
    25     { L"HKEY_CURRENT_USER", HKEY_CURRENT_USER },
    26     { L"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE},
    27     { L"HKEY_USERS", HKEY_USERS},
    28     { L"HKEY_PERFORMANCE_DATA", HKEY_PERFORMANCE_DATA},
    29     { L"HKEY_PERFORMANCE_TEXT", HKEY_PERFORMANCE_TEXT},
    30     { L"HKEY_PERFORMANCE_NLSTEXT", HKEY_PERFORMANCE_NLSTEXT},
    31     { L"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG},
    32     { L"HKEY_DYN_DATA", HKEY_DYN_DATA}
    33 };
    35 // Returns true if the provided path points to a pipe.
    36 bool IsPipe(const std::wstring& path) {
    37   size_t start = 0;
    38   if (0 == path.compare(0, sandbox::kNTPrefixLen, sandbox::kNTPrefix))
    39     start = sandbox::kNTPrefixLen;
    41   const wchar_t kPipe[] = L"pipe\\";
    42   return (0 == path.compare(start, arraysize(kPipe) - 1, kPipe));
    43 }
    45 }  // namespace
    47 namespace sandbox {
    49 HKEY GetReservedKeyFromName(const std::wstring& name) {
    50   for (size_t i = 0; i < arraysize(kKnownKey); ++i) {
    51     if (name == kKnownKey[i].name)
    52       return kKnownKey[i].key;
    53   }
    55   return NULL;
    56 }
    58 bool ResolveRegistryName(std::wstring name, std::wstring* resolved_name) {
    59   for (size_t i = 0; i < arraysize(kKnownKey); ++i) {
    60     if (name.find(kKnownKey[i].name) == 0) {
    61       HKEY key;
    62       DWORD disposition;
    63       if (ERROR_SUCCESS != ::RegCreateKeyEx(kKnownKey[i].key, L"", 0, NULL, 0,
    64                                             MAXIMUM_ALLOWED, NULL, &key,
    65                                             &disposition))
    66         return false;
    68       bool result = GetPathFromHandle(key, resolved_name);
    69       ::RegCloseKey(key);
    71       if (!result)
    72         return false;
    74       *resolved_name += name.substr(wcslen(kKnownKey[i].name));
    75       return true;
    76     }
    77   }
    79   return false;
    80 }
    82 DWORD IsReparsePoint(const std::wstring& full_path, bool* result) {
    83   std::wstring path = full_path;
    85   // Remove the nt prefix.
    86   if (0 == path.compare(0, kNTPrefixLen, kNTPrefix))
    87     path = path.substr(kNTPrefixLen);
    89   // Check if it's a pipe. We can't query the attributes of a pipe.
    90   if (IsPipe(path)) {
    91     *result = FALSE;
    92     return ERROR_SUCCESS;
    93   }
    95   std::wstring::size_type last_pos = std::wstring::npos;
    97   do {
    98     path = path.substr(0, last_pos);
   100     DWORD attributes = ::GetFileAttributes(path.c_str());
   101     if (INVALID_FILE_ATTRIBUTES == attributes) {
   102       DWORD error = ::GetLastError();
   103       if (error != ERROR_FILE_NOT_FOUND &&
   104           error != ERROR_PATH_NOT_FOUND &&
   105           error != ERROR_INVALID_NAME) {
   106         // Unexpected error.
   107         NOTREACHED();
   108         return error;
   109       }
   110     } else if (FILE_ATTRIBUTE_REPARSE_POINT & attributes) {
   111       // This is a reparse point.
   112       *result = true;
   113       return ERROR_SUCCESS;
   114     }
   116     last_pos = path.rfind(L'\\');
   117   } while (last_pos != std::wstring::npos);
   119   *result = false;
   120   return ERROR_SUCCESS;
   121 }
   123 // We get a |full_path| of the form \??\c:\some\foo\bar, and the name that
   124 // we'll get from |handle| will be \device\harddiskvolume1\some\foo\bar.
   125 bool SameObject(HANDLE handle, const wchar_t* full_path) {
   126   std::wstring path(full_path);
   127   DCHECK(!path.empty());
   129   // Check if it's a pipe.
   130   if (IsPipe(path))
   131     return true;
   133   std::wstring actual_path;
   134   if (!GetPathFromHandle(handle, &actual_path))
   135     return false;
   137   // This may end with a backslash.
   138   const wchar_t kBackslash = '\\';
   139   if (path[path.length() - 1] == kBackslash)
   140     path = path.substr(0, path.length() - 1);
   142   // Perfect match (case-insesitive check).
   143   if (0 == _wcsicmp(actual_path.c_str(), path.c_str()))
   144     return true;
   146   // Look for the drive letter.
   147   size_t colon_pos = path.find(L':');
   148   if (colon_pos == 0 || colon_pos == std::wstring::npos)
   149     return false;
   151   // Only one character for the drive.
   152   if (colon_pos > 1 && path[colon_pos - 2] != kBackslash)
   153     return false;
   155   // We only need 3 chars, but let's alloc a buffer for four.
   156   wchar_t drive[4] = {0};
   157   wchar_t vol_name[MAX_PATH];
   158   memcpy(drive, &path[colon_pos - 1], 2 * sizeof(*drive));
   160   // We'll get a double null terminated string.
   161   DWORD vol_length = ::QueryDosDeviceW(drive, vol_name, MAX_PATH);
   162   if (vol_length < 2 || vol_length == MAX_PATH)
   163     return false;
   165   // Ignore the nulls at the end.
   166   vol_length = static_cast<DWORD>(wcslen(vol_name));
   168   // The two paths should be the same length.
   169   if (vol_length + path.size() - (colon_pos + 1) != actual_path.size())
   170     return false;
   172   // Check up to the drive letter.
   173   if (0 != _wcsnicmp(actual_path.c_str(), vol_name, vol_length))
   174     return false;
   176   // Check the path after the drive letter.
   177   if (0 != _wcsicmp(&actual_path[vol_length], &path[colon_pos + 1]))
   178     return false;
   180   return true;
   181 }
   183 bool ConvertToLongPath(const std::wstring& short_path,
   184                        std::wstring* long_path) {
   185   // Check if the path is a NT path.
   186   bool is_nt_path = false;
   187   std::wstring path = short_path;
   188   if (0 == path.compare(0, kNTPrefixLen, kNTPrefix)) {
   189     path = path.substr(kNTPrefixLen);
   190     is_nt_path = true;
   191   }
   193   DWORD size = MAX_PATH;
   194   scoped_ptr<wchar_t[]> long_path_buf(new wchar_t[size]);
   196   DWORD return_value = ::GetLongPathName(path.c_str(), long_path_buf.get(),
   197                                          size);
   198   while (return_value >= size) {
   199     size *= 2;
   200     long_path_buf.reset(new wchar_t[size]);
   201     return_value = ::GetLongPathName(path.c_str(), long_path_buf.get(), size);
   202   }
   204   DWORD last_error = ::GetLastError();
   205   if (0 == return_value && (ERROR_FILE_NOT_FOUND == last_error ||
   206                             ERROR_PATH_NOT_FOUND == last_error ||
   207                             ERROR_INVALID_NAME == last_error)) {
   208     // The file does not exist, but maybe a sub path needs to be expanded.
   209     std::wstring::size_type last_slash = path.rfind(L'\\');
   210     if (std::wstring::npos == last_slash)
   211       return false;
   213     std::wstring begin = path.substr(0, last_slash);
   214     std::wstring end = path.substr(last_slash);
   215     if (!ConvertToLongPath(begin, &begin))
   216       return false;
   218     // Ok, it worked. Let's reset the return value.
   219     path = begin + end;
   220     return_value = 1;
   221   } else if (0 != return_value) {
   222     path = long_path_buf.get();
   223   }
   225   if (return_value != 0) {
   226     if (is_nt_path) {
   227       *long_path = kNTPrefix;
   228       *long_path += path;
   229     } else {
   230       *long_path = path;
   231     }
   233     return true;
   234   }
   236   return false;
   237 }
   239 bool GetPathFromHandle(HANDLE handle, std::wstring* path) {
   240   NtQueryObjectFunction NtQueryObject = NULL;
   241   ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject);
   243   OBJECT_NAME_INFORMATION initial_buffer;
   244   OBJECT_NAME_INFORMATION* name = &initial_buffer;
   245   ULONG size = sizeof(initial_buffer);
   246   // Query the name information a first time to get the size of the name.
   247   NTSTATUS status = NtQueryObject(handle, ObjectNameInformation, name, size,
   248                                   &size);
   250   scoped_ptr<OBJECT_NAME_INFORMATION> name_ptr;
   251   if (size) {
   252     name = reinterpret_cast<OBJECT_NAME_INFORMATION*>(new BYTE[size]);
   253     name_ptr.reset(name);
   255     // Query the name information a second time to get the name of the
   256     // object referenced by the handle.
   257     status = NtQueryObject(handle, ObjectNameInformation, name, size, &size);
   258   }
   260   if (STATUS_SUCCESS != status)
   261     return false;
   263   path->assign(name->ObjectName.Buffer, name->ObjectName.Length /
   264                                         sizeof(name->ObjectName.Buffer[0]));
   265   return true;
   266 }
   268 bool GetNtPathFromWin32Path(const std::wstring& path, std::wstring* nt_path) {
   269   HANDLE file = ::CreateFileW(path.c_str(), 0,
   270     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
   271     OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
   272   if (file == INVALID_HANDLE_VALUE)
   273     return false;
   274   bool rv = GetPathFromHandle(file, nt_path);
   275   ::CloseHandle(file);
   276   return rv;
   277 }
   279 bool WriteProtectedChildMemory(HANDLE child_process, void* address,
   280                                const void* buffer, size_t length) {
   281   // First, remove the protections.
   282   DWORD old_protection;
   283   if (!::VirtualProtectEx(child_process, address, length,
   284                           PAGE_WRITECOPY, &old_protection))
   285     return false;
   287   SIZE_T written;
   288   bool ok = ::WriteProcessMemory(child_process, address, buffer, length,
   289                                  &written) && (length == written);
   291   // Always attempt to restore the original protection.
   292   if (!::VirtualProtectEx(child_process, address, length,
   293                           old_protection, &old_protection))
   294     return false;
   296   return ok;
   297 }
   299 };  // namespace sandbox
   301 // TODO(jschuh): http://crbug.com/11789
   302 // I'm guessing we have a race where some "security" software is messing
   303 // with ntdll/imports underneath us. So, we retry a few times, and in the
   304 // worst case we sleep briefly before a few more attempts. (Normally sleeping
   305 // would be very bad, but it's better than crashing in this case.)
   306 void ResolveNTFunctionPtr(const char* name, void* ptr) {
   307   const int max_tries = 5;
   308   const int sleep_threshold = 2;
   310   static HMODULE ntdll = ::GetModuleHandle(sandbox::kNtdllName);
   312   FARPROC* function_ptr = reinterpret_cast<FARPROC*>(ptr);
   313   *function_ptr = ::GetProcAddress(ntdll, name);
   315   for (int tries = 1; !(*function_ptr) && tries < max_tries; ++tries) {
   316     if (tries >= sleep_threshold)
   317       ::Sleep(1);
   318     ntdll = ::GetModuleHandle(sandbox::kNtdllName);
   319     *function_ptr = ::GetProcAddress(ntdll, name);
   320   }
   322   CHECK(*function_ptr);
   323 }

mercurial