Distributed COM relies on configuration information stored in the Registry of every computer running a distributed application. Each CLSID that can be activated remotely must have a profile in the Registry, which maintains important settings such as the path to the EXE file that's used to launch the server's process. To properly set up a distributed application, you must also configure an applicationwide profile for the server's process. This profile affects the entire out-of-process server, which is known as a COM application. COM uses the AppID key in the Registry to hold important settings associated with a COM application.
The Registry key HKEY_CLASSES_ROOT\AppID contains a list of all the local EXEs that export CLSIDs. Each local server (each a potential remote server) is represented in this list with its own GUID, which serves as its AppID. There's only one AppID for each out-of-process server. If the server contains three or four CLSIDs, all of these coclasses share a common applicationwide profile defined by the server's AppID.
When the SCM attempts to launch an out-of-process server, it uses the settings in the AppID to determine whether an administrator has authorized the client to use the server. This creates a bit of a problem because many servers don't register an AppID as part of the self-registration process. For example, the self-registration code in your Visual Basic server never writes anything to the Registry that has to do with an AppID. Fortunately, the SCM has a way to deal with this situation.
When the SCM launches an out-of-process server during an activation request, it examines the CLSID to see whether it has an associated AppID. If the CLSID has an associated AppID, the Registry settings for the AppID tell the SCM about the server's security profile. If the CLSID doesn't have an AppID, the SCM uses a systemwide default profile. This allows every out-of-process server to be launched under the control of a secure profile.
While you can modify AppIDs in the Registry by hand, it's better to use the configuration utility called Dcomcnfg.exe, which is on all machines that run Distributed COM. You run this utility by choosing the Run command from the system's Start menu. When you run Dcomcnfg.exe, you see a list of all the registered AppIDs on the local machine. (It might take a few seconds to start up Dcomcnfg.exe the first time you launch it because the utility walks through the list of out-of-process CLSIDs and creates an AppID for each one that needs one.)
Dcomcnfg.exe is fairly easy to use. Once you learn how to use it, you can run and test distributed applications in the development environment. You should note that Dcomcnfg.exe is intended for administrators, not programmers. Distributed COM gives more responsibilities to system administrators while taking many responsibilities away from programmers. A programmer can create a component in a generic manner, and the administrator can configure it to run in many possible ways at deployment time. COM security is a good example of how this works.
COM offers both declarative security and programmatic security. Declarative security is beneficial because it makes the system administrator responsible for deciding and enforcing who has access to an application. As the security requirements for the application change, the administrator can address them without having to rewrite any application code.
The administrator configures and adjusts security settings in a Microsoft Windows NT network environment. Often the same individual is responsible for access permissions to resources and services such as Microsoft Exchange, Microsoft SQL Server, and network shares on a file server. The administrator typically knows the physical layout of computers in the network as well as the user and group accounts. Learning how to administer security in a COM-based distributed application is pretty straightforward.
Windows NT security is based on user and group accounts. Each Windows NT account has an associated security ID (SID). When a user logs on to a Windows NT domain, the login process creates a security token that contains the SID of the user's account and the SID of any group account to which the user has been added. The security token is carried by each process that the user runs. When a user attempts to access a resource, the system checks this security token to make sure the user is authorized.
A Windows NT administrator authorizes users by modifying a discretionary access control list (DACL), which is a list of SIDs associated with a resource that tells the system which users have access rights. An administrator manages this list with a DACL editor, as shown in Figure 8-4. Dcomcnfg.exe provides a DACL editor for configuring access rights to a distributed application.
Figure 8-4. A developer or a system administrator can use a DACL editor to configure permissions for a user or a group.
Figure 8-5 shows the Default Properties tab of Dcomcnfg.exe, which contains the machinewide settings for Distributed COM. The check box that enables or disables Distributed COM is selected by default. If you deselect it, the SCM of the local machine will deny any activation request from a remote client.
Figure 8-5. You can use the Default Properties tab of Dcomcnfg.exe to adjust machinewide settings.
You can also assign a machinewide authentication level on this tab. Authentication is the process of verifying the user's identity. When a user named Bob attempts to activate an object from across the network, the COM security layer on the server examines his credentials to verify that it really is Bob and not another user who is pretending to be Bob.
Authentication is performed by the RPC layer and a loadable security module called a security support provider (SSP). All SSPs are written to a standard API called the Security Support Provider Interface (SSPI). This practice allows third-party vendors to create custom SSPs that can be used in a Windows NT network environment. Windows NT 4 ships with the Windows NTLM SSP, but Windows NT 5 also includes the Kerberos SSP.
Both the client computer and the server computer have their own default authentication levels. The level on each machine should be seen as a low-water mark. When two computers have different authentication levels, the RPC layer uses the higher one. Higher levels of authentication have a negative impact on performance. The levels of authentication are shown in Figure 8-6.
Figure 8-6. The authentication level determines how actively the service provider authenticates messages from clients. Each level is a superset of the previous level and has a greater run-time cost.
|(None)||COM calls are never authenticated.|
|Default||The default authentication level specified by the SSP.|
|Connect||Authentication occurs only once, when connection is established (the default in Windows NT 4).|
|Call||Authenticity is guaranteed only for the first RPC packet of each method call. (This level is unsupported and is always promoted to Packet anyway.)|
|Packet||Authenticity is guaranteed for each packet header in every method call.|
|Packet Integrity||Packet authentication level plus cryptographic checksum of payload guarantees detection of tampering.|
|Packet Privacy||Integrity authentication level plus payload is encrypted for privacy.|
Authentication is part of the infrastructure of Windows NT and RPC. You don't explicitly write application code with Visual Basic that addresses authentication. This is usually beneficial because it makes application code much simpler. A system administrator can also easily change to a new SSP or a different authentication level after an application is in production without having to modify the application code.
In some situations, it's beneficial or necessary to raise or lower the authentication level programmatically. In such situations, the code must been written in a low-level language such as C++ that can talk to COM's security API. The COM library exposes functions that allow you to adjust the authentication level and other security settings on a proxy-by-proxy basis. This might be a good reason to create a shim DLL written in C++ to provide more flexibility to the servers and the client applications that you create with Visual Basic.
While the authentication level allows the object to determine the identity of the caller and provides for privacy on the wire, 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. Figure 8-7 shows a list of the available options in Dcomcnfg.exe.
Figure 8-7. The impersonation level determines what the server can do with the client's security credentials. Each level is a superset of the previous level.
|Anonymous||The server can't see the credentials of the client. This level is unsupported and is transparently promoted to Identify in Windows NT 4 and Windows NT 5.|
|Identify||The server can see the security credentials and can use them for identification and authorization only (Windows NT 4 default).|
|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 in Windows NT 4.)|
As with the authentication level, Visual Basic programmers can't easily change the impersonation level at run time. You can use one of three approaches to configure authentication and impersonation. The first and easiest approach is to use the default levels, which are Connect for authentication and Identify for impersonation. The second option is to configure the machinewide levels on each computer for some other level(s). The third approach is to take on the challenge of writing code that changes these levels programmatically. This approach typically requires some help from another language, such as C++.
Each AppID represents a COM application. A COM application is a collection of CLSIDs whose objects are all loaded into a single-server process. When you configure the AppID for a server, you can specify which users can access the application and which Windows NT user account will be used to serve as the identity of the server process. This user account is known as the server's security principal.
To configure the settings for an AppID with Dcomcnfg.exe, you must select the AppID on the Applications tab. Each entry in the Applications list box is a local AppID. Remember that Dcomcnfg.exe often creates AppIDs using information from the first CLSID it finds in the Registry. This practice can result in confusion because the application's description looks as if it pertains to the CLSID, not to the server application. When Dcomcnfg.exe creates an AppID, it uses the same GUID that defines the first CLSID. However, once you find the correct COM application in this list, you can select Properties. This opens another dialog box, in which you can modify the attributes for the AppID.
To configure the server's security settings, you can use the Security tab, as shown in Figure 8-8. This tab offers three security configuration choices; you can set and modify each one using a standard Windows NT DACL editor. All access permissions are set in terms of Windows NT user and group accounts. A user must have access permissions to activate and call methods on objects. A user must have these permissions explicitly or through a group in order to use the server. The SYSTEM account must be included in the Access Control DACL (at least in Windows NT 4). If you forget this, your application won't function properly.
Figure 8-8. You can use the Security tab of Dcomcnfg.exe to configure access and launch permissions to a COM application.
A user must have launch permissions to launch the application if the server process isn't already running. This configurable option lets an administrator restrict the times when the application can be used. For example, users who have access rights but not launch rights can't start the server after the administrator has shut it down for the day.
The last set of options on the Security tab allows you to set the configuration permissions. This setting tells the system which users can configure the AppID. Configuration permissions are typically given only to the system administrator and simply set the DACL on the physical Registry key of the AppID, thus preventing unauthorized users from changing the more interesting Access and Launch permission DACLs or other AppID related settings.
Any application that doesn't have a dedicated AppID uses a machinewide default profile. The system retrieves this default profile from the Registry when it fails to find a valid AppID. You can use the Default Security tab in Dcomcnfg.exe to modify these default settings.
Each COM application assumes the identity of one of Windows NT's user accounts when it's launched. This means that objects loaded into the server process run under the identity of this security principal. The setting for a server's identity is stored in the AppID key under the RunAs named value. You can modify which user account is used on the Identity tab for a specific AppID in Dcomcnfg.exe, as shown in Figure 8-9. You have three choices when configuring your server's RunAs identity. You can select The Interactive User, The Launching User, or a dedicated Windows NT user account.
Figure 8-9. You can use the Identity tab to specify which user account you want a COM application to run as.
The default setting in Windows NT 4 is The Launching User, but this is almost always the wrong choice for a distributed application. With this setting, the activating client's user account serves as the security principal for the server's process. This can cause multiple instances of the server to be launched on one computer. For example, consider what happens if you use this setting and two remote users, Bob and Martha, both activate objects. This causes two separate instances of the server process to run on the server, since Bob's objects need to run as Bob and Martha's objects need to run as Martha. It's unfortunate that this is the default for Windows NT 4. Future versions of COM will remove this configuration option altogether.
The most common approach is to create a dedicated Windows NT user account that serves as the security principal for distributed applications. Once you create a local or a domain account, you can configure the AppID by selecting This User and entering the user name and password, as shown in Figure 8-7. (You must know the password of the user account.) Dcomcnfg.exe does two other important tasks when you select a user in this manner. It grants the Logon As Batch Job right to the user account, and it writes the account's password to a special place in the Registry. These two tasks are required to properly assign a user account to the server's identity. Simply adding a RunAs named value to an AppID by hand (using Regedit.exe, for example) is insufficient.
Once you configure your application to run using a specific user account, all objects activated from the AppID run in a single-server process. This also makes it easy to configure other areas of security in a distributed application. If your business objects access a file server or use SQL Server's integrated security model, the administrator can grant permissions to a single business object account. There's no need to grant access permissions on a user-by-user basis. The administrator simply has to grant permissions to individual users as they attempt to access the distributed application.
The remaining option you have for server identity is to run the server process as an interactive user. This isn't a good idea for a distributed server application in a production environment. When you configure a server to run as the interactive user, the SCM launches the server's process using the user account of whoever happens to be logged on to the console of the server's computer at the time. If the administrator is logged on, you get one set of permissions. If a tape operator is logged on, you get a different set of permissions. If nobody is logged on, the distributed application can't be launched. As you can see, this choice can be problematic.
There are a few reasons why you might run a server as the interactive user. If the server process will run on the same computer as the client application, you probably want both processes to run under the same account. You can see that the interactive user is the same as the launching user when a user activates an object from a local server. Only the interactive user account can display windows on the desktop of the local machine. When you run objects under a dedicated user account, you can't display windows on the screen. Actually displaying a window will succeed, but the window will be displayed in a "noninteractive window station," which means no human will be there to see it. This situation means that the interactive user account can be useful during debugging, when you want to display a message box from an out-of-process server. This also means that invoking a message box from a business object running under a dedicated user account will hang the object indefinitely, since nobody will ever press any buttons to dismiss it. This is a good reason to create your servers using Visual Basic's Unattended Execution option.