Visual CSharp 2005 Recipes (2006) [eng]
.pdfC H A P T E R 1 0 ■ N E T W O R K I N G A N D R E M OT I N G |
389 |
Solution
Configure a lease policy by using configuration file settings, override the MarshalByRefObject. InitializeLifetimeService method, or implement a custom lease provider.
How It Works
If a remotable object uses single-call activation, it will be automatically destroyed at the end of each method call. This behavior changes with client-activated and singleton objects, which are given a longer lifetime dictated by a lifetime lease. With the default settings, a remote object will be automatically destroyed if it’s inactive for 2 minutes, provided it has been in existence for at least 5 minutes.
The component host, remote object, and client each have the opportunity to change lifetime settings, as described here:
•The component host can specify different lease lifetime defaults in the configuration file using the <lifetime> tag. The leaseTime attribute of the tag specifies the default lifetime for all hosted object. The renewOnCallTime attribute specifies the amount of time by which the lease is extended when a call is made against a hosted object. You can specify the values for both attributes as positive integers with a time unit suffix for days (D), hours (H), minutes (M), or seconds (S). For example, 10 hours is 10H, and 30 seconds is 30S.
•The remote class can override its InitializeLifetimeService method (inherited from MarshalByRefObject) to modify its initial lease settings by configuring and returning an object that implements the System.Runtime.Remoting.Lifetime.ILease interface. You obtain an ILease instance by calling the base class method InitializeLifetimeService. Then configure the returned ILease by setting the InitialLeaseTime and RenewOnCallTime properties to the desired values using System.TimeSpan objects. If you want the object to have an unlimited lifetime, simply return a null reference instead of an ILease object. This is most commonly the case if you are creating a singleton object that needs to run independently (and permanently), even if clients aren’t currently using it.
•The client can call the MarshalByRefObject.GetLifetimeService method on a specific remote object to retrieve an ILease instance. The client can then call the ILease.Renew method to specify a minimum amount of time the object should be kept alive.
The Code
The following example demonstrates how to use a component host’s configuration file to control lifetime leases. The configuration gives each hosted object an initial lifetime of 10 minutes, and each time a member of the object is invoked, the lifetime is set to be at least 3 minutes.
<configuration>
<system.runtime.remoting>
<application>
<service>
<wellknown
mode = "Singleton" type="Apress.VisualCSharpRecipes.Chapter10.Recipe10_19,Recipe10-19" objectUri = "Recipe10-19" />
</service>
<channels>
<channel ref="tcp" port="19080" /> </channels>
C H A P T E R 1 0 ■ N E T W O R K I N G A N D R E M OT I N G |
391 |
How It Works
.NET Remoting doesn’t include any intrinsic support for versioning. When a client creates a remote object, the component host automatically uses the version in the local directory or, in the case of a shared assembly, the latest version from the GAC. To support multiple versions, you have three choices:
•Create separate component host applications: Each component host will host a different version of the remote object assembly and will register its version with a different URI. This approach forces you to run multiple component host applications at once and is most practical if you are using IIS hosting (as described in recipe 10-18).
•Create an entirely new remote object assembly (instead of simply changing the version): You can then register the classes from both assemblies at different URIs by using the same component host.
•Install all versions of the remote object assembly in the GAC: You can now create a component host that maps different URIs to specific versions of the remote object assembly.
The Code
The last option is the most flexible in cases where you need to support multiple versions. The following configuration file registers two versions of the RemoteObjects assembly at two different endpoints. Notice that you need to include the exact version number and public key token when using assemblies from the GAC. You can find this information by viewing the assembly in the Windows Explorer GAC plug-in (browse to C:\[WindowsDir]\Assembly). The client configuration file won’t change at all (aside from updating the URI, if required). The client “chooses” the version it wants to use by using the corresponding URI.
<configuration>
<system.runtime.remoting>
<application>
<service>
<!-- The type information is split over two lines to accommodate the bounds of the page. In the configuration file, this information must all be placed on a single line. -->
<wellknown mode="SingleCall"
type="RemoteObjects.RemoteObject, RemoteObjects, Version 1.0.0.1, Culture=neutral, PublicKeyToken=8b5ed84fd25209e1"
objectUri="RemoteObj" />
<wellknown mode="SingleCall"
type="RemoteObjects.RemoteObject, RemoteObjects, Version 2.0.0.1, Culture=neutral, PublicKeyToken=8b5ed84fd25209e1"
objectUri="RemoteObj_2.0" /> </service>
<channels>
<channel ref="tcp" port="19080" /> </channels>
</application>
</system.runtime.remoting>
</configuration>
C H A P T E R 1 1 ■ S E C U R I T Y A N D C RY P TO G R A P H Y |
395 |
Solution
Apply the assembly-level attribute System.Security.AllowPartiallyTrustedCallersAttribute to your shared assembly.
How It Works
To minimize the security risks posed by malicious code, the runtime does not allow assemblies granted only partial trust to access strong-named assemblies. This restriction dramatically reduces the opportunity for malicious code to attack your system, but the reasoning behind such a heavyhanded approach requires some explanation.
Assemblies that contain important functionality that is shared between multiple applications are usually strong-named and often installed in the global assembly cache (GAC). This is particularly true of the assemblies that constitute the .NET Framework class library. Other strong-named assemblies from well-known and widely distributed products will also be in the GAC and accessible to managed applications. The high chance that certain assemblies will be present in the GAC, their easy accessibility, and their importance to many different applications makes strong-named assemblies the most likely target for any type of subversive activity by malicious managed code.
Generally, the code most likely to be malicious is that which is loaded from remote locations, such as the Internet, over which you have little or no control. Under the default security policy in version 1.x and 2.0 of the .NET Framework, all code run from the local machine has full trust, whereas code loaded from remote locations has only partial trust. Stopping partially trusted code from accessing strong-named assemblies means that partially trusted code has no opportunity to use the features of the assembly for malicious purposes, and cannot probe and explore the assembly to find exploitable holes. Of course, this theory hinges on the assumption that you correctly administer your security policy. If you simply assign all code full trust, not only will any assembly be able to access your strong-named assembly, but the code will also be able to access all of the functionality of the .NET Framework and even Win32 or any COM object through P/Invoke and COM Interop. That would be a security disaster!
■Note If you design, implement, and test your shared assembly correctly using CAS to restrict access to important members, you do not need to impose a blanket restriction to prevent partially trusted code from using your assembly. However, for an assembly of any significance, it’s impossible to prove there are no security holes that malicious code can exploit. Therefore, you should carefully consider the need to allow partially trusted code to access your strong-named assembly before applying AllowPartiallyTrustedCallersAttribute. However, you might have no choice. If you are exposing public classes that provide events, you must apply this attribute. If you do not, an assembly that is not strong-named will be allowed to register a handler for one of your events, but when it is called, a security exception will be thrown. Code in an assembly that is not strong-named is not allowed to call code in a strong-named assembly.
The runtime stops partially trusted code from accessing strong-named assemblies by placing an implicit LinkDemand for the FullTrust permission set on every public and protected member of every publicly accessible type defined in the assembly. This means that only assemblies granted the permissions equivalent to the FullTrust permission set are able to access the types and members from the strong-named assembly. Applying AllowPartiallyTrustedCallersAttribute to your strongnamed assembly signals the runtime not to enforce the LinkDemand on the contained types and members.
C H A P T E R 1 1 ■ S E C U R I T Y A N D C RY P TO G R A P H Y |
397 |
■Note You could permanently turn off CAS in .NET Framework versions 1.0 and 1.1 both programmatically and using Caspol.exe. In .NET Framework 2.0, you can turn off CAS only temporarily and only by using Caspol.exe.
How It Works
In some cases, code-level security might not be of interest to you. For example, when you are debugging code, you might want to exclude the possible interference caused by CAS. On rare occasions, the need for performance might outweigh the need for security. CAS is a key element of the .NET runtime’s security model and one that sets it apart from many other computing platforms. Although CAS was implemented with performance in mind and has been used prudently throughout the .NET class library, some overhead is associated with each security demand and resulting stack walk that the runtime must execute to check every caller in the chain of execution.
■Note You should disable CAS only for performance reasons after you have exhausted all other possible measures to achieve the performance characteristics your application requires. Profiling your code will usually identify areas where you can improve performance significantly without the need to disable CAS. In addition, you should ensure that your system resources have appropriate protection using operating system security mechanisms, such as Windows access control lists (ACLs), before disabling CAS.
In these situations, you can temporarily disable CAS and remove the overhead and possible interference caused by code-level security checks. Turning off CAS has the effect of giving all code the ability to perform any action supported by the .NET Framework (equivalent to the FullTrust permission set). This includes the ability to load other code, call native libraries, and use pointers to access memory directly.
Caspol.exe is a utility provided with the .NET Framework that allows you to configure all aspects of your code access security policy from the command line. When you enter the command caspol –s off from the command line, you will see the following message indicating that CAS has been temporarily disabled:
Microsoft (r) .NET Framework CasPol 2.0.50727.42
Copyright (c) Microsoft Corporation. Al rights reserved.
CAS enforcement is being turned off temporarily. Press <enter> when you want to restore the setting back on.
As the message states, CAS enforcement is off until you press Enter, or until the console in which Caspol.exe is running terminates.
Notes
In versions 1.0 and 1.1 of the .NET Framework, running the command caspol –s off turned off CAS enforcement permanently until you turned it on again using the command caspol –s on. In addition, it was possible to turn CAS on and off programmatically using the System.Security.SecurityManager class. The SecurityManager class contains a set of static methods that provide access to critical security functionality and data. This includes the SecurityEnabled property, which turns CAS checks on and off.
To disable CAS, your code must run as a Windows Administrator and must have the ControlPolicy element of the permission System.Security.Permissions.SecurityPermission. Naturally, you do not need any specific permissions to enable CAS.