1
votes

I'm trying to write a MS Dynamics CRM 2011 plugin to run in sandbox mode but utilizes classes from a GAC'd assembly. I'm running into an issue where creating a List of a type from the GAC'd assembly is generating an error and I don't understand why. The exception being generated is of type System.MethodAccessException and has this message:

Attempt by method 'IsolatedModeExceptionsTestingPlugin.Plugin.Execute(System.IServiceProvider)' to access method 'System.Collections.Generic.List`1<System.__Canon>..ctor()' failed.

Can someone explain why this is failing so that I can modify my code/environment to address the failure?

I've recreated the scenario in a simple set of classes below.

I ran my tests on a Win2008 R2 server running Dynamics 2011 Rollup 7, then again on Rollup 11. Both my assemblies are built against .Net 4.0 and the Dynamics IIS AppPool is configured for .Net 4. Both assemblies are signed (using the same key pair).

Note that if I change the plugin's isolation mode from "sandbox" to "none", the plugin works fine -- it's only in sandbox mode that this occurs, which implies some sort of security violation when generating a List<>. Note also that creating a List (or other .Net types) works fine, as does creating a List<> of classes within my own assembly. It seems to be specific to GAC'd assemblies.

The assembly I'm placing in the GAC is named "ClassLibrary1" and contains only one class:

namespace ClassLibrary1
{    
    public class MyCustomAssemblyClass
    {
        public string GetAString()
        {
            return "The current time is " + System.DateTime.Now.ToString();
        }
    }
} 

I placed it in the GAC by running the following on the server (which has the Windows SDK v7.1 installed): "c:\program files\Windows SDKs\Windows\v7.1\bin\NETFX 4.0 Tools\gacutil.exe" -i classlibrary1.dll

My plugin code then looks like this:

using System.Collections.Generic;
using System.Text;
using System;
using ClassLibrary1;
using Microsoft.Xrm.Sdk;

namespace IsolatedModeExceptionsTestingPlugin
{
    public class Plugin : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {            
            var pluginExecutionContext = (IPluginExecutionContext) serviceProvider.GetService(typeof (IPluginExecutionContext));
            var tracingService = (ITracingService) serviceProvider.GetService(typeof (ITracingService));

            try
            {
                tracingService.Trace("Entered Plugin.Execute()");
                tracingService.Trace(string.Format("Isolation Mode: {0}, Mode: {1}\n\n",
                                                   pluginExecutionContext.IsolationMode,
                                                   pluginExecutionContext.Mode));

                try
                {                    
                    tracingService.Trace(string.Format("*** Creating List<{0}>", typeof (MyCustomAssemblyClass)));
                    var list = new List<MyCustomAssemblyClass>(); //XXX Exception thrown here!!
                    tracingService.Trace("    Success");
                }
                catch (Exception ex)
                {                    
                    tracingService.Trace(GetExpectionTraceMessage(ex));
                    throw new Exception("An error occurred in one of the tests", ex);
                }
                finally
                {
                    tracingService.Trace("*** Finished TestListOfGACClassCxtr\n\n");
                }

                //This will force a failure to allow viewing of the trace file
                //    throw new Exception("TEST SUCCESSFULL");                

            }           
            finally
            {
                tracingService.Trace("Exiting Plugin.Execute()");
            }
        }


        private string GetExpectionTraceMessage(Exception ex)
        {
            var message = new StringBuilder();
            message.AppendFormat("EXCEPTION: {0}\n", ex.GetType());
            message.AppendFormat("\tMessage: {0}\n", ex.Message);
            message.AppendFormat("\tStack: {0}\n", ex.StackTrace);

            if (ex.InnerException != null)
            {
                message.AppendLine("---- INNER EXCEPTION ----");
                message.AppendLine(GetExpectionTraceMessage(ex.InnerException));
            }

            return message.ToString();
        }

    }
}

I registered the plugin to run in sandbox mode from the database when an entity is updated. Upon execution of the plugin, I get the following output via the Business Process Error dialog's "Download Log File":

Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: Unexpected exception from plug-in (Execute): IsolatedModeExceptionsTestingPlugin.Plugin: System.Exception: An error occurred in one of the testsDetail: 
<OrganizationServiceFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
  <ErrorCode>-2147220956</ErrorCode>
  <ErrorDetails xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
  <Message>Unexpected exception from plug-in (Execute): IsolatedModeExceptionsTestingPlugin.Plugin: System.Exception: An error occurred in one of the tests</Message>
  <Timestamp>2012-11-29T18:09:26.5671559Z</Timestamp>
  <InnerFault i:nil="true" />
  <TraceText>

[IsolatedModeExceptionsTestingPlugin: IsolatedModeExceptionsTestingPlugin.Plugin]
[6d245108-b638-e211-bae5-000c29d5e4ba: IsolatedModeExceptionsTestingPlugin.Plugin: Update of new_insurancequote]

Entered Plugin.Execute()
Isolation Mode: 2, Mode: 0


*** Creating List&lt;ClassLibrary1.MyCustomAssemblyClass&gt;
EXCEPTION: System.MethodAccessException
    Message: Attempt by method 'IsolatedModeExceptionsTestingPlugin.Plugin.Execute(System.IServiceProvider)' to access method 'System.Collections.Generic.List`1&lt;System.__Canon&gt;..ctor()' failed.
    Stack:    at IsolatedModeExceptionsTestingPlugin.Plugin.Execute(IServiceProvider serviceProvider)

*** Finished TestListOfGACClassCxtr


Exiting Plugin.Execute()


</TraceText>
</OrganizationServiceFault>

For reference, the System.__Canon type seems to be used by the .Net framework to create reusable code at JIT for all generics. See: http://www.marklio.com/marklio/PermaLink,guid,1fa8a82b-a6d6-4fbb-8cca-5e352ff3c9e9.aspx

2
1. Please confirm that the exact code above is generating the error referenced. I've run into issues where people post simplified code but the real error is in the actual, more complex, code. 2. If you relocate the GAC'd code to the Plug-in DLL itself does the error resolve itself or does it still occur when running in Sandbox mode?Nicknow
@Nicknow answers: 1) It is the exact code I posted and 2) if I move the class from the GAC'd assembly into my plugin assembly, I have no issues using the class.John M. Wright

2 Answers

1
votes

Isolation mode mimics CRM Online. In CRM Online you can't add to the GAC so fundamentally without examining the low level causes I believe this is why your code fails.

(consequently i'd guess that List<> is a red herring and that even instantiating a single instance of a GACed Type would fail too)

Edit

GAC grants Full Trust to assemblies by default. Plug-ins that run in Sandbox/Isolation Mode run in Partial Trust - this is why I don't believe you'd be able to instantiate MyCustomAssemblyClass when run in Sandbox Mode (which is why I think List<> is irrelevant).

From my own comment below - I think this sort of summarises it:

I think the issue is fundamentally one of elevation - i.e. whether a partially trusted assembly (i.e. the plug-in, which is restricted in what it can do) should be trusted to run a fully trusted plug-in (which can do anything). It seems logical that if partially-trusted code was allowed to run fully-trusted code, it would effectively elevate the permissions of the partially-trusted assembly therefore creating a vulnerability.

0
votes

Take a look at this link and try adding the allow partially trusted callers attribute to your GAC.

At the end of the day CRM is using the AppDomain class in .NET 4 (link on AppDomain) and running the sandbox plug-in using an AppDomain.

The challenge I'm having is that I cannot find any Microsoft documentation that clearly states what rules are applied by the CRM Sandbox service to the AppDomain it is creating to execute the plug-in. This is making it basically trial and error to configure a class in the GAC such that it is callable by sandboxed code.