< C++ .Net System Prog. 6 | Main | C++ .Net System Prog. 8 >


 

 

Early Stages of the C++ .Net 21

(Managed Extensions for C++)

 

 

The following are the topics available in this page.

  1. Publisher Policy Files

  2. Security

  3. Code Access Security

  4. Role-Based Security

 

 

 

 

Publisher Policy Files

 

As said before, that only executable assemblies have a configuration file. That is not completely true because a shared library assembly installed can have a publisher policy file. This file is a resource-only assembly. The resource is an XML file in the format of a configuration file that is linked (not embedded) to the assembly. The difference between a policy file and a config file is that the resource in a policy file is associated with a library assembly, contains information pertinent to applications using the library, and is used to identify version redirects or a codebase. The resource-only assembly is installed into the GAC, so it must have a strong name. The linker cannot be used to create the policy file because it will only embed resources, and a publisher policy file must have a linked resource. Instead, you use the assembly linker tool, al.

 

Running the assembly linker tool, al

 

Figure 23

 

For example:

al /linkresource:myAssem.config /version:1.0.0.0

   /keyfile:mykey.snk /out:Policy.1.0.myAssem.dll

The name of the policy file has three parts:

Policy.<version>.<assembly>.dll

Policy indicates to the runtime that this is a policy file, <assembly> gives the short name and <version> is the major and minor version of the assembly to which the policy file refers. Thus, the example applies to the versions of myAssem that have a major and minor version of 1.0.

 

Security

 

Security is hugely important. Every project design must start with security considerations, and only after the security permissions required by the code have been decided should you consider designing the code. The Windows NT security model is based on security accounts possessing a security token and secured objects (files, named pipes, synchronization objects, and so on) possessing an access control list (ACL). The token gives access to a unique security ID (SID), and an ACL contains a list of SIDs that have access or are denied access, to the secured object. Windows NT security checks are performed on principals, and principals have a SID, which allows authentication to be performed (a mechanism in which a principal is checked to ensure it is who the principal says it is). Many Win32 APIs access secured objects (for example, ::CreateFile to access an existing file), and the API will automatically perform an access check on the object’s ACL using the current access token to see whether the principal has access to the object. These access checks are performed automatically; the developer does not have to provide additional code. Much of Windows NT security programming involves maintaining ACLs on secured objects and allowing Win32 to perform access checks. The Win32 API allows you to programmatically access security, but the process is rather arcane and obscure, and as a consequence, most designs rely on using the default security. Finally, COM+ provides role-based security, which brings security into the domain of Visual Basic programmers. .NET security is implemented using Windows NT security and provides code to perform access checks and role-based security. However, .NET security goes one step further than this because .NET security also provides code access security (also referred to as evidence-based security), whereby access checks are performed on the call stack rather than on the principal.

 

Code Access Security

 

Access checks on principals are fine as long as you know what the code is about to do. When you run an application, Windows will attach your access token to the process so that when the application attempts to access a secured object, the access check (and auditing, if enabled) will be performed using your access token. If you have administrator’s access to your machine, any code that you run will have administrator’s access. Do you know the resources that each application on your machine tries to access? Are those accesses legitimate? Should a screen saver, for example, have access to your address book and be able to send e-mail? If the screen saver runs under your account, there is no mechanism in Windows to prevent the screen saver from sending spam to all the contacts in your address book.

This issue becomes even more important when you run code in DLLs. When you load a DLL, either statically or dynamically, the code in the DLL is treated the same as the code in the application from a security point of view. If the DLL code attempts to access a secured object that your account has access to, the DLL code will also have access. If you cannot guarantee the integrity of the code, it is clearly a risky action to allow the DLL to run under your account. You cannot specify that all code in a specified DLL should be run with a different access token than the one used by the process that loads the DLL. A process can impersonate another account on a thread (for example, one with lower privileges), as long as the process is run under an account that is allowed to perform impersonation. You could ensure that the DLL is called only on this impersonating thread, but if the thread needs to call code outside of the suspect DLL, that code will be called under the impersonating token and your code could suffer.

COM provides some protection because a DLL server can be run under a surrogate process. Such a surrogate can be assigned a Windows identity that provides the access token for the surrogate process, so this process can be a low privileged account. However, using a surrogate process requires interprocess communications, which presents a performance issue, and the technique cannot be used for ActiveX controls, which have to be loaded in process.

Code access security For a more complete appraisal of code access security, see Keith Brown’s article, “Enforce Code Access Rights with the Common Language Runtime,” MSDN Magazine, February 2001 (Enforce Code Access Rights) in the .NET Framework solves this issue by applying the access checks on the code within the stack trace. Classes and methods can have a security permission attribute to indicate that the code requires specific permissions to run. Code in a method can also explicitly demand a permission. When such a check is performed, code access security will perform the access check for each method within the stack trace until the access check fails, or until all methods have been checked.

Assemblies are the unit of security. When your code requests a permission and that permission is granted, it will be granted based on the evidence of the assembly and such a permission is granted to the assembly. The evidence can include any information that you choose, but by default includes information about the origin of the code. From the evidence, code access security assigns the code to one of the code groups defined in the security configuration files. A code group defines the permissions that the code will be given. Typically, the machine administrator manages code groups and permission sets that the code groups will obtain through the Microsoft .NET Framework Configuration MMC snap-in. The System::Security::Policy namespace also has classes to programmatically manipulate the security policy files.

The significant point is that these checks are performed on all code further up in the call stack, Code in the call stack that has the specified permissions can call CodeAccessPermission::Assert to stop the check from propagating to code higher in the call stack. So if your trusted code calls a library method which then calls a .NET Framework class to perform some action that requires permissions, code access security will check all the code in the stack. If the library was downloaded from the Internet and the permission that is requested is not allowed for code in the code group for downloaded code, the permission will not be granted, even if your trusted code has sufficient privileges.

You know what your assembly can do, so you can specify that the assembly has code that requires specific permissions. If an attempt is made to load your assembly by code that does not have the permission, the load will fail. To specify that your assembly requires specific permissions, your code can add the [SecurityPermission] attribute to the assembly, providing information about the permissions that your assembly requires. Every assembly that you compile with the C++ compiler will have this attribute. If you look at the manifest of an assembly, you’ll see a .permissionset reqmin directive and an XML permission set that has the SkipVerification permission.

[assembly: SecurityPermission(SecurityAction::RequestMinimum, SkipVerification=true)];

This line indicates that the minimum permission that will be required is to skip verification of the code. I will cover verification in the section “Verifiable Code” later in this chapter, but note that C++ .NET code is not verifiable, and as a consequence, it can be called only by code in the code groups that are able to skip verification, which by default means only code that originates on the local machine. This limitation means that you cannot use C++ to write .NET code that will be accessed from another machine or downloaded from another machine. You can read the permission set for an assembly by passing the name of the assembly file to the permview tool.

 

Running the permview tool at command prompt

 

Figure 24

 

You can also apply access permissions to classes and to methods. If the caller does not have the correct permission when calling such a method, or a member of an object of such a class, then a security exception will be thrown. Furthermore, a method can specifically demand a permission by creating a permission object and calling the Demand method. Demand will cause a stack walk to check that the code further up the stack has the relevant permission, thus allowing you to perform fine-grain code access checks. There are classes in the System::Security::Permissions namespace (as well as System::Diagnostics and System::ServiceProcess) for determining the permissions for various common actions, such as accessing the file system, the registry, and user interface API. These classes are shown in Table 4. These permissions are supplied both as a class that you can call in your code and as attributes. The SecurityPermission class and attribute allows you to demand permissions for various .NET actions such as creating and manipulating App­Domain objects, configuring .NET, extending remoting, skipping verification, and calling unmanaged code. These actions are specified as members of the SecurityPermissionFlag enumeration.

One interesting attribute that you will find in the System::Security namespace is [SuppressUnmanagedCodeSecurity]. When managed code makes a call through to unmanaged code, the runtime will demand the Security­PermissionFlag::Unmanaged permission. In some code, particularly C++ code that makes calls through IJW, there might be many managed/unmanaged transitions, which will involve multiple stack walks with a corresponding detrimental effect on performance. The [SuppressUnmanagedCodeSecurity] attribute indicates that the demand for the Unmanaged permission occurs only once, when the code is first JIT-compiled, and the demand is suppressed for subsequent calls to the unmanaged code. The C++ compiler will add this attribute to all the thunks that it generates for IJW.

 

Class

Description

DirectoryServicesPermission

Determines the permissions to read, write, delete, and browse items in directory services

EnvironmentPermission

Determines the permission to read and write environment variables

EventLogPermission

Determines the permissions to read and write to logs, and to create sources and logs

FileDialogPermission

Determine the permissions to show the Open and Save file dialog boxes

FileIOPermission

Determines the permissions to read and write files, append files, and traverse directories

IsolatedStorageFilePermission

Determines the permissions to access isolated storage

PerformanceCounterPermission

Determines the permissions to read, write, and create performance counter categories

ReflectionPermission

Permissions concerning whether the code can use Reflection to access protected and private members of a type

RegistryPermission

Determines the permissions to read, write, and create entries in the registry

SecurityPermission

Determine various permissions concerned with managed code

ServiceControllerPermission

Determines the permissions to connect to or control Windows services

SiteIdentityPermission

Ensures that callers are from a specific Web site

StrongNameIdentityPermission

Ensures that callers have a specific strong name

UIPermission

Determines the permissions to draw and access user inputs from windows, and access the clipboard

UrlIdentityPermission

Ensures that callers are from a specific URL

ZoneIdentityPermission

Ensure that callers are from a specific Internet Explorer Zone

 

Table 4:  Common Code Access Security Permission Classes

 

Role-Based Security

 

The .NET Framework also gives access to principals. You can define your own principal type, but you are more likely to use the .NET Framework classes that give access to Windows principals. You can access the identity of the current principal through the static WindowsIdentity::GetCurrent property, shown here:

Console::WriteLine(S"The principal is {0}", WindowsIdentity::GetCurrent()->Name);

The WindowsPrincipal class has a property that is an IIdentity pointer, which is an instance of the WindowsIdentity class. The WindowsPrincipal is intended to check to see whether the current principal is within a particular role. A role is a description of the type of actions that an account can perform. WindowsPrincipal treats security groups as roles, so IPrincipal::IsInRole tests a principal for membership of a group.

WindowsPrincipal* p = new WindowsPrincipal(WindowsIdentity::GetCurrent());

if (p->IsInRole(WindowsBuiltInRole::Administrator))

{

  // Do some administrative task.

}

You can also use roles in code access security through the Principal­Permission class and the [PrincipalPermission] attribute. These types will test the permissions against the principal for the current thread. By default, the Thread::CurrentPrincipal will not be set, so you have to set this property yourself. After you have done that, you can use either the attribute or demand the permission through the class, as the following code shows:

__gc class Secret

{

public:

   [PrincipalPermission(SecurityAction::Demand, Role="BUILTIN\\Administrators")]

   void SpecialAction();

};

 

void main()

{

   WindowsPrincipal* p;

   p = new WindowsPrincipal(WindowsIdentity::GetCurrent());

   // Set the thread principal to the principal based on the windows identity.

   Thread::CurrentPrincipal = p;

   // Demand a permission to do some action.

   PrincipalPermission* perm;

   perm = new PrincipalPermission(S"MACHINE_A\\Richard", S"BUILTIN\\Administrators");

 

   try

   {

      perm->Demand();

      // Do something that only Richard can do.

   }

   catch(Exception*) { }

 

   // Create the secret object.

   Secret* secret = new Secret;

   try

   {

      // Do something that only Administrators can do.

      secret->SpecialAction();

   }

   catch(Exception*) { }

}

In this action, we demand just one permission; however, we can demand more than one permission through a PermissionSet object that essentially acts as a container for permission objects. Demanding a permission set is likely to be more efficient than demanding individual permissions. Be careful about demanding permissions because a demand (either explicitly or through an attribute) will cause a stack walk and if you are calling a .NET Framework class to perform some action, it too might demand some permission. For example, there is no point in demanding the FileIOPermission before accessing one of the .NET Framework IO classes because they will also demand the same permissions, so a stack walk will be performed twice. An example where demanding the FileIOPermission is useful is if you have some object that does many file actions in its methods, and you could demand the FileIOPermission in the constructor so that if the permission cannot be accessed, the object will not be created. If the demand in the constructor succeeds, the object can be created and you know that when a System::IO class demands the same permissions the demand will succeed. Because the constructor has already confirmed that a demand will succeed, your object’s methods could call Assert so that demands for permissions will not propagate further up the stack.

 

Part 1 | Part 2 | Part 3 | Part 4 | Part 5 | Part 6 | Part 7 | Part 8

 


 

< C++ .Net System Prog. 6 | Main | C++ .Net System Prog. 8 >