security/sandbox/chromium/base/win/registry.cc

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 // Copyright (c) 2012 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 "base/win/registry.h"
     7 #include <shlwapi.h>
     8 #include <algorithm>
    10 #include "base/logging.h"
    11 #include "base/strings/string_util.h"
    12 #include "base/threading/thread_restrictions.h"
    14 #pragma comment(lib, "shlwapi.lib")  // for SHDeleteKey
    16 namespace base {
    17 namespace win {
    19 namespace {
    21 // RegEnumValue() reports the number of characters from the name that were
    22 // written to the buffer, not how many there are. This constant is the maximum
    23 // name size, such that a buffer with this size should read any name.
    24 const DWORD MAX_REGISTRY_NAME_SIZE = 16384;
    26 // Registry values are read as BYTE* but can have wchar_t* data whose last
    27 // wchar_t is truncated. This function converts the reported |byte_size| to
    28 // a size in wchar_t that can store a truncated wchar_t if necessary.
    29 inline DWORD to_wchar_size(DWORD byte_size) {
    30   return (byte_size + sizeof(wchar_t) - 1) / sizeof(wchar_t);
    31 }
    33 }  // namespace
    35 // RegKey ----------------------------------------------------------------------
    37 RegKey::RegKey()
    38     : key_(NULL),
    39       watch_event_(0) {
    40 }
    42 RegKey::RegKey(HKEY key)
    43     : key_(key),
    44       watch_event_(0) {
    45 }
    47 RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access)
    48     : key_(NULL),
    49       watch_event_(0) {
    50   if (rootkey) {
    51     if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
    52       Create(rootkey, subkey, access);
    53     else
    54       Open(rootkey, subkey, access);
    55   } else {
    56     DCHECK(!subkey);
    57   }
    58 }
    60 RegKey::~RegKey() {
    61   Close();
    62 }
    64 LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
    65   DWORD disposition_value;
    66   return CreateWithDisposition(rootkey, subkey, &disposition_value, access);
    67 }
    69 LONG RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey,
    70                                    DWORD* disposition, REGSAM access) {
    71   DCHECK(rootkey && subkey && access && disposition);
    72   Close();
    74   LONG result = RegCreateKeyEx(rootkey, subkey, 0, NULL,
    75                                REG_OPTION_NON_VOLATILE, access, NULL, &key_,
    76                                disposition);
    77   return result;
    78 }
    80 LONG RegKey::CreateKey(const wchar_t* name, REGSAM access) {
    81   DCHECK(name && access);
    82   HKEY subkey = NULL;
    83   LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE,
    84                                access, NULL, &subkey, NULL);
    85   Close();
    87   key_ = subkey;
    88   return result;
    89 }
    91 LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
    92   DCHECK(rootkey && subkey && access);
    93   Close();
    95   LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &key_);
    96   return result;
    97 }
    99 LONG RegKey::OpenKey(const wchar_t* relative_key_name, REGSAM access) {
   100   DCHECK(relative_key_name && access);
   101   HKEY subkey = NULL;
   102   LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey);
   104   // We have to close the current opened key before replacing it with the new
   105   // one.
   106   Close();
   108   key_ = subkey;
   109   return result;
   110 }
   112 void RegKey::Close() {
   113   StopWatching();
   114   if (key_) {
   115     ::RegCloseKey(key_);
   116     key_ = NULL;
   117   }
   118 }
   120 void RegKey::Set(HKEY key) {
   121   if (key_ != key) {
   122     Close();
   123     key_ = key;
   124   }
   125 }
   127 HKEY RegKey::Take() {
   128   StopWatching();
   129   HKEY key = key_;
   130   key_ = NULL;
   131   return key;
   132 }
   134 bool RegKey::HasValue(const wchar_t* name) const {
   135   return RegQueryValueEx(key_, name, 0, NULL, NULL, NULL) == ERROR_SUCCESS;
   136 }
   138 DWORD RegKey::GetValueCount() const {
   139   DWORD count = 0;
   140   LONG result = RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
   141                                 NULL, NULL, NULL, NULL);
   142   return (result == ERROR_SUCCESS) ? count : 0;
   143 }
   145 LONG RegKey::GetValueNameAt(int index, std::wstring* name) const {
   146   wchar_t buf[256];
   147   DWORD bufsize = arraysize(buf);
   148   LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL);
   149   if (r == ERROR_SUCCESS)
   150     *name = buf;
   152   return r;
   153 }
   155 LONG RegKey::DeleteKey(const wchar_t* name) {
   156   DCHECK(key_);
   157   DCHECK(name);
   158   LONG result = SHDeleteKey(key_, name);
   159   return result;
   160 }
   162 LONG RegKey::DeleteValue(const wchar_t* value_name) {
   163   DCHECK(key_);
   164   LONG result = RegDeleteValue(key_, value_name);
   165   return result;
   166 }
   168 LONG RegKey::ReadValueDW(const wchar_t* name, DWORD* out_value) const {
   169   DCHECK(out_value);
   170   DWORD type = REG_DWORD;
   171   DWORD size = sizeof(DWORD);
   172   DWORD local_value = 0;
   173   LONG result = ReadValue(name, &local_value, &size, &type);
   174   if (result == ERROR_SUCCESS) {
   175     if ((type == REG_DWORD || type == REG_BINARY) && size == sizeof(DWORD))
   176       *out_value = local_value;
   177     else
   178       result = ERROR_CANTREAD;
   179   }
   181   return result;
   182 }
   184 LONG RegKey::ReadInt64(const wchar_t* name, int64* out_value) const {
   185   DCHECK(out_value);
   186   DWORD type = REG_QWORD;
   187   int64 local_value = 0;
   188   DWORD size = sizeof(local_value);
   189   LONG result = ReadValue(name, &local_value, &size, &type);
   190   if (result == ERROR_SUCCESS) {
   191     if ((type == REG_QWORD || type == REG_BINARY) &&
   192         size == sizeof(local_value))
   193       *out_value = local_value;
   194     else
   195       result = ERROR_CANTREAD;
   196   }
   198   return result;
   199 }
   201 LONG RegKey::ReadValue(const wchar_t* name, std::wstring* out_value) const {
   202   DCHECK(out_value);
   203   const size_t kMaxStringLength = 1024;  // This is after expansion.
   204   // Use the one of the other forms of ReadValue if 1024 is too small for you.
   205   wchar_t raw_value[kMaxStringLength];
   206   DWORD type = REG_SZ, size = sizeof(raw_value);
   207   LONG result = ReadValue(name, raw_value, &size, &type);
   208   if (result == ERROR_SUCCESS) {
   209     if (type == REG_SZ) {
   210       *out_value = raw_value;
   211     } else if (type == REG_EXPAND_SZ) {
   212       wchar_t expanded[kMaxStringLength];
   213       size = ExpandEnvironmentStrings(raw_value, expanded, kMaxStringLength);
   214       // Success: returns the number of wchar_t's copied
   215       // Fail: buffer too small, returns the size required
   216       // Fail: other, returns 0
   217       if (size == 0 || size > kMaxStringLength) {
   218         result = ERROR_MORE_DATA;
   219       } else {
   220         *out_value = expanded;
   221       }
   222     } else {
   223       // Not a string. Oops.
   224       result = ERROR_CANTREAD;
   225     }
   226   }
   228   return result;
   229 }
   231 LONG RegKey::ReadValue(const wchar_t* name,
   232                        void* data,
   233                        DWORD* dsize,
   234                        DWORD* dtype) const {
   235   LONG result = RegQueryValueEx(key_, name, 0, dtype,
   236                                 reinterpret_cast<LPBYTE>(data), dsize);
   237   return result;
   238 }
   240 LONG RegKey::ReadValues(const wchar_t* name,
   241                         std::vector<std::wstring>* values) {
   242   values->clear();
   244   DWORD type = REG_MULTI_SZ;
   245   DWORD size = 0;
   246   LONG result = ReadValue(name, NULL, &size, &type);
   247   if (FAILED(result) || size == 0)
   248     return result;
   250   if (type != REG_MULTI_SZ)
   251     return ERROR_CANTREAD;
   253   std::vector<wchar_t> buffer(size / sizeof(wchar_t));
   254   result = ReadValue(name, &buffer[0], &size, NULL);
   255   if (FAILED(result) || size == 0)
   256     return result;
   258   // Parse the double-null-terminated list of strings.
   259   // Note: This code is paranoid to not read outside of |buf|, in the case where
   260   // it may not be properly terminated.
   261   const wchar_t* entry = &buffer[0];
   262   const wchar_t* buffer_end = entry + (size / sizeof(wchar_t));
   263   while (entry < buffer_end && entry[0] != '\0') {
   264     const wchar_t* entry_end = std::find(entry, buffer_end, L'\0');
   265     values->push_back(std::wstring(entry, entry_end));
   266     entry = entry_end + 1;
   267   }
   268   return 0;
   269 }
   271 LONG RegKey::WriteValue(const wchar_t* name, DWORD in_value) {
   272   return WriteValue(
   273       name, &in_value, static_cast<DWORD>(sizeof(in_value)), REG_DWORD);
   274 }
   276 LONG RegKey::WriteValue(const wchar_t * name, const wchar_t* in_value) {
   277   return WriteValue(name, in_value,
   278       static_cast<DWORD>(sizeof(*in_value) * (wcslen(in_value) + 1)), REG_SZ);
   279 }
   281 LONG RegKey::WriteValue(const wchar_t* name,
   282                         const void* data,
   283                         DWORD dsize,
   284                         DWORD dtype) {
   285   DCHECK(data || !dsize);
   287   LONG result = RegSetValueEx(key_, name, 0, dtype,
   288       reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize);
   289   return result;
   290 }
   292 LONG RegKey::StartWatching() {
   293   DCHECK(key_);
   294   if (!watch_event_)
   295     watch_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
   297   DWORD filter = REG_NOTIFY_CHANGE_NAME |
   298                  REG_NOTIFY_CHANGE_ATTRIBUTES |
   299                  REG_NOTIFY_CHANGE_LAST_SET |
   300                  REG_NOTIFY_CHANGE_SECURITY;
   302   // Watch the registry key for a change of value.
   303   LONG result = RegNotifyChangeKeyValue(key_, TRUE, filter, watch_event_, TRUE);
   304   if (result != ERROR_SUCCESS) {
   305     CloseHandle(watch_event_);
   306     watch_event_ = 0;
   307   }
   309   return result;
   310 }
   312 bool RegKey::HasChanged() {
   313   if (watch_event_) {
   314     if (WaitForSingleObject(watch_event_, 0) == WAIT_OBJECT_0) {
   315       StartWatching();
   316       return true;
   317     }
   318   }
   319   return false;
   320 }
   322 LONG RegKey::StopWatching() {
   323   LONG result = ERROR_INVALID_HANDLE;
   324   if (watch_event_) {
   325     CloseHandle(watch_event_);
   326     watch_event_ = 0;
   327     result = ERROR_SUCCESS;
   328   }
   329   return result;
   330 }
   332 // RegistryValueIterator ------------------------------------------------------
   334 RegistryValueIterator::RegistryValueIterator(HKEY root_key,
   335                                              const wchar_t* folder_key)
   336     : name_(MAX_PATH, L'\0'),
   337       value_(MAX_PATH, L'\0') {
   338   LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
   339   if (result != ERROR_SUCCESS) {
   340     key_ = NULL;
   341   } else {
   342     DWORD count = 0;
   343     result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
   344                                NULL, NULL, NULL, NULL);
   346     if (result != ERROR_SUCCESS) {
   347       ::RegCloseKey(key_);
   348       key_ = NULL;
   349     } else {
   350       index_ = count - 1;
   351     }
   352   }
   354   Read();
   355 }
   357 RegistryValueIterator::~RegistryValueIterator() {
   358   if (key_)
   359     ::RegCloseKey(key_);
   360 }
   362 DWORD RegistryValueIterator::ValueCount() const {
   363   DWORD count = 0;
   364   LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL,
   365                                   &count, NULL, NULL, NULL, NULL);
   366   if (result != ERROR_SUCCESS)
   367     return 0;
   369   return count;
   370 }
   372 bool RegistryValueIterator::Valid() const {
   373   return key_ != NULL && index_ >= 0;
   374 }
   376 void RegistryValueIterator::operator++() {
   377   --index_;
   378   Read();
   379 }
   381 bool RegistryValueIterator::Read() {
   382   if (Valid()) {
   383     DWORD capacity = static_cast<DWORD>(name_.capacity());
   384     DWORD name_size = capacity;
   385     // |value_size_| is in bytes. Reserve the last character for a NUL.
   386     value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t));
   387     LONG result = ::RegEnumValue(
   388         key_, index_, WriteInto(&name_, name_size), &name_size, NULL, &type_,
   389         reinterpret_cast<BYTE*>(vector_as_array(&value_)), &value_size_);
   391     if (result == ERROR_MORE_DATA) {
   392       // Registry key names are limited to 255 characters and fit within
   393       // MAX_PATH (which is 260) but registry value names can use up to 16,383
   394       // characters and the value itself is not limited
   395       // (from http://msdn.microsoft.com/en-us/library/windows/desktop/
   396       // ms724872(v=vs.85).aspx).
   397       // Resize the buffers and retry if their size caused the failure.
   398       DWORD value_size_in_wchars = to_wchar_size(value_size_);
   399       if (value_size_in_wchars + 1 > value_.size())
   400         value_.resize(value_size_in_wchars + 1, L'\0');
   401       value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t));
   402       name_size = name_size == capacity ? MAX_REGISTRY_NAME_SIZE : capacity;
   403       result = ::RegEnumValue(
   404           key_, index_, WriteInto(&name_, name_size), &name_size, NULL, &type_,
   405           reinterpret_cast<BYTE*>(vector_as_array(&value_)), &value_size_);
   406     }
   408     if (result == ERROR_SUCCESS) {
   409       DCHECK_LT(to_wchar_size(value_size_), value_.size());
   410       value_[to_wchar_size(value_size_)] = L'\0';
   411       return true;
   412     }
   413   }
   415   name_[0] = L'\0';
   416   value_[0] = L'\0';
   417   value_size_ = 0;
   418   return false;
   419 }
   421 // RegistryKeyIterator --------------------------------------------------------
   423 RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
   424                                          const wchar_t* folder_key) {
   425   LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
   426   if (result != ERROR_SUCCESS) {
   427     key_ = NULL;
   428   } else {
   429     DWORD count = 0;
   430     LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
   431                                     NULL, NULL, NULL, NULL, NULL);
   433     if (result != ERROR_SUCCESS) {
   434       ::RegCloseKey(key_);
   435       key_ = NULL;
   436     } else {
   437       index_ = count - 1;
   438     }
   439   }
   441   Read();
   442 }
   444 RegistryKeyIterator::~RegistryKeyIterator() {
   445   if (key_)
   446     ::RegCloseKey(key_);
   447 }
   449 DWORD RegistryKeyIterator::SubkeyCount() const {
   450   DWORD count = 0;
   451   LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
   452                                   NULL, NULL, NULL, NULL, NULL);
   453   if (result != ERROR_SUCCESS)
   454     return 0;
   456   return count;
   457 }
   459 bool RegistryKeyIterator::Valid() const {
   460   return key_ != NULL && index_ >= 0;
   461 }
   463 void RegistryKeyIterator::operator++() {
   464   --index_;
   465   Read();
   466 }
   468 bool RegistryKeyIterator::Read() {
   469   if (Valid()) {
   470     DWORD ncount = arraysize(name_);
   471     FILETIME written;
   472     LONG r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL,
   473                             NULL, &written);
   474     if (ERROR_SUCCESS == r)
   475       return true;
   476   }
   478   name_[0] = '\0';
   479   return false;
   480 }
   482 }  // namespace win
   483 }  // namespace base

mercurial