Figure 28: The ReadSD program that can read and edit a registry security descriptor.

Introduction

This four part series will discuss the Windows Access Control model and its implementation in Windows NT and 2000. In the third part of the series, we will delve into the new security classes introduced for version 2.0 of the .NET framework.

I'm not sure if you have read parts 1 and 2 of this series. If you are new to ACL based security and haven't read part 1[^], I strongly recommend you read it before continuing. (Don't worry. Even though it's in the C++ section, there isn't much C++ code in it.) Part 2 should be safe to skip if you are a .NET programmer. To prepare for part 3, make sure you have a copy of Visual Studio 2005 and the .NET Framework v2.0 (currently in beta 2).

This article is based on a pre-release version of Visual Studio. The framework may change in future releases, meaning the code may not work in the final version.

Filepermsbox - A program to display the security descriptor on an NTFS file or folder.

History

20. History of .NET security.

Security in .NET was built using the role based security model (which was first seen in NT4 SP3). Role based security was intended as a replacement for the ACL based security model created in 1988. Whilst role based security and code access security have been largely successful for newer applications, software that utilized older protocols (such as Active Directory, COM+, and NTFS), found .NET lacking in fine access control management. To manage security descriptors for these objects, you either had to use the fragile WMI classes, or P/Invoke to some of the most complex functions in the Windows API.

Part of the reason the .NET Framework didn't support ACL-based security was the security descriptor itself. In part 1, the structure of the security descriptor was described, and I think you will agree that the security descriptor is a horrible structure. Microsoft spent most of the 2 years between .NET 1.1 and .NET 2.0 coming up with a class that can handle the security descriptor! However, .NET v2.0 includes a new namespace called System.Security.AccessControl, capable of handling any security descriptor. In the third part of this series, we are going to take a tour on the new security namespace and its related functions.

21. Reading a SID in .NET

The System.Security.Principal namespace (the home of the basic WindowsIdentity/GenericIdentity classes) has two new classes that make viewing SIDs a snap. They are the NTAccount class, and the SecurityIdentifier class. Both inherit from the abstract class IdentityReference, and to translate between the two, you use the virtual method Translate(). The SecurityIdentifier works by utilizing SDDL. Consequentially, the SecurityIdentifier class allows you specify any SDDL user (such as SY) in its constructor. The NTAccount class works by translating the SIDs using the LookupAccountName / LookupAccountSid functions. To print out the SID and username, just call ToString()!

The WindowsIdentity class (which is how you obtained the current username in .NET 1.1) has new properties that return NTAccounts and SecurityIdentifiers for the current user. We can now make a SID lookup program easily:

If you need the SID of a well known account, just pass in its SDDL representation to the SecurityIdentifier class.

22. Whoami .NET

The WindowsIdentity class has had some improvements that can allow you to obtain some information from an access token (such as the token owner, and the list of groups). Whilst the improvements are definitely welcome, it just doesn't go far enough to be able to port Whoami to .NET. For one thing, it is not possible to list the privileges inside a token, nor can you obtain / set the primary group in a token. WindowsIdentity does not allow you to alter any information from the token.

If you want to obtain Whoami-level information from a token, you'll still have to make some interop calls to the advapi32 functions. That, or find a class that handles the interop for you. I could not find a class that can obtain token information, so I created my own!

AccessToken is a class written in C++/CLI that extends the WindowsIdentity class and adds a few methods of its own to make it as functional as the ATL's CAccessToken class (almost as functional). Although there are some flaws (example, AccessToken maintains a separate token handle that is not equal to the internal WindowsIdentity token—this results in two access token handles) and design issues (<CODE lang=c++>class ManagedTokenHandle?!), you can use the class to obtain the token information you can't get with WindowsIdentity. To construct the class you will need an access token. This can be obtained either by interop, or using the provided static method GetAccessToken().

I wrote a program that can obtain a token from any process and extract information from it. It works as long as you have the rights to read a process token (given to all authenticated users by default).

23. Privilege handling in .NET

If you stick with the .NET Framework, you never need to worry about privileges. The .NET functions automatically enable and disable privileges as needed (they throw PrivilegeNotHeldExceptions if the user is not allowed to use a privilege). However, if you are interacting with low-level / legacy code, then enabling and disabling privileges can be a useful technique. Unfortunately, .NET doesn't yet provide a way to enable privileges in a token (an attempt to keep you clear of legacy code).

The good news is that my AccessToken class can easily enable and disable privileges in a token (it cannot assign privileges to a user...yet). It has the SDK[^] SetPrivilege() function, ported to the .NET Framework. Simply call SetPrivilege with the name of your privilege, and the class will enable it for you. The test program I have made for fig. 31 can enable / disable privileges as long as you have the rights to write a process token (normally given to administrators and owners only). Simply right click a privilege, and select enable.

Note, if you can find a .NET Framework function that can perform your task, use the framework function instead.

24. Running as an unprivileged user in .NET

When I was writing the code for part 2 of this series, I used two methods to run a process as a limited user. The first method used the classic CreateRestrictedToken() API to create the restricted token, and the other was to utilize WinXP's Software Restriction policies to return a predefined restricted token. When I tried to execute notepad using the first method, I found that notepad failed to execute. (GetLastError() mentioned something about an SXS failure.) The workaround was to start an older version of notepad (one from Win98 in this case). The second method never had any of these problems, and GetLastError() didn't return any error. For this part I am going to create a user using the Software Restriction Policies (sorry Win2K users).

AccessToken.Safer is a port of the SaferRaiiWrapper class I wrote in part 2 to the .NET Framework. The SAFER functions are one of the easier functions to P/Invoke, so it was easy writing the class in C#. Fig. 32 shows the Safer wrapper class, ported to a C# class library.

25. Obtaining and editing a Security Descriptor in .NET, and applying it to an object

All .NET programmers would have at least encountered one of the classes listed in column 1 of fig. 33. They're the base classes that handle file system objects, registry keys, and synchronization objects. If you take a look at the docs[^] for these classes, you may notice a new method called GetAccessControl present in all of these classes. GetAccessControl returns the security descriptor for the object. The return type of GetAccessControl is dependent on which object you are securing. The second column in fig. 33 illustrates which class is returned from GetAccessControl.

GetAccessControl provides a gateway to a whole new namespace: System.Security.AccessControl. This namespace comprises the BCL's implementation of the security descriptor. You'll notice that there are new classes specially designed for manipulating the security descriptor and its parts. Let's take a tour of the AccessControl namespace.

Class

Return type of GetAccessControl

<N/A>

ActiveDirectorySecurity

Directory

DirectorySecurity

DirectoryInfo

DirectorySecurity

EventWaitHandle

EventWaitHandleSecurity

File

FileSecurity

FileInfo

FileSecurity

FileStream

FileSecurity

Mutex

MutexSecurity

RegistryKey (The rest of the article will be based on this class.)

RegistrySecurity

Semaphore

SemaphoreSecurity

<N/A>

DirectoryObjectSecurity

<N/A>

CryptoKeySecurity

Figure 33: The list of supported secure objects in the .NET Framework.

The possible return types for GetAccessControl.

As was mentioned, GetAccessControl returns a System.Security.AccessControl class that depends on the object being secured. These classes derive from CommonObjectSecurity (which in turn implements ObjectSecurity), and provides methods to access the five parts of the security descriptor. If you recall from part 1, there is no such thing as a "Common Security Descriptor" (due to differing sets of access masks). Therefore, GetAccessControl has to return differing classes for different objects.

If I was writing the class, I would have made CommonObjectSecurity a generic class, where the template parameter is the enum you are passing into the function. That way, GetAccessControl wouldn't need to return differing access control types.

For the purpose of this article, we will focus solely on the RegistrySecurity. With minor differences, what applies to RegistrySecurity applies to all the other classes. The RegistryKey is a pure container class—securing registry values is not supported. As a container, it also supports auto-inherited ACLs. You can read a key, enumerate its values, create a sub key, create a hard link, get notified of key changes, delete a key, and read and alter the permissions. The complete list of what you can do with a registry key is listed in the RegistryRightsenum. RegistrySecurity provides RegistryAuditRule to enumerate its SACL and RegistryAccessRule to enumerate the DACL.

Extracting information from a CommonObjectSecurity.

The CommonObjectSecurity class seems to have been modeled after the CSecurityDesc from ATL. You will notice that the owner, group, DACL and SACL are not referenced by properties in the class. Instead you have to call the pseudo-property functions GetOwner and SetOwner and friends:

GetOwner/ SetOwner: retrieves the owner of the security descriptor as a SecurityIdentifier.

GetGroup / SetGroup: retrieves the primary group of the security descriptor as a SecurityIdentifier.

GetAuditRules: retrieves the SACL of the security descriptor as an AuthorizationRuleCollection.

GetAccessRules: retrieves the DACL of the security descriptor as an AuthorizationRuleCollection.

GetSecurityDescriptorBinaryForm / SetSecurityDescriptorBinaryForm: retrieves the PSECURITY_DESCRIPTOR (native) form of the security descriptor.

SetAccessRuleProtection: makes the DACL auto-inherit from its parent.

SetAuditRuleProtection: makes the SACL auto-inherit from its parent.

However, the control flags are implemented as properties (talk about inconsistency!). You can retrieve the auto inheritance settings from the AreAccessRulesProtected / AreAuditRulesProtected (recall if an ACL is protected, it does not auto-inherit). If you read part 2, you'll know that some of the Windows APIs did not support inheritance, hence could corrupt the inheritance settings of your machine. The good news is that .NET fully supports the inheritance mechanism and will properly preserve the inheritance settings. If you opened a security descriptor that somehow got a disordered ACL (maybe from some rogue Win32 app), an InvalidOperationException will be thrown if you tried to edit it.

If you did want to retrieve the size of the security descriptor (possibly an external function couldn't be bothered with calling GetSecurityDescriptorLength), just get the binary form of the security descriptor as a byte[] array, and return the Length.

If you build the security descriptor from scratch using the generic AccessControl classes (such as CommonSecurityDescriptor or RawSecurityDescriptor), the security descriptor parts are implemented as properties.

This section applies to all objects deriving from common object security, not just RegistrySecurity. The provided ReadSD program is a Windows Forms dialog that illustrates how to obtain a registry security. Supply the location and name of a registry key to view its security descriptor, then press OK. The owner, group, and SDDL of the registry key is then displayed:

Reading the access control lists from a security descriptor.

By now, you should have already called GetAccessRules / GetAuditRules on the ObjectSecurity object. This returns your access control list as a collection. This can be iterated over with a foreach statement or array indexing. The data type encapsulated in the collection depends on the object we are securing. For registry keys, it is a RegistryAccessRule / RegistryAuditRule.

Note that unlike the Win32 ACE, the ACE flags aren't bunched into a single DWORD. They are split over the properties of the ACE:

IdentityReference: Who the ACE applies to (either an IdentityReference or NTAccount). Corresponds to SidStart in the ACCESS_ALLOWED_ACE.

InheritanceFlags: Which type of children the ACE propagates to. Corresponds to CI and OI in AceFlags.

IsInherited: Whether the ACE was an inherited ACL. Corresponds to ID in AceFlags.

PropagationFlags: Specifies the type of inheritance for the ACE. Corresponds to NP and IO in AceFlags.

AccessMask: the set of access rights for the ACE as an object-dependent enumeration (in our example this is the RegistryRightsEnum). Corresponds to AccessMask in ACCESS_ALLOWED_ACE.

Now it is possible to view the access control list for the registry key. The provided ReadSD program reads each access control entry from the list, obtains the list of data, and presents the results in an un-optimized DataGridView. For reasons which will become clear, the DataGridView only shows explicit ACEs and hides the inherited ACEs. ReadSD presents the ACE flags as a separate dialog box (just a bunch of checkboxes databound to the ACE flags). Simply press the View button to see the ACE flags.

When writing to an access control list, you have to consider several issues. First is the preferred ordering of an ACE. The framework does provide the AreAccessRulesCanonical / AreAuditRulesCanonical property to determine if the ACL is well ordered. But what you want is to never have disordered ACEs in the first place. How are you going to deal with this situation?

The second issue is how to proceed if there is already a matching ACE. Suppose you wanted to add read access to an object for yourself, but there was already an ACE that granted your group full access. What do you do about the present ACE? Do you erase the entry, or leave it as it is, or do you completely erase the entire ACL and start from scratch? It depends on why you need to alter the DACL in the first place.

The third issue is how to deal with auto-inheritance. You may note from part 2 that auto-inherited ACEs cannot be edited. (This is the reason why ReadSD's DataGridView doesn't show inherited ACEs). You can only override the inherited settings with deny ACEs, or set the ACL to be protected. If you enable ACL protection you should remember to copy the inherited ACEs onto the current ACL, since a fully auto-inherited ACL has no explicit ACEs.

Figure 35: Reading the contents of an discretionary access control list to a DataGridView with transformations.

Editing the system / discretionary access control list

The good news is that the .NET Framework provides a wealth of functions to resolve all of these issues in whatever way you want.

Starting with the third issue. You can toggle the protection (inheritance) for a security descriptor's DACL using SetAccessRuleProtection, specify true to enable protection (disable auto-inheritance) and false to disable protection (enable auto-inheritance). What's that? You want to copy over the inherited ACEs rather than remove them? No problem—just take a look at the second parameter of SetAccessRuleProtection. For the SACL, the function to use is called SetAuditRuleProtection. Note that an auto-inherited ACL cannot be edited. Therefore, when searching for ACEs to remove, .NET ignores inherited rights. An exception is when you are adding rights to yourself. If .NET finds that an inherited ACE grants you the requested access, then a rewrite of the ACL is not necessary and .NET does not add the inherited ACE.

On the second issue, the .NET Framework provides a plethora of functions to add (or update or whatever) a discretionary ACE:

DACL Editing

SACL Editing

Description

Returns

AddAccessRule

AddAuditRule

Adds an ACE. If one already exists, the new ACE is merged onto the old one.

void

ModifyAccessRule

ModifyAuditRule

Modifies an existing ACE. If none exists, no changes are applied.

false if an existing ACE was not found.

RemoveAccessRule

RemoveAuditRule

Removes rights from an existing compatible ACE. If a more liberal ACE is found, these flags are removed from it (the remaining flags are retained). You are left with a more restrictive ACE.

false if a compatible ACE was not found.

RemoveAccessRule<BR>Specific

RemoveAuditRule<BR>Specific

Removes an existing ACE. The existing ACE must exactly match this ACE. The ACE is then removed.

false if the ACE was not found.

RemoveAccessRuleAll

RemoveAuditRuleAll

Removes an entire ACE. The ACE only needs to match the user name and Allow / Deny / Audit flag.

void. No action is taken if the rule doesn't exist.

PurgeAccessRules

PurgeAuditRules

Removes all ACEs that match the user. The user no longer appears in the ACL.

void.

ResetAccessRule

Removes all ACEs that match the user, then applies the specified AccessRule to the object.

void. If the user does not yet exist in the ACL, they are added.

SetAccessRule

SetAuditRule

Removes an entire ACE. The ACE only needs to match the user name and Allow/Deny/Audit flag. Then this ACE is applied.

void. If the user does not yet exist in the ACL, they are added.

Figure 36: Available methods for ObjectSecurity

If you want to edit an auto-inherited ACE, you must override them with explicit deny ACEs. The .NET Framework does not take into account group membership issues (e.g. if you wanted to remove a deny ACE on yourself, but the deny ACE is applied to one of your groups, then .NET will not find that group). The functions do take into account the preferred ordering of ACEs however.

If you prefer to work with SDDL, that's completely fine—you can use Get / SetSecurityDescriptorSddlForm. And if you prefer to work with the low level functions (say you needed to interface with a native library), that's fine too. You can use the GenericAcl classes to build an ACL, and GenericSecurityDescriptor to build a security descriptor in binary (these low level classes do have real properties like Control and DiscretionaryAcl, unlike the ObjectSecurity classes). Note however, that with these object-agnostic classes, it becomes once again possible to disorder the ACEs (for a few cases, that may be your intent). To convert a GenericSecurityDescriptor to an ObjectSecurity class, call the GetBinaryForm method, and apply it to the ObjectSecurity object using SetSecurityDescriptorBinaryForm.

When using ReadSD, you may have noticed that ReadSD displays different entries than what Regedit shows. In particular, you may have noticed for each inherited ACE, ReadSD shows each entry twice. What ReadSD is showing you is the complete DACL, as it is read in the registry. You see, Regedit merges similar allow and deny entries together so they appear as one ACE. ReadSD doesn't perform this merge (it shows the unedited security descriptor), hence the duplicated entry. In addition, Regedit remaps generic rights (such as GENERIC_READ) to registry specific rights (in this case KEY_READ or RegistryRights.Read) using the Win32 MapGenericMask(). In order to behave like Regedit, ReadSD provides an option to remap the generic rights into specific rights. To do so we need to make use of the Win32 MapGenericMask API. By selecting the checkbox labeled "remap generic rights", the generic access rights will be remapped to Regedit-style. Unfortunately, ReadSD does not merge ACEs belonging to the same user. The solution to this problem will be discussed in part 4.

ReadSD's DataGridView is editable and allows the user to edit the DACL for the registry key. To facilitate SACL auditing, an inherited form is created from the DACL dialog. Some of the DACL specific functions in the form are overridden in the derived form so that we can reuse the DACL dialog for SACL editing.

Committing the security descriptor.

When you update the RegistrySecurity (or ObjectSecurity) object, changes are not immediately reflected in the registry key. This allows you to preview your changes and possibly cancel them before committing. It also allows you to make more than one change to the security descriptor.

In order to change the owner of a security descriptor, you need to have a SecurityIdentifier / NTAccount / IdentityReference object referencing that user (refer back to fig. 29 for that). ReadSD allows you to type in the username in its dialog, which it will then convert to an NTAccount class. Once you have an IdentityReference, call the SetOwner method on the RegistrySecurity object to write the new owner. In order to set the group, use the corresponding SetGroup.

Now we have come to the point to actually write the security descriptor. Do you remember in fig. 33 there were a number of classes that implemented a GetAccessControl method to retrieve a security descriptor? For all of these classes, there is a corresponding SetAccessControl method that (you guessed it) writes the security descriptor to the resource. Once you have created / edited the security descriptor, [re] open a handle to your resource object and call SetAccessControl on it. With luck (and sufficient rights), the new security descriptor should then be applied onto the object. And that should be enough to secure your objects with the .NET Framework.

I've implemented a number of checks in ReadSD to prevent you from applying a security descriptor once you have edited it (for example, you have to preview the SDDL, and enable an undocumented App.config setting) before ReadSD will write a security descriptor to the registry. I've implemented these checks to stop you from misusing ReadSD to edit the security descriptors. ReadSD shouldn't be used in production machines since it is capable of corrupting your registry. To find out what you need to do to get ReadSD to write security descriptors, you'll have to study the code. A more durable interface will be presented in part 4 of the series.

26. Access checks in .NET

In part 2, we went through performing access checks using the Win32 AccessCheck API. Unfortunately, there doesn't seem to be an equivalent managed function that can perform the task. It's not recommended for you perform an access check in .NET. Instead, you should make use of role-based security to perform an access check for you (This is what ReadSD does. Before ReadSD writes a security descriptor, it needs to check if you are allowed to alter the security descriptor. It does this by reading the security descriptor and calling GenericPrincipal.IsInRole to check for group membership). This only works if your objects are designed for role-based security. It does not work with objects secured by security descriptors.

If you need to perform an access check on an object with a security descriptor (Registry key in our case), you wouldn't use AccessCheck to do so (even in Win32). The proper method is to open up the registry key, and if the security descriptor denies access, you will get an "access denied" exception.

In simple access checks, you may be able to perform the access check yourself with the help of an imperative role-based security (fig. 38):

Figure 38: Manually performing an access check with the help of role based security.

This code suffers from a flaw. To check for group membership, fig. 38 makes use of the IsInRole method. The IsInRole method does not take into account that some SIDs may be deny-only SIDs. If a deny-only SID is encountered, AccessCheck does not consider the SID as valid if it is checking an allow ACE (if an allow ACE allows a deny only user, AccessCheck will not apply the rules). The IsInRole method does not include deny-only groups, meaning our AccessCheck implementation is incomplete. For this reason, I provide a demo of a .NET P/Invoked AccessCheck function (fig. 39). However, in order for it to work, you require the AccessToken class from fig. 30:

Figure 39: Performing an access check with the help of the Win32 AccessCheck API.

And privately secured objects? Don't even bother. Make use of declarative code access security instead.

27. Summary

In this part, you were shown the new AccessControl classes coming in .NET v2.0. You were shown how to obtain, edit and translate SIDs with the classes (previously not possible in managed code). You were then introduced to a port of the CAccessToken class to .NET, and implemented a Whoami program using the AccessToken .NET class. A similar port of the SaferRaiiWrapper (from part 2) class was created. You used this class to spawn a low-privileged application. Next you were shown how security descriptors were implemented across the .NET Framework. Finally, a demonstration of access checking in .NET was shown (hastily thrown together).

The new .NET access control classes bring the power of the Framework to access control (and vice versa). Although not perfect, they make access control programming in .NET far easier (compare the code in this article to the code inside the GotDotNet security library [^] class). However, there are areas where the AccessControl classes can improve on. Four such areas are with access tokens, privileges, generic right mapping and access checking. Being in Beta, the Framework may change between now and the final release.

Thanks to the power of the .NET Framework classes, the sample programs for part 3 are far more usable [and reusable] than their cousins in part 2. The class library contains the C++/CLI code for class AccessToken, and the C# code for class SAFERWrapper and class AccessCheckWrapper. The demo project is a set of five programs:

"Read Token" is a port of the Whoami GUI to C#.NET (with the help of AccessToken).

"Make Unprivileged User" demonstrates how to use the SAFERWrapper class to create a low-privileged program.

"Security Descriptor Factory" is a program that displays the security descriptor for a registry key. Both local and remote registry keys are supported (as long as the necessary services are started).

"Access Check" demonstrates how to perform Access Checks with the .NET classes and platform interop.

With the exception of class AccessToken, all code was written in Visual C# 2005 Express Edition beta 2. The entire code for this article requires version 2.0 of the .NET Framework. If you get a BadImageException whilst running the program, it's because you don't have .NET v2.0. If you get numerous CS1518 errors about "partial", it's because you are using Visual Studio .NET [2003]. Upgrade to Visual Studio 2005.

Coming up

At last you now have prerequisite knowledge to program the Windows ACL Editor. The Windows 2000 ACL editor offers a superior interface to security descriptor editing (compared to my pathetic DataGridView -based editor in ReadSD). Part 4 will cover the structure of the ACL Editor, and provide a programming guide to the ISecurityInformation interface (the core of the ACL UI). The ACL editor was originally going to be the only article I wanted to post on access control [then I got carried away and started this series]. Part 4 will make a return to C++ grounds, and will bring in COM. For you .NET programmers, you may still be able to benefit from part 4, with the help of Keith Brown's CCW class (Note that Keith Brown's class was written before .NET v2.0. It's not aware of System.Security.AccessControl). As a preparation for the final part, make sure you know about implementing COM classes from scratch (see here, here, and here).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Share

About the Author

Mr. Shah is a reclusive C++/C# developer lurking somewhere in the depths of the city of London. He learnt physics at Kings' College London and obtained a Master in Science there. Having earned an MCAD, he teeters on the brink of transitioning from C++ to C#, unsure of which language to jump to. Fortunately, he also knows how to use .NET interop to merge code between the two languages (which means he won't have to make the choice anytime soon).

His interests (apart from programming) are walking, football (the real one!), philosophy, history, retro-gaming, strategy gaming, and any good game in general.

He maintains a website / blog / FAQ / junk at shexec32.serveftp.net, where he places the best answers he's written to the questions you've asked. If you can find him, maybe you can hire Mr. Shah to help you with anything C++[/CLI]/C#/.NET related .

Comments and Discussions

I'm currently looking for a solution to secure my business objects with (inheritable) access control lists. Users are application-specific (non-Windows) and stored in a database.

Before I start implementing the basic ACL handler functionnality myself I thought that System.Security.AccessControl could provide all the handywork - both on SQL server (stored procedures and custom functions) and on my c# data layer components. But I doubt that this namespace was invented for such a scenario. Am I correct in my assumptions?

I have been searching around the web to find a solution to modifying a system's local security policy. It seems like Windows has the local security policy locked down. In my research, ends are beginning to meet at the Local Security Authority. Unfortunately, with the lack of documentation provided on the subject I am yet to have found a solution to my issue. The issue is this... I am writing a program to harden a computer to various standards. If you look at hardening standards defined by CIS or the NSA these are the standards I am going for. The documentation for hardening these standards talk about creating a security template with MMC and installing them with MMC or with secedit.exe. Now I could just create a security template (a .inf file) and invoke secedit.exe within my code to harden my box, but I was wondering if you knew of any solution that exists within the LSA api or anywhere that will allow me to programmatically modify the local security policy for a computer. The portions of the policy that have my interest are the event log settings, the password policy, Account lockout policy, and the audit policy. To provide enough background in case you are not familiar with the windows Local Security Policy. If you run MMC.exe from %SystemRoot%/System32, you can select File -> Add/Remove snap-in. Then click add and select Security Templates. The templates listed from this snap-in can be applied to a computer and you can also create your own custom template. I was curious if the policy loaded by LSAOpenPolicy will allow me access to these portions of the local security policy. If not do you know of any other APIs that will let me modify/access the local security policy?

Most of those settings are a mix of different Windows / registry / LSA policy settings all bunched into one MMC snap-in. Some of the registry entries that can be hunted down by scouring the registry (particularly the HKLM\SYSTEM\CurrentControlSet\Control\Lsa branch), some can be found by scouring the adm files that exist somewhere in the windows directory. Other settings require googling (for example a google search of "Hide the display settings tab registry" reveals the registry key to hide that option should be set somewhere in HKCU\Software\Policies). The User Rights Assignment page can be programmed through the LsaOpenPolicy/LsaEnumerateAccountRights/LsaClosePolicy functions. I don't know how to fiddle with audit policy though .

My question doesn't relate directly to this article, but I'm hoping someone can still help me.

I have C# .NET 2003 standard and I have the .NET 2 framework installed. Can someone tell how I can use the SecureString? I must be missing something because as far as I can tell from example code on the net, using SecureString should be as easy as:

If you put your password in an nonencrypted string, then you've sortta defeated the purpose of having a SecureString . The SecureString has to be encrypted from the moment you obtain it (from the app.config? From the user? From the database? From a file?) all the way until you pass it to whatever API expects it.For example, let's suppose you got the password from the user. Instead of a custom form to prompt for the password, use the credui.CredUIPromptForCredentials() function. And in the pinvoke signature, instead of using a string for the pszPassword parameter, use a SecureString. This way the password will be encrypted from the moment you retrieve it from the user, to the moment you use the password.But if you really needed to pass a string to an API that takes a SecureString, you can make use of these two helper functions:

I'm trying to find out how I can use SecureString in my program. Let me clarify, I'm aware of HOW to use it, but it doesn't seem to be available to me in VS IDE, and it throws an exception when I try to use it. I get the error that "the namespace SecureString cannot be found (are you missing a using directive)"...

It's almost as if I don't have .NET v2 installed, but I do. I'm trying to figure out how I can get the compiler to recognize System.Security.SecureString. Do I have to reference a dll or something?

In all the example code I see (including your post), it looks really straight forward to use SecureString, however I can't seem to use it.

As an alternative, does anyone have a custom class that can emulate the SecureString?

I'm not sure why you can't access SecureString. System.Security.SecureString should work, so I'm just going to throw up some stadard troubleshooting tips.

*Make sure you've got the Visual Studio 2005 IDE. Visual Studio 2003 is hardcoded to make use of .NET 1.1 only, even if you've got .NET2.0 installed too.*Although the reference should be there anyway, you should make sure you have a reference to mscorlib (however, if this project reference is missing, there's not much stuff you can do .NET related anyway).*Check to see if your project is at fault. Try creating a completely new project and see if you can access SecureString from there.*Check to see if the IDE is at fault. Instead of compiling from the IDE, create a single project, then compile it from the command line.*Is SecureString the only .NET2 class you can't access? See if you can access some of the other .NET2 classes: System.Runtime.InteropServices.SafeHandle, System.Security.AccessControl, System.Xml.Xsl.XslCompiledTransform, System.Windows.Forms.WebBrowser.*At this point, we're going to have to resort to the Sledgehammer (tm). Reinstall (1st .NET, then Windows, then your computer).

It's impossible to get VS2003 to work with the v2 framework.As a customer of VS2003, you qualify for the upgrade pricing to VS2005, which for the standard edition is only £134.00.

If that's too expensive, you do have another option: the Express Editions[^]. These are free versions of the 2005 IDE and compilers, that come with most of the features of the standard editions, can coexist with both VS2003 and VS2005 Standard/Pro. They are lacking in certain features such as connecting to non-MS Databases, SQL CLR programming, debugging across interop boundaries, and the class designer. But if you can live without these features, it is highly recommended.

Hi, great article, it taught me a lot being that this is my first time programming ACL's.

Anyway, I'm trying to use the 2 calls to AccessCheck(), using the first to get the privilege set size, then allocating the buffer, and then calling it again to get the privilege set properly. But the second call does not work properly, it always gives me an error 122: ERROR_INSUFFICIENT_BUFFER. I have assumed that this is referring to the PrivilegeSet buffer, but i am not 100% certain.

In any case, i allocate the privilege set buffer using the length from the previous call and still get

a false return value

an access status of true

and a granted access mask of 0

on both calls. I hvae tried calling it against a variety of SD's, and always get the same result.

I cannot think of anything else that could be the problem, i was getting other errors but have corrected those.

Finally, i didn't see the privilege set used anywhere outside of the calls to AccessCheck(), do you ever use that result?

Note: I am attempting to create a private object driven security system with this. But for my testing of AccessCheck(), i have discluded the object specific information when setting up the security descriptors (trying to work this down to the minimal case)

Thanks for the quick response. I think that i was looking at the wrong thing actually. Prior to calling AccessCheck(), i was verifying the validity of the security descriptor using IsValidSecurityDescriptor(), which reports that it is, but somehow the owner or group information was incorrect in a way that AccessCheck() did not like, but was acceptable to IsValidSecurtiyDescriptor().

If so, then get the old access rules and the old audit rules, a new filesecurity object, apply the access and audit rules to the new filesecurity object, and then apply the filesecurity object to the file. Does that sound correct?

(Took me longer to answer this one, because I had to verify my answer with some of my own tests).

You don't need to do the same for the audit rules. Even if they are disordered too, this will have no effect on the reordering of the DACL. .NET treats the ordering of the DACL separately from the SACL. This means for example, if both the access and audit rules are disordered, you only need to rebuild the DACL for ModifyAccessRule to succeeed.

When you call SetAccessControl, .NET will detect which bits of the FileSecurity you actually modified and only apply the modified sections to the resultant security descriptor. For example, if you built just the DACL in FileSecurity, SetAccessControl will update only the DACL (the SACL, owner and group will be left untouched).

'set newSEC Ace rules For Each ace In sec.GetAccessRules(True, True, GetType(System.Security.Principal.NTAccount)) newSEC.AddAccessRule(ace) Next

'set newSEC Audit Rules For Each aud In sec.GetAuditRules(True, True, GetType(System.Security.Principal.NTAccount)) newSEC.AddAuditRule(aud) Next

If Not sec.GetGroup(GetType(System.Security.Principal.NTAccount)) Is Nothing Then newSEC.SetGroup(sec.GetGroup(GetType(System.Security.Principal.NTAccount))) End If If Not sec.GetOwner(GetType(System.Security.Principal.NTAccount)) Is Nothing Then newSEC.SetOwner(sec.GetOwner(GetType(System.Security.Principal.NTAccount))) End If

For Each ace In aces right = 0 If Not ace.IsInherited Then For Each ace2 In aces If ace2.IsInherited Then If ace.IdentityReference = ace2.IdentityReference Then right = right Or ace2.FileSystemRights End If End If Next

If (right And ace.FileSystemRights) = ace.FileSystemRights Then 'the user and right already exist in the inherited permissions 'remove it sec.RemoveAccessRule(ace) End If End If Next

For Each aud In auds flag = 0 If Not aud.IsInherited Then For Each aud2 In auds If aud2.IsInherited Then If aud.IdentityReference = aud2.IdentityReference Then flag = flag Or aud2.AuditFlags End If End If Next

If (flag And aud.AuditFlags) = aud.AuditFlags Then 'the flag already exist in the inherited flags 'remove it sec.RemoveAuditRule(aud) End If End If Next

'set newSEC Ace rules For Each ace In sec.GetAccessRules(True, True, GetType(System.Security.Principal.NTAccount)) newSEC.AddAccessRule(ace) Next

'set newSEC Audit Rules For Each aud In sec.GetAuditRules(True, True, GetType(System.Security.Principal.NTAccount)) newSEC.AddAuditRule(aud) Next

If Not sec.GetGroup(GetType(System.Security.Principal.NTAccount)) Is Nothing Then newSEC.SetGroup(sec.GetGroup(GetType(System.Security.Principal.NTAccount))) End If If Not sec.GetOwner(GetType(System.Security.Principal.NTAccount)) Is Nothing Then newSEC.SetOwner(sec.GetOwner(GetType(System.Security.Principal.NTAccount))) End If

For Each ace In aces right = 0 If Not ace.IsInherited Then For Each ace2 In aces If ace2.IsInherited Then If ace.IdentityReference = ace2.IdentityReference Then right = right Or ace2.FileSystemRights End If End If Next

If (right And ace.FileSystemRights) = ace.FileSystemRights Then 'the user and right already exist in the inherited permissions 'remove it sec.RemoveAccessRule(ace) End If End If Next

For Each aud In auds flag = 0 If Not aud.IsInherited Then For Each aud2 In auds If aud2.IsInherited Then If aud.IdentityReference = aud2.IdentityReference Then flag = flag Or aud2.AuditFlags End If End If Next

If (flag And aud.AuditFlags) = aud.AuditFlags Then 'the flag already exist in the inherited flags 'remove it sec.RemoveAuditRule(aud) End If End If Next

Your conceptual logic is correct (and this type of functionality does have to be implemented by yourself). You have to enumerate each explicit ACE one by one, see if the current ACE is covered by other ACEs, and if there are any matching entries, XOR out the rights. By the end, if the total XOR'ed rights for this ACE are zero, remove this entry. This, you are doing correctly.

However, you need to do more than just check if the usernames are equal. Remember, an ACE might consist of both Deny and Allow entries, and the propagation of rights may be different too (you can argue that having any deny/CIIOOI flags can lead to administrative headaches so removing them could be beneficial). You need to check for these.

Another problem is how to proceed when a user belongs to a group. How do you proceed if User1 is a member of the Administrators group and you find that there is an explicit ACE for User1, and an identical inherited ACE on the Administrators group? Be VERY careful with this answer, "LocalSystem" is part of the Administrators group, but every file still needs to have an ACE for LocalSystem AND "Administrators". If you're not careful, your function could end up altering every single file/folder on the system.

Great suggestions!! I have added the code to handle DENY rights. I am have problems finding a way to get a WindowsPrincipal based on the IdentityReference from the ACL. Do you know of a way to do this or a better approach to check if the user accounts already exist in the roles.

I would also like to discuss publishing this code once it is finished. I am sure I am not the only person that could use or needs to use something like this.

AFAIK, you'll have to find another method to determine Group membership (ADSI? NetApi?). Because to obtain a WindowsIdentity (Access token) from a user name, you'll have to log that user on. For that you need their password.

I suppose the reason .NET doesn't provide a WindowsIdentity(IdentityReference), is because a WindowsIdentity can not only be used to check membership, it can be used to impersonate that user and do anything that user can do. I admit it's quite annoying when you just want to check the group membership. c'est la vie.

I must say that you did a nice job here. I stumbled upon your article while searching for a way to programmatically change the user rights of windows, specifically the SeDebugPrivilege. There is a utility included in windows resource kit called ntrights.exe which is a command line utility that gives you the ability to set user rights. Your article describes how to read the rights but what about changing them? what steps would I need to go through for that?

That's a good question. And because I wanted to post a decent response, I have taken my time at crafting it (which is why this reply is so late, sorry). I've written an article (almost as long as this one ) on security policy at my homesite[^]. The article is:

excellent article again. I was actually still looking for the answer so this help out greatly. I basically needed to enable the privelege during the install process and administrative rights is required to install my app. I basically wanted to automate the setting of priveleges rather than direct the user to open group policy and make the adjustments. The C# example was exactly what I was looking for. Thanks again and for your hard work.

You are right, DirectorySecurity only alters NTFS permissions. In order to edit the security descriptor of a LM network share, you should roll out your own class. If you make your class derive from NativeObjectSecurity, and set the ResourceType to ResourceType.LMShare, NativeObjectSecurity will be able to extract the security descriptor and present the familiar AccessControl methods to edit the share.

1.After asking you question, I tried to "googling" an example class that derives from NativeObjectSecurity yesterday. But find nothing and finally found the solution here. Your example is excellent and easy to use on .NET. It will be greate if you place it in your article.

2.Is "ShareRights" plateform dependent? I try your flags on W2K3 enterprise server, only FullControl flag is working. And it will throw exception if I try to set it to NONE (0x0). Here are my values of ShareRights.

2. I'd be surprised if ShareRights was platform-dependent. You may simply be victim of a misunderstanding. Microsoft seems to only support "Read", "Change", and "Full Control" on their security descriptors. However, Microsoft's definition of "Read" differs from my definition of READ (I follow the platform SDK's definition).

There's a minor bug in your code: @"NT AUTHORITY\ANONYMOUS LOGON". You're hardcoding a username into your program. On foreign OSes like German Windows, you can have accounts like "NT Authorität\System", and your program will fail on internationalisation. You can either translate from WellKnownSidType.AnonymousSid, or the SDDL "AN".I'll let you off though, because even Microsoftcanmake that mistake.

3. The IL of mscorlib pointed me to NativeObjectSecurity, and then I modelled my ShareObjectSecurity after the behaviour of DirectorySecurity.

Your question seems similar to Narb's question above. Inside Narb's response, you should find a link to an article I've written on the topic.

To cut a (very) long story short, .Net doesn't directly provide a means of gaining a privilege in its framework. Instead, you may want to P/Invoke to the LSA functions: LsaOpenPolicy, LsaEnumerateAccountRights, LsaAddAccountRights, LsaClose (all in advapi32). Fortunately, at pinvoke.net, inside the entry for LsaOpenPolicy, you will see sample C# code that shows you how to grant a privilege using these functions.

But take a look at my article (see Narb's thread above) before you do this.

Wow, I've been looking everywhere for that, took me 2 hours to find it with google searching (why I didn't just start here I got no idea ) anyway, I'm rather new to all this, I have my NetShareAdd class, but how do I pass this to the SECURITY_DESCRIPTOR element of the structure? Also, what data type should I have that set to? Information on the matter is quite hard to find, especially in managed code that isn't C++.

Whilst researching for part 3, I had a look at the IlDasm of System.Security.AccessControl (in mscorlib.dll), and found some interesting information.

Apparently, Microsoft DO have an access token class of their own . It's called Microsoft.Win32.SafeHandles.SafeTokenHandle, and it seems to be a clone of my AccessToken.ManagedTokenHandle! [Or is that the other way round?] However, the class has been marked private, which means that you can't use the class.

They also have System.Security.AccessControl.Privilege! That class has also been marked private. Had Microsoft opened up this interface, it would have rendered my entire AccessToken class redundant.

Read this as a silent plea to get Microsoft to open up the System.Security.AccessControl.Privilege class.

Finally, some apologies: When I said that I couldn't find a Whoami library for .NET, I clearly wasn't looking hard enough! In fact, I actually wrote where to find the library (my article links to it ). An AccessToken[Process] class exists inside the GotDotNet Security library[^]!

Even though the timeframe between Part 2 - 3 was longer than part 1 - 2, that's not telling the whole story. In reality, it took longer to write part 2 than it did part 3. When I posted part 2, I had not even begun to touch the content of part 3 (whereas I was already well into part 2 on 6th April).

I also found part 3 easier to write, but that could be because .NET is far easier to develop with than C. <cool_the_flame_war>Or it could be that .NET is better suited to access control than C</cool_the_flame_war>

With Part 4, I've already started some material for part 4, so it should take less time to write. However, for the next 2 weeks, I'm taking two big BIG courses: 2555 and 2710. Those courses may cause me to delay part 4 for June.