#Google Analytic Tracker

Pages

Showing posts with label CAS. Show all posts
Showing posts with label CAS. Show all posts

Jun 24, 2010

.NET Remoting Causes Security Exception After Upgrade to .NET 4.0

This may well be my last blog post on .NET Remoting. I am not sure how many applications out there still using .NET Remoting, but the current trend is to migrate from .NET Remoting to WCF. Eventually, we will, but in the meantime, the product I am working on will still relay on .NET Remoting.

Yesterday, I updated all the VS project target framework from .NET 3.5 to .NET 4.0.  Usually migrating from one version of .NET framework to a newer one should be fairly simple.  There are some difference between .NET 3.5 to .NET 4.0, for example MS removed the CAS and promote the use of Security Transparent Code. It also introduces new classes like Tuple, which is useful for represent data from a data row.

After changing all the target frameworks and rebuild the solution, everything should works just like before. Started my server and client app, boom…. System.TypeInitializationException!!!

Exception

Exception: "The type initializer for 'ME.APTS.ServiceInterface.DataAccess.DataAccessBase`1' threw an exception."

Inner Exception: System.Security.SecurityException {"Request failed."}

Stack Trace:

 
Server stack trace: 
at System.Runtime.Serialization.FormatterServices.nativeGetSafeUninitializedObject(RuntimeType type)
at System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject(Type type)
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ParseObject(ParseRecord pr)
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Parse(ParseRecord pr)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryObjectWithMapTyped record)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryHeaderEnum binaryHeaderEnum)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Remoting.Channels.CoreChannel.DeserializeBinaryRequestMessage(String objectUri, Stream inputStream, Boolean bStrictBinding, TypeFilterLevel securityLevel)
at System.Runtime.Remoting.Channels.BinaryServerFormatterSink.ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, IMessage& responseMsg, ITransportHeaders& responseHeaders, Stream& responseStream)
 
Exception rethrown at [0]: 
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at ME.APTS.ServiceInterface.IBroadcastListener.Process(ArrayList broadcastQueueArrayList)
at ME.APTS.Service.Provider.Remoting.BroadcastQueueRemoting.Process(ArrayList listOfObjectsToProcess) in C:\dev\Streets\Main\APTS\ME.APTS.Service.Provider\Remoting\BroadcastQueueRemoting.cs:line 119

This exception is being throw by my client app, when my server app tries to do a remoting call on the client application. The server when to send a piece of data to the client, and it needs to create an object, but apparently the server has no permission (that’s what I assumed) to create object on the client side.

The Confusion

After spending hours debugging the problem, it became even more confusing when some client application works and some aren’t. These client applications run the same piece of code on the same machine, and yet only some of them work.

Trying to confuse myself more, I tried to look for security attributes setting that we may have used. Nothing turns up. Since we didn’t change anything in the code and not even the application configuration files, it must be changes in the .NET framework.

Debugging

After hours of frustration, my brain was too tired to think. I asked my colleague to look into this issue together. After a couple of testing,  we realized we forget to check the client configuration file. We don’t use App.config, but our setting file is in a very similar format. After doing some configuration files comparison, BINGO!

Solution

It all comes down to a missing .NET Remoting setting, which is highlighted below.

<configuration>
  <system.runtime.remoting>
    <customErrors mode="off" />
    <debug loadTypes="true" />
    <application >
      <service>
        <wellknown mode="SingleCall" objectUri="BroadcastListener.rem" type="ME.APTS.Client.Model.BroadcastListener, ME.APTS.Client.Model" />
      </service>
      <channels>
        <channel ref="tcp" port="9999">
          <serverProviders>
            <formatter ref="binary" typeFilterLevel="Full" />
            <provider type="MyCompany.SecurityServerChannelSinkProvider, MyCompany.ServiceInterface" securityPackage="NTLM" impersonationLevel="impersonate" authenticationLevel="call" logging="true"/>
          </serverProviders>
        </channel>
      </channels>
    </application>
  </system.runtime.remoting>
  <appSettings>

After specifying the typeFilterLevel attribute to Full, the app works again. Here is the strange part, why all of the other working application has a specified typeFilterLevel, and not the one that I was debugging. I checked the existing release build, which runs on .NET 3.5, and it doesn’t have this setting either.

Conclusion

The only conclusion I can draw from this is that a change in .NET 4.0 causing this issue. By default, the typeFilterLevel is set to low. The application that I was testing must have been using the low setting. From the typeFilterLevel documentation, it isn’t clear when you should use typeFilterLevel.Low or typeFilterLevel.Full.

Low: The low deserialization level for .NET Framework remoting. It supports types associated with basic remoting functionality.

Full: The full deserialization level for .NET Framework remoting. It supports all types that remoting supports in all situations.

The changes in .NET Remoting security cause my application to use typeFilterLevel to full. It maybe related to the changes of the CAS. It would be interesting to see what exactly has changed, but there is no time for it. Soon our application will migrate to WCF and .NET Remoting will become the past.

Feb 3, 2008

Some .NET Application Security Experience

While there are so many hype on .NET Framework 3.0 and 3.5, I am still studying .NET 2.0 for a MCTS exam.

In my past experience, I rarely deal with .NET programming using its build-in security framework.

Introduction

CAS (Code Access Security), is a .net security system that allow administrators to control .net application authorization to the OS resource. This security system ONLY works for .NET application (or managed code). It can't have security control on (unmanaged code).

For example, say your .NET application called a Excel COM library function, or calling a dll imported function, even you set your .net application no write access to C drive, it only means within your .NET application has no access to write on C drive. Your unmanaged dll will still able to write access to C drive as long as your OS allowed it to.

Layers of Security

Here is the basic security layers on how a user get access to the computer's resources:

  User's Action (i.e. click a button)
Your Application .NET Application Assembly
  .NET Framework CAS
  Operation System Security (i.e. Windows RBS)
Resources: Hard Disk, Registry, Printers, etc...

RBS - Role Base Security (Security based on user credential such as Administrator, Power User, System User, Guest, etc)

The first line of your application security defence is your .net assembly itself. You can prevent user from access resources programmatically. However, when you are running an unknown assembly that you are not comfortable with (say you downloaded a program, but don't want this program to access your C:\Windows\ directory, this is where CAS comes into play. Depending on the type or how your assembly is beginning execute (i.e. execute from web vs. from local computer) and etc, different permissions can be apply to your assembly.

However, even you give a write C:\ drive permission on .NET CAS level, if your OS doesn't allows your application to write to C:, say you log on as a Guest, .NET Framework will throw you a System.UnauthorizedAccessException.

How to configure the CAS

Other than using .NET Framework Caspol tool, which is usually located at C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Caspol.exe, you should use the NET 2.0 Framework Configuration Tool.

Go to Control Panel -> Administrative Tools -> Microsoft .NET Framework 2.0 Configuration.

image

Under the Runtime Security Policy tree node, you will find all the CAS related settings.

In general, all assembly execute with something call Evidence. Evidence is basically your assembly's identification. Evidence can be the assembly's hash code, its Strong Name, its URL that it downloaded from (i.e. www.microsoft.com/assembly.exe), its site (i.e. www.microsoft.com), publisher or its Zone. Please refer to Evidence Class for details

image

Basics

First, when your assembly runs, it has to check though all 3 levels (Enterprise, Machine, and User). Your assembly will be checked though all 3 levels. CAS will takes the minimal set of all 3 level permissions. So say if under machine level, your assembly can't write to C:\Windows, even on Enterprise or User allows your assembly to do so, CAS will not let your application to write to C:\Windows.

Second, when CAS elevates your assembly for each level, it determines which "code" your assembly belong to. Is your assembly running under "My_Computer_Zone", or under "Internet_Zone" (see above diagram). An assembly can beyond to multiple code. For example, it can be in a "My_Computer_Zone", while it is also in "MyCodeGroup" (a custom one that I personally made) . CAS will take the union of all the code group and combine together them together.

If you don't want your application to have multiple permission, you can set the "This policy level will only have ...." option in the code group setting.

image

This way, even your assembly has access write to c:\ drive because it belongs to "My_Computer_Zone", since you set this option, the My_Computer_Zone no longer apply (like a intersection, instead of union) .

Membership Condition

In each code group, you can set the "Membership Condition". You can say if your assembly run from a specific URL, it will belong to this group, or if your assembly has a particular strong name, it will belong to another group. You can even made your custom condition, which I personally have no idea how to make one.

Example (any assembly has this Strong Name belong to "Local_Shared_Folder" code group that I made):

image

Permission Set

Notice that by default, .NET 2.0 configuration comes with these permission sets.

Permission Sets Name Brief Description
FullTrust Unrestricted access to all protected resources
SkipVerification Grants assembly right to bypass the verification
Execution Permit assembly to execute (doesn't mean it can access protected resources!)
Nothing Denies all resources, not even execution (Of course, if the assembly has skipverification permission, it can easily by pass this nothing...I guess)
LocalIntranet Some default permissions ideal for local intranet application
Internet Some default permissions ideal for Internet application
Everything Allows unrestricted access to all resources covered by .NET build-in permissions.

For example: My_Computer_Zone's permission set is "FullTrust", while Restricted_Zone's permission set is Nothing. If your assembly happens to have both of these zone (which it shouldn't), since CAS takes the union of all permission, your assembly will still able to access the FullTrust permission sets. That's why the option of setting policy level with the only grant permission helps.

The Everything Permission Set

Here is a list of all the build-in permissions.

image

Declarative and Imperative Security

Here is the confusing part about CAS and this "declarative and imperative security" when I first study the .NET security.

Since now you know how administrator can limit your assembly to access certain resources, as a developer, you should write your program to participate in the event when your assembly runs into security problems.  Otherwise, your assembly will get a very unfriendly System.Security's exceptions and your application has no idea what to do other than crashed.

Note that if your application are has the "FullTrusted" permission, nothing will matter because CAS will not check any permission when running your assembly.

Declarative Security Check

Basically all declarative security is done though the Permission Attributes . By applying declarative security check to your class or your methods, if the check failed, none of the code will run. For example:

[FileIOPermission(SecurityAction.Demand, Write = @"C:\Documents and Settings\Administrator\Something.txt")]
private void DeclarativeCreateFile()
{
    Console.WriteLine("Prepare to write to Administrator folder");
    CreateFile(@"C:\Documents and Settings\Administrator\something.txt");
    Console.WriteLine("Write completed");
}

Because we used declarative security check to "demand" if we have the right to write files under the Administrator folder, NONE of the code in this DeclarativeCreateFile() will run. When calling this method, you will get an System.Security.SecurityException.

Also, with declarative security, .NET Framework has provided some tool to check if your application can run under certain permission. I am guessing since we uses attributes, the runtime can use reflection to find all the declarative security check.

Imperative Security Check

To me, imperative security is a more refine way to check an assembly permission rights. Here is an example:

private void ImperativeDemandCreateFile()
{
    FileIOPermission writeFilePermission = new FileIOPermission(FileIOPermissionAccess.Write, @"C:\Documents and Settings\Administrator\something.txt");
 
    try
    {
        Console.WriteLine("Prepare to write to Administrator folder");
        writeFilePermission.Demand();
        createFile(fileName);
        Console.WriteLine("Write completed");
    }
    catch(Exception ex)
    {
        Console.WriteLine("Failed before attempting to create file. ");
    }
}

In the above example, if CAS doesn't give permission for this assembly to write to the administrator folder, the "writeFilePermission.Demand()" will throw you an SecurityException.

There are definitively different advantages of using imperative compare to declarative permission. Please refer to http://msdn2.microsoft.com/en-us/library/aa302422.aspx for more details.

Evaluating your Assembly

Since an assembly can belong to multiple code group, how can you tell what permission it has in the end? You can uses the .NET Framework 2.0 Configuration, click on "Runtime Security Policy" and you will see a link "Evaluate Assembly" on the right panel. Click on the link, select a .net assembly in the wizard and it will tells you what permission your selected assembly has.

Conclusion

CAS is very powerful, but yet I feel that it is hard to master it because of its flexibility and complexity.  I guess the only way to master this technology is to apply it in your daily programming routine. Especially when security is becoming a important issue. We can't expect all our user will run our application in Administrator mode. Therefore our application has to handle situations where the application has no permission to access certain resources.