Security in Windows 2000

[Previous] [Next]

You can't get up to speed on COM+ security without understanding a few core concepts of the underlying Windows security model. If you're already familiar with this topic, feel free to skim this section. Otherwise, you should take the time to read this section carefully before moving to the section on COM+ security.

The first step in understanding security is learning how authentication works. You need to know what a security principal is and how it differs from an account. You should also understand the purpose of an authority and how it manages an accounts database. As you'll see, domain controllers are special computers that play a central role in allowing one principal to prove its identity to another.

I'll also describe how Windows enforces security at runtime using authorization checks. Windows uses logon sessions and tokens to cache authorization information about users. Windows also uses discretionary access control lists (DACLs) to map users and groups to permissions on specific resources. These pieces are the basic building blocks that allow you to configure an effective security policy.

This section will also focus on the additional complexities that are introduced when you perform authentication across the network. Fortunately, Windows provides authentication services transparently. A client application running on a desktop computer can safely communicate with a COM+ application on a server computer as long as a path of trust exists between them. As you'll see, the all-encompassing concept of trust permeates almost every aspect of security.

Principals and Authorities

Windows security is based on a central entity known as a principal. In Windows 2000, users and computers are recognized as security principals. (In Windows NT, computers aren't really recognized as first-class principals.) In this security model, one principal proves its identity to another principal through a process called authentication. Without authentication, it would be impossible to enforce a security policy. You'd have no way of knowing whom you were granting or denying access to.

Authentication is performed by an authority. An authority is a Windows service that verifies a principal's identity. For instance, when Bob (principal 1) enters his password in a logon screen and attempts to log on to the local computer (principal 2), an authority is called upon to determine whether it's really Bob. The authority keeps an encrypted copy of Bob's password and compares the password to what's been entered in the logon screen. If there's a match, Bob has proven his identity and the authority informs the local computer that it is indeed Bob who just logged on. In other words, the authority vouches for Bob's identity.

Each authority owns an accounts database of security-related configuration data. The authority maintains a separate account for each principal that it knows. When an account is created, the authority generates a unique identifier known as a security ID (SID). A SID is similar to a GUID in the sense that it's a system-generated value that's unique in both time and space. However, the structure of a SID is different from that of a GUID. A SID always contains the ID of the authority that created it.

A principal account holds security-related data for a specific user or computer. For example, a user account can track group assignments and a password. The fact that the authority tracks user passwords enables users to prove their identity at runtime. A password is just one example of principal credentials—credentials that allow principals to prove their identities. In some scenarios, principals use more exotic forms of credentials to prove their identities, such as digital certificates and smart cards.

Local Accounts Versus Domain Accounts

Every computer running Windows 2000 or Windows NT runs an authority that owns an accounts database. Some computers run a local security authority (LSA), which maintains local accounts. Other computers run a domain authority, which manages domain accounts. In both cases, the authority service is launched at machine startup time from LSASS.EXE.

In Windows 2000, principals can be identified using several different naming formats. Windows 2000 and Active Directory allows for the user principal name (UPN) format of principal@authority. For example, a domain account looks like Bob@MyDomain. In most cases, you can also identify a principal by using the older Windows NT Authority\Principle format, as in MyMachine\Bob or MyDomain\Bob. In fact, sometimes you have to use the older Authority\Principle format—such as when you assign an account name to serve as the identity for a COM+ application.

You can configure security within a relatively small group of computers without a domain authority. For example, one user can enable the local Guest account to allow other users to access local resources such as a shared directory without being authenticated. However, this approach doesn't provide much granularity. When you grant access to a resource, you must do it for everyone on the network. If you want to selectively grant access to specific users or a particular group, each user must be authenticated by an authority.

You can authenticate users in an environment without a domain authority though the use of duplicate local accounts. Each user must have an account on every computer in the network, and each account associated with a user must have the same user name and password. For example, if Bob is logged on to computer A and attempts to access a resource on computer B, the local security authority services on these two computers work together to try and authenticate him. If the authority on computer B determines that there is a local account with the same user name and password as the account that Bob logged on to on computer A, Bob is authenticated.

The problem with creating duplicate accounts is that they're hard to administer. For example, a network with 10 users and 10 computers would require 100 accounts. Image how difficult it would be for Bob to change his password. He would have to visit every computer on the network to do so. As you can see, managing security with duplicate accounts is feasible only in the smallest network scenarios. This approach simply can't scale because the administrative burden increases dramatically every time you add a new user or a new computer.

To create a security policy that can scale to accommodate a medium-sized or large population of users, you must set up your network to have one or more domain authorities. A domain authority manages a network-wide accounts database and performs authentication services for principals in the domain. Because a domain represents a set of users and computers, it is sometimes referred to as a security boundary.

A computer that runs a domain authority is called a domain controller. In Windows 2000, domain controllers maintain their accounts databases using Active Directory. Note that a domain can be configured with multiple domain controllers to improve performance, fault tolerance, and availability. When a domain has more than one domain controller, Active Directory uses replication to maintain a copy of the accounts database on each one.

The Windows security model is based on the concept of trust. Without trust, authentication would be meaningless. Users must trust their domain authority as well as the administrators of the domain authority. For example, Bob trusts the domain authority with his password, and he expects that the domain authority won't authenticate other users who are attempting to assume his identity.

Computers must also trust their domain authority. When you add a computer to a domain, the computer's local security authority implicitly trusts the domain authority. For example, when Bob attempts to access a resource on another computer in the domain, the local authority of that computer must trust the domain authority to verify Bob's identity. This is often called a path of trust.

Domain authorities can also trust one another. This makes possible authentication of users from one domain when they attempt to access resources on a computer in another domain. In Windows 2000, it's much easier to establish a path of trust between domains than in Windows NT. All Windows 2000 domains in the same forest have an implicit bidirectional trust set up between them. In Windows NT, trust relationships across domains must be configured manually in both directions. In Windows 2000, you must manually configure trust relationships to create a path of trust between domains that aren't part of the same forest.

User and Group Accounts

When it comes to securing a resource such as a COM+ application, it's more common to configure access permissions in terms of group accounts rather than user accounts. If you configure access permissions to resources in terms of individual user accounts, you must continually reconfigure these access permissions as users enter and leave the organization or change roles within the organization.

Working in terms of group accounts eases the administrative burden. A group typically represents a set of users who play a specific role or who make up an organizational unit. If you configure access permissions in terms of groups, you don't have to change them as often. It's also much easier to add and remove users accounts from these groups.

You can create group accounts within the scope of a local security authority or within the scope of a larger domain or forest. A local group (also called an alias) has an associated account in the local accounts database and is defined only on the local computer. A domain group is any group with a wider scope than a local group.

Windows 2000 recognizes three types of domain groups. The differences between these types have to do with whether a group is recognized across many domains in a forest or recognized only within a single domain. For the purposes of this chapter, I won't distinguish between the different types of domain groups.

It's important to understand the functionality of a local group. A local group exists only on a single computer. You use it to configure access permissions to resources on the local machine. You can add local users, domain users, and domain groups to a local group, but you can't nest local groups within one another.

In Windows 2000, you can nest domain groups. (Note that the domain authority must be running in native mode as opposed to mixed mode to do this). Nesting domain groups allows a company to map organizational groups to specific roles. For example, let's say that your company has one group for salespeople and another group for marketing folks and you need to grant users from both groups a fairly complex set of access permissions to some shared directories and files. While you can configure the same set of access permissions for both groups, it's usually easier to create a single role-specific group and assign the access permissions to that. Then you simply add the groups that contain users into the group that has the configured access permissions.

In a Windows NT domain, you can't nest domain group accounts. However, you can still nest domain groups within local groups. The scheme used by Windows NT doesn't provide as much flexibility as the scheme used by Windows 2000, but you can produce similar results in the two environments. The inability to nest domain groups in Windows NT means that local groups are more valuable in Windows NT than they are in Windows 2000. However, local groups still work the same way in Windows 2000 that they do in Windows NT.

Preconfigured Accounts

Windows 2000 includes a few preconfigured user and group accounts that are automatically assigned a set of commonly used privileges and access permissions. I'll briefly describe the accounts that are most pertinent to administering and securing a COM+ application.

Preconfigured accounts are created when the accounts database is created. For a local security authority, this happens when the operating system is installed. For a domain authority, this happens when a server is promoted to a domain controller, causing the creation of a new domain.

Each local security authority has a local Administrators group, which has an extensive set of privileges and access permissions by default. Many of these privileges and access permissions can't be reconfigured. Members of this group can create and configure COM+ applications by default. They can also install applications and services on the local machine and install and launch applications and services from across the network.

Every local security authority has an Administrator account, which is automatically added to the local Administrators group. It's important to see that the Administrator account is nothing special except for being a member of the local Administrators group. It doesn't have any explicitly assigned privileges or permissions. This account is important because it allows you to log on with administrative privileges and access permissions immediately after installing the operating system. After initially configuring a computer, many administrators delete or rename the Administrator account because it's an obvious choice for a break-in attempt.

Each domain authority has a Domain Admins group. The domain authority's Administrator account is automatically part of this group, and any user in this group can add other user and group accounts as well. The purpose of this account is to extend administrative privileges and access permissions to administrators who are responsible for managing the domain.

When you add a computer to a domain, the Domain Admins group is silently added to the local Administrators group. This means that a member of the Domain Admins group is implicitly given administrator's privileges and access permissions on every computer in the domain.

Each domain authority has a Domain Users group, which includes all domain user accounts by default. Likewise, each local authority has a local Users group, which includes all local user accounts by default. When a computer is added to a domain, the Domain Users group is added to the local Users group as well. Note that the local Users group has the privilege to log on locally. This means that any user with a valid domain user account can log on to any computer in the domain (except a domain controller) by default.

Every local security authority and domain authority also has a Guest account. This account is disabled by default and its purpose is to provide access to principals that do not have an account that can be authenticated. For example, suppose you want to share a directory with users who are in a domain that doesn't have a trust relationship with your domain, or you want to share a directory with users who aren't in a domain at all. Since there's no path of trust between your computer and their authority, you'll need the Guest account.

When users try to access a resource on your computer, your local security authority attempts to authenticate them. If your local security authority can't find a matching user name in an accounts database, it looks to see if the Guest account is enabled. If it's disabled, unauthenticated users are denied access. If it's enabled, your local security authority maps unauthenticated users to the Guest account. By enabling the Guest account and configuring the appropriate access permissions, you can allow for anonymous access.

A problem can happen when you try to provide anonymous access using the Guest account. If a user account from an untrusted authority has the same user name as a user account in your local security authority or domain authority, the Windows security model checks to see whether the two accounts have the same password. If the passwords are identical, the user is authenticated under the identity of the trusted account. If the passwords don't match, the user isn't mapped to the Guest account; your local security authority simply denies access to the user. As you can see, the potential for conflicting account names makes using the Guest account a less-than-perfect solution for providing anonymous access. After all, lots of companies and organizations have a user account named Bob.

Each authority also recognizes two universal SIDs named Everyone and Authenticated Users. These SIDs are like group accounts in the sense that you can use them to configure access permissions. But they aren't really groups because they don't have accounts in any accounts database. The local security authority is hard-coded to associated these SIDs with the identity of a principal whenever it's appropriate.

The Everyone SID represents all users, including those who haven't been authenticated, which means that it includes users who've been mapped to the Guest account. The Authenticated Users SID includes all principals who've been authenticated by a trusted authority. Many administrators configure security permissions using the Everyone SID even when the Authenticated Users SID would be a better choice. It's important that you understand the distinction between the two.

Logon Sessions and Tokens

Now that I've covered the basic of principals and authentication, let's focus on how a security policy is enforced at runtime. We'll begin with a look at logon sessions and tokens.

I'd like to start with a simple example. Let's say Bob logs on using a local account on his local computer. When he enters his password and tries to log on, the local security authority attempts to authenticate his identity. If authentication succeeds, the authority creates an interactive logon session and a token for Bob, as shown in Figure 11-1.

click to view at full size.

Figure 11-1. Each process is created using a process token, which associates the process with an established logon session. The principal of this logon session serves as the identity for the process.

The Windows security model makes a distinction between logon sessions and tokens. But you generally don't have to worry about differentiating between the two unless you're writing system-level security code in C++. As a Visual Basic programmer, you're better off thinking more in terms of tokens because that's what Windows uses to conduct security checks at runtime.

While Bob could have many different tokens on his local computer, he'll only have one logon session. You can think of a logon session as a collection of tokens associated with a specific identity. The local authority caches the SID for Bob's user account and the SIDs for all the local groups and domain groups of which Bob is a member in his tokens. The local security authority also caches any privileges he's been granted. As you can see, a token acts as Bob's badge because it contains the information that allows the system to conduct authorization checks whenever Bob attempts to access a resource.

One of the main advantages of using tokens in addition to logon sessions is that it allows one process to dynamically modify the authorization information associated with a principal without affecting other processes. For example, one process can add and enable a privilege for a token at runtime without affecting how other processes view the corresponding logon session. If processes were to use logon sessions directly instead of tokens, one process's modifications would be seen by others.

Here's a key point: Every process is created with a special token that gives the process its identity. This token is known as a process token. A process runs under the same identity as the principal associated with the process token's logon session.

Now let's go back to the example in Figure 11-1. When Bob successfully logs on to his local computer, the system starts up an instance of the Windows shell application (EXPLORER.EXE) and creates a process token from Bob's interactive logon session. If Bob launches another application from the shell, such as Microsoft Word (WINWORD.EXE), the system copies the process token from one process to another. However, both tokens point back to Bob's interactive logon session. Both of these processes run under Bob's identity.

The user associated with an interactive logon session takes on the identity of the Interactive User. (The identity of the Interactive User is crucial to our upcoming discussion of how to configure the identity of a COM+ application.) When no user is logged on to a computer, there is no interactive logon session and the Interactive User identity doesn't exist on the local computer.

What happens when Bob logs off his computer? His interactive logon session is shut down. When Windows shuts down his logon session, it also terminates every process that's running under the identity of one of his tokens. This example should shed some light on the relationship between logon sessions and tokens. Think of a logon session as a set of tokens. A logon session gives the operating system a way to manage a set of processes that are all running under the identity of the same principal.

In addition to interactive logon sessions there are several other types of logon sessions, including system logon sessions, network logon sessions, service logon sessions, and batch logon sessions. You need a general understanding of when these types of logon sessions are created and how they're used.

When a computer is booted, the startup process creates a system logon session, a highly privileged logon session that essentially has no restrictions on what it can do. Many important Windows services run under the identity of the system. An example of such a service is the local security authority (LSASS.EXE), as shown in Figure 11-1. Once again, you must consider the concept of trust. Any code that runs under the identity of the system must be trusted. Note that the system logon session runs from the time the computer is booted until it's shut down.

You might assume that the system logon session is created using a local account in the accounts database, but this isn't the case. Windows 2000 is hardcoded to start and shut down the system logon session without requiring an account or an associated password. If Windows were to require a system account in the accounts database, this would simply open up another hole for the bad guy to steal the system's identity and circumvent your security policies.

The system logon session has no account, so it can't be recognized by other principals on the network. Note that privileged processes running as the system use the identity of the local computer account when they call out across the network. This enables other computers to authenticate requests from privileged services.

Let's look at an example to clarify this last point. When Bob logs on to his local computer using a domain account, his local security authority can't authenticate him without help from the domain authority. The local security authority on Bob's computer runs with a token created from the system logon session (as shown in Figure 11-2). However, when the local security authority calls out to the domain authority, it uses the identity of the local computer account. When the domain authority receives the request to authenticate Bob, it can determine that the request is coming from a computer that's a member of the domain.

click to view at full size.

Figure 11-2. When a process running under the identity of the system calls to another computer, the call is made using the identity of the local computer account.

I'd like to point out two important things about a logon session. A logon session is always associated with one specific principal, and it's always limited to a single computer. So what happens if Bob attempts to access a resource across the network? The remote computer can't create a token to perform an authorization check using a logon session from another computer. The local security authority on the remote computer must create a network logon session and then create an associated token in order to determine whether Bob should have access to the requested resource.

Figure 11-3 shows that Bob has an interactive logon session on his local computer and a network logon session on another. It's important to understand that tokens created from Bob's interactive logon session and tokens created from his network logon session are usually quite different.

click to view at full size.

Figure 11-3. A user can have network logon sessions in addition to a local interactive logon session.

Tokens created from Bob's network logon session are similar to tokens created from his local logon session in that they contain the SID of his user account and the SID for each domain group to which he belongs. However, the set of privileges and the SIDs of local groups are almost always different. For example, Bob might be a member of the local Administrators group on his own computer, but this doesn't mean that he's a member of the local Administrators group on the remote computer. In fact, it's likely that Bob is a member of the local Users group but not the local Administrators group on the remote computer.

The last two types of logon sessions I want to describe are service logon sessions and batch logon sessions, which are very similar. They're both used primarily for creating process tokens for local processes, and they allow you to run a process under an identity other than the system or the Interactive User.

When you configure a service on a Windows 2000 computer, you can run the service's process under the identity of the system or the identity of a user account. If you run the service under the system's identity, the service is given a process token that's associated with the system logon session. If you run the service using a user account, the operating system creates a service logon session and uses it to create the process token for the service's process.

Most processes don't require all the permissions and privileges that are extended to the system logon session. In fact, Windows doesn't even allow you to run an application under the system's identity. This configuration option is available only to Windows services. However, it's good practice to run even Windows services under a less privileged user account, if possible. Unnecessarily running a service under the identity of the system simply gives the bad guy one more opportunity to wreak havoc on your computer.

Unlike the process associated with a Windows service, the process for a COM+ application can't run under the identity of the system logon session. It must run under the identity of a specific user. You can configure the identity of a COM+ server application using the Identity tab of the application's Properties dialog box, as shown in Figure 11-4. You can configure a server application to run under the identity of the Interactive User or under the identity of a specific user account.

Figure 11-4. On the Identity tab of a server application's Properties dialog box, you can specify which principal will serve as the application's identity.

Running a COM+ server application as the Interactive User is a common thing to do during development, but it's not a good choice for a COM+ application running on a production server. Let's examine some of the problems with running an application under the identity of the Interactive User.

You typically want the process for a COM+ server application to run independently from the user who happens to be logged on to the server computer. The process for a COM+ server application that's configured with the identity of the Interactive User can run only during an interactive logon session. If no one's logged on to the server computer, client applications can't activate objects from across the network. You should also realize that the process for the COM+ server application is automatically shut down when the current user logs off. Moreover, the identity of the interactive logon session has different privileges and access permissions depending on which user is logged on.

Here's the bottom line: A COM+ server application can't run under the identity of either the system logon session or a service logon session, and you shouldn't run it under the identity of the Interactive User.

This brings us to the last type of logon session: the batch logon session. Batch logon sessions are similar to service logon sessions except that they're used for applications instead of services. When you configure a COM+ server application to run under the identity of a user account such as MyServer\MyUserAccount, the instance of DLLHOST.EXE associated with the server application is always launched with a process token created from a batch logon session. Note that when you configure the identity of a COM+ server application, you must use the Windows NT format for the account name. COM+ doesn't accept the newer UPN format (MyUserAccount@MyServer).

As you'll recall from Chapter 6, the COM+ Service Control Manager (SCM) launches the process for a COM+ server application when necessary. The SCM must also create the batch logon session in order to create the process token. However, in order to create the batch logon session, the SCM needs the password of the user account.

When you configure the identity of a COM+ server application to a specific user account, you must supply the user's password. The COM+ Catalog Manager writes the encrypted password to a secret place in the COM+ catalog, where the SCM can retrieve it when needed. Another important point is that the user account requires the Log On As Batch Job privilege in order to serve as the identity of a batch logon session. When you configure a COM+ server application to use a specific user account as its identity, COM+ automatically assigns the account this privilege.

The last thing I'd like to say about a COM+ application's identity setting has to do with testing and debugging components. In the development environment, it's common to run a COM+ server application under the identity of the Interactive User. One reason for doing this is that it's easier than creating and configuring a dedicated user account to serve as the application's identity. When you run your Visual Basic objects in DLLHOST.EXE, they run with your privileges and access permissions.

Another reason for running code under the identity of the Interactive User during development is that your objects can display messages boxes to the user of the local computer. If a message box is invoked from an object running under the identity of a batch logon session, it's displayed on a virtual monitor that no user will ever see.

While it can be helpful to invoke a message box from an object in a COM+ application during development, doing so in a DLL that's put into production can have tragic results. Remember that compiling a production build for a DLL using the Unattended Execution option (as described in Chapter 4) is also a nice safety precaution.

Resources and DACLs

Now that I've covered the basics of logon sessions and tokens, I can explain how Windows conducts authorization checks at runtime. Many types of resources, such as directories and files on an NTFS partition, can be configured to allow access to certain users and groups while denying access to others. A securable resource has a set of security-related configuration data known as a security descriptor. The security descriptor holds the SID of the object's owner. The owner can configure who has access to the object. A security descriptor also can hold a security access control list (SACL) and a discretionary access control list (DACL).

A SACL is a list of configuration data used for auditing purposes. For example, you can set up auditing so that you'll know whenever a user modifies or deletes a particular file. I won't go into more detail about SACLs because COM+ doesn't use them. Later in the chapter, I'll describe how COM+ provides programmatic support for auditing.

A DACL is a list of access permissions that tells the system which tokens can access a resource and in what manner. Remember that a DACL is part of a security descriptor, so it's related to a specific resource such as a file or a shared directory. Each entry in the DACL maps a SID for a user or group to a specific permission for that resource. For example, an entry in the DACL for a shared directory might indicate that Bob has read/write permissions. Another entry might indicate that the Marketing group has read permissions.

The Windows subsystem that uses DACLs to conduct authorization checks is known as the security reference monitor (SRM). When Windows calls upon the SRM to conduct an authorization check at runtime, the SRM compares the SIDs from a token to the SIDs in a DACL to determine whether a token (and its associated user) is authorized to access the resource. The SRM determines whether the principal has the access permissions to do what it's attempting to do.

I'd like to make two final points about authorization through the use of DACLs. First, if the security descriptor for a resource has a DACL, users must have explicit access permissions to access the resource. Second, if the security descriptor for a resource doesn't have its own DACL, it's given a NULL DACL and all users are implicitly granted all access permissions for the resource.

Network Authentication and Impersonation

Local authentication is fairly easy to conceptualize. But authentication over a network is far more complex. The authentication mechanism must facilitate passing sensitive information across the network in a manner that prevents the bad guy from reading and tampering with messages as they're sent from one computer to another.

Network authentication services are designed to prevent both active attacks and passive attacks. An example of an active attack is when the bad guy tries to hijack Bob's network logon session by executing method calls under the guise of Bob. The authentication service looks for evidence of such tampering and fails method calls when it finds that tampering has occurred. Network authentication services also prevent passive attacks, such as when the bad guy simply eavesdrops on messages in an effort to steal sensitive information. You can guard against eavesdropping by asking the authentication service to encrypt messages before they're sent across the wire so the bad guy can't decipher their contents.

In order to use network authentication, two separate parties running on different computers must agree on a shared secret in the form of a cryptographic session key. This key is used to encrypt messages on the sending side and to decrypt them on the receiving side. When two parties use a session key to pass messages, they're said to be communicating across a secure channel.

In a Windows 2000 network, authentication and the creation of secure channels is performed by a loadable security module called a security support provider (SSP). To make SSPs plug-compatible with one another, Windows requires all SSPs to conform to a standard API called the Security Support Provider Interface (SSPI).

Figure 11-5 shows the layers that exist between a COM+ application and the SSP that's used to perform authentication services. This layered architecture is valuable because your applications are always shielded from the code that performs authentication services. Moreover, you don't need to write or buy an SSP. You can simply leverage one of the SSPs that ships with the operating system.

click to view at full size.

Figure 11-5. SSPs are plug-in modules that perform authentication services.

In most cases, you tell the SSP what level of authentication services you want by adjusting a declarative application attribute called the authentication level. The trade-off with different levels is that some authentication services are pretty expensive in terms of processing time. Setting the authentication level has a definite impact on performance.

NTLM and Kerberos are commonly used SSPs that provide authentication services for COM+ applications in a private network environment. NTLM is the default SSP for Window NT, and Kerberos is the preferred SSP for Windows 2000. Windows 2000 continues to support NTLM for backward compatibility with Windows NT. Secure Channel (SCHANNEL) is an SSP most often used with HTTP (which I'll describe later in the chapter).

The default SSP for Windows 2000 is called the Simple Protected Negotiator (SPNEGO). SPNEGO isn't really an authentication service provider; it's a service that helps two computers negotiate the best SSP when they need to communicate in an authenticated fashion.

It's important to understand that the SSP must be supported by both computers in order for a secure channel to be established. Kerberos is usually used when two Windows 2000 computers establish a secure channel. NTLM is used when a Windows NT computer establishes a secure channel with a Windows 2000 computer or another Windows NT computer. The two computers involved can negotiate and agree on an SSP entirely behind the scenes.

NTLM and Kerberos have a few high-level similarities. For example, the authentication protocols for both SSPs can use a trusted domain controller to help establish a secure channel between two computers. Figure 11-6 shows an example of the parties involved when a secure channel is established between a client application and a COM+ server application. After the two computers negotiate which SSP to use, the SSP executes a handshake protocol to produce a secret session key that is shared by the two machines. This makes possible the establishment of a secure channel.

The low-level details of how NTLM and Kerberos work are fairly complex. NTLM uses a challenge-response protocol, and Kerberos uses an authentication scheme based on the distribution of tickets from services running on a Windows 2000 domain controller. While the details are interesting, I won't discuss them any further. (Keith Brown's book is an excellent resource for those of you who want to read more.) For Visual Basic programmers, the details of how NTLM and Kerberos establish a secure channel are just implementation details. They're hidden from you for your own good.

While knowing the implementation details of each SSP isn't all that important, you should observe that NTLM has a few notable limitations compared to Kerberos. First, NTLM does not support mutual authentication. While NTLM allows the server computer to be sure that it's receiving method calls from an authenticated client, it can't guarantee that the client's method calls will be executed only on the intended server computer. Kerberos supports mutual authentication so the bad guy can't redirect Bob's method calls to an untrusted server by using some routing trickery.

The Kerberos ticket-passing scheme is more scalable than the challenge-response protocol used by NTLM. Kerberos has a more intelligent caching scheme, which results in fewer round trips to domain controllers. Consequently, Kerberos performs better in a larger network environment. Furthermore, Kerberos is significantly faster than NTLM when it comes to creating a secure channel between two computers that live in different, yet trusted, domains.

The final limitation of NTLM is that it doesn't support delegation. This means that NTLM limits the way a distributed application can make use of impersonation. However, later in the chapter I'll argue that you should avoid using delegation in COM+ applications anyway. If you agree with my argument, this last limitation of NTLM won't really be a limitation at all.

click to view at full size.

Figure 11-6. Network authentication with NTLM or Kerberos requires the participation of a trusted domain controller in order to establish a secure channel between two computers.

Setting the authentication level

Each COM+ server application has an AuthenticationLevel attribute that you can set to tell the SSP how actively you want it to perform authentication services. Any call that doesn't meet the requested level of authentication will fail. Higher levels of authentication provide more protection against tampering and snooping, but they also have a greater impact on performance. Table 11-1 lists the responsibilities of the SSPat each authentication level.

Table 11-1 SSP Authentication Levels

Authentication Level Configuration Value Description Protects Against
(None) 1 Method calls are never authenticated. This setting effectively turns off security. N/A
Connect 2 Authentication occurs only once, when the connection is established. Tampering
Call 3 Authenticity is guaranteed only for the first RPC packet of each method call. (This level is unsupported and is always promoted to Packet anyway.) Tampering
Packet 4 Authenticity is guaranteed for each packet header in every method call. Tampering
Packet Integrity 5 Same as Packet authentication level plus encrypted checksum of payload, which guarantees detection of tampering. Tampering
Packet Privacy 6 Same as Packet Integrity authentication level plus payload encryption for privacy. Tampering and snooping

You can adjust the authentication level for a COM+ server application on the Security tab of the application's Properties dialog box in the Component Services administrative tool, as shown in Figure 11-7. The authentication level is set on an application-wide basis when a COM+ server application is launched. Behind the scenes, the COM+ runtime sets the authentication level during application initialization by retrieving the configured value from The COM+ catalog.

The configured authentication level of a COM+ server application is only half the story, however. COM+ (or COM on a non-Windows 2000 computer) also examines the authentication level of the client application when it determines what level to use. You should understand that the process-specific authentication level on either side is a minimum authentication level—a low-water mark. When the client process and the server process have different low-water marks, COM uses the higher of the two.

Figure 11-7. On the Security tab, you can turn on role-based authorization checking and configure the application's security, authentication, and impersonation levels.

When you configure the authentication level of the server application, you're guaranteed that authentication will run at that level or higher. However, if you want to turn off authentication or run it at a lower level, you must make sure that the client application doesn't request an authentication level that's higher than what you want.

When you install a Visual Basic client application that's based on a Standard EXE project, you can configure its authentication level in two ways: the easy and incorrect way or the hard and better way. The easy and incorrect way is to adjust the machine-wide default authentication level. You can easily do this using an administrative utility named DCOMCNFG.EXE. However, this opens a security hole on the client computer, particularly if you adjust the machine-wide authentication level to None.

The hard and better way to configure a client application's authentication level is to add a custom set of AppID keys to the Windows Registry. This technique works for client applications running on Windows 2000 or Windows NT. Figure 11-8 shows the keys and named values that you must add to the Registry. Note that you must map the name of the client application's executable image to a custom AppID and you must configure the AppID key with a named value that holds the configured authentication level.

click to view at full size.

Figure 11-8. These AppID settings are required when you configure the AuthenticationLevel setting for a client-side application based on a Standard EXE project. If there's no AppID, the application uses the machine-wide default AuthenticationLevel setting.

You should note a few issues related to adding these entries to the Windows Registry of the client computer. First, the key that holds the GUID for the AppID must have an AuthenticationLevel named value of type DWORD (not String). Its value must also match one of the configuration values shown in Table 11-1. Second, after you add the AppID key to the Registry, you can adjust its authentication level using DCOMCNFG.EXE.

So far, I've only described how to set the authentication level declaratively. However, it can sometimes be beneficial or necessary to raise or lower the authentication level programmatically. In such situations, you must write the code in a low-level language such as C++ that can talk directly to COM's security API. The COM library exposes functions such as CoInitializeSecurity and interfaces that allow you to adjust the authentication level and other security settings on a process-wide basis. These functions also allow you to make more granular changes. For instance, you can change security-related settings for the channel associated with a proxy/stub pair. You can thus raise or lower the authentication level on a call-by-call basis.

Some companies have created a custom shim DLL using C++ to complement Visual Basic components and to gain extra programmatic control. However, if you want to maintain all your code in Visual Basic, you should assume that the authentication level is a static process-wide value.

I'd like to conclude with one more point about authentication levels. Remember that remote communication using COM should generally be restricted to a private network, in which messages associated with COM method calls are typically less vulnerable than on a public network such as the Internet. In a controlled environment, it's not always necessary to crank up the authentication level to one of the higher values (such as packet privacy). However, each company must decide for itself. If you can run authentication services at a lower level, you'll definitely experience better run-time performance.

Setting the impersonation level

In addition to an authentication level, a remote connection between a client and an object also has an impersonation level. The authentication level provides for privacy on the wire and allows the server computer to determine the identity of the caller; the impersonation level protects the client. Because authentication allows a client to effectively pass its security credentials across the network, the impersonation level determines how the server process can use the client's token. Higher levels of impersonation allow the server to do more with the client's security credentials. Table 11-2 lists the options for configuring the impersonation level.

Table 11-2 Impersonation Levels

Impersonation Level Description
Anonymous The server can't see the client's credentials. This level is unsupported across the wire and is transparently promoted to Identify in Windows 2000 and Windows NT. This level is supported locally but it causes all method calls to fail. You should always avoid this setting.
Identify The server can see the security credentials and can use them only for identification and authorization.
Impersonate The server can use the client's security credentials to access local resources.
Delegate The server can use the client's security credentials to access local and remote resources. (Not available through NTLM.)

It's important to see that the impersonation level, unlike the authentication level, is determined entirely by the client. When you configure the impersonation level in a COM+ server application, the setting has an effect only when its objects act as clients that call methods on objects in other processes.

As with the authentication level, Visual Basic programmers can't easily change the impersonation level at runtime. If you're running a Visual Basic client application, the impersonation level used is the machine-wide default. Once again, you can change this default setting using DCOMCNFG.EXE.

Let me take a step back and explain the philosophy behind impersonation. Impersonation is a mechanism that allows a client application to delegate its security credentials to another process so that the other process can perform a task under the identity of the client. At the physical level, impersonation has some pretty heavy requirements. It requires the server application to discontinue running a client request under the identity of the process token and to switch to the identity of the client's token. While running under the identity of the client, the thread servicing the request can do only things that the client has been authorized to do. Moreover, if any operation in the request is audited by the system, the client's user name will show up in the Windows event log.

In some situations, impersonation works great—such as when you need access to a file server. If Bob accesses a shared directory across the network and tries to delete a file, he is assisted by a redirector service that runs under the identity of the system. But Bob's request isn't processed on a thread running under the identity of the system. Instead, before the redirector service attempts to delete the file, it uses impersonation to switch the thread to a token created from Bob's network logon session. The scheme works because Bob must have access rights in order to delete the file. It also means that Bob can be audited if he attempts such an undertaking.

A Web server is another type of service that relies on impersonation. For example, IIS runs as a service under the identity of the system. However, it wouldn't be very safe if IIS were to process every client request using the system's identity. IIS uses impersonation to switch worker threads to a less privileged identity. (Later in this chapter, I'll revisit this topic in greater detail.)

When it comes to building multitier applications using COM+, impersonation works much better in theory than in practice. The first reason to avoid impersonation is that it's somewhat difficult to set up and use, particularly if you want an object running in a COM+ server application to impersonate its client while making an off-host call.

For example, say that Bob runs a client application and wants to activate an object from a COM+ server application running across the network. Bob's interactive logon session on his local computer caches an encrypted copy of his password. When Bob activates an object, the server computer must authenticate him in order to create a network logon session. The local computer must supply the cached copy of Bob's password during the authentication process. You can say that the password in Bob's interactive logon session represents his network credentials.

In order for the server computer to call to a third computer and impersonate Bob, it must also cache an encrypted copy of Bob's password. This type of impersonation, which extends across two or more network hops, is known as delegation. To use delegation, you must use Kerberos because delegation isn't supported by NTLM. The client application must be configured with an impersonation level of Delegation. Moreover, both the client's user account and the user account that serves as the server application's identity must be configured with special property settings in Active Directory. The client's user account must have the Account Is Sensitive And Cannot Be Delegated property disabled. The server application's account must have the Trusted For Delegation property enabled.

Once you've jumped through the administrative hoops, you still have to request impersonation programmatically on a request-by-request basis in the method implementations of your components. There are several programmatic techniques for switching a thread from the process token to the client's token. However, these techniques are much more practical to use from C++ than from Visual Basic.

Now that I've listed a few technical reasons why you should avoid impersonation and delegation, let me give you some design reasons. When you use impersonation in a COM+ application, you're simply passing the buck to the administrator who's managing the data tier. Instead of enforcing a security policy, you're leaving everything up to the person who manages data-tier resources such as databases and shared directories. This security philosophy simply doesn't scale with a distributed application.

Impersonation forces the data tier administrator to explicitly grant and deny access permissions to user accounts and/or group accounts in an organization. When the administrator creates a new group, he or she might have to reconfigure access permissions in several database servers as well as a file server. A security policy becomes increasingly hard to manage as the number of data tier servers increases. Moreover, it's hard to discern the intentions of a user when you configure access permissions for a resource.

For example, if you grant a group read/write access permission on a file, you can't really enforce which parts of the file can be modified. You can't distinguish between a user who want to wants to make a minor change to a file and a user who wants to delete the entire contents of the file.

When you design a distributed application, you can substantially reduce the administrative burden by conducting all your authorization checks at the point at which client requests enter the middle tier. Most companies who've successfully deployed distributed applications on a large scale have found that this is the only sane approach.

It can also be far more effective to configure access permissions based on components and methods rather than on resources such as files and database tables. Both components and methods have associated semantics that make it easier to discern the user's intentions. For example, one method might make a minor change to a file and another method might delete it contents. You can decide which users should be allowed to execute each method. This makes for a far more effective security policy.

From a performance perspective, it's much better to immediately fail a method call in a COM+ application rather than forward the request downstream and have it be rejected by a file server or a database server. The earlier you can fail a call, the better. Moreover, in larger applications the processing cycles of the database server are often the most precious. If this is the case, you shouldn't waste processing cycles by conducting authorization checks on the database server when this work can be offloaded to the middle tier.

In another performance-related matter, impersonation and delegation reduce or eliminate the opportunity to conduct database connection pooling because every connection in a pool must have the same user and password. From the perspective of the DBMS, each connection in a pool has the same user identity.

To sum up, you should run authorization checks in the middle tier as close to the client as possible. This approach requires a significant mind shift for many companies, especially those that have worked extensively with two-tier applications. I recommend that you design a security policy based on the idea that the data tier trusts the user accounts that serve as the identities for middle-tier applications. These middle-tier user accounts are dedicated to running objects in COM+ applications. They're different from other user accounts in that they aren't associated with a human, so database administrators don't really know who's accessing their database.

Keep in mind that the database administrator needs to trust these middle-tier user accounts only enough to grant typical user permissions. For example, a middle-tier user account usually needs read/write permissions on tables and execute permissions on stored procedures. There's no need to grant administrative permissions to middle-tier accounts for activities such as creating and dropping tables.

Database administrators lose a degree of control that they had in the past, and they're no longer responsible for configuring access permissions for the company's user and group accounts. They can't perform user-by-user auditing using the various monitors provided by most commercial DBMSs. While some people (especially database administrators) see this loss of control as scary or threatening, it's a necessity when you deploy a large-scale, 3-tier application.



Programming Distributed Applications with COM+ and Microsoft Visual Basic 6.0
Programming Distributed Applications with Com and Microsoft Visual Basic 6.0 (Programming/Visual Basic)
ISBN: 1572319615
EAN: 2147483647
Year: 2000
Pages: 70
Authors: Ted Pattison

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net