1 // ==++== 2 // 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // 5 // ==--== 6 7 /* 8 Note on transaction support: 9 Eventually we will want to add support for NT's transactions to our 10 RegistryKey API's (possibly Whidbey M3?). When we do this, here's 11 the list of API's we need to make transaction-aware: 12 13 RegCreateKeyEx 14 RegDeleteKey 15 RegDeleteValue 16 RegEnumKeyEx 17 RegEnumValue 18 RegOpenKeyEx 19 RegQueryInfoKey 20 RegQueryValueEx 21 RegSetValueEx 22 23 We can ignore RegConnectRegistry (remote registry access doesn't yet have 24 transaction support) and RegFlushKey. RegCloseKey doesn't require any 25 additional work. . 26 */ 27 28 /* 29 Note on ACL support: 30 The key thing to note about ACL's is you set them on a kernel object like a 31 registry key, then the ACL only gets checked when you construct handles to 32 them. So if you set an ACL to deny read access to yourself, you'll still be 33 able to read with that handle, but not with new handles. 34 35 Another peculiarity is a Terminal Server app compatibility hack. The OS 36 will second guess your attempt to open a handle sometimes. If a certain 37 combination of Terminal Server app compat registry keys are set, then the 38 OS will try to reopen your handle with lesser permissions if you couldn't 39 open it in the specified mode. So on some machines, we will see handles that 40 may not be able to read or write to a registry key. It's very strange. But 41 the real test of these handles is attempting to read or set a value in an 42 affected registry key. 43 44 For reference, at least two registry keys must be set to particular values 45 for this behavior: 46 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\RegistryExtensionFlags, the least significant bit must be 1. 47 HKLM\SYSTEM\CurrentControlSet\Control\TerminalServer\TSAppCompat must be 1 48 There might possibly be an interaction with yet a third registry key as well. 49 50 */ 51 52 53 namespace Microsoft.Win32 { 54 55 using System; 56 using System.Collections; 57 using System.Collections.Generic; 58 using System.Security; 59 #if FEATURE_MACL 60 using System.Security.AccessControl; 61 #endif 62 using System.Security.Permissions; 63 using System.Text; 64 using System.Threading; 65 using System.IO; 66 using System.Runtime.Remoting; 67 using System.Runtime.InteropServices; 68 using Microsoft.Win32.SafeHandles; 69 using System.Runtime.Versioning; 70 using System.Globalization; 71 using System.Diagnostics.Contracts; 72 using System.Diagnostics.CodeAnalysis; 73 74 #if !FEATURE_PAL 75 76 /** 77 * Registry hive values. Useful only for GetRemoteBaseKey 78 */ 79 [Serializable] 80 [System.Runtime.InteropServices.ComVisible(true)] 81 public enum RegistryHive 82 { 83 ClassesRoot = unchecked((int)0x80000000), 84 CurrentUser = unchecked((int)0x80000001), 85 LocalMachine = unchecked((int)0x80000002), 86 Users = unchecked((int)0x80000003), 87 PerformanceData = unchecked((int)0x80000004), 88 CurrentConfig = unchecked((int)0x80000005), 89 DynData = unchecked((int)0x80000006), 90 } 91 92 /** 93 * Registry encapsulation. To get an instance of a RegistryKey use the 94 * Registry class's static members then call OpenSubKey. 95 * 96 * @see Registry 97 * @security(checkDllCalls=off) 98 * @security(checkClassLinking=on) 99 */ 100 #if FEATURE_REMOTING 101 [ComVisible(true)] 102 public sealed class RegistryKey : MarshalByRefObject, IDisposable 103 #else 104 [ComVisible(true)] 105 public sealed class RegistryKey : IDisposable 106 #endif 107 { 108 109 // We could use const here, if C# supported ELEMENT_TYPE_I fully. 110 internal static readonly IntPtr HKEY_CLASSES_ROOT = new IntPtr(unchecked((int)0x80000000)); 111 internal static readonly IntPtr HKEY_CURRENT_USER = new IntPtr(unchecked((int)0x80000001)); 112 internal static readonly IntPtr HKEY_LOCAL_MACHINE = new IntPtr(unchecked((int)0x80000002)); 113 internal static readonly IntPtr HKEY_USERS = new IntPtr(unchecked((int)0x80000003)); 114 internal static readonly IntPtr HKEY_PERFORMANCE_DATA = new IntPtr(unchecked((int)0x80000004)); 115 internal static readonly IntPtr HKEY_CURRENT_CONFIG = new IntPtr(unchecked((int)0x80000005)); 116 internal static readonly IntPtr HKEY_DYN_DATA = new IntPtr(unchecked((int)0x80000006)); 117 118 // Dirty indicates that we have munged data that should be potentially 119 // written to disk. 120 // 121 private const int STATE_DIRTY = 0x0001; 122 123 // SystemKey indicates that this is a "SYSTEMKEY" and shouldn't be "opened" 124 // or "closed". 125 // 126 private const int STATE_SYSTEMKEY = 0x0002; 127 128 // Access 129 // 130 private const int STATE_WRITEACCESS = 0x0004; 131 132 // Indicates if this key is for HKEY_PERFORMANCE_DATA 133 private const int STATE_PERF_DATA = 0x0008; 134 135 // Names of keys. This array must be in the same order as the HKEY values listed above. 136 // 137 private static readonly String[] hkeyNames = new String[] { 138 "HKEY_CLASSES_ROOT", 139 "HKEY_CURRENT_USER", 140 "HKEY_LOCAL_MACHINE", 141 "HKEY_USERS", 142 "HKEY_PERFORMANCE_DATA", 143 "HKEY_CURRENT_CONFIG", 144 "HKEY_DYN_DATA" 145 }; 146 147 // MSDN defines the following limits for registry key names & values: 148 // Key Name: 255 characters 149 // Value name: 16,383 Unicode characters 150 // Value: either 1 MB or current available memory, depending on registry format. 151 private const int MaxKeyLength = 255; 152 private const int MaxValueLength = 16383; 153 154 [System.Security.SecurityCritical] // auto-generated 155 private volatile SafeRegistryHandle hkey = null; 156 private volatile int state = 0; 157 private volatile String keyName; 158 private volatile bool remoteKey = false; 159 private volatile RegistryKeyPermissionCheck checkMode; 160 private volatile RegistryView regView = RegistryView.Default; 161 162 /** 163 * RegistryInternalCheck values. Useful only for CheckPermission 164 */ 165 private enum RegistryInternalCheck { 166 CheckSubKeyWritePermission = 0, 167 CheckSubKeyReadPermission = 1, 168 CheckSubKeyCreatePermission = 2, 169 CheckSubTreeReadPermission = 3, 170 CheckSubTreeWritePermission = 4, 171 CheckSubTreeReadWritePermission = 5, 172 CheckValueWritePermission = 6, 173 CheckValueCreatePermission = 7, 174 CheckValueReadPermission = 8, 175 CheckKeyReadPermission = 9, 176 CheckSubTreePermission = 10, 177 CheckOpenSubKeyWithWritablePermission = 11, 178 CheckOpenSubKeyPermission = 12 179 }; 180 181 182 /** 183 * Creates a RegistryKey. 184 * 185 * This key is bound to hkey, if writable is <b>false</b> then no write operations 186 * will be allowed. 187 */ 188 [System.Security.SecurityCritical] // auto-generated RegistryKey(SafeRegistryHandle hkey, bool writable, RegistryView view)189 private RegistryKey(SafeRegistryHandle hkey, bool writable, RegistryView view) 190 : this(hkey, writable, false, false, false, view) { 191 } 192 193 194 /** 195 * Creates a RegistryKey. 196 * 197 * This key is bound to hkey, if writable is <b>false</b> then no write operations 198 * will be allowed. If systemkey is set then the hkey won't be released 199 * when the object is GC'ed. 200 * The remoteKey flag when set to true indicates that we are dealing with registry entries 201 * on a remote machine and requires the program making these calls to have full trust. 202 */ 203 [System.Security.SecurityCritical] // auto-generated RegistryKey(SafeRegistryHandle hkey, bool writable, bool systemkey, bool remoteKey, bool isPerfData, RegistryView view)204 private RegistryKey(SafeRegistryHandle hkey, bool writable, bool systemkey, bool remoteKey, bool isPerfData, RegistryView view) { 205 this.hkey = hkey; 206 this.keyName = ""; 207 this.remoteKey = remoteKey; 208 this.regView = view; 209 if (systemkey) { 210 this.state |= STATE_SYSTEMKEY; 211 } 212 if (writable) { 213 this.state |= STATE_WRITEACCESS; 214 } 215 if (isPerfData) 216 this.state |= STATE_PERF_DATA; 217 ValidateKeyView(view); 218 } 219 220 /** 221 * Closes this key, flushes it to disk if the contents have been modified. 222 */ Close()223 public void Close() { 224 Dispose(true); 225 } 226 227 [System.Security.SecuritySafeCritical] // auto-generated Dispose(bool disposing)228 private void Dispose(bool disposing) { 229 if (hkey != null) { 230 231 if (!IsSystemKey()) { 232 try { 233 hkey.Dispose(); 234 } 235 catch (IOException){ 236 // we don't really care if the handle is invalid at this point 237 } 238 finally 239 { 240 hkey = null; 241 } 242 } 243 else if (disposing && IsPerfDataKey()) { 244 // System keys should never be closed. However, we want to call RegCloseKey 245 // on HKEY_PERFORMANCE_DATA when called from PerformanceCounter.CloseSharedResources 246 // (i.e. when disposing is true) so that we release the PERFLIB cache and cause it 247 // to be refreshed (by re-reading the registry) when accessed subsequently. 248 // This is the only way we can see the just installed perf counter. 249 // NOTE: since HKEY_PERFORMANCE_DATA is process wide, there is inherent ---- in closing 250 // the key asynchronously. While Vista is smart enough to rebuild the PERFLIB resources 251 // in this situation the down level OSes are not. We have a small window of ---- between 252 // the dispose below and usage elsewhere (other threads). This is By Design. 253 // This is less of an issue when OS > NT5 (i.e Vista & higher), we can close the perfkey 254 // (to release & refresh PERFLIB resources) and the OS will rebuild PERFLIB as necessary. 255 SafeRegistryHandle.RegCloseKey(RegistryKey.HKEY_PERFORMANCE_DATA); 256 } 257 } 258 } 259 260 [System.Security.SecuritySafeCritical] // auto-generated Flush()261 public void Flush() { 262 if (hkey != null) { 263 if (IsDirty()) { 264 Win32Native.RegFlushKey(hkey); 265 } 266 } 267 } 268 269 #if FEATURE_CORECLR IDisposable.Dispose()270 void IDisposable.Dispose() 271 #else 272 public void Dispose() 273 #endif 274 { 275 Dispose(true); 276 } 277 278 /** 279 * Creates a new subkey, or opens an existing one. 280 * 281 * @param subkey Name or path to subkey to create or open. 282 * 283 * @return the subkey, or <b>null</b> if the operation failed. 284 */ 285 [ResourceExposure(ResourceScope.Machine)] 286 [ResourceConsumption(ResourceScope.Machine)] 287 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")] CreateSubKey(String subkey)288 public RegistryKey CreateSubKey(String subkey) { 289 return CreateSubKey(subkey, checkMode); 290 } 291 292 [ComVisible(false)] 293 [ResourceExposure(ResourceScope.Machine)] 294 [ResourceConsumption(ResourceScope.Machine)] CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck)295 public RegistryKey CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck) 296 { 297 return CreateSubKeyInternal(subkey, permissionCheck, null, RegistryOptions.None); 298 } 299 300 [ComVisible(false)] 301 [ResourceExposure(ResourceScope.Machine)] 302 [ResourceConsumption(ResourceScope.Machine)] CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck, RegistryOptions options)303 public RegistryKey CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck, RegistryOptions options) 304 { 305 return CreateSubKeyInternal(subkey, permissionCheck, null, options); 306 } 307 308 [ComVisible(false)] CreateSubKey(String subkey, bool writable)309 public RegistryKey CreateSubKey(String subkey, bool writable) 310 { 311 return CreateSubKeyInternal(subkey, writable ? RegistryKeyPermissionCheck.ReadWriteSubTree : RegistryKeyPermissionCheck.ReadSubTree, null, RegistryOptions.None); 312 } 313 314 [ComVisible(false)] CreateSubKey(String subkey, bool writable, RegistryOptions options)315 public RegistryKey CreateSubKey(String subkey, bool writable, RegistryOptions options) 316 { 317 return CreateSubKeyInternal(subkey, writable ? RegistryKeyPermissionCheck.ReadWriteSubTree : RegistryKeyPermissionCheck.ReadSubTree, null, options); 318 } 319 320 321 #if FEATURE_MACL 322 [ComVisible(false)] 323 [ResourceExposure(ResourceScope.Machine)] 324 [ResourceConsumption(ResourceScope.Machine)] CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck, RegistrySecurity registrySecurity)325 public unsafe RegistryKey CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck, RegistrySecurity registrySecurity) 326 { 327 return CreateSubKeyInternal(subkey, permissionCheck, registrySecurity, RegistryOptions.None); 328 } 329 330 [ComVisible(false)] 331 [ResourceExposure(ResourceScope.Machine)] 332 [ResourceConsumption(ResourceScope.Machine)] CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck, RegistryOptions registryOptions, RegistrySecurity registrySecurity)333 public unsafe RegistryKey CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck, RegistryOptions registryOptions, RegistrySecurity registrySecurity) 334 { 335 return CreateSubKeyInternal(subkey, permissionCheck, registrySecurity, registryOptions); 336 } 337 #endif 338 339 [System.Security.SecuritySafeCritical] // auto-generated 340 [ComVisible(false)] 341 [ResourceExposure(ResourceScope.Machine)] 342 [ResourceConsumption(ResourceScope.Machine)] CreateSubKeyInternal(String subkey, RegistryKeyPermissionCheck permissionCheck, object registrySecurityObj, RegistryOptions registryOptions)343 private unsafe RegistryKey CreateSubKeyInternal(String subkey, RegistryKeyPermissionCheck permissionCheck, object registrySecurityObj, RegistryOptions registryOptions) 344 { 345 ValidateKeyOptions(registryOptions); 346 ValidateKeyName(subkey); 347 ValidateKeyMode(permissionCheck); 348 EnsureWriteable(); 349 subkey = FixupName(subkey); // Fixup multiple slashes to a single slash 350 351 // only keys opened under read mode is not writable 352 if (!remoteKey) { 353 RegistryKey key = InternalOpenSubKey(subkey, (permissionCheck != RegistryKeyPermissionCheck.ReadSubTree)); 354 if (key != null) { // Key already exits 355 CheckPermission(RegistryInternalCheck.CheckSubKeyWritePermission, subkey, false, RegistryKeyPermissionCheck.Default); 356 CheckPermission(RegistryInternalCheck.CheckSubTreePermission, subkey, false, permissionCheck); 357 key.checkMode = permissionCheck; 358 return key; 359 } 360 } 361 362 CheckPermission(RegistryInternalCheck.CheckSubKeyCreatePermission, subkey, false, RegistryKeyPermissionCheck.Default); 363 364 Win32Native.SECURITY_ATTRIBUTES secAttrs = null; 365 #if FEATURE_MACL 366 RegistrySecurity registrySecurity = (RegistrySecurity)registrySecurityObj; 367 // For ACL's, get the security descriptor from the RegistrySecurity. 368 if (registrySecurity != null) { 369 secAttrs = new Win32Native.SECURITY_ATTRIBUTES(); 370 secAttrs.nLength = (int)Marshal.SizeOf(secAttrs); 371 372 byte[] sd = registrySecurity.GetSecurityDescriptorBinaryForm(); 373 // We allocate memory on the stack to improve the speed. 374 // So this part of code can't be refactored into a method. 375 byte* pSecDescriptor = stackalloc byte[sd.Length]; 376 Buffer.Memcpy(pSecDescriptor, 0, sd, 0, sd.Length); 377 secAttrs.pSecurityDescriptor = pSecDescriptor; 378 } 379 #endif 380 int disposition = 0; 381 382 // By default, the new key will be writable. 383 SafeRegistryHandle result = null; 384 int ret = Win32Native.RegCreateKeyEx(hkey, 385 subkey, 386 0, 387 null, 388 (int)registryOptions /* specifies if the key is volatile */, 389 GetRegistryKeyAccess(permissionCheck != RegistryKeyPermissionCheck.ReadSubTree) | (int)regView, 390 secAttrs, 391 out result, 392 out disposition); 393 394 if (ret == 0 && !result.IsInvalid) { 395 RegistryKey key = new RegistryKey(result, (permissionCheck != RegistryKeyPermissionCheck.ReadSubTree), false, remoteKey, false, regView); 396 CheckPermission(RegistryInternalCheck.CheckSubTreePermission, subkey, false, permissionCheck); 397 key.checkMode = permissionCheck; 398 399 if (subkey.Length == 0) 400 key.keyName = keyName; 401 else 402 key.keyName = keyName + "\\" + subkey; 403 return key; 404 } 405 else if (ret != 0) // syscall failed, ret is an error code. 406 Win32Error(ret, keyName + "\\" + subkey); // Access denied? 407 408 BCLDebug.Assert(false, "Unexpected code path in RegistryKey::CreateSubKey"); 409 return null; 410 } 411 412 /** 413 * Deletes the specified subkey. Will throw an exception if the subkey has 414 * subkeys. To delete a tree of subkeys use, DeleteSubKeyTree. 415 * 416 * @param subkey SubKey to delete. 417 * 418 * @exception InvalidOperationException thrown if the subkey has child subkeys. 419 */ 420 [ResourceExposure(ResourceScope.Machine)] 421 [ResourceConsumption(ResourceScope.Machine)] DeleteSubKey(String subkey)422 public void DeleteSubKey(String subkey) { 423 DeleteSubKey(subkey, true); 424 } 425 426 [System.Security.SecuritySafeCritical] // auto-generated 427 [ResourceExposure(ResourceScope.Machine)] 428 [ResourceConsumption(ResourceScope.Machine)] DeleteSubKey(String subkey, bool throwOnMissingSubKey)429 public void DeleteSubKey(String subkey, bool throwOnMissingSubKey) { 430 ValidateKeyName(subkey); 431 EnsureWriteable(); 432 subkey = FixupName(subkey); // Fixup multiple slashes to a single slash 433 CheckPermission(RegistryInternalCheck.CheckSubKeyWritePermission, subkey, false, RegistryKeyPermissionCheck.Default); 434 435 // Open the key we are deleting and check for children. Be sure to 436 // explicitly call close to avoid keeping an extra HKEY open. 437 // 438 RegistryKey key = InternalOpenSubKey(subkey,false); 439 if (key != null) { 440 try { 441 if (key.InternalSubKeyCount() > 0) { 442 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_RegRemoveSubKey); 443 } 444 } 445 finally { 446 key.Close(); 447 } 448 449 int ret; 450 451 try { 452 ret = Win32Native.RegDeleteKeyEx(hkey, subkey, (int)regView, 0); 453 } 454 catch (EntryPointNotFoundException) { 455 // 456 ret = Win32Native.RegDeleteKey(hkey, subkey); 457 } 458 459 if (ret!=0) { 460 if (ret == Win32Native.ERROR_FILE_NOT_FOUND) { 461 if (throwOnMissingSubKey) 462 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSubKeyAbsent); 463 } 464 else 465 Win32Error(ret, null); 466 } 467 } 468 else { // there is no key which also means there is no subkey 469 if (throwOnMissingSubKey) 470 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSubKeyAbsent); 471 } 472 } 473 474 /** 475 * Recursively deletes a subkey and any child subkeys. 476 * 477 * @param subkey SubKey to delete. 478 */ 479 [ResourceExposure(ResourceScope.Machine)] 480 [ResourceConsumption(ResourceScope.Machine)] DeleteSubKeyTree(String subkey)481 public void DeleteSubKeyTree(String subkey) { 482 DeleteSubKeyTree(subkey, true /*throwOnMissingSubKey*/); 483 } 484 485 [System.Security.SecuritySafeCritical] // auto-generated 486 [ComVisible(false)] 487 [ResourceExposure(ResourceScope.Machine)] 488 [ResourceConsumption(ResourceScope.Machine)] DeleteSubKeyTree(String subkey, Boolean throwOnMissingSubKey)489 public void DeleteSubKeyTree(String subkey, Boolean throwOnMissingSubKey) { 490 ValidateKeyName(subkey); 491 492 // Security concern: Deleting a hive's "" subkey would delete all 493 // of that hive's contents. Don't allow "". 494 if (subkey.Length==0 && IsSystemKey()) { 495 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegKeyDelHive); 496 } 497 498 EnsureWriteable(); 499 500 subkey = FixupName(subkey); // Fixup multiple slashes to a single slash 501 CheckPermission(RegistryInternalCheck.CheckSubTreeWritePermission, subkey, false, RegistryKeyPermissionCheck.Default); 502 503 RegistryKey key = InternalOpenSubKey(subkey, true); 504 if (key != null) { 505 try { 506 if (key.InternalSubKeyCount() > 0) { 507 String[] keys = key.InternalGetSubKeyNames(); 508 509 for (int i=0; i<keys.Length; i++) { 510 key.DeleteSubKeyTreeInternal(keys[i]); 511 } 512 } 513 } 514 finally { 515 key.Close(); 516 } 517 518 int ret; 519 try { 520 ret = Win32Native.RegDeleteKeyEx(hkey, subkey, (int)regView, 0); 521 } 522 catch (EntryPointNotFoundException) { 523 // 524 ret = Win32Native.RegDeleteKey(hkey, subkey); 525 } 526 527 if (ret!=0) Win32Error(ret, null); 528 } 529 else if(throwOnMissingSubKey) { 530 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSubKeyAbsent); 531 } 532 } 533 534 // An internal version which does no security checks or argument checking. Skipping the 535 // security checks should give us a slight perf gain on large trees. 536 [System.Security.SecurityCritical] // auto-generated 537 [ResourceExposure(ResourceScope.Machine)] 538 [ResourceConsumption(ResourceScope.Machine)] DeleteSubKeyTreeInternal(string subkey)539 private void DeleteSubKeyTreeInternal(string subkey) { 540 RegistryKey key = InternalOpenSubKey(subkey, true); 541 if (key != null) { 542 try { 543 if (key.InternalSubKeyCount() > 0) { 544 String[] keys = key.InternalGetSubKeyNames(); 545 546 for (int i=0; i<keys.Length; i++) { 547 key.DeleteSubKeyTreeInternal(keys[i]); 548 } 549 } 550 } 551 finally { 552 key.Close(); 553 } 554 555 int ret; 556 try { 557 ret = Win32Native.RegDeleteKeyEx(hkey, subkey, (int)regView, 0); 558 } 559 catch (EntryPointNotFoundException) { 560 // 561 ret = Win32Native.RegDeleteKey(hkey, subkey); 562 } 563 if (ret!=0) Win32Error(ret, null); 564 } 565 else { 566 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSubKeyAbsent); 567 } 568 } 569 570 /** 571 * Deletes the specified value from this key. 572 * 573 * @param name Name of value to delete. 574 */ 575 [ResourceExposure(ResourceScope.None)] 576 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] DeleteValue(String name)577 public void DeleteValue(String name) { 578 DeleteValue(name, true); 579 } 580 581 [System.Security.SecuritySafeCritical] // auto-generated 582 [ResourceExposure(ResourceScope.None)] 583 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] DeleteValue(String name, bool throwOnMissingValue)584 public void DeleteValue(String name, bool throwOnMissingValue) { 585 EnsureWriteable(); 586 CheckPermission(RegistryInternalCheck.CheckValueWritePermission, name, false, RegistryKeyPermissionCheck.Default); 587 int errorCode = Win32Native.RegDeleteValue(hkey, name); 588 589 // 590 // From windows 2003 server, if the name is too long we will get error code ERROR_FILENAME_EXCED_RANGE 591 // This still means the name doesn't exist. We need to be consistent with previous OS. 592 // 593 if (errorCode == Win32Native.ERROR_FILE_NOT_FOUND || errorCode == Win32Native.ERROR_FILENAME_EXCED_RANGE) { 594 if (throwOnMissingValue) { 595 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSubKeyValueAbsent); 596 } 597 // Otherwise, just return giving no indication to the user. 598 // (For compatibility) 599 } 600 // We really should throw an exception here if errorCode was bad, 601 // but we can't for compatibility reasons. 602 BCLDebug.Correctness(errorCode == 0, "RegDeleteValue failed. Here's your error code: "+errorCode); 603 } 604 605 /** 606 * Retrieves a new RegistryKey that represents the requested key. Valid 607 * values are: 608 * 609 * HKEY_CLASSES_ROOT, 610 * HKEY_CURRENT_USER, 611 * HKEY_LOCAL_MACHINE, 612 * HKEY_USERS, 613 * HKEY_PERFORMANCE_DATA, 614 * HKEY_CURRENT_CONFIG, 615 * HKEY_DYN_DATA. 616 * 617 * @param hKey HKEY_* to open. 618 * 619 * @return the RegistryKey requested. 620 */ 621 [System.Security.SecurityCritical] // auto-generated GetBaseKey(IntPtr hKey)622 internal static RegistryKey GetBaseKey(IntPtr hKey) { 623 return GetBaseKey(hKey, RegistryView.Default); 624 } 625 626 [System.Security.SecurityCritical] // auto-generated GetBaseKey(IntPtr hKey, RegistryView view)627 internal static RegistryKey GetBaseKey(IntPtr hKey, RegistryView view) { 628 629 int index = ((int)hKey) & 0x0FFFFFFF; 630 BCLDebug.Assert(index >= 0 && index < hkeyNames.Length, "index is out of range!"); 631 BCLDebug.Assert((((int)hKey) & 0xFFFFFFF0) == 0x80000000, "Invalid hkey value!"); 632 633 bool isPerf = hKey == HKEY_PERFORMANCE_DATA; 634 // only mark the SafeHandle as ownsHandle if the key is HKEY_PERFORMANCE_DATA. 635 SafeRegistryHandle srh = new SafeRegistryHandle(hKey, isPerf); 636 637 RegistryKey key = new RegistryKey(srh, true, true,false, isPerf, view); 638 key.checkMode = RegistryKeyPermissionCheck.Default; 639 key.keyName = hkeyNames[index]; 640 return key; 641 } 642 643 644 [System.Security.SecuritySafeCritical] // auto-generated 645 [ResourceExposure(ResourceScope.Machine)] 646 [ResourceConsumption(ResourceScope.Machine)] 647 [ComVisible(false)] OpenBaseKey(RegistryHive hKey, RegistryView view)648 public static RegistryKey OpenBaseKey(RegistryHive hKey, RegistryView view) { 649 ValidateKeyView(view); 650 CheckUnmanagedCodePermission(); 651 return GetBaseKey((IntPtr)((int)hKey), view); 652 } 653 654 /** 655 * Retrieves a new RegistryKey that represents the requested key on a foreign 656 * machine. Valid values for hKey are members of the RegistryHive enum, or 657 * Win32 integers such as: 658 * 659 * HKEY_CLASSES_ROOT, 660 * HKEY_CURRENT_USER, 661 * HKEY_LOCAL_MACHINE, 662 * HKEY_USERS, 663 * HKEY_PERFORMANCE_DATA, 664 * HKEY_CURRENT_CONFIG, 665 * HKEY_DYN_DATA. 666 * 667 * @param hKey HKEY_* to open. 668 * @param machineName the machine to connect to 669 * 670 * @return the RegistryKey requested. 671 */ 672 [ResourceExposure(ResourceScope.Machine)] 673 [ResourceConsumption(ResourceScope.Machine)] OpenRemoteBaseKey(RegistryHive hKey, String machineName)674 public static RegistryKey OpenRemoteBaseKey(RegistryHive hKey, String machineName) { 675 return OpenRemoteBaseKey(hKey, machineName, RegistryView.Default); 676 } 677 678 [System.Security.SecuritySafeCritical] // auto-generated 679 [ComVisible(false)] 680 [ResourceExposure(ResourceScope.Machine)] 681 [ResourceConsumption(ResourceScope.Machine)] OpenRemoteBaseKey(RegistryHive hKey, String machineName, RegistryView view)682 public static RegistryKey OpenRemoteBaseKey(RegistryHive hKey, String machineName, RegistryView view) { 683 if (machineName==null) 684 throw new ArgumentNullException("machineName"); 685 int index = (int)hKey & 0x0FFFFFFF; 686 if (index < 0 || index >= hkeyNames.Length || ((int)hKey & 0xFFFFFFF0) != 0x80000000) { 687 throw new ArgumentException(Environment.GetResourceString("Arg_RegKeyOutOfRange")); 688 } 689 ValidateKeyView(view); 690 691 CheckUnmanagedCodePermission(); 692 // connect to the specified remote registry 693 SafeRegistryHandle foreignHKey = null; 694 int ret = Win32Native.RegConnectRegistry(machineName, new SafeRegistryHandle(new IntPtr((int)hKey), false), out foreignHKey); 695 696 if (ret == Win32Native.ERROR_DLL_INIT_FAILED) 697 // return value indicates an error occurred 698 throw new ArgumentException(Environment.GetResourceString("Arg_DllInitFailure")); 699 700 if (ret != 0) 701 Win32ErrorStatic(ret, null); 702 703 if (foreignHKey.IsInvalid) 704 // return value indicates an error occurred 705 throw new ArgumentException(Environment.GetResourceString("Arg_RegKeyNoRemoteConnect", machineName)); 706 707 RegistryKey key = new RegistryKey(foreignHKey, true, false, true, ((IntPtr) hKey) == HKEY_PERFORMANCE_DATA, view); 708 key.checkMode = RegistryKeyPermissionCheck.Default; 709 key.keyName = hkeyNames[index]; 710 return key; 711 } 712 713 /** 714 * Retrieves a subkey. If readonly is <b>true</b>, then the subkey is opened with 715 * read-only access. 716 * 717 * @param name Name or path of subkey to open. 718 * @param readonly Set to <b>true</b> if you only need readonly access. 719 * 720 * @return the Subkey requested, or <b>null</b> if the operation failed. 721 */ 722 #if FEATURE_CORECLR 723 [System.Security.SecurityCritical] // auto-generated 724 #else 725 [System.Security.SecuritySafeCritical] 726 #endif 727 [ResourceExposure(ResourceScope.Machine)] 728 [ResourceConsumption(ResourceScope.Machine)] OpenSubKey(string name, bool writable )729 public RegistryKey OpenSubKey(string name, bool writable ) { 730 ValidateKeyName(name); 731 EnsureNotDisposed(); 732 name = FixupName(name); // Fixup multiple slashes to a single slash 733 734 CheckPermission(RegistryInternalCheck.CheckOpenSubKeyWithWritablePermission, name, writable, RegistryKeyPermissionCheck.Default); 735 SafeRegistryHandle result = null; 736 int ret = Win32Native.RegOpenKeyEx(hkey, 737 name, 738 0, 739 GetRegistryKeyAccess(writable) | (int)regView, 740 out result); 741 742 if (ret == 0 && !result.IsInvalid) { 743 RegistryKey key = new RegistryKey(result, writable, false, remoteKey, false, regView); 744 key.checkMode = GetSubKeyPermissonCheck(writable); 745 key.keyName = keyName + "\\" + name; 746 return key; 747 } 748 749 // Return null if we didn't find the key. 750 if (ret == Win32Native.ERROR_ACCESS_DENIED || ret == Win32Native.ERROR_BAD_IMPERSONATION_LEVEL) { 751 // We need to throw SecurityException here for compatibility reasons, 752 // although UnauthorizedAccessException will make more sense. 753 ThrowHelper.ThrowSecurityException(ExceptionResource.Security_RegistryPermission); 754 } 755 756 return null; 757 } 758 759 #if FEATURE_MACL 760 761 [System.Security.SecuritySafeCritical] // auto-generated 762 [ComVisible(false)] 763 [ResourceExposure(ResourceScope.Machine)] 764 [ResourceConsumption(ResourceScope.Machine)] OpenSubKey(String name, RegistryKeyPermissionCheck permissionCheck)765 public RegistryKey OpenSubKey(String name, RegistryKeyPermissionCheck permissionCheck) { 766 ValidateKeyMode(permissionCheck); 767 return InternalOpenSubKey(name, permissionCheck, GetRegistryKeyAccess(permissionCheck)); 768 } 769 770 [System.Security.SecuritySafeCritical] 771 [ComVisible(false)] OpenSubKey(String name, RegistryRights rights)772 public RegistryKey OpenSubKey(String name, RegistryRights rights) 773 { 774 return InternalOpenSubKey(name, this.checkMode, (int)rights); 775 } 776 777 [System.Security.SecuritySafeCritical] // auto-generated 778 [ComVisible(false)] 779 [ResourceExposure(ResourceScope.Machine)] 780 [ResourceConsumption(ResourceScope.Machine)] OpenSubKey(String name, RegistryKeyPermissionCheck permissionCheck, RegistryRights rights)781 public RegistryKey OpenSubKey(String name, RegistryKeyPermissionCheck permissionCheck, RegistryRights rights) { 782 return InternalOpenSubKey(name, permissionCheck, (int)rights); 783 } 784 785 [System.Security.SecurityCritical] // auto-generated 786 [ResourceExposure(ResourceScope.Machine)] 787 [ResourceConsumption(ResourceScope.Machine)] InternalOpenSubKey(String name, RegistryKeyPermissionCheck permissionCheck, int rights)788 private RegistryKey InternalOpenSubKey(String name, RegistryKeyPermissionCheck permissionCheck, int rights) { 789 ValidateKeyName(name); 790 ValidateKeyMode(permissionCheck); 791 792 ValidateKeyRights(rights); 793 794 EnsureNotDisposed(); 795 name = FixupName(name); // Fixup multiple slashes to a single slash 796 797 CheckPermission(RegistryInternalCheck.CheckOpenSubKeyPermission, name, false, permissionCheck); 798 CheckPermission(RegistryInternalCheck.CheckSubTreePermission, name, false, permissionCheck); 799 SafeRegistryHandle result = null; 800 int ret = Win32Native.RegOpenKeyEx(hkey, name, 0, (rights | (int)regView), out result); 801 if (ret == 0 && !result.IsInvalid) { 802 RegistryKey key = new RegistryKey(result, (permissionCheck == RegistryKeyPermissionCheck.ReadWriteSubTree), false, remoteKey, false, regView); 803 key.keyName = keyName + "\\" + name; 804 key.checkMode = permissionCheck; 805 return key; 806 } 807 808 // Return null if we didn't find the key. 809 if (ret == Win32Native.ERROR_ACCESS_DENIED || ret == Win32Native.ERROR_BAD_IMPERSONATION_LEVEL) { 810 // We need to throw SecurityException here for compatiblity reason, 811 // although UnauthorizedAccessException will make more sense. 812 ThrowHelper.ThrowSecurityException(ExceptionResource.Security_RegistryPermission); 813 } 814 815 return null; 816 } 817 #endif 818 819 // This required no security checks. This is to get around the Deleting SubKeys which only require 820 // write permission. They call OpenSubKey which required read. Now instead call this function w/o security checks 821 [System.Security.SecurityCritical] // auto-generated 822 [ResourceExposure(ResourceScope.Machine)] 823 [ResourceConsumption(ResourceScope.Machine)] InternalOpenSubKey(String name, bool writable)824 internal RegistryKey InternalOpenSubKey(String name, bool writable) { 825 ValidateKeyName(name); 826 EnsureNotDisposed(); 827 828 SafeRegistryHandle result = null; 829 int ret = Win32Native.RegOpenKeyEx(hkey, 830 name, 831 0, 832 GetRegistryKeyAccess(writable) | (int)regView, 833 out result); 834 835 if (ret == 0 && !result.IsInvalid) { 836 RegistryKey key = new RegistryKey(result, writable, false, remoteKey, false, regView); 837 key.keyName = keyName + "\\" + name; 838 return key; 839 } 840 return null; 841 } 842 843 /** 844 * Returns a subkey with read only permissions. 845 * 846 * @param name Name or path of subkey to open. 847 * 848 * @return the Subkey requested, or <b>null</b> if the operation failed. 849 */ 850 [ResourceExposure(ResourceScope.Machine)] 851 [ResourceConsumption(ResourceScope.Machine)] 852 #if FEATURE_CORECLR 853 [System.Security.SecurityCritical] 854 #endif OpenSubKey(String name)855 public RegistryKey OpenSubKey(String name) { 856 return OpenSubKey(name, false); 857 } 858 859 /** 860 * Retrieves the count of subkeys. 861 * 862 * @return a count of subkeys. 863 */ 864 public int SubKeyCount { 865 [System.Security.SecuritySafeCritical] // auto-generated 866 get { 867 CheckPermission(RegistryInternalCheck.CheckKeyReadPermission, null, false, RegistryKeyPermissionCheck.Default); 868 return InternalSubKeyCount(); 869 } 870 } 871 872 [ComVisible(false)] 873 public RegistryView View { 874 [System.Security.SecuritySafeCritical] 875 get { 876 EnsureNotDisposed(); 877 return regView; 878 } 879 } 880 881 #if !FEATURE_CORECLR 882 [ComVisible(false)] 883 public SafeRegistryHandle Handle { 884 [ResourceExposure(ResourceScope.Machine)] 885 [ResourceConsumption(ResourceScope.Machine)] 886 [System.Security.SecurityCritical] 887 [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] 888 get { 889 EnsureNotDisposed(); 890 int ret = Win32Native.ERROR_INVALID_HANDLE; 891 if (IsSystemKey()) { 892 IntPtr baseKey = (IntPtr)0; 893 switch (keyName) { 894 case "HKEY_CLASSES_ROOT": 895 baseKey = HKEY_CLASSES_ROOT; 896 break; 897 case "HKEY_CURRENT_USER": 898 baseKey = HKEY_CURRENT_USER; 899 break; 900 case "HKEY_LOCAL_MACHINE": 901 baseKey = HKEY_LOCAL_MACHINE; 902 break; 903 case "HKEY_USERS": 904 baseKey = HKEY_USERS; 905 break; 906 case "HKEY_PERFORMANCE_DATA": 907 baseKey = HKEY_PERFORMANCE_DATA; 908 break; 909 case "HKEY_CURRENT_CONFIG": 910 baseKey = HKEY_CURRENT_CONFIG; 911 break; 912 case "HKEY_DYN_DATA": 913 baseKey = HKEY_DYN_DATA; 914 break; 915 default: 916 Win32Error(ret, null); 917 break; 918 } 919 // open the base key so that RegistryKey.Handle will return a valid handle 920 SafeRegistryHandle result; 921 ret = Win32Native.RegOpenKeyEx(baseKey, 922 null, 923 0, 924 GetRegistryKeyAccess(IsWritable()) | (int)regView, 925 out result); 926 927 if (ret == 0 && !result.IsInvalid) { 928 return result; 929 } 930 else { 931 Win32Error(ret, null); 932 } 933 } 934 else { 935 return hkey; 936 } 937 throw new IOException(Win32Native.GetMessage(ret), ret); 938 } 939 } 940 941 [ResourceExposure(ResourceScope.Machine)] 942 [ResourceConsumption(ResourceScope.Machine)] 943 [System.Security.SecurityCritical] 944 [ComVisible(false)] 945 [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] FromHandle(SafeRegistryHandle handle)946 public static RegistryKey FromHandle(SafeRegistryHandle handle) { 947 return FromHandle(handle, RegistryView.Default); 948 } 949 950 [ResourceExposure(ResourceScope.Machine)] 951 [ResourceConsumption(ResourceScope.Machine)] 952 [System.Security.SecurityCritical] 953 [ComVisible(false)] 954 [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] FromHandle(SafeRegistryHandle handle, RegistryView view)955 public static RegistryKey FromHandle(SafeRegistryHandle handle, RegistryView view) { 956 if (handle == null) throw new ArgumentNullException("handle"); 957 ValidateKeyView(view); 958 959 return new RegistryKey(handle, true /* isWritable */, view); 960 } 961 #endif 962 963 [System.Security.SecurityCritical] // auto-generated InternalSubKeyCount()964 internal int InternalSubKeyCount() { 965 EnsureNotDisposed(); 966 967 int subkeys = 0; 968 int junk = 0; 969 int ret = Win32Native.RegQueryInfoKey(hkey, 970 null, 971 null, 972 IntPtr.Zero, 973 ref subkeys, // subkeys 974 null, 975 null, 976 ref junk, // values 977 null, 978 null, 979 null, 980 null); 981 982 if (ret != 0) 983 Win32Error(ret, null); 984 return subkeys; 985 } 986 987 /** 988 * Retrieves an array of strings containing all the subkey names. 989 * 990 * @return all subkey names. 991 */ 992 #if FEATURE_CORECLR 993 [System.Security.SecurityCritical] // auto-generated 994 #else 995 [System.Security.SecuritySafeCritical] 996 #endif GetSubKeyNames()997 public String[] GetSubKeyNames() { 998 CheckPermission(RegistryInternalCheck.CheckKeyReadPermission, null, false, RegistryKeyPermissionCheck.Default); 999 return InternalGetSubKeyNames(); 1000 } 1001 1002 [System.Security.SecurityCritical] // auto-generated InternalGetSubKeyNames()1003 internal unsafe String[] InternalGetSubKeyNames() { 1004 EnsureNotDisposed(); 1005 int subkeys = InternalSubKeyCount(); 1006 String[] names = new String[subkeys]; // Returns 0-length array if empty. 1007 1008 if (subkeys > 0) { 1009 char[] name = new char[MaxKeyLength + 1]; 1010 1011 int namelen; 1012 1013 fixed (char *namePtr = &name[0]) 1014 { 1015 for (int i=0; i<subkeys; i++) { 1016 namelen = name.Length; // Don't remove this. The API's doesn't work if this is not properly initialised. 1017 int ret = Win32Native.RegEnumKeyEx(hkey, 1018 i, 1019 namePtr, 1020 ref namelen, 1021 null, 1022 null, 1023 null, 1024 null); 1025 if (ret != 0) 1026 Win32Error(ret, null); 1027 names[i] = new String(namePtr); 1028 } 1029 } 1030 } 1031 1032 return names; 1033 } 1034 1035 /** 1036 * Retrieves the count of values. 1037 * 1038 * @return a count of values. 1039 */ 1040 public int ValueCount { 1041 [System.Security.SecuritySafeCritical] // auto-generated 1042 get { 1043 CheckPermission(RegistryInternalCheck.CheckKeyReadPermission, null, false, RegistryKeyPermissionCheck.Default); 1044 return InternalValueCount(); 1045 } 1046 } 1047 1048 [System.Security.SecurityCritical] // auto-generated InternalValueCount()1049 internal int InternalValueCount() { 1050 EnsureNotDisposed(); 1051 int values = 0; 1052 int junk = 0; 1053 int ret = Win32Native.RegQueryInfoKey(hkey, 1054 null, 1055 null, 1056 IntPtr.Zero, 1057 ref junk, // subkeys 1058 null, 1059 null, 1060 ref values, // values 1061 null, 1062 null, 1063 null, 1064 null); 1065 if (ret != 0) 1066 Win32Error(ret, null); 1067 return values; 1068 } 1069 1070 /** 1071 * Retrieves an array of strings containing all the value names. 1072 * 1073 * @return all value names. 1074 */ 1075 [System.Security.SecuritySafeCritical] // auto-generated GetValueNames()1076 public unsafe String[] GetValueNames() { 1077 CheckPermission(RegistryInternalCheck.CheckKeyReadPermission, null, false, RegistryKeyPermissionCheck.Default); 1078 EnsureNotDisposed(); 1079 1080 int values = InternalValueCount(); 1081 String[] names = new String[values]; 1082 1083 if (values > 0) { 1084 char[] name = new char[MaxValueLength + 1]; 1085 int namelen; 1086 1087 fixed (char *namePtr = &name[0]) 1088 { 1089 for (int i=0; i<values; i++) { 1090 namelen = name.Length; 1091 1092 int ret = Win32Native.RegEnumValue(hkey, 1093 i, 1094 namePtr, 1095 ref namelen, 1096 IntPtr.Zero, 1097 null, 1098 null, 1099 null); 1100 1101 if (ret != 0) { 1102 // ignore ERROR_MORE_DATA if we're querying HKEY_PERFORMANCE_DATA 1103 if (!(IsPerfDataKey() && ret == Win32Native.ERROR_MORE_DATA)) 1104 Win32Error(ret, null); 1105 } 1106 1107 names[i] = new String(namePtr); 1108 } 1109 } 1110 } 1111 1112 return names; 1113 } 1114 1115 /** 1116 * Retrieves the specified value. <b>null</b> is returned if the value 1117 * doesn't exist. 1118 * 1119 * Note that <var>name</var> can be null or "", at which point the 1120 * unnamed or default value of this Registry key is returned, if any. 1121 * 1122 * @param name Name of value to retrieve. 1123 * 1124 * @return the data associated with the value. 1125 */ 1126 [System.Security.SecuritySafeCritical] // auto-generated GetValue(String name)1127 public Object GetValue(String name) { 1128 CheckPermission(RegistryInternalCheck.CheckValueReadPermission, name, false, RegistryKeyPermissionCheck.Default); 1129 return InternalGetValue(name, null, false, true); 1130 } 1131 1132 /** 1133 * Retrieves the specified value. <i>defaultValue</i> is returned if the value doesn't exist. 1134 * 1135 * Note that <var>name</var> can be null or "", at which point the 1136 * unnamed or default value of this Registry key is returned, if any. 1137 * The default values for RegistryKeys are OS-dependent. NT doesn't 1138 * have them by default, but they can exist and be of any type. On 1139 * Win95, the default value is always an empty key of type REG_SZ. 1140 * Win98 supports default values of any type, but defaults to REG_SZ. 1141 * 1142 * @param name Name of value to retrieve. 1143 * @param defaultValue Value to return if <i>name</i> doesn't exist. 1144 * 1145 * @return the data associated with the value. 1146 */ 1147 #if FEATURE_CORECLR 1148 [System.Security.SecurityCritical] // auto-generated 1149 #else 1150 [System.Security.SecuritySafeCritical] 1151 #endif GetValue(String name, Object defaultValue)1152 public Object GetValue(String name, Object defaultValue) { 1153 CheckPermission(RegistryInternalCheck.CheckValueReadPermission, name, false, RegistryKeyPermissionCheck.Default); 1154 return InternalGetValue(name, defaultValue, false, true); 1155 } 1156 1157 #if FEATURE_CORECLR 1158 [System.Security.SecurityCritical] // auto-generated 1159 #else 1160 [System.Security.SecuritySafeCritical] 1161 #endif 1162 [ComVisible(false)] GetValue(String name, Object defaultValue, RegistryValueOptions options)1163 public Object GetValue(String name, Object defaultValue, RegistryValueOptions options) { 1164 if( options < RegistryValueOptions.None || options > RegistryValueOptions.DoNotExpandEnvironmentNames) { 1165 throw new ArgumentException(Environment.GetResourceString("Arg_EnumIllegalVal", (int)options), "options"); 1166 } 1167 bool doNotExpand = (options == RegistryValueOptions.DoNotExpandEnvironmentNames); 1168 CheckPermission(RegistryInternalCheck.CheckValueReadPermission, name, false, RegistryKeyPermissionCheck.Default); 1169 return InternalGetValue(name, defaultValue, doNotExpand, true); 1170 } 1171 1172 [System.Security.SecurityCritical] // auto-generated InternalGetValue(String name, Object defaultValue, bool doNotExpand, bool checkSecurity)1173 internal Object InternalGetValue(String name, Object defaultValue, bool doNotExpand, bool checkSecurity) { 1174 if (checkSecurity) { 1175 // Name can be null! It's the most common use of RegQueryValueEx 1176 EnsureNotDisposed(); 1177 } 1178 1179 Object data = defaultValue; 1180 int type = 0; 1181 int datasize = 0; 1182 1183 int ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, (byte[])null, ref datasize); 1184 1185 if (ret != 0) { 1186 if (IsPerfDataKey()) { 1187 int size = 65000; 1188 int sizeInput = size; 1189 1190 int r; 1191 byte[] blob = new byte[size]; 1192 while (Win32Native.ERROR_MORE_DATA == (r = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref sizeInput))) { 1193 if (size == Int32.MaxValue) { 1194 // ERROR_MORE_DATA was returned however we cannot increase the buffer size beyond Int32.MaxValue 1195 Win32Error(r, name); 1196 } 1197 else if (size > (Int32.MaxValue / 2)) { 1198 // at this point in the loop "size * 2" would cause an overflow 1199 size = Int32.MaxValue; 1200 } 1201 else { 1202 size *= 2; 1203 } 1204 sizeInput = size; 1205 blob = new byte[size]; 1206 } 1207 if (r != 0) 1208 Win32Error(r, name); 1209 return blob; 1210 } 1211 else { 1212 // For stuff like ERROR_FILE_NOT_FOUND, we want to return null (data). 1213 // Some OS's returned ERROR_MORE_DATA even in success cases, so we 1214 // want to continue on through the function. 1215 if (ret != Win32Native.ERROR_MORE_DATA) 1216 return data; 1217 } 1218 } 1219 1220 if (datasize < 0) { 1221 // unexpected code path 1222 BCLDebug.Assert(false, "[InternalGetValue] RegQueryValue returned ERROR_SUCCESS but gave a negative datasize"); 1223 datasize = 0; 1224 } 1225 1226 1227 switch (type) { 1228 case Win32Native.REG_NONE: 1229 case Win32Native.REG_DWORD_BIG_ENDIAN: 1230 case Win32Native.REG_BINARY: { 1231 byte[] blob = new byte[datasize]; 1232 ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize); 1233 data = blob; 1234 } 1235 break; 1236 case Win32Native.REG_QWORD: 1237 { // also REG_QWORD_LITTLE_ENDIAN 1238 if (datasize > 8) { 1239 // prevent an AV in the edge case that datasize is larger than sizeof(long) 1240 goto case Win32Native.REG_BINARY; 1241 } 1242 long blob = 0; 1243 BCLDebug.Assert(datasize==8, "datasize==8"); 1244 // Here, datasize must be 8 when calling this 1245 ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, ref blob, ref datasize); 1246 1247 data = blob; 1248 } 1249 break; 1250 case Win32Native.REG_DWORD: 1251 { // also REG_DWORD_LITTLE_ENDIAN 1252 if (datasize > 4) { 1253 // prevent an AV in the edge case that datasize is larger than sizeof(int) 1254 goto case Win32Native.REG_QWORD; 1255 } 1256 int blob = 0; 1257 BCLDebug.Assert(datasize==4, "datasize==4"); 1258 // Here, datasize must be four when calling this 1259 ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, ref blob, ref datasize); 1260 1261 data = blob; 1262 } 1263 break; 1264 1265 case Win32Native.REG_SZ: 1266 { 1267 if (datasize % 2 == 1) { 1268 // handle the case where the registry contains an odd-byte length (corrupt data?) 1269 try { 1270 datasize = checked(datasize + 1); 1271 } 1272 catch (OverflowException e) { 1273 throw new IOException(Environment.GetResourceString("Arg_RegGetOverflowBug"), e); 1274 } 1275 } 1276 char[] blob = new char[datasize/2]; 1277 1278 ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize); 1279 if (blob.Length > 0 && blob[blob.Length - 1] == (char)0) { 1280 data = new String(blob, 0, blob.Length - 1); 1281 } 1282 else { 1283 // in the very unlikely case the data is missing null termination, 1284 // pass in the whole char[] to prevent truncating a character 1285 data = new String(blob); 1286 } 1287 } 1288 break; 1289 1290 case Win32Native.REG_EXPAND_SZ: 1291 { 1292 if (datasize % 2 == 1) { 1293 // handle the case where the registry contains an odd-byte length (corrupt data?) 1294 try { 1295 datasize = checked(datasize + 1); 1296 } 1297 catch (OverflowException e) { 1298 throw new IOException(Environment.GetResourceString("Arg_RegGetOverflowBug"), e); 1299 } 1300 } 1301 char[] blob = new char[datasize/2]; 1302 1303 ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize); 1304 1305 if (blob.Length > 0 && blob[blob.Length - 1] == (char)0) { 1306 data = new String(blob, 0, blob.Length - 1); 1307 } 1308 else { 1309 // in the very unlikely case the data is missing null termination, 1310 // pass in the whole char[] to prevent truncating a character 1311 data = new String(blob); 1312 } 1313 1314 if (!doNotExpand) 1315 data = Environment.ExpandEnvironmentVariables((String)data); 1316 } 1317 break; 1318 case Win32Native.REG_MULTI_SZ: 1319 { 1320 if (datasize % 2 == 1) { 1321 // handle the case where the registry contains an odd-byte length (corrupt data?) 1322 try { 1323 datasize = checked(datasize + 1); 1324 } 1325 catch (OverflowException e) { 1326 throw new IOException(Environment.GetResourceString("Arg_RegGetOverflowBug"), e); 1327 } 1328 } 1329 char[] blob = new char[datasize/2]; 1330 1331 ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize); 1332 1333 // make sure the string is null terminated before processing the data 1334 if (blob.Length > 0 && blob[blob.Length - 1] != (char)0) { 1335 try { 1336 char[] newBlob = new char[checked(blob.Length + 1)]; 1337 for (int i = 0; i < blob.Length; i++) { 1338 newBlob[i] = blob[i]; 1339 } 1340 newBlob[newBlob.Length - 1] = (char)0; 1341 blob = newBlob; 1342 } 1343 catch (OverflowException e) { 1344 throw new IOException(Environment.GetResourceString("Arg_RegGetOverflowBug"), e); 1345 } 1346 blob[blob.Length - 1] = (char)0; 1347 } 1348 1349 1350 IList<String> strings = new List<String>(); 1351 int cur = 0; 1352 int len = blob.Length; 1353 1354 while (ret == 0 && cur < len) { 1355 int nextNull = cur; 1356 while (nextNull < len && blob[nextNull] != (char)0) { 1357 nextNull++; 1358 } 1359 1360 if (nextNull < len) { 1361 BCLDebug.Assert(blob[nextNull] == (char)0, "blob[nextNull] should be 0"); 1362 if (nextNull-cur > 0) { 1363 strings.Add(new String(blob, cur, nextNull-cur)); 1364 } 1365 else { 1366 // we found an empty string. But if we're at the end of the data, 1367 // it's just the extra null terminator. 1368 if (nextNull != len-1) 1369 strings.Add(String.Empty); 1370 } 1371 } 1372 else { 1373 strings.Add(new String(blob, cur, len-cur)); 1374 } 1375 cur = nextNull+1; 1376 } 1377 1378 data = new String[strings.Count]; 1379 strings.CopyTo((String[])data, 0); 1380 } 1381 break; 1382 case Win32Native.REG_LINK: 1383 default: 1384 break; 1385 } 1386 1387 return data; 1388 } 1389 1390 1391 [System.Security.SecuritySafeCritical] // auto-generated 1392 [ComVisible(false)] GetValueKind(string name)1393 public RegistryValueKind GetValueKind(string name) { 1394 CheckPermission(RegistryInternalCheck.CheckValueReadPermission, name, false, RegistryKeyPermissionCheck.Default); 1395 EnsureNotDisposed(); 1396 1397 int type = 0; 1398 int datasize = 0; 1399 int ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, (byte[])null, ref datasize); 1400 if (ret != 0) 1401 Win32Error(ret, null); 1402 if (type == Win32Native.REG_NONE) 1403 return RegistryValueKind.None; 1404 else if (!Enum.IsDefined(typeof(RegistryValueKind), type)) 1405 return RegistryValueKind.Unknown; 1406 else 1407 return (RegistryValueKind) type; 1408 } 1409 1410 /** 1411 * Retrieves the current state of the dirty property. 1412 * 1413 * A key is marked as dirty if any operation has occured that modifies the 1414 * contents of the key. 1415 * 1416 * @return <b>true</b> if the key has been modified. 1417 */ IsDirty()1418 private bool IsDirty() { 1419 return (this.state & STATE_DIRTY) != 0; 1420 } 1421 IsSystemKey()1422 private bool IsSystemKey() { 1423 return (this.state & STATE_SYSTEMKEY) != 0; 1424 } 1425 IsWritable()1426 private bool IsWritable() { 1427 return (this.state & STATE_WRITEACCESS) != 0; 1428 } 1429 IsPerfDataKey()1430 private bool IsPerfDataKey() { 1431 return (this.state & STATE_PERF_DATA) != 0; 1432 } 1433 1434 public String Name { 1435 [System.Security.SecuritySafeCritical] // auto-generated 1436 get { 1437 EnsureNotDisposed(); 1438 return keyName; 1439 } 1440 } 1441 SetDirty()1442 private void SetDirty() { 1443 this.state |= STATE_DIRTY; 1444 } 1445 1446 /** 1447 * Sets the specified value. 1448 * 1449 * @param name Name of value to store data in. 1450 * @param value Data to store. 1451 */ SetValue(String name, Object value)1452 public void SetValue(String name, Object value) { 1453 SetValue(name, value, RegistryValueKind.Unknown); 1454 } 1455 1456 [System.Security.SecuritySafeCritical] //auto-generated 1457 [ComVisible(false)] SetValue(String name, Object value, RegistryValueKind valueKind)1458 public unsafe void SetValue(String name, Object value, RegistryValueKind valueKind) { 1459 if (value==null) 1460 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); 1461 1462 if (name != null && name.Length > MaxValueLength) { 1463 throw new ArgumentException(Environment.GetResourceString("Arg_RegValStrLenBug")); 1464 } 1465 1466 if (!Enum.IsDefined(typeof(RegistryValueKind), valueKind)) 1467 throw new ArgumentException(Environment.GetResourceString("Arg_RegBadKeyKind"), "valueKind"); 1468 1469 EnsureWriteable(); 1470 1471 if (!remoteKey && ContainsRegistryValue(name)) { // Existing key 1472 CheckPermission(RegistryInternalCheck.CheckValueWritePermission, name, false, RegistryKeyPermissionCheck.Default); 1473 } 1474 else { // Creating a new value 1475 CheckPermission(RegistryInternalCheck.CheckValueCreatePermission, name, false, RegistryKeyPermissionCheck.Default); 1476 } 1477 1478 if (valueKind == RegistryValueKind.Unknown) { 1479 // this is to maintain compatibility with the old way of autodetecting the type. 1480 // SetValue(string, object) will come through this codepath. 1481 valueKind = CalculateValueKind(value); 1482 } 1483 1484 int ret = 0; 1485 try { 1486 switch (valueKind) { 1487 case RegistryValueKind.ExpandString: 1488 case RegistryValueKind.String: 1489 { 1490 String data = value.ToString(); 1491 ret = Win32Native.RegSetValueEx(hkey, 1492 name, 1493 0, 1494 valueKind, 1495 data, 1496 checked(data.Length * 2 + 2)); 1497 break; 1498 } 1499 1500 case RegistryValueKind.MultiString: 1501 { 1502 // Other thread might modify the input array after we calculate the buffer length. 1503 // Make a copy of the input array to be safe. 1504 string[] dataStrings = (string[])(((string[])value).Clone()); 1505 int sizeInBytes = 0; 1506 1507 // First determine the size of the array 1508 // 1509 for (int i=0; i<dataStrings.Length; i++) { 1510 if (dataStrings[i] == null) { 1511 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetStrArrNull); 1512 } 1513 sizeInBytes = checked(sizeInBytes + (dataStrings[i].Length+1) * 2); 1514 } 1515 sizeInBytes = checked(sizeInBytes + 2); 1516 1517 byte[] basePtr = new byte[sizeInBytes]; 1518 fixed(byte* b = basePtr) { 1519 IntPtr currentPtr = new IntPtr( (void *) b); 1520 1521 // Write out the strings... 1522 // 1523 for (int i=0; i<dataStrings.Length; i++) { 1524 // Assumes that the Strings are always null terminated. 1525 String.InternalCopy(dataStrings[i],currentPtr,(checked(dataStrings[i].Length*2))); 1526 currentPtr = new IntPtr((long)currentPtr + (checked(dataStrings[i].Length*2))); 1527 *(char*)(currentPtr.ToPointer()) = '\0'; 1528 currentPtr = new IntPtr((long)currentPtr + 2); 1529 } 1530 1531 *(char*)(currentPtr.ToPointer()) = '\0'; 1532 currentPtr = new IntPtr((long)currentPtr + 2); 1533 1534 ret = Win32Native.RegSetValueEx(hkey, 1535 name, 1536 0, 1537 RegistryValueKind.MultiString, 1538 basePtr, 1539 sizeInBytes); 1540 } 1541 break; 1542 } 1543 1544 case RegistryValueKind.None: 1545 case RegistryValueKind.Binary: 1546 byte[] dataBytes = (byte[]) value; 1547 ret = Win32Native.RegSetValueEx(hkey, 1548 name, 1549 0, 1550 (valueKind == RegistryValueKind.None ? Win32Native.REG_NONE: RegistryValueKind.Binary), 1551 dataBytes, 1552 dataBytes.Length); 1553 break; 1554 1555 case RegistryValueKind.DWord: 1556 { 1557 // We need to use Convert here because we could have a boxed type cannot be 1558 // unboxed and cast at the same time. I.e. ((int)(object)(short) 5) will fail. 1559 int data = Convert.ToInt32(value, System.Globalization.CultureInfo.InvariantCulture); 1560 1561 ret = Win32Native.RegSetValueEx(hkey, 1562 name, 1563 0, 1564 RegistryValueKind.DWord, 1565 ref data, 1566 4); 1567 break; 1568 } 1569 1570 case RegistryValueKind.QWord: 1571 { 1572 long data = Convert.ToInt64(value, System.Globalization.CultureInfo.InvariantCulture); 1573 1574 ret = Win32Native.RegSetValueEx(hkey, 1575 name, 1576 0, 1577 RegistryValueKind.QWord, 1578 ref data, 1579 8); 1580 break; 1581 } 1582 } 1583 } 1584 catch (OverflowException) { 1585 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind); 1586 } 1587 catch (InvalidOperationException) { 1588 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind); 1589 } 1590 catch (FormatException) { 1591 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind); 1592 } 1593 catch (InvalidCastException) { 1594 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind); 1595 } 1596 1597 if (ret == 0) { 1598 SetDirty(); 1599 } 1600 else 1601 Win32Error(ret, null); 1602 1603 } 1604 CalculateValueKind(Object value)1605 private RegistryValueKind CalculateValueKind(Object value) { 1606 // This logic matches what used to be in SetValue(string name, object value) in the v1.0 and v1.1 days. 1607 // Even though we could add detection for an int64 in here, we want to maintain compatibility with the 1608 // old behavior. 1609 if (value is Int32) 1610 return RegistryValueKind.DWord; 1611 else if (value is Array) { 1612 if (value is byte[]) 1613 return RegistryValueKind.Binary; 1614 else if (value is String[]) 1615 return RegistryValueKind.MultiString; 1616 else 1617 throw new ArgumentException(Environment.GetResourceString("Arg_RegSetBadArrType", value.GetType().Name)); 1618 } 1619 else 1620 return RegistryValueKind.String; 1621 } 1622 1623 /** 1624 * Retrieves a string representation of this key. 1625 * 1626 * @return a string representing the key. 1627 */ 1628 [System.Security.SecuritySafeCritical] // auto-generated ToString()1629 public override String ToString() { 1630 EnsureNotDisposed(); 1631 return keyName; 1632 } 1633 1634 #if FEATURE_MACL GetAccessControl()1635 public RegistrySecurity GetAccessControl() { 1636 return GetAccessControl(AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group); 1637 } 1638 1639 [System.Security.SecuritySafeCritical] // auto-generated GetAccessControl(AccessControlSections includeSections)1640 public RegistrySecurity GetAccessControl(AccessControlSections includeSections) { 1641 EnsureNotDisposed(); 1642 return new RegistrySecurity(hkey, keyName, includeSections); 1643 } 1644 1645 [System.Security.SecuritySafeCritical] // auto-generated SetAccessControl(RegistrySecurity registrySecurity)1646 public void SetAccessControl(RegistrySecurity registrySecurity) { 1647 EnsureWriteable(); 1648 if (registrySecurity == null) 1649 throw new ArgumentNullException("registrySecurity"); 1650 1651 registrySecurity.Persist(hkey, keyName); 1652 } 1653 #endif 1654 1655 /** 1656 * After calling GetLastWin32Error(), it clears the last error field, 1657 * so you must save the HResult and pass it to this method. This method 1658 * will determine the appropriate exception to throw dependent on your 1659 * error, and depending on the error, insert a string into the message 1660 * gotten from the ResourceManager. 1661 */ 1662 [System.Security.SecuritySafeCritical] // auto-generated Win32Error(int errorCode, String str)1663 internal void Win32Error(int errorCode, String str) { 1664 switch (errorCode) { 1665 case Win32Native.ERROR_ACCESS_DENIED: 1666 if (str != null) 1667 throw new UnauthorizedAccessException(Environment.GetResourceString("UnauthorizedAccess_RegistryKeyGeneric_Key", str)); 1668 else 1669 throw new UnauthorizedAccessException(); 1670 1671 case Win32Native.ERROR_INVALID_HANDLE: 1672 /** 1673 * For normal RegistryKey instances we dispose the SafeRegHandle and throw IOException. 1674 * However, for HKEY_PERFORMANCE_DATA (on a local or remote machine) we avoid disposing the 1675 * SafeRegHandle and only throw the IOException. This is to workaround reentrancy issues 1676 * in PerformanceCounter.NextValue() where the API could throw {NullReference, ObjectDisposed, ArgumentNull}Exception 1677 * on reentrant calls because of this error code path in RegistryKey 1678 * 1679 * Normally we'd make our caller synchronize access to a shared RegistryKey instead of doing something like this, 1680 * however we shipped PerformanceCounter.NextValue() un-synchronized in v2.0RTM and customers have taken a dependency on 1681 * this behavior (being able to simultaneously query multiple remote-machine counters on multiple threads, instead of 1682 * having serialized access). 1683 * 1684 * 1685 1686 */ 1687 if (!IsPerfDataKey()) { 1688 this.hkey.SetHandleAsInvalid(); 1689 this.hkey = null; 1690 } 1691 goto default; 1692 1693 case Win32Native.ERROR_FILE_NOT_FOUND: 1694 throw new IOException(Environment.GetResourceString("Arg_RegKeyNotFound"), errorCode); 1695 1696 default: 1697 throw new IOException(Win32Native.GetMessage(errorCode), errorCode); 1698 } 1699 } 1700 1701 [SecuritySafeCritical] Win32ErrorStatic(int errorCode, String str)1702 internal static void Win32ErrorStatic(int errorCode, String str) { 1703 switch (errorCode) { 1704 case Win32Native.ERROR_ACCESS_DENIED: 1705 if (str != null) 1706 throw new UnauthorizedAccessException(Environment.GetResourceString("UnauthorizedAccess_RegistryKeyGeneric_Key", str)); 1707 else 1708 throw new UnauthorizedAccessException(); 1709 1710 default: 1711 throw new IOException(Win32Native.GetMessage(errorCode), errorCode); 1712 } 1713 } 1714 FixupName(String name)1715 internal static String FixupName(String name) 1716 { 1717 BCLDebug.Assert(name!=null,"[FixupName]name!=null"); 1718 if (name.IndexOf('\\') == -1) 1719 return name; 1720 1721 StringBuilder sb = new StringBuilder(name); 1722 FixupPath(sb); 1723 int temp = sb.Length - 1; 1724 if (temp >= 0 && sb[temp] == '\\') // Remove trailing slash 1725 sb.Length = temp; 1726 return sb.ToString(); 1727 } 1728 1729 FixupPath(StringBuilder path)1730 private static void FixupPath(StringBuilder path) 1731 { 1732 Contract.Requires(path != null); 1733 int length = path.Length; 1734 bool fixup = false; 1735 char markerChar = (char)0xFFFF; 1736 1737 int i = 1; 1738 while (i < length - 1) 1739 { 1740 if (path[i] == '\\') 1741 { 1742 i++; 1743 while (i < length) 1744 { 1745 if (path[i] == '\\') 1746 { 1747 path[i] = markerChar; 1748 i++; 1749 fixup = true; 1750 } 1751 else 1752 break; 1753 } 1754 1755 } 1756 i++; 1757 } 1758 1759 if (fixup) 1760 { 1761 i = 0; 1762 int j = 0; 1763 while (i < length) 1764 { 1765 if(path[i] == markerChar) 1766 { 1767 i++; 1768 continue; 1769 } 1770 path[j] = path[i]; 1771 i++; 1772 j++; 1773 } 1774 path.Length += j - i; 1775 } 1776 1777 } 1778 1779 // 1780 // Read/Write/Create SubKey Permission 1781 // GetSubKeyReadPermission(string subkeyName, out RegistryPermissionAccess access, out string path)1782 private void GetSubKeyReadPermission(string subkeyName, out RegistryPermissionAccess access, out string path) { 1783 access = RegistryPermissionAccess.Read; 1784 path = keyName + "\\" + subkeyName + "\\."; 1785 } GetSubKeyWritePermission(string subkeyName, out RegistryPermissionAccess access, out string path)1786 private void GetSubKeyWritePermission(string subkeyName, out RegistryPermissionAccess access, out string path) { 1787 // If we want to open a subkey of a read-only key as writeable, we need to do the check. 1788 access = RegistryPermissionAccess.Write; 1789 path = keyName + "\\" + subkeyName + "\\."; 1790 } GetSubKeyCreatePermission(string subkeyName, out RegistryPermissionAccess access, out string path)1791 private void GetSubKeyCreatePermission(string subkeyName, out RegistryPermissionAccess access, out string path) { 1792 access = RegistryPermissionAccess.Create; 1793 path = keyName + "\\" + subkeyName + "\\."; 1794 } 1795 1796 // 1797 // Read/Write/ReadWrite SubTree Permission 1798 // GetSubTreeReadPermission(string subkeyName, out RegistryPermissionAccess access, out string path)1799 private void GetSubTreeReadPermission(string subkeyName, out RegistryPermissionAccess access, out string path) { 1800 access = RegistryPermissionAccess.Read; 1801 path = keyName + "\\" + subkeyName + "\\"; 1802 } GetSubTreeWritePermission(string subkeyName, out RegistryPermissionAccess access, out string path)1803 private void GetSubTreeWritePermission(string subkeyName, out RegistryPermissionAccess access, out string path) { 1804 access = RegistryPermissionAccess.Write; 1805 path = keyName + "\\" + subkeyName + "\\"; 1806 } GetSubTreeReadWritePermission(string subkeyName, out RegistryPermissionAccess access, out string path)1807 private void GetSubTreeReadWritePermission(string subkeyName, out RegistryPermissionAccess access, out string path) { 1808 access = RegistryPermissionAccess.Write | RegistryPermissionAccess.Read; 1809 path = keyName + "\\" + subkeyName; 1810 } 1811 1812 // 1813 // Read/Write/Create Value Permission 1814 // GetValueReadPermission(string valueName, out RegistryPermissionAccess access, out string path)1815 private void GetValueReadPermission(string valueName, out RegistryPermissionAccess access, out string path) { 1816 access = RegistryPermissionAccess.Read; 1817 path = keyName+"\\"+valueName; 1818 } GetValueWritePermission(string valueName, out RegistryPermissionAccess access, out string path)1819 private void GetValueWritePermission(string valueName, out RegistryPermissionAccess access, out string path) { 1820 access = RegistryPermissionAccess.Write; 1821 path = keyName+"\\"+valueName; 1822 } GetValueCreatePermission(string valueName, out RegistryPermissionAccess access, out string path)1823 private void GetValueCreatePermission(string valueName, out RegistryPermissionAccess access, out string path) { 1824 access = RegistryPermissionAccess.Create; 1825 path = keyName+"\\"+valueName; 1826 } 1827 1828 // Read Key Permission GetKeyReadPermission(out RegistryPermissionAccess access, out string path)1829 private void GetKeyReadPermission(out RegistryPermissionAccess access, out string path) { 1830 access = RegistryPermissionAccess.Read; 1831 path = keyName + "\\."; 1832 } 1833 1834 [System.Security.SecurityCritical] // auto-generated CheckPermission(RegistryInternalCheck check, string item, bool subKeyWritable, RegistryKeyPermissionCheck subKeyCheck)1835 private void CheckPermission(RegistryInternalCheck check, string item, bool subKeyWritable, RegistryKeyPermissionCheck subKeyCheck) { 1836 bool demand = false; 1837 RegistryPermissionAccess access = RegistryPermissionAccess.NoAccess; 1838 string path = null; 1839 1840 #if !FEATURE_CORECLR 1841 if (CodeAccessSecurityEngine.QuickCheckForAllDemands()) { 1842 return; // full trust fast path 1843 } 1844 #endif // !FEATURE_CORECLR 1845 1846 switch (check) { 1847 // 1848 // Read/Write/Create SubKey Permission 1849 // 1850 case RegistryInternalCheck.CheckSubKeyReadPermission: 1851 if (remoteKey) { 1852 CheckUnmanagedCodePermission(); 1853 } 1854 else { 1855 BCLDebug.Assert(checkMode == RegistryKeyPermissionCheck.Default, "Should be called from a key opened under default mode only!"); 1856 BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)"); 1857 BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)"); 1858 demand = true; 1859 GetSubKeyReadPermission(item, out access, out path); 1860 } 1861 break; 1862 case RegistryInternalCheck.CheckSubKeyWritePermission: 1863 if (remoteKey) { 1864 CheckUnmanagedCodePermission(); 1865 } 1866 else { 1867 BCLDebug.Assert(checkMode != RegistryKeyPermissionCheck.ReadSubTree, "We shouldn't allow creating sub key under read-only key!"); 1868 BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)"); 1869 BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)"); 1870 if( checkMode == RegistryKeyPermissionCheck.Default) { 1871 demand = true; 1872 GetSubKeyWritePermission(item, out access, out path); 1873 } 1874 } 1875 break; 1876 case RegistryInternalCheck.CheckSubKeyCreatePermission: 1877 if (remoteKey) { 1878 CheckUnmanagedCodePermission(); 1879 } 1880 else { 1881 BCLDebug.Assert(checkMode != RegistryKeyPermissionCheck.ReadSubTree, "We shouldn't allow creating sub key under read-only key!"); 1882 BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)"); 1883 BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)"); 1884 if( checkMode == RegistryKeyPermissionCheck.Default) { 1885 demand = true; 1886 GetSubKeyCreatePermission(item, out access, out path); 1887 } 1888 } 1889 break; 1890 // 1891 // Read/Write/ReadWrite SubTree Permission 1892 // 1893 case RegistryInternalCheck.CheckSubTreeReadPermission: 1894 if (remoteKey) { 1895 CheckUnmanagedCodePermission(); 1896 } 1897 else { 1898 BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)"); 1899 BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)"); 1900 if( checkMode == RegistryKeyPermissionCheck.Default) { 1901 demand = true; 1902 GetSubTreeReadPermission(item, out access, out path); 1903 } 1904 } 1905 break; 1906 case RegistryInternalCheck.CheckSubTreeWritePermission: 1907 if (remoteKey) { 1908 CheckUnmanagedCodePermission(); 1909 } 1910 else { 1911 BCLDebug.Assert(checkMode != RegistryKeyPermissionCheck.ReadSubTree, "We shouldn't allow writing value to read-only key!"); 1912 BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)"); 1913 BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)"); 1914 if( checkMode == RegistryKeyPermissionCheck.Default) { 1915 demand = true; 1916 GetSubTreeWritePermission(item, out access, out path); 1917 } 1918 } 1919 break; 1920 case RegistryInternalCheck.CheckSubTreeReadWritePermission: 1921 if (remoteKey) { 1922 CheckUnmanagedCodePermission(); 1923 } 1924 else { 1925 BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)"); 1926 BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)"); 1927 // If we want to open a subkey of a read-only key as writeable, we need to do the check. 1928 demand = true; 1929 GetSubTreeReadWritePermission(item, out access, out path); 1930 } 1931 break; 1932 // 1933 // Read/Write/Create Value Permission 1934 // 1935 case RegistryInternalCheck.CheckValueReadPermission: 1936 ///*** no remoteKey check ***/// 1937 BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)"); 1938 BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)"); 1939 if( checkMode == RegistryKeyPermissionCheck.Default) { 1940 // only need to check for default mode (dynamice check) 1941 demand = true; 1942 GetValueReadPermission(item, out access, out path); 1943 } 1944 break; 1945 case RegistryInternalCheck.CheckValueWritePermission: 1946 if (remoteKey) { 1947 CheckUnmanagedCodePermission(); 1948 } 1949 else { 1950 BCLDebug.Assert(checkMode != RegistryKeyPermissionCheck.ReadSubTree, "We shouldn't allow writing value to read-only key!"); 1951 BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)"); 1952 BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)"); 1953 // skip the security check if the key is opened under write mode 1954 if( checkMode == RegistryKeyPermissionCheck.Default) { 1955 demand = true; 1956 GetValueWritePermission(item, out access, out path); 1957 } 1958 } 1959 break; 1960 case RegistryInternalCheck.CheckValueCreatePermission: 1961 if (remoteKey) { 1962 CheckUnmanagedCodePermission(); 1963 } 1964 else { 1965 BCLDebug.Assert(checkMode != RegistryKeyPermissionCheck.ReadSubTree, "We shouldn't allow creating value under read-only key!"); 1966 BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)"); 1967 BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)"); 1968 // skip the security check if the key is opened under write mode 1969 if( checkMode == RegistryKeyPermissionCheck.Default) { 1970 demand = true; 1971 GetValueCreatePermission(item, out access, out path); 1972 } 1973 } 1974 break; 1975 // 1976 // CheckKeyReadPermission 1977 // 1978 case RegistryInternalCheck.CheckKeyReadPermission: 1979 ///*** no remoteKey check ***/// 1980 if( checkMode == RegistryKeyPermissionCheck.Default) { 1981 BCLDebug.Assert(item == null, "CheckKeyReadPermission should never have a non-null item parameter!"); 1982 BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)"); 1983 BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)"); 1984 1985 // only need to check for default mode (dynamice check) 1986 demand = true; 1987 GetKeyReadPermission(out access, out path); 1988 } 1989 break; 1990 // 1991 // CheckSubTreePermission 1992 // 1993 case RegistryInternalCheck.CheckSubTreePermission: 1994 BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)"); 1995 if( subKeyCheck == RegistryKeyPermissionCheck.ReadSubTree) { 1996 if( checkMode == RegistryKeyPermissionCheck.Default) { 1997 if( remoteKey) { 1998 CheckUnmanagedCodePermission(); 1999 } 2000 else { 2001 demand = true; 2002 GetSubTreeReadPermission(item, out access, out path); 2003 } 2004 } 2005 } 2006 else if(subKeyCheck == RegistryKeyPermissionCheck.ReadWriteSubTree) { 2007 if( checkMode != RegistryKeyPermissionCheck.ReadWriteSubTree) { 2008 if( remoteKey) { 2009 CheckUnmanagedCodePermission(); 2010 } 2011 else { 2012 demand = true; 2013 GetSubTreeReadWritePermission(item, out access, out path); 2014 } 2015 } 2016 } 2017 break; 2018 2019 // 2020 // CheckOpenSubKeyWithWritablePermission uses the 'subKeyWritable' parameter 2021 // 2022 case RegistryInternalCheck.CheckOpenSubKeyWithWritablePermission: 2023 BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)"); 2024 // If the parent key is not opened under default mode, we have access already. 2025 // If the parent key is opened under default mode, we need to check for permission. 2026 if(checkMode == RegistryKeyPermissionCheck.Default) { 2027 if( remoteKey) { 2028 CheckUnmanagedCodePermission(); 2029 } 2030 else { 2031 demand = true; 2032 GetSubKeyReadPermission(item, out access, out path); 2033 } 2034 break; 2035 } 2036 if( subKeyWritable && (checkMode == RegistryKeyPermissionCheck.ReadSubTree)) { 2037 if( remoteKey) { 2038 CheckUnmanagedCodePermission(); 2039 } 2040 else { 2041 demand = true; 2042 GetSubTreeReadWritePermission(item, out access, out path); 2043 } 2044 break; 2045 } 2046 break; 2047 2048 // 2049 // CheckOpenSubKeyPermission uses the 'subKeyCheck' parameter 2050 // 2051 case RegistryInternalCheck.CheckOpenSubKeyPermission: 2052 BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)"); 2053 if(subKeyCheck == RegistryKeyPermissionCheck.Default) { 2054 if( checkMode == RegistryKeyPermissionCheck.Default) { 2055 if(remoteKey) { 2056 CheckUnmanagedCodePermission(); 2057 } 2058 else { 2059 demand = true; 2060 GetSubKeyReadPermission(item, out access, out path); 2061 } 2062 } 2063 } 2064 break; 2065 2066 default: 2067 BCLDebug.Assert(false, "CheckPermission default switch case should never be hit!"); 2068 break; 2069 } 2070 2071 if (demand) { 2072 new RegistryPermission(access, path).Demand(); 2073 } 2074 } 2075 2076 [System.Security.SecurityCritical] // auto-generated CheckUnmanagedCodePermission()2077 static private void CheckUnmanagedCodePermission() { 2078 #pragma warning disable 618 2079 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); 2080 #pragma warning restore 618 2081 } 2082 2083 [System.Security.SecurityCritical] // auto-generated ContainsRegistryValue(string name)2084 private bool ContainsRegistryValue(string name) { 2085 int type = 0; 2086 int datasize = 0; 2087 int retval = Win32Native.RegQueryValueEx(hkey, name, null, ref type, (byte[])null, ref datasize); 2088 return retval == 0; 2089 } 2090 2091 [System.Security.SecurityCritical] // auto-generated EnsureNotDisposed()2092 private void EnsureNotDisposed(){ 2093 if (hkey == null) { 2094 ThrowHelper.ThrowObjectDisposedException(keyName, ExceptionResource.ObjectDisposed_RegKeyClosed); 2095 } 2096 } 2097 2098 [System.Security.SecurityCritical] // auto-generated EnsureWriteable()2099 private void EnsureWriteable() { 2100 EnsureNotDisposed(); 2101 if (!IsWritable()) { 2102 ThrowHelper.ThrowUnauthorizedAccessException(ExceptionResource.UnauthorizedAccess_RegistryNoWrite); 2103 } 2104 } 2105 GetRegistryKeyAccess(bool isWritable)2106 static int GetRegistryKeyAccess(bool isWritable) { 2107 int winAccess; 2108 if (!isWritable) { 2109 winAccess = Win32Native.KEY_READ; 2110 } 2111 else { 2112 winAccess = Win32Native.KEY_READ | Win32Native.KEY_WRITE; 2113 } 2114 2115 return winAccess; 2116 } 2117 GetRegistryKeyAccess(RegistryKeyPermissionCheck mode)2118 static int GetRegistryKeyAccess(RegistryKeyPermissionCheck mode) { 2119 int winAccess = 0; 2120 switch(mode) { 2121 case RegistryKeyPermissionCheck.ReadSubTree: 2122 case RegistryKeyPermissionCheck.Default: 2123 winAccess = Win32Native.KEY_READ; 2124 break; 2125 2126 case RegistryKeyPermissionCheck.ReadWriteSubTree: 2127 winAccess = Win32Native.KEY_READ| Win32Native.KEY_WRITE; 2128 break; 2129 2130 default: 2131 BCLDebug.Assert(false, "unexpected code path"); 2132 break; 2133 } 2134 2135 return winAccess; 2136 } 2137 GetSubKeyPermissonCheck(bool subkeyWritable)2138 private RegistryKeyPermissionCheck GetSubKeyPermissonCheck(bool subkeyWritable) { 2139 if( checkMode == RegistryKeyPermissionCheck.Default) { 2140 return checkMode; 2141 } 2142 2143 if(subkeyWritable) { 2144 return RegistryKeyPermissionCheck.ReadWriteSubTree; 2145 } 2146 else { 2147 return RegistryKeyPermissionCheck.ReadSubTree; 2148 } 2149 } 2150 ValidateKeyName(string name)2151 static private void ValidateKeyName(string name) { 2152 Contract.Ensures(name != null); 2153 if (name == null) { 2154 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.name); 2155 } 2156 2157 int nextSlash = name.IndexOf("\\", StringComparison.OrdinalIgnoreCase); 2158 int current = 0; 2159 while (nextSlash != -1) { 2160 if ((nextSlash - current) > MaxKeyLength) 2161 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegKeyStrLenBug); 2162 2163 current = nextSlash + 1; 2164 nextSlash = name.IndexOf("\\", current, StringComparison.OrdinalIgnoreCase); 2165 } 2166 2167 if ((name.Length - current) > MaxKeyLength) 2168 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegKeyStrLenBug); 2169 2170 } 2171 ValidateKeyMode(RegistryKeyPermissionCheck mode)2172 static private void ValidateKeyMode(RegistryKeyPermissionCheck mode) { 2173 if( mode < RegistryKeyPermissionCheck.Default || mode > RegistryKeyPermissionCheck.ReadWriteSubTree) { 2174 ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidRegistryKeyPermissionCheck, ExceptionArgument.mode); 2175 } 2176 } 2177 ValidateKeyOptions(RegistryOptions options)2178 static private void ValidateKeyOptions(RegistryOptions options) { 2179 if (options < RegistryOptions.None || options > RegistryOptions.Volatile) { 2180 ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidRegistryOptionsCheck, ExceptionArgument.options); 2181 } 2182 } 2183 ValidateKeyView(RegistryView view)2184 static private void ValidateKeyView(RegistryView view) { 2185 if (view != RegistryView.Default && view != RegistryView.Registry32 && view != RegistryView.Registry64) { 2186 ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidRegistryViewCheck, ExceptionArgument.view); 2187 } 2188 } 2189 2190 2191 #if FEATURE_MACL ValidateKeyRights(int rights)2192 static private void ValidateKeyRights(int rights) { 2193 if(0 != (rights & ~((int)RegistryRights.FullControl))) { 2194 // We need to throw SecurityException here for compatiblity reason, 2195 // although UnauthorizedAccessException will make more sense. 2196 ThrowHelper.ThrowSecurityException(ExceptionResource.Security_RegistryPermission); 2197 } 2198 } 2199 #endif 2200 // Win32 constants for error handling 2201 private const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; 2202 private const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; 2203 private const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000; 2204 } 2205 2206 [Flags] 2207 public enum RegistryValueOptions { 2208 None = 0, 2209 DoNotExpandEnvironmentNames = 1 2210 } 2211 2212 // the name for this API is meant to mimic FileMode, which has similar values 2213 2214 public enum RegistryKeyPermissionCheck { 2215 Default = 0, 2216 ReadSubTree = 1, 2217 ReadWriteSubTree = 2 2218 } 2219 2220 #endif // !FEATURE_PAL 2221 } 2222