Registry Security Basics [Mike Rousos]

Registry Security Basics [Mike Rousos]

  • Comments 5

Registry Permission Basics

 

RegistryKey.CreateSubKey(string, RegistryKeyPermissionCheck, RegistrySecurity)

RegistryKey.OpenSubKey(string, RegistryKeyPermissionCheck, RegistryRights)

 

The fact that there are three different ‘security’ looking parameter types in these methods can be confusing, so this is just a quick, simple, blog to make clear what those parameters are for.

 

Registry Security

Let’s start with RegistrySecurity as it’s what most people probably think of when they think of a security parameter. An instance of the RegistrySecurity type is essentially a collection of RegistryAccessRules. The rules specify who is allowed to do what with the registry key. Each rule is a pairing of a user and a particular type of access indicated by a member of the registry rights enum (see the various registry rights listed in the next section). Each rule will indicate that the specified right is either allowed or denied for that user. This information can later be retrieved or changed with the RegistryKey.GetAccessControl and RegistryKey.SetAccessControl methods, but the CreateSubKey overload shown above allows the rules to be set immediately at the time of key creation so that there’s no lag between the key being created and the security going into effect. The rules specified here are persisted in the registry even after the instance of the registry key being created is gone.

 

Registry Rights

While the RegistrySecurity type allows permissions to be set for a newly created registry key, the registry rights type is used to request permissions when opening a key. The registry rights enum contains the following members:

  • ChangePermissions       // Needed to call SetAccessControl
  • CreateSubKey             // Needed to create subkeys under the opened key
  • Delete                          // Needed to delete the key
  • EnumerateSubKeys      // Needed to enumerate through all subkeys
  • FullControl                   // All permissions
  • Notify                           // Needed to register to be notified of changes in the key
  • QueryValues                // Needed to read values in the registry key
  • ReadKey                      // Combo of QueryValues, EnumerateSubKeys, and Notify
  • ReadPermissions          // Needed to call GetAccessControl
  • SetValue                      // Needed to set values in the key
  • TakeOwnership            // Needed to change the owner of the key
  • WriteKey                     // Combo of SetValue and CreateSubKey

When opening a key, one or more of these rights can be requested. Only actions permitted by the rights requested will be allowed for the registry key that is opened. This parameter will do nothing to change what permissions users can have, as stored in the registry. It simply determines what rights this particular instance of the registry key will allow. Of course, requesting rights that were not allowed by the rules used in the registry security parameter when creating the key (or in the rules subsequently applied with a SetAccessControl call) will throw an exception.

 

If no rights are specified, default rights are requested based on the overload of OpenSubKey used:

  • OpenSubKey(string) will, by default, request ReadKey permissions
  • OpenSubKey(string, bool) will request ReadKey if the bool indicates that the key is not writeable and ReadKey and WriteKey if it specifies that it is.
  • OpenSubKey(string, RegistryKeyPermissionCheck) will open the key with ReadKey rights if the RegistryKeyPermissionCheck is default or ReadSubTree and ReadKey and WriteKey permissions if the check is ReadWriteSubTree.

 

RegistryKeyPermissionCheck

This type is different from the previous two. While the RegistryRights and RegistrySecurity types actually determined the permissions that users had or were requesting, this type simply determines when we do permission checks. Selecting different values for the RegistryKeyPermissionCheck parameter will not change which rights the key has requested (if there is a RegistryRights or RegistrySecurity parameter, at least) but it will determine whether or not some checks are done once up front in the name of performance. Consider the three RegistryKeyPermissionCheck values that can be selected:

  • RegistryKeyPermissionCheck.Default – A registry key opened with this permission check will function as if no permission check were specified. Whenever it would typically demand permissions, it will do so.
  • RegistryKeyPermissionCheck.ReadSubTree – A registry key opened with this option will check that it has read rights as it’s created and then will never check those rights again. It will assume that it has them. This can result in a large perf gain if there were previously a lot of read demands happening. It will also reject any operation that would demand write access (the assumption is that if write permissions were necessary, the ReadWriteSubTree enum value would have been used). For operations that demand permissions other than Read (and not Write), such as getting ACLs, a security demand will still be done. Note that if the key has or later gets write access, actions requiring that permission will still fail. Conversely, if the read rights to this key are removed after it has been opened, read actions will continue to succeed because the permission check was done once at the creation of this instance of the key.
  • RegistryKeyPermissionCheck.ReadWriteSubTree – This option is similar to the ReadSubTree value, but both read and write permissions will be demanded once (at creation of the subkey) and then not demanded again for that instance of the key. Again, further permissions (such as setting ACLs) may still be demanded and read/write will not be demanded even if it is later rescinded.

 

Code Example

Here’s some sample code that demonstrates the concepts discussed above.

using System;

using Microsoft.Win32;

using System.Security.AccessControl;

 

class Program

{

 static void Main(string[] args)

 {

   //***** Creating a registry key

   // Here, we use the registry rights enum to define some

   // registry access rules.

   RegistryAccessRule rule1 = new RegistryAccessRule(@"DOMAIN\user",

                                               RegistryRights.ReadKey,

                                               AccessControlType.Allow);

   RegistryAccessRule rule2 = new RegistryAccessRule(@"DOMAIN\user",

                                               RegistryRights.SetValue,

                                               AccessControlType.Allow);

   // Allowing the delete permission when doing tests like this

   // is a good idea because it makes cleaning up later simpler.

   RegistryAccessRule rule3 = new RegistryAccessRule(@"DOMAIN\user",

                                               RegistryRights.Delete,

                                               AccessControlType.Allow);

   RegistrySecurity rs = new RegistrySecurity();

   rs.AddAccessRule(rule1);

   rs.AddAccessRule(rule2);

   rs.AddAccessRule(rule3);

    

   // Here, we use the regitry security object to create a key

   // and specify that the user may only read, set values, and delete the key.

   RegistryKey rk1 = Registry.CurrentUser.CreateSubKey("TestKey",

                                               RegistryKeyPermissionCheck.Default,

                                               rs);

   rk1.SetValue("TestValue", 11);

   rk1.Close();

 

 

   //***** Opened a registry key

   RegistryKey rk2 = Registry.CurrentUser.OpenSubKey("TestKey",

                                               RegistryKeyPermissionCheck.Default,

                                               RegistryRights.ReadKey);

   Console.WriteLine(rk2.GetValue("TestValue"));

   // Even though the user is allowed to set values, this next line would fail

   // because the key was only opened with readkey permissions

   // rk2.SetValue("Test", "Testing");

   rk2.Close();

 

 

   //***** Opening a registry key with RegistryKeyPermissionCheck

   RegistryKey rk3 = Registry.CurrentUser.OpenSubKey("TestKey",

                                               RegistryKeyPermissionCheck.ReadWriteSubTree,

                                               RegistryRights.ReadKey |

                                               RegistryRights.SetValue);

   Console.WriteLine(rk3.GetValue("TestValue"));

   // Even if we removed the SetValues right from this key at this

   // point in this program, the following line would secceed

   // because the key was opened with ReadWriteSubTree permissions and

   // the check is therefore only done when the key is created.

   rk3.SetValue("Test2", 12);

   rk3.Close();

 

   // This will throw a security exception because we're trying to

   // request a permission we didn't give this key (write permissions)

   // RegistryKey rk4 = Registry.CurrentUser.OpenSubKey("TestKey",

   //                                          RegistryKeyPermissionCheck.ReadWriteSubTree,

   //                                          RegistryRights.ReadKey |

   //                                          RegistryRights.SetValue |

   //                                          RegistryRights.WriteKey);

   // rk4.Close();

  

   Console.WriteLine("Test Done");

   Console.ReadLine();

 }

}

  • I Hope there's functionality around to get the "DOMAIN\user" bit, having to programmatically build it could be a pain and error prone.
  • Any particular reason for three RegistryAccessRule instances? I would have expected:

    new RegistryAccessRule(@"DOMAIN\user",
    RegistryRights.ReadKey | RegistryRights.SetValue | RegistryRights.Delete,
    AccessControlType.Allow);

    at the start of Main.
  • Richard,
    Good observation. I should have mentioned that and didn't think of it. Of course, registry access rules can be created just as you said in your comment and it would be a more succinct way to create the registry security instance in my sample. When I put the three separate rules, I was just demonstrating the idea that multiple rules can be added to a single registry security object. In practice, this is likely only necessary if some rules are 'allows' and some are 'denies'.

    Also, there are a lot of ways to retrieve user names and domains without having to use hard coded strings. For this sample, though, I just used "DOMAIN\user" as a place holder.

    Thanks,
    Mike Rousos [MSFT]
  • Re: functionality around DOMAIN\User ... there's an equivilent RegistryAccessRule overload that takes an IdentityReference. One subclass of IdentityReference is an NTAccount which can be constructed with a domain, account pair. Another is SecurityIdentifier if you'd like to use a SID instead.

    -Shawn
  • PingBack from http://www.etheredge.us/index.php/2006/01/30/net-registry-premissions/
Page 1 of 1 (5 items)