User-Based Security






User-Based Security

Now that you've seen how to secure code based on code identity, let's take a look at some of the facilities the .NET Framework offers to secure operations based on user identity. These capabilities range from managing identities and role-based security, to Access Control List—based integration that builds on top of Windows Security constructs.

Identity

When the OS starts up a process, it is run under the context of a specific user identity. This is represented as an identity token in the Process Environment Block (PEB, an internal OS data structure). Furthermore, each thread can have an impersonation token (stored in the Thread Environment Block, that is, TEB), which overrides the identity token under which the process was started. The CLR also makes a third identity accessible through the Thread.CurrentPrincipal static member, which is a convenient place to hang identity context off of. It holds and returns an object of type System.Security.Principal.IPrincipal. It sets this to the identity of the process by default.

Note

Note that with ASP.NET, the identity of the client can be obtained from the User property on the Page type; the web-processing pipeline ensures that it is set to the correct principal, while Thread.CurrentPrincipal will refer to the user identity under which the ASP.NET worker process is running.

IPrincipal gives you access to the IIdentity of the principal, in addition to providing a simple means of performing authorization using the IsInRole method. The two basic implementations of IPrincipal are WindowsPrincipal and GenericPrincipal. In 2.0, ASP.NET has introduced a new System.Web.Security.RolePrincipal class for its role management features.

All of these identity types are virtually identical, but the WindowsPrincipal enables you to perform authorization based on Windows group information, a capability we'll look at in just a moment. IIdentity likewise has two implementations, WindowsIdentity and GenericIdentity. Both give you an IsAuthenticated Boolean to indicate whether the user has been authenticated or not, a string-based Name representing the name, and an AuthenticationType string, which tells you how the user was authenticated. WindowsIdentity gives you a collection of the Windows groups the user belongs to, provides impersonation information, and offers a whole host of convenience functions for interacting with Windows security.

Simple Authentication

The IsInRole method for IPrincipal interface indicates whether the target identity is a member of the supplied role. For GenericPrincipal and RolePrincipal, the role is represented as a string, whereas WindowsPrincipal offers other overloads taking an identifier or SecurityIdentifier representing the Windows group to check membership against.

For example, to check whether the current user belongs to the "Administrators" role, this will work for client applications:

IPrincipal user = Thread.CurrentPrincipal;
if (!user.IsInRole("Administrators"))
    throw new Exception("Not authorized");
// protected logic

Similarly, to check for membership in the "Administrators" role, the following will work for ASP.NET scenarios:

protected override void OnLoad(EventArgs e)
{
    IPrincipal user = this.User;
    if (!user.IsInRole("Administrators"))
        Response.Redirect("AccessDenied.aspx");
    // protected logic
}

Clearly, there is a lot more to authentication than just this. Please refer to the "Further Reading" section for follow-up information.

Impersonation

As noted above, changing the Thread.CurrentPrincipal property is not the same as actually impersonating a user as far as Win32 is concerned. The WindowsIdentity class offers an Impersonate method to perform this. It will modify the thread's current impersonation token. The ACL functionality in particular that is described below recognizes such impersonation tokens:

WindowsIdentity identity = /*...*/;
WindowsImpersonationContext context = identity.Impersonate();
try
{
    // Perform some privileged operations...
}
finally
{
    context.Undo();
}

Unlike CurrentPrincipal, identity is not flowed as part of the SecurityContext, for example when initiating asynchronous operations or scheduling work on the ThreadPool.

Access Controls

Windows kernel (executive) objects and system resources can be protected by Access Control Lists (ACLs). An ACL is different from CAS in that it protects an object based on user identity; contrast this with CAS, which is concerned with protecting code from other code based on its identity. But much like a CAS permission, each type of ACL typically has a range of flags that can be used to specify explicitly what operations must be protected, and each ACL can be applicable to a specific object and user identity. Prior to version 2.0 of the Framework, although there may have been general-purpose APIs for working with resources that were protected by ACLs by the OS, there was no common infrastructure in managed code to access this information.

Now the System.Security.AccessControl namespace has a wealth of general abstractions for representing such ACL information. Specific resources have APIs that return the associated instance information. For example, the most widely known ACL-protected resource is probably a file-system object, that is, a file or directory. You can set rights to read, write, add subfolders, modify attributes (such as read-onliness), and so forth, on a per-user basis, using UI built into the Windows Shell. But doing so from code previously involved a painful process of interoperating with unmanaged code. Accessing this information is now simply a matter of calling the System.IO.File.GetAccessControl method, which returns a System.Security.AccessControl.FileSecurity object. This instance contains all of the file's ACL information. Similarly, the FileStream type has a GetAccessControl method, as does the Directory class. This type of integration has been added to many areas of the Framework that interact with and directly use Windows objects.

Note

Note that "identity" in the paragraphs below refers to the process identity token and thread impersonation token, and not the Thread.CurrentPrincipal object! Remember: this is simply a convenient place to flow identity information for application-specific purposes. Win32 code — which is how ACLs are implemented — recognizes only the process and thread tokens.

In the following paragraphs, we'll take a short look at the general-purpose types in the AccessControl namespace and then look at how you might use these types to access real protected information.

Access Rules

ACLs consist of any number of Access Control Entries (ACEs), each of which permits or denies permissions to perform specific operations against specific objects for a set of one or more user identities. Each object has two types of ACLs: Discretionary ACLs (DACLs) and System ACLs (SACLs). DACLs are used to govern permissions to access objects in particular ways, while SACLs are used by administrators to audit access attempts to objects. Whenever code attempts to access protected objects, Windows will scan the permissions information to ensure that the current thread's user identity is allowed to perform the requested operation, and also to determine whether audit logging is required.

Each object has a security descriptor containing this list of its DACLs and SACLs. This descriptor is what you will work directly with using the new common abstractions. The base class for representing a descriptor is the System.Security.AccessControl.ObjectSecurity class. There is a tree of classes that inherit from this type, representing ACLs for various Windows objects that you can access in the BCL. Figure shows this hierarchy.

Image from book
Figure: System.Security.AccessControl class hierarchy for ACL types.

We won't spend a lot of time looking at each implementation of ObjectSecurity, although we'll look at a few examples in the next section. Each type of protected object differs mainly in the operations that it governs. For example, the MutexSecurtiy type uses an enumeration MutexRights to define what operation a rule applies to; this includes FullControl, Delete, Modify, ChangePermissions, ReadPermissions, Synchronize, and TakeOwnership. Similarly, RegistrySecurity uses the RegistryRights enumeration, which offers many similar values, but also offers things like CreateSubKey, ReadKey, WriteKey, and so forth.

Notice that most ACL types derive from CommonObjectSecurity. This class offers methods to access ACE information, such as GetAccessRules and GetAuditRules; both methods return a list of AuthorizationRules. An authorization rule (or ACE) gives you information about the identity of the user this ACE applies to and whether the rule is inherited by children. Two primary subtypes also exist, AccessRule and AuditRule, which specify whether a given rule is for a DACL or SACL, respectively. Further, there are specific subtypes that represent information specific to a type of ACL, for example FileSystemAccessRule.

Example: Using File ACLs

File ACLs are often the most familiar to users and developers on the Windows platform. These ACLs are also persisted to disk, unlike some kernel object security descriptors, and can be explicitly managed by users through the Windows shell. We already noted that you can gain access to a FileSecurity object using the File.GetAccessControl method. This example simply retrieves and prints out the DACL information for the C:\Windows\ directory:

FileSecurity acl = File.GetAccessControl(@"C:\Windows\");
AuthorizationRuleCollection rules = acl.GetAccessRules(
    true, true, typeof(NTAccount));
foreach (AuthorizationRule rule in rules)
{
    Console.WriteLine(rule.IdentityReference);
    FileSystemAccessRule accessRule = rule as FileSystemAccessRule;
    if (accessRule != null)
        Console.WriteLine("  ...{0}", accessRule.FileSystemRights);
}

Similarly, you can supply ACL information when creating a new file, preventing any sort of race condition where a user can see the file before you're able to apply security to it:

FileSecurity acl = new FileSecurity();
acl.AddAccessRule(new FileSystemAccessRule(@"DOMAIN\SomeAccount",
    FileSystemRights.CreateFiles | FileSystemRights.Modify |
    FileSystemRights.Delete, AccessControlType.Allow));
using (FileStream file = File.Open(fileName, FileMode.OpenOrCreate))
{
    // output file contents
}

And of course, similar approaches can be taken to modify the ACL for an existing file.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows