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