ANEJO VI. INGENIERÍA DE LAS OBRAS (NAVE)
RESISTENCIA CARACTERISTICA
Security is an increasingly important issue for most developers because the developer is being made
responsible for ensuring the safety of data produced by an application. The .NET Framework comes complete with some good security features that are easier to use than security features in the past. However, the security emphasized by the .NET Framework is role based—it emphasizes the role an object or user occupies when requesting data to a system resource. Some development scenarios work well with this new technology; others don’t. For example, I can’t imagine trying to create a massive Web application using token−based
security—that type of project works best when you can define the roles that the users will fulfill.
Many developers are used to using the token−based security originally implemented in Windows NT. The token−based security uses a lock and key view of application security. In some cases, this view is actually easier to use and implement. For example, if you want to check the locks on an individual file or the keys owned by an individual user, then you’ll need to use the older style of security.
This chapter doesn’t answer the question of which security strategy is best for a given situation. However, it does provide you with the techniques for accessing both strategies from within a managed application. (We actually started this discussion with the AccessToken example found in Chapter 3.) We’ll take a quick tour of the two security strategies and then look at several examples of how you can implement the older token−based security strategy in a .NET application.
Note This chapter provides a quick overview of the .NET role−based security model for comparison purposes. It doesn’t provide any complete role−based security examples because you can create them using standard .NET language calls. You’ll find a few code snippets that demonstrate differences between token−based and role−based security. You’ll find examples of the standard .NET language calls in my book Visual C# .NET Developer’s Handbook (Sybex, 2002).
An Overview of Windows Security
The Windows security API is vast and performs many functions within the operating system and the
applications it supports. Unfortunately, understanding the security portion of the Win32 API is about as easy learning a new language while performing a handstand. It’s not that the concept is so difficult to understand. The difficulty most developers have is getting the essentials they need from the vast supply of documentation that Microsoft provides—much of which is written in securityspeak. The most important purpose of this section of the chapter is to provide you with Win32 API–based security information without all of the mumbo jumbo.
We’re going to talk about two essential topics in this portion of the chapter. The first is the security API, which we’ll discuss in detail from a programmer’s perspective. While the user may be faintly aware that there’s a security API, they’re unlikely to use it or even care that it exists. As a programmer, you need to be very aware of this part of Windows 2000 and Windows XP and know how to use the various API calls to make your applications secure.
Tip One security API to consider relies on biometrics, the use of human body parts such as the iris and fingerprints for identification purposes. The Biometrics API (BAPI) helps programmers embed biometric technology into applications. A consortium of vendors—including IBM, Compaq, IO Software,
Microsoft, Sony, Toshiba, and Novell—originated BAPI. Learn more about BAPI at the IO Software Web Site (http://www.iosoftware_.com/products/licensing/bapi/). You can download an overview, general information, technical information, and the BAPI software development kit (SDK). Lest you think that all of these APIs are vendor specific, you can also find biometrics standards at the Biometrics Consortium Web site (http://www.biometrics.org/). This site contains helpful information about seminars, standards progress, and public information such as periodicals. Another interesting place to look for information is the National Institute of Standards and Technology
(http://www.itl.nist.gov/div895/isis/projects/biometrics−_project.html). The main interests at this site are the publications, conferences, products, and success stories.
The second important topic is the use of security functions. This section provides an overview of some of the security−related Win32 API functions you need to know about. As previously mentioned, there are many security−related functions, so knowing where to start is essential. We’ll discuss some functions that will help you gain the access you need quickly. Of course, there are many esoteric functions you’ll learn about as you delve more deeply into the security functions.
Why Worry About Token−Based Security?
Some developers are under the misconception that the .NET Framework is a complete solution or that it will answer every need. The problem is that the .NET Framework is new technology that extends what developers used in the past—you can’t count on it to answer many of the old problems you have. In many cases, you’ll find that a particular level of functionality is completely missing. The examples in this chapter demonstrate those lost security features.
However, the problem isn’t limited to just missing functionality. The .NET Framework also presents situations where you could assume one level of functionality when the .NET Framework provides another. Consider the System.IO.FileStream.Lock() method. In theory, you should use this method to lock a file. In fact, it will lock the file if no one else is using it at the time.
Unfortunately, the Lock() method uses the LockFile() function found in KERNEL32.DLL, not the more functional LockFileEx() function. This means you don’t have the option to ask Lock() to wait until it can lock the file—the method always returns immediately. In addition, you can’t differentiate between a shared and an exclusive lock. Your only choices to get around this problem are to create a loop and continually poll the file until it locks or use PInvoke to execute the LockFileEx() function. In short, the .NET Framework is
incomplete and you’ll need to know how to work with the Windows API to overcome those limitations.
A Detailed View of the Windows Security API
The security portion of the Win32 API is large and cumbersome. However, the actual theory behind Windows security is simple. Every object has a lock and every object requestor has a key. If the requestor’s key fits the lock, then the requestor gains access to the object and the resources it provides. This is token−based security. The user’s token is their key to resources on the local machine, the network and intranet, and even the Internet.
It’s important to understand that the user’s access is limited to the combination of groups and individual rights that the administrator assigns. However, most of the configuration options available to the administrator affect Windows as a whole. If you want the administrator to set user−level access for your application, then you
must provide a feature to set user access for each object or task your application provides.
User−level access depends on a security ID (SID). When the user first logs into the system, Windows assigns an access token to the user and places the user’s SID (stored on the domain controller or other security database) within it. The user object carries both the access token and the SID around for the duration of the session. An access token also contains both a _Discretionary Access Control List (DACL) and a Security Access Control List (SACL). The combination of access control lists (ACLs) and SIDs within the access token is a key that allows the user access to certain system resources.
A key is no good without a lock to open. The lock placed on Windows resources is called a security
descriptor. In essence, a security descriptor tells what rights the user needs to access the resource. If the rights within the ACLs meet or exceed the rights in the security descriptor, then the lock opens and the resource becomes available. Figure 8.1 shows the content of the ACL and the security descriptor used for token−based security. The following sections provide more details about how token−based security actually works. We’ll use Figure 8.1 as the point of discussion.
Figure 8.1: Token−based security relies on ACLs and security descriptors.
Understanding Access Tokens
There are two ways of looking at a user’s rights under Windows: individual access and group access.
Remember previously that we talked about the user’s SID—the account number that Windows assigns to the user during login. The access token that holds the SID also contains other structures that identify the groups the user belongs to and what privileges the user has. Each group entry also has a SID. This SID points to other structures that tell what rights the group has. To understand what rights the user has, you need to know both the user’s individual rights and the rights of the groups that the user belongs to. You’d normally use the Local Users and Groups or the Active Directory Users and Computers Microsoft Management Console (MMC) snap−in to change the contents of this access token.
Let’s talk about the “privileges” section of the access token shown in Figure 8.1. It begins with a count of the number of privileges that the user has—the number of special privilege entries in the access token. This section also contains an array of privilege entries. Each privilege entry contains a locally unique identifier
(LUID)—essentially a pointer to the entry object—and an attribute mask. The attribute mask tells what rights the user has to the object. Group SID entries are essentially the same. They contain a privilege count and an array of privilege entries.
One of the things that you need to know as part of working with some kinds of objects is that object rights flow down to the lowest possible node unless overridden by another SID.
For example, if you give a user read and write rights to the \Temp directory on a hard drive, those rights would also apply to the \Temp\Stuff directory unless you assigned the user specific rights to that directory. The same holds true for containers. Assigning a user rights to a container object like a Word document gives the user the right to look at everything within that container, even other files in most cases. It’s important to track a user’s exact rights to objects on your server through the use of security surveys, because you could inadvertently give the user more rights than they need to perform a certain task.
Using Access Tokens
Let’s talk briefly about the token calls in the security API, because they are the first stepping−stones you’ll need to know about. To do anything with a user’s account—even if you want to find out who has access to a particular workstation—you need to know about tokens. As previously stated, tokens are the central part of the user side of the security equation. You’ll usually begin a user account access with a call to
OpenProcessToken(). Notice the name of this call—it deals with any kind of a process, user or otherwise. The purpose of this call is to get a token handle with specific rights attached to it. For example, if you want to query the user account, you need the TOKEN_QUERY privilege. (Your access token must contain the rights that you request from the system, which is why an administrator can access a token but other users can’t.) Any changes to the user’s account require the TOKEN_ADJUST_PRIVILEGES privilege. There are quite a few of these access rights, so we won’t go through them all here.
Note We’ve already looked at a simple example of how to use access tokens in the AccessToken example found in Chapter 3. Even though this example is simple, it does explain how security works in reference to this discussion. You might want to look at the example again to see the relationship between the theory in this section and the code in the example.
Once you have an access token handle, you need to decide what to do with it. For example, you can change a user’s privilege to do something by accessing the LUID for the privilege you want to change. All of these appear in the WINNT.H file with an SE_. For example, the SE_SYSTEM_PROFILE_NAME privilege enables the application to gather profiling information for the entire system. Some SE values don’t relate to users (for example, the SE_LOCK_MEMORY_NAME privilege that allows a process to lock system
memory). You get the LUID for a privilege using the LookupPrivilegeValue() call. Now you can combine the information you’ve gotten so far to change the privilege. In general, you’ll use the AdjustTokenPrivileges() call to make the required change.
Querying the user’s account (or other token information) is straightforward. You use the
GetTokenInformation() call to retrieve any information you need. This call requires a token class parameter, which tells Windows the type of information required. For example, you’d use the TokenUser class to learn about a specific user. You’ll also supply an appropriate structure that Windows can use for storing the information you request—which differs by token class.
Understanding Security Descriptors
At this point, you have a better idea of how the access token (the key) works. Now let’s look at the security descriptor (the lock). Figure 8.1 shows that each security descriptor contains five main sections. The following
list describes each section.
Flags The header consists of version information and a list of control flags. The flags tell you the descriptor
status. For example, the SE_DACL_PRESENT flag indicates the presence of a DACL. If the DACL is missing or if it’s NULL, then Windows allows everyone to use the object. Knowing the security descriptor status can greatly reduce the work you need to perform when determining security descriptor specifics.
Owner SID The owner SID tells who owns the object. This doesn’t have to be an individual user; Windows
allows you to use a group SID here as well. The limiting factor is that the group SID must appear in the token of the person changing the entry.
Group SID The group SID tells which group owns the object. This entry only contains the main group
responsible for the object and won’t contain a list of all groups with access to the object.
Note Of the two security descriptor SIDs, the owner SID is important only under Windows. The Macintosh and POSIX security environments use the group SID. According to the Platform SDK documentation, Windows 2000 and above ignores the contents of the group SID.
SACL This section controls the Windows auditing feature. Every time a user or group accesses an object
when the auditing feature for that object is on, Windows makes an entry in the audit log. There’s more than one entry in this section in most cases, so Windows stores the information in an array. The SACL is often left as a NULL value or not included in the security descriptor at all.
DACL This section controls object use. You can assign groups and users to a specific object. There’s more
than one entry in this section in most cases, so Windows stores the information in an array. A DACL can contain a custom value, a default value, or a NULL value or not appear in the security descriptor at all (this last option is rare and dangerous). You’ll normally find more objects with default values than any other DACL type.
Understanding the Security Descriptor Types
There are two types of security descriptors: absolute and self−relative. Absolute security descriptors contain a copy of each ACL within its structure. Use this type of security descriptor for objects that require special handling. For example, the root directory of a disk drive often uses an absolute security descriptor.
The self−relative security descriptor only contains a pointer to the SACL and DACL. This type of descriptor saves memory and reduces the time required to change rights for a group of objects. You’d use it when all objects in a particular group require the same level of security. For example, you could use this method to secure all threads within a single application.
Windows requires that you convert self−relative security descriptors to absolute format before you save them or transfer them to another process. Every descriptor you retrieve using API calls are of the self−relative type. You can convert a security descriptor from one type to another using the MakeAbsoluteSD() and
MakeSelfRelativeSD() API calls.
Understanding ACLs
As previously mentioned, a security descriptor relies on a SACL and a DACL to control the security of an object. Both of these elements use the same basic ACL data structure, but for different purposes. An ACL consists of two entry types. The first is a header that lists the number of access control entries (ACEs) in the ACL. Windows uses this number to determine when it’s reached the end of the ACE list. (There isn’t any of end−of−structure record or other way to determine the size of each ACE in the structure.) The second entry is an array of ACEs.
Warning Never directly manipulate the contents of an ACL or SID—Microsoft may change its structure in future versions of Windows. The Windows API provides functions such as
GetSecurityDescriptorDacl() and SetSecurityDescriptorDacl() to change the contents of these structures. (Of course, you have to create the security descriptor structure using the
InitializeSecurityDescriptor() function—you’ll learn more about security descriptor construction as the chapter progresses.) Always use an API call to perform any task with either structure type to reduce the impact of changes in structure on your application.
An ACE defines the object rights for a single user or group. Every ACE has a header that defines the type,