Running Secure Mobile Code
Brian A. Randell
MCW Technologies, LLC
Summary: Demonstrates how to use smart deployment and the Code Access Security (CAS) model to adjust your local CAS policy settings to allow applications to run and perform desired functions from an intranet or Internet location. (19 printed pages)
Note To run the sample application, you will need a version of Microsoft Windows® with version 1.0 SP2 of the .NET Framework installed. All code presented in this article is Visual Basic® .NET , written and tested using Visual Studio® 2002. Testing was done on a Windows XP Professional SP1 system. You will need access to a local Web server in order to test the deployment scenarios.
Two of the major enhancements to developing Windows applications with Visual Basic® .NET (and other .NET languages) are ease of application deployment and the new component-centric Code Access Security (CAS) model. This sample demonstrates how to use smart deployment and CAS. You will adjust your local CAS policy settings to get the application to run and perform the functions desired when running from either an intranet or Internet location as if it were already installed on your machine. The application code is not very interesting—getting the application to run—that's the interesting part!
The sample application, MCodeVB.EXE, performs various application operations that are common in rich Windows applications. Each operation requires one or more security permissions. Before you do anything, understand that you will be modifying the security policies on your machine. This requires that you are a member of your computer's local Administrators group. In addition, you should, at a minimum, back up your current security configuration in case something goes wrong. See the Backing Up Your Security Settings section later in this article for instructions. Before you make any changes, run the program from your local hard drive. It should look like the image in Figure 1.
Figure 1. Sample application running locally
Once you've done that, go ahead a take a peek at the code. Most of the code is simple run-of-the-mill .NET code. The only thing you might notice is some of the partitioning and the use of security demands (more on this later).
Now let's try to run the application using one of the smart client features in .NET. You may be wondering, "What are these features?" If you're not, skip on down to the section entitled Run It from a Web Server. Otherwise, I'm glad you asked. Smart deployment allows you to deploy components, even entire applications, from a remote URL. The Common Language Runtime (CLR) (specifically, the CLR's Assembly resolver and loader) takes care of the rest.
In fact, the best thing to do is to fire up Visual Studio® .NET and do it yourself. Create a Visual Basic .NET Windows Forms application named Quicky. Add a single button. In the code-behind, enter the following line of code:
Dim strMsg As String = Reflection.Assembly.GetExecutingAssembly.CodeBase()
Then display strMsg using the MsgBox statement (MessageBox.Show, if you prefer). Compile the application and copy the EXE to the root of your local Web server. Now, fire up an instance of Internet Explorer and type in http://localhost/Quicky.exe and press Enter. After a few seconds, you should see your application floating over your browser window (similar to Figure 2). Click the button to display the data from the message box. You will notice two things:
- The CodeBase value listed should be the address you typed into Internet Explorer.
- Internet Explorer did not pop up any warning dialogs about running an executable.
Pretty cool, eh?
Figure 2. Quicky.exe running from a local Web server
If you need more details about what's going on, check the References section at the end of this article. Being a smart client means more than just HTTP deployment. The goal of this sample is to deploy a traditional thick client using HTTP and have it use CAS correctly.
Copy MCodeVB.exe to your local Web server. You'll notice a few options running. Others display a message box telling you that the permission requested was not granted. Now try and run the application again. This time, instead of using localhost, use your machine's IP address. Instead of seeing the application run, you most likely will see a dialog similar to the image in Figure 3.
Figure 3. Application not running successfully
What happened? It turns out that under version 1.0 SP2 of the .NET Runtime, code running from the Internet Zone (which is what the dotted address is assumed to be) is not allowed. Naturally, the goal is to get the application to run from either an Internet or intranet location, and for the application to function properly.
Code Access Security is a non-trivial subject. In fact, it is one block of many used to build secure applications in a connected and dangerous world. I will provide a quick introduction so you have enough information to correctly configure your applications for mobile execution.
Permissions express trusted operations. A specific permission might be the ability to execute unmanaged code, such as a COM object. A default CLR installation has a well-defined set of permissions that are grouped into permission sets. You can extend the system by creating your own permissions and/or permission sets.
When the CLR loads code, it gathers evidence. The .NET Framework SDK documentation describes evidence as "properties of code, such as a digital signature or the zone or site of its origin, that are used by security policy to grant permissions to code." In short, evidence is information that the CLR uses to determine what your code should be able to do. That is, what permissions your code will have. This model is a big step forward.
Traditional Windows security is based upon the identity of the user account that the code runs under. Windows security is user-centric. The CLR model of examining evidence and applying a security policy is component-centric. In addition, the CLR model is preventive. It is designed to prevent bad code from running. In contrast, Authenticode, the process of a publisher signing their code with a digital signature and having the end user decide on whether or not to fully trust the code was punitive. If the code did something bad, you had to find the publisher and try and punish them through out-of-hand techniques, such as litigation.
Types of evidence
There are two types of evidence:
Host evidence is provided by the hosting application, either managed or unmanaged code. In either case, a core piece of host evidence is where the code executes from—its origin. Assembly evidence is part of the assembly itself. The default security policy only relies upon host evidence. To run this sample, you will configure your machine's policy to use both types of evidence.
URLs, Zones, and Policy Levels
A critical piece of host evidence is code origin. Every assembly has a CodeBase that expresses where the assembly comes from. This information is available to the CLR as URL evidence. Using this information, the assembly can be assigned membership condition in a specific zone. Zones are used to partition code into groups based upon the standard Internet Explorer zones. Finally, site evidence is derived from the URL if it is HTTP, HTTPS, or FTP. This allows custom settings for assemblies that share the same remote publication location.
Runtime security policy is evaluated at four levels (using evidence as input):
- Application domain
Policy is applied based upon the intersection of all four levels in a hierarchical pattern starting with the enterprise policy, then machine, user, and application domain. Application domain policy is applied on a host-by-host basis at execution time. Of the other three levels, only the machine level imposes any restrictions out-of-the-box. When the .NET Framework is first installed on a machine, a default security policy is created.
All policy levels can be adjusted. Enterprise, machine, and user can each be manipulated in one of three ways:
- Using the command-line tool CASPOL.EXE
- Using the Microsoft .NET Framework Configuration MMC snap-in
Application domain policy is done using code. You will use the MMC snap-in for these exercises, but learning the other two mechanisms is encouraged as an exercise to try on your own. Each policy level uses three mechanisms for controlling policy:
- Code groups
- Membership conditions
- Permission sets
Code groups provide a way for the CLR to grant permissions to an assembly. Code groups are defined by two items—membership conditions and permission sets. Membership conditions are one of the first items used by the CLR to map evidence to a set of permissions for an assembly. By default, the CLR maps a standard set of membership condition classes to the standard evidences classes. You can also define your own membership conditions. Table 1 lists the standard membership conditions.
Table 1. Default membership conditions
|All Code||All managed code|
|Application Directory||The application's installation directory|
|Hash||An MD5, SHA1, or other cryptographic hash of the assembly|
|Publisher||A public key based upon an Authenticode signature|
|Site||The HTTP, HTTPS, or FTP site from which code originates|
|Strong Name||A cryptographically strong signature|
|URL||The location where the code originates|
|Zone||The zone where the code originates|
If the assembly meets a membership condition, it is given the permissions defined in the code group's associated permission set. Permission sets are groupings of individual permissions. The default .NET installation defines seven permission sets, listed in Table 2.
Table 2. Default permission sets
|Nothing||Managed code cannot execute|
|SkipVerification||Grants code the right to bypass the CLR's code verifier (but no other rights)|
|Execution||Allows managed code to run but no rights to use protected resources|
|Internet||Minimal rights for code coming from unknown or dotted name locations|
|LocalIntranet||Basic rights deemed acceptable for internal enterprise use|
|Everything||All standard permissions, except the right to skip verification|
|FullTrust||Provides full access to resources (including the ability to skip verification)|
The standard .NET installation defines one top-level code group, named All_Code, for each of the three core policy levels. The machine policy level defines five code groups that are children of the All_Code group. The LocalIntranet_Zone has two children and the Trusted_Zone has one child, both of which use custom membership conditions. Table 3 lists the default code groups for the machine policy level (excluding the All_Code group), their membership condition, and permission set.
Table 3. Default code groups for the machine policy level
|Code Group||Membership Condition||Permission Set|
At this point, I'm sure you're chomping at the bit to get going. As mentioned earlier, BACKUP YOUR CONFIGURATION before going further (yes, I was shouting). Now clean up your environment. Close your browsers, Outlook, and other distractions. You need three applications—one an instance of Internet Explorer, two the Microsoft .NET Framework Configuration tools, and the a CMD Prompt instance that has the environment setup for running the SDK command-line tools.
Here's a quick summary of what needs to be done:
- Backup your configuration.
- Clear your download cache.
- Recompile the sample application with a strong name.
- Create a new code group using site as the membership condition and assign the Execute permission set.
- Create a child code group of the new code group using the strong name applied in step 3, add the membership condition, and assign the FullTrust permission set.
- Test your application in intranet mode.
- Test your application in Internet mode.
- Take a break and have a beverage of your choosing.
It turns out that there is no built-in tool to back up your security configuration. Security information is stored in XML configuration files. There will be two + n XML config files where n represents the number of user accounts with custom security configurations. The enterprise and machine configuration files are located in the same directory. They will be in the CONFIG folder, which is a child of your Microsoft.NET\Framework\v.1.0.3705\ folder. They are named Enterprisesec.config and security.config. You will find user files located under the Documents and Settings folder by user. Just search for them. They are all named the same—security.config. Copy the files to a safe location. Note that there is an option when using both CASPOL and the GUI tools to reset your configuration. Also note that using this command puts your security policies back to their default settings. Note again that the Reset All command only resets the Enterprise, Machine, and currently logged on user s' configurations. It does not affect other custom user configurations.
Clearing Your Download Cache
Earlier when you ran your tests, the CLR downloaded the EXEs to a special location known as the download cache. Note that the download cache is independent of the Internet Explorer cache. However, there is a download cache per user just like in Internet Explorer. You can view the contents of your download cache either through a Windows Explorer shell namespace extension or through the command-line.
To view it from Windows Explorer do the following:
- Start the Windows Explorer.
- Navigate to your Windows installation directory (see Figure 4).
Figure 4. Finding the download cache
- If you're running Windows XP, pay close attention. Find the child folder named Assembly but don't click on it! There's a little bug that sporadically prevents the download folder from showing up. So make sure you expand the Windows folder in the Folders pane and click on the plus sign (+) next to the assembly folder. (See Figure 5).
Figure 5. Click here first
- Click the expanded Download folder to see the current contents of your download cache. (See Figure 6).
Figure 6. The local download cache
An alternative is to use GACUTIL.EXE. From a command prompt, type GACUTIL /LDL. This lists the contents of your Download cache. In addition, there is one feature of GACUTIL that is not available from the shell—the ability to clear the cache. Type GACUTIL /CDL to clear the entire contents of the Download cache. (See Figures 7 and 8.)
Figure 7. GACUTIL /LDL
Figure 8. GACUTIL /CDL
Adding a Strong Name
Although it is possible for the sample application to run from an Internet address without recompiling it, this would require modifying one of the existing code groups. Instead, you are going to define your own code groups so that only code you trust is executed. To do this, you need to combine host evidence with assembly evidence. There are three primary forms of assembly evidence:
- Strong name
The most useful are strong name and publisher, both of which rely upon cryptographic signatures to verify the publisher of the code. The major difference between the two is that strong names contain no publisher identity. It is not possible to discern from just the strong name who published an assembly. On the other hand, Authenticode digital certificates contain that specific piece of information. I recommend that you use both, if possible.
For this sample, you will create a key file containing a public/private key pair. This is easily accomplished using the Strong Name Utility provided with the .NET Framework SDK. Using a command prompt, type SN –k testkey.snk. Note that the switches are case sensitive. You need to place the file created in the directory containing the sample's solution file. Next, open the sample code using Visual Studio .NET. Open the AssemblyInfo.vb file and uncomment the line:
' <Assembly: AssemblyKeyFile("..\..\testkey.snk")>
Save your changes and compile the solution. Now, using whatever means makes you happy, copy the new EXE to the root of your local Web server.
Key File Management
Before you go any further, I cannot stress enough the importance of a key file management policy in your organization. Treat them like you would any other valuable corporate asset. In fact, if you have a policy in place for using Authenticode certificates, you should duplicate and adjust it for strong name key files.
Creating Code Groups
Switch to the Framework management tools and expand the Runtime Security Policy node until it looks like Figure 9.
Figure 9. Microsoft .NET Framework configuration tools
You will need to expand a set of child nodes in succession starting with the Machine node. Once you found the All_Code node, right-click and select New …. This starts the Create Code Group Wizard (see Figure 10). Give your new code group a name and description and click the Next button.
Figure 10. Create code group Wizard
On the Choose a condition type page, select Site as shown in Figure 11. Enter localhost in the Site name text box and click the Next button again.
Figure 11. Choose a condition type
On the Assign a Permission Set to the Code Group page , choose the Execution permission set as shown if Figure 12. Select Next and then click the Finish button.
Figure 12. Assign a permission set to the code group
You're halfway there. Now, with your new code group selected, right-click and select New … again. You will see the same pages as before, but you will be choosing different options. Give your new code group a name and continue. On the Condition Type page, select Strong Name from the drop-down list as shown in Figure 13.
Figure 13. Choosing the strong name condition type
Click the Import button and navigate to the directory where you have compiled the sample application with a strong name. Select the EXE and leave the two check box options unchecked as demonstrated in Figure 14.
Figure 14. Strong name condition type completed
Finally, on the Assign a Permission Set page, choose the FullTrust item and finish the wizard by clicking Next button and then the Finish button. One of the great things about CAS policy adjustments is that they are immediate. Assuming you've been following along, navigate to the sample EXE using with Internet Explorer, using localhost for the machine name. Everything in the application should execute. If it doesn't, you most likely missed a step. Changes to CAS are immediately in effect for any new code that executes in a new AppDomain.
Figure 15. Strong name condition type completed
If you would like your application to run from an Internet address, you can repeat the process and put a dotted DNS entry or IP address instead of localhost in the top-level Site code group. or you can duplicate your new code group and simply modify the names and settings. The choice is yours.
If you were building a true smart client, you would first need to limit your application to only access-managed code. Today, CAS can only works if your code is under the control of the CLR where it can be contained and verified. When writing any application, your goal is to run the application using the least amount of permissions and privileges. This will require you do a lot more research, code writing, and on-the-job implementations.
One thing you can do is check with the runtime to see whether you have permission to perform an operation. This is known as performing a demand. For example, to see if you can execute COM code, do the following:
Try Dim perm As SecurityPermission perm = New SecurityPermission(SecurityPermissionFlag.UnmanagedCode) perm.Demand() ' You have permission Me.ComCall() Catch ex As SecurityException ' You do not have permission End Try
Note that you must perform the demand from a different method than the one that executes the restricted operation. Otherwise, when the JIT compiler tries to compile your code, it will fail with a security exception. If you start writing smart and secure applications, you will have tons of code similar to the preceding example throughout your applications.
As more code is written using managed code and core operating system functions are exposed through managed code, you will find new ways to employ CAS. While complicated, it is in everyone's best interest that developers take the time to write more secure applications.
For additional information, see the documentation that comes with the .NET Framework SDK and/or Visual Studio .NET. In addition, there have been some good articles written on the subject. Check out the following links:
- Security and Versioning Models in the Windows Forms Engine Help You Create and Deploy Smart Clients by Chris Sells
- Wonders of Windows Forms (column) by Chris Sells
Naturally, there's a whole lot left to discover in security:
- .NET Framework Security by Brian A. LaMacchia, et al, Addison Wesley Professional, 2002. ISBN: 067232184X
- Essential .NET by Don Box, Addison Wesley Professional, 2002. ISBN: 0201734117