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;

classProgram

{

staticvoidMain(string[] args)

{

//***** Creating a registry key

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

// registry access rules.

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

RegistryRights.ReadKey,

AccessControlType.Allow);

RegistryAccessRule rule2 = newRegistryAccessRule(@"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 = newRegistryAccessRule(@"DOMAIN\user",

RegistryRights.Delete,

AccessControlType.Allow);

RegistrySecurity rs = newRegistrySecurity();

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

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.

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.