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

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

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

mercurial