locked
C# Reflection

    Question

  • Hi I'm working on a project for application collaboration and an idea I'm throwing around is the idea of a plugablle dll-based architecture that my app would reflect into.

    However I want my app to be able to enforce restrictions for things like tcp access, file access etc; one idea I had was to reflect into the method and check for method calls.

    Is that even possible, and are their any better ways to enforce these rules, without resorting to trusting the developers to enforce the rules in their code.

    Thanks

    A VisualBasic Novice, In Need Of Help...
    Friday, March 13, 2009 8:57 PM

Answers

  • OK so heres a sample that is based on limiting a plugin to file access to a c:\work\testsandbox\sandbox directory. Here the plugin is installed in that directory. The plugin is loaded into a separate AppDomain - here is the contract between the app and the plugin

    namespace Contract  
    {  
        // this interface is used as a contract between the app and   
        // the plugin so the app can call the plugin without having   
        // any compile time knowledge of the type  
        public interface IPlugIn  
        {  
            void DoIt();  
        }  

    now the plugin implements this contract but must also derive from MarshalByRefObject as it is going to be called across the AppDomain boundary. It tries to open a file in the sandbox and then one outside of the sandbox

    namespace Plugin  
    {  
        // Plugin must derive from MarshalByRefObject as it will be   
        // called across the AppDomain boundary  
        public class PlugIn : MarshalByRefObject, IPlugIn  
        {  
            string validPath = @"c:\work\testsandbox\sandbox\foo.txt";  
            string invalidPath = @"c:\work\testsandbox\secret\foo.txt";  
            public void DoIt()  
            {  
                try 
                {  
                    using (StreamReader sr = new StreamReader(validPath))  
                    {  
                        Console.WriteLine(sr.ReadToEnd());  
                    }  
                }  
                catch (SecurityException)  
                {  
                    Console.WriteLine("Failed to open valid file");  
                }  
     
                try 
                {  
                    using (StreamReader sr = new StreamReader(invalidPath))  
                    {  
                        Console.WriteLine(sr.ReadToEnd());  
                    }  
                }  
                catch (SecurityException)  
                {  
                    Console.WriteLine("Failed to open invalid file");  
                }  
     
            }  
        }  

    finally the app creates a new appdomain with restricted rights, creates the plugin in the AppDomain and calls it using the contract

    namespace Sandboxer  
    {  
        static class Program  
        {  
            static void Main()  
            {  
                string sandboxpath = @"c:\work\testsandbox\sandbox";  
                PermissionSet set = new PermissionSet(PermissionState.None);  
     
                // this permission forms the sandbox  
                IPermission perm = new FileIOPermission(FileIOPermissionAccess.Read |   
                                                        FileIOPermissionAccess.Write |   
                                                        FileIOPermissionAccess.PathDiscovery,   
                                                        @"c:\work\testsandbox\sandbox");  
     
                set.AddPermission(perm);  
     
                // this permission allows the plugin assembly to execute  
                perm = new SecurityPermission(SecurityPermissionFlag.Execution);  
                set.AddPermission(perm);  
     
                // Create the new AppDomain  
                AppDomainSetup info = new AppDomainSetup { ApplicationBase =  sandboxpath };  
                AppDomain ad = AppDomain.CreateDomain("sandboxAd"null, info, setnull);  
     
                // Create an instance of the plugin type in the new AddDomain  
                object o = Activator.CreateInstanceFrom(ad, sandboxpath + @"\plugin.dll""Plugin.PlugIn").Unwrap();  
     
                // Cast to a known interface and call the plugin  
                IPlugIn pi = (IPlugIn)o;  
                pi.DoIt();  
            }  
        }  

    Richard Blewett, thinktecture - http://www.dotnetconsult.co.uk/weblog2
    Monday, March 16, 2009 8:23 AM

All replies

  • This sounds like a great opportunity to create common interfaces that the developers would have to inherit from.  Not only would the classes in the plugin architecture be consistent, but you wouldn't have to use much reflection at all in your main application. 


    David Morton - http://blog.davemorton.net/
    Friday, March 13, 2009 9:15 PM
  • Hi,
    We applied the concept of plugablle DLLs and in that we provided SDK to build plugablle Dlls and depending on the type of functionality he/she wants, particular class and interfaces were implemented.
    So our application access the Dll types using the interfaces :) This provides us a loosely coupled architecture.

    For security, I believe you can consider Code Access Security and try to apply them in your application or as attributes on your base class or interface.

    Please feel free to discuss further. This is really interesting area :)
    Adil Mughal - MCP http://adilamughal.blogspot.com
    • Proposed as answer by Adil MughalMVP Saturday, March 14, 2009 12:37 PM
    Saturday, March 14, 2009 12:36 PM
  • The functionality I'm trying to shoot for is that developers code their "piece's" code and put it in a DLL, this of course would adhere to certain rules, such as using certain interfaces for interaction with the high-level system (my code).

    However, due to the nature of the system (think Bonjour mixed with an "open data environment", where every program is aware of one another and one anothers capabilities, and they (and the user) can take advantage of this); because of this I need to be able to ensure certain rules are adhered to (and not just implementation) I need to be able to make sure that the "part" isn't accessing the hard drive or internet without permission from the top-system; this would be difficult to do purely on a base interaction level.

    This is why I was wondering if it was possible to reflect into these parts code and see if there are any calls to certain methods, for example stream objects, and to keep track of these, and when the code does indeed call these methods the top-class can be there to check if the part was authorised to do so.

    Is this possible?

    A VisualBasic Novice, In Need Of Help...
    Saturday, March 14, 2009 5:02 PM
  • In this case you would need to load the plug-ins into a new AppDomain.

    You can create new appdomains programatically using AppDomain.CreateDomain. One of the overloads allows you to pass in a PermissionSet - Adil mentions Code Access Security (CAS) - PermissionSet allows you to specify a set of permissions (obviously). A permission is an object that models a sensitive operation that you may want to restrict. You create permission objects based on the set of functionality you want to allow and combine them into a PermissionSet (see [1]). You then pass this permission set into the CreateDomain method.

    Now you need to create instances of your plug-in in the new AppDomain. Look at Activator.CreateInstanceAndUnwrap and friends. Now your plug-in will be restricted by the CAS infrastructure [2]

    [1] http://msdn.microsoft.com/en-us/library/system.security.permissionset.aspx
    [2] http://msdn.microsoft.com/en-us/library/930b76w0(VS.71).aspx
    Richard Blewett, thinktecture - http://www.dotnetconsult.co.uk/weblog2
    Saturday, March 14, 2009 10:55 PM
  • Hi, this seems to be the answer I was looking for I've been messing around with it and it doesn't seem to work.

    The access control seems to work until I try to create the instance, I gave it execution priviliges however because the assembly is in a different file it requires (or so it claims) read and path discovery priviliges; I give these to it and it executes however the class running in the new app domain doesn't appear to be restricted, it is still able to create a file without throwing a security exception.

    Could you possibly post some example code(c# preferrable), showing how to load an external assembly and bind it with appropriate permissions.

    Thank you

    A VisualBasic Novice, In Need Of Help...
    Sunday, March 15, 2009 10:57 AM
  • OK so heres a sample that is based on limiting a plugin to file access to a c:\work\testsandbox\sandbox directory. Here the plugin is installed in that directory. The plugin is loaded into a separate AppDomain - here is the contract between the app and the plugin

    namespace Contract  
    {  
        // this interface is used as a contract between the app and   
        // the plugin so the app can call the plugin without having   
        // any compile time knowledge of the type  
        public interface IPlugIn  
        {  
            void DoIt();  
        }  

    now the plugin implements this contract but must also derive from MarshalByRefObject as it is going to be called across the AppDomain boundary. It tries to open a file in the sandbox and then one outside of the sandbox

    namespace Plugin  
    {  
        // Plugin must derive from MarshalByRefObject as it will be   
        // called across the AppDomain boundary  
        public class PlugIn : MarshalByRefObject, IPlugIn  
        {  
            string validPath = @"c:\work\testsandbox\sandbox\foo.txt";  
            string invalidPath = @"c:\work\testsandbox\secret\foo.txt";  
            public void DoIt()  
            {  
                try 
                {  
                    using (StreamReader sr = new StreamReader(validPath))  
                    {  
                        Console.WriteLine(sr.ReadToEnd());  
                    }  
                }  
                catch (SecurityException)  
                {  
                    Console.WriteLine("Failed to open valid file");  
                }  
     
                try 
                {  
                    using (StreamReader sr = new StreamReader(invalidPath))  
                    {  
                        Console.WriteLine(sr.ReadToEnd());  
                    }  
                }  
                catch (SecurityException)  
                {  
                    Console.WriteLine("Failed to open invalid file");  
                }  
     
            }  
        }  

    finally the app creates a new appdomain with restricted rights, creates the plugin in the AppDomain and calls it using the contract

    namespace Sandboxer  
    {  
        static class Program  
        {  
            static void Main()  
            {  
                string sandboxpath = @"c:\work\testsandbox\sandbox";  
                PermissionSet set = new PermissionSet(PermissionState.None);  
     
                // this permission forms the sandbox  
                IPermission perm = new FileIOPermission(FileIOPermissionAccess.Read |   
                                                        FileIOPermissionAccess.Write |   
                                                        FileIOPermissionAccess.PathDiscovery,   
                                                        @"c:\work\testsandbox\sandbox");  
     
                set.AddPermission(perm);  
     
                // this permission allows the plugin assembly to execute  
                perm = new SecurityPermission(SecurityPermissionFlag.Execution);  
                set.AddPermission(perm);  
     
                // Create the new AppDomain  
                AppDomainSetup info = new AppDomainSetup { ApplicationBase =  sandboxpath };  
                AppDomain ad = AppDomain.CreateDomain("sandboxAd"null, info, setnull);  
     
                // Create an instance of the plugin type in the new AddDomain  
                object o = Activator.CreateInstanceFrom(ad, sandboxpath + @"\plugin.dll""Plugin.PlugIn").Unwrap();  
     
                // Cast to a known interface and call the plugin  
                IPlugIn pi = (IPlugIn)o;  
                pi.DoIt();  
            }  
        }  

    Richard Blewett, thinktecture - http://www.dotnetconsult.co.uk/weblog2
    Monday, March 16, 2009 8:23 AM
  • Thank you very much! That has clarified things for me so much, and has worked.

    Clint

    A VisualBasic Novice, In Need Of Help...
    Monday, March 16, 2009 12:55 PM
  • I apply this plugin scheme in my project.  I can load the C# plugin and call methods in the interface successfully.  Then, here is my problem:

    In the Plugin class, I use some helper objects which are defined in some of my company's C# class library DLLs.  When I call the methods in these objects, I get error message saying: Type "some_class" is not marked as serializable.  I try to place [Serializable] before the helper class declaration but does not help.  How can I resolve this problem?  Moreover, I will use my company's C# class library extensively.  There are a lot of classes in there.  Is there any generic way (e.g. compiler option) to make it work?

    Thanks for your help.

     

    Monday, July 26, 2010 6:21 PM