Eugene Bobukh's WebLog

  • More on 2.0 changes: Delegates Security

    =================================

    The text below is provided "AS IS", without any responsibilities attached to it. It represents author's personal opinion and knowledge, and does not necessarily reflect recommended best practices of Microsoft. Author does not assume any responsibility caused by the use of the following information.

    =================================

    If you create a Delegate around a function protected by security Demand, and then somebody invokes that Delegate, the following Security checks will happen in that simple scenario:

    1. At Delegate creation time, LinkDemands on method passed to delegate constructor [“Target”] are evaluated against grant set of creating assembly [“Creator”]. If they fail, Security exception is thrown.
    2. At Delegate creation time, various pieces of Security-related Assembly identity of the Creator are captured and stored in the Delegate.
    3. At Delegate invocation time:
     
    3.1. Creator's identity is evaluated to produce the grant set of the Creator.
     3.2. Permission set demanded by the Target is compared to Creator's grant set. If it's found to be not a subset of that grant, Security Exception is thrown.

     3.3. Normal Demand against Delegate caller is initiated. If it fails, Security exception is thrown, too.

    These Security checks can be viewed in a simpler form: when an assembly creates a delegate to a method in another assembly, this is treated as though it created the delegate to a method in its own assembly which in turn called out to the other assembly. This guarantees that the code creating the delegate is always seen in any Security stack walks that take place while executing code called through the delegate.

    Steps 2, 3.1, and 3.2 are marked with bold green because they are new as compared to .NET 1.0 and 1.1. Before 2.0, there was no Security check against Delegate Creator performed at invocation time.

    Why this change has been made? The short answer is: to enable easy implementation of secure coding practices around Delegates. And if you want the longer story, here it is with some extra details.

    The model which existed in 1.x times permitted untrusted code to create Delegates around virtually any code, and then to inject them into other fully trusted code, staying at the same time outside of the Security checks. At some cases, this is dangerous, since it potentially allows low-trusted code to trick higher trusted one into calling other highly trusted methods which it did not intend to invoke. While this usually does not cause Security holes per se if proper mitigating techniques are used, it certainly makes difficult to implement good Security solution in scenarios where Delegates are involved.

    Consider the following pattern [note that it will work only in 1.0 or 1.1] which shows how easy it was to accidentally introduce Security issue in 1.x if some specific guidelines [explained below] were not followed:
     
    1. SystemLib is a highly trusted library exposing methods that could be potentially dangerous if all callers are allowed to invoke them. It could be one of existing .NET APIs, or some third party plug-in which performs low-level system actions, etc. Full Trust is required to call methods of this library; for that reason, they are protected by proper Demands:

    public class SystemLib
    {
     // It has the Declarative form of protection:
     [method:PermissionSetAttribute(SecurityAction.Demand, Unrestricted = true)]
     public static void DoDangerousThing()
     {
      // Restarts the computer, or erases system files, etc.
     }
    }

    Then you, as a developer, define in your code the following highly trusted classes:

    2.1. Assembly B is fully trusted but accepting delegates from the low-trust side. This does not represent ideal Security practices, but sometimes is needed:

    public delegate void BDelegate();

    public class B
    {
     public event BDelegate mOnButtonClick;

     // This is the method called by other classes to notify B that it's time to execute its’ delegates:
     public void RunEVentDelegates()
     {
      // We can even add an extra Security check to this place, just to ensure
      // that only fully trusted callers invoke mOnButtonClick() code:
      (new PermissionSet(PermissionState.Unrestricted)).Demand();
      if (null != mOnButtonClick)  mOnButtonClick();
     }
    }

    2.2. Assembly C is simply the caller of B, and it is highly trusted, too. This can be the code of yours, or of somebody else, doesn’t matter.

    public class C
    {
     public static void PingB(B pB)
     {
      pB.RunEventDelegates();
     }
    }

    At this point, everything looks safe. System method DoDangerousThing has Security Demand on it, so only fully trusted callers can invoke it. RunEventDelegates is protected by Demand, too. It seems like nobody without Full Trust grant can do anything bad. Unfortunately, that’s not enough. A smart attacker can create a code which will trick one part of your [highly trusted] code to call into another fully trusted code -- by manipulating Delegates:

    Evil assembly has low trust. It wants to invoke the method SystemLib::DoDangerousThing(), but can't since it does not have required permissions. So it takes different approach: it creates a delegate around desired System code, passes it for storage to highly trusted class B and then waits 'till highly trusted class C fires the event that would invoke the delegate.

    public class Evil
    {
     public static void Main()
     {
      // Here we create B explicitly for demo purposes. In real life it is possible that
      // an existing instance of B will be accessible for Evil:
      B mB = new B();
      // Then Evil "infects" class B with the system method:
      mB.mOnButtonClick += new BDelegate(L.DoDangerousThing);
     }
    }

    Evil’s job is done at this point. Now it just wait’s ‘till something happens what will make C to call B.RunEventDelegates, which in turn will call into SystemLib.DoDangerousThing(). At the moment of the call, Security Demand will be initiated. However, it will check only B and C classes, not the Evil one, and the call will succeed, resulting in computer restart, or in whatever DoDangerousThing was programmed to do.

    This looks scary, and may represent one of the common coding mistakes in 1.x .NET. What can you do to ensure your code doesn’t fall a victim of Delegates injection attack described above?

    In order to exploit the aforementioned scenario, Evil code needs to enjoy the coincidence of three quite unlikely conditions. If you deprive it of one [or better all] of them, it won’t succeed. What are those conditions?

    First, Evil needs to be able to link to dangerous System method, in order to pass it to the Delegate constructor. So if DoDangerousThing is protected by a LinkDemand, Evil will not be able to access it directly and very likely won't be able to access it at all. This protection corresponds to Step 1 of Security checks outlined at the beginning of this post.

    OK, this may work if you own SystemLib class and can modify it accordingly, but what if not?

    The second and very important is that Evil code needs to find a publicly exposed Event with the signature exactly matching the signature of the System function. In the example above that was easy: DoDangerousThing was "void (void)" function, as well as mOnButtonClick event. If you change the signature of your Event and BDelegate to accept and/or return the instance of privately defined class, it will never match any system’s code function. Therefore, such function won’t be able to be passed as an argument for Delegate creation, and attack will fail.

    Overall, if you work with highly trusted publicly accessible Delegates/Events in 1.x, it is critical that their signatures are unique, so they can’t accept anything which it not explicitly designed to match them.

    This rule applies to .NET 1.x itself. A big effort was made to ensure that all publicly exposed events of powerful .NET code have unique signatures. As an example, take a look at the AssemblyLoad Event of System.AppDomain class. Its’ signature is

    public delegate void AssemblyLoadEventHandler(
       object sender,
       AssemblyLoadEventArgs args
    );

     
    -- which is quite impossible to wrap around something like void File.Delete(string).

    Finally, Evil code needs the Event to be accessible, too. So if you can put a Demand or LinkDemand on the Events of your class, it should prevent low-trusted code from adding Delegates to them.

    The above seems like enough protection. Then why Secure Delegates were introduced?

    The primary reason was the relaxation of Delegate signatures matching rules. In 2.0, you can create a Delegate over method with signature not strictly the same as the Delegate’s one. Some restrictions [which I won’t list here as being not really an expert in the area] still apply there, but the resulting effect is that now everyone can create Delegates over much broader set of targets than before. This significantly reduces the protection offered by rule #2. To compensate on that, Delegates Security was added to the game, so people are still able to create secure and robust Delegates solutions in easy and straightforward manner. Thus now, when every Delegate Creator is automatically put on the Security stack and checked against target method Security requirements, you don’t need to worry about injection attack. .NET will do the checks.

    As a final note please keep in mind that if Delegate creator and caller live in the same assembly no Security checks are applied against Delegate creator at calling time. This is reasonable, since in this situation Creator most likely can invoke the target method directly without bothering with Delegates.

  • FullTrust means Full Trust

    The text below is provided "AS IS", without any responsibilities attached to it. It represents author's personal opinion and knowledge, and does not necessarily reflect recommended best practices of Microsoft. Author does not assume any responsibility caused by the use of the following information.

    =================================

    Well, here is another post after a long, long break. This time it's about a set of recent Whidbey changes affecting Permissions and Policy functionality.

    In .NET Framework, all Permission classes can be categorized into 3 distinct buckets: "normal" CAS Permisisons, Identity Permissions and non-CAS Permissions.

    Non-CAS permissions do not perform stack walk when Demand on them is invoked. In .NET 1.x and 2.0, there is only one such class which is PrincipalPermission.

    "Normal" CAS Permisisons, examples of which would be FileIOPermission or SecurityPermission, do check all the callers when Demand is invoked. Their behavior is well known and described in thousands of samples, docs, etc.

    Identity Permissions [like StrongNameIdentityPermission] are very similar in their nature to "normal" CAS ones. However, a number of subtle differences puts them into "special" category and oftentimes requires special treatment.

    First of all, Identity permissions are granted to assemblies by Policy not through the process of determining their Code Groups membership, but rather as simple reflection of assembly's identity. So you can't grant ZoneIdentityPermission for MyComputer to a particular Code Group [e.g., Internet] via Policy configuration. Instead, all the code "arriving" from Internet will be automatically grated ZoneIdentityPermission for Internet Zone, no matter how the machine code groups are set up.

    Identities which have corresponding permissions are: PublisherIdentity [Authenticode signature], Site, StrongName, Url, Zone. In 2.0 version of the .NET Framework, there is also Gac identity added.

    The second and more important is that in versions 1.x of the .NET Identity Permissions [unless empty] were not considered to be subset of Unrestricted PermissionSet. Therefore, for instance, even if your assembly was running from LocalComputer in FullTrust, Demands for StrongNameIdentityPermission for any public key X would have failed unless your assembly was signed with the key X. Period.

    At the first glance, this looks logical. If I want my component to be callable only by known third parties, I put StrongNameIdentityPermission, or PublisherIdentityPermission {Link|Inheritance}Demand on it, and thus make sure that nobody else can ever access it. Everything works fine, I'm happy.

    Unfortunately, the assumption above is not correct. Moreover, it can cause Security problems bubbling up at the very late stages of the product cycle, throwing the original design into waste.

    FullTrust really means Full Trust. That's it, there is no CAS protection, ever, against malicious code granted FullTrust. Identity Permissions are not the exception from this rule.

    If malicious fully trusted code Evil.exe wants to invoke your code protected with some kind of Identity Demand, it can do tons of things to achieve that goal. The most simple one would be to create an AppDomain, then call in it Assembly.Load(AssemblyName, Evidence), and pass as parameters assembly Evil.exe and Evidence created based on Identity requested by your code. After it's done, Evil.dll will "look" just like it has proper Identity, therefore it will be granted corresponding Identity Pemission, what will make the Demand pass.

    There are other ways, more or less simple. Evil code can, for example, switch the Security off [unless it's not prevented by set of measures outlined by Shawn in his article: http://blogs.msdn.com/shawnfa/archive/2005/05/04/414686.aspx], or recompile the protected code, etc., etc., etc.

    The bottom line is, Identity permissions Demands could not [and should not] be used as measure of Security protection against highly privileged code. The best they provide in Full Trust is an illusion of protection, what can be even worse than no protection at all.

    There were other problems with their behavior in 1.x. The most obvious was performance cost: when Identity permission is demanded, Security checks have to be performed even in FullTrust environment, even though [as we saw] they do not make much sense there.

    To fix those problems, in Whidbey it was decided to come up with the set of changes known internally under the name "FullTrust means Full Trust". In essence, they consist of 3 parts:

    1. Identity Permissions in any state become subset of Unrestricted PermissionSet.

    2. Set logics for IdentityPermissions changes.

    3. LinkDemands are optimized out in FullTrust.

    Here are the details on each item:

    1. Identity Permissions in any state become subset of Unrestricted PermissionSet.

    Effectively that means that Demand for any Identity Permission will now pass in FullTrust. This improves FullTrust performance through simplificaiton of many Security checks.

    This also resolves a painful problems with Delegates around system code protected by LinkDemands. Such Delegates are often created and then called by 3rd party Full Trust applications [such as WinForms UI registering a system function to be invoked as result of button click]. When application runs, it eventually invokes a system method. At that moment, LinkDemand is evaluated, and since the application is not signed with Microsoft public key, the execution blows up with SecurityException.

    Now, such problems are gone to the past.

    Of course, the change also means that no Identity Permissions Demands can be used to protect one code from being accessed by another in FullTrust. However, this just makes explicit what was hidden before: there is no CAS protection against Full Trust.

    Note that the change affects FullTrust code only. The old behavior still stands strong for partially trusted scenarios -- for example, for default Security for Internet Zone.

    2. Set logics for IdentityPermissions changes.

    This is essentially a consequence of #1. Here are some details:

    2.1 Now you can create IdentityPermissions in Unrestricted state:

    StrongNameIdentityPermission P = new StrongNameIdentityPermission(PermissionState.Unrestricted);

    2.2. You can union two different Identity Pemrissions and get a meaningful result storing both used states:

    ZoneIdentityPermission Z1 = new ZoneIdentityPermission(SecurityZone.MyComputer);

    ZoneIdentityPermission Z2 = new ZoneIdentityPermission(SecurityZone.Internet);

    ZoneIdentityPermission Z3 = (ZoneIdentityPermission) Z1.Union(Z2);

    The result is:

    <IPermission class="System.Security.Permissions.ZoneIdentityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

    version="1">

    <Zone Zone="MyComputer"/>

    Zone Zone="Internet"/>

    </IPermission>

    Obviously such permission states are mostly useless. Then why I seem to be so happy about this functionality? Because it simplifies the life of developer. You won't have to specialcase Identity Permisisons anymore when performing set logics operations, or creating them. Everything is straighforward from the beginning. At least, this is my opinion :)

    3. LinkDemands are optimized out in FullTrust.

    This change is important, as it may break the code of some [hopefully very, very few] people.

    LinkDemands do not know anything about stack walk modifiers like Deny, Assert, and PermitOnly. And they check only the grant set of the immediate caller. Combined together, two previous statements inevitably mean that any LinkDemand for any Identity or "normal" CAS Permission will now pass in FullTrust.

    If so, why waste CPU cycles on doing checks with result already known? So the decision was made to simply skip any LinkDemands if they happen in FullTrust environment. Of course, this does not apply to scenarios where methods are called through Reflection and LinkDemands need to be converted to full Demands.

    This change was a clear win for JIT and startup time, but cut off some corner scenarios with non-CAS permissions. Since they don't do a stackwalk and may implement just any kind of arbitrary logics of their Demands, there is no guarantee their LinkDemands will always succeed in FullTrust. Which means the code has to maintain record of them, even in FullTrust environment. But due to the details of internal CAS implementation, doing so kills most of the performance benefits achieved from LinkDemands optimization for other types of permissions.

    So finally a tough decision was made to simply ignore non-CAS LinkDemands in FullTrust, too. For PrincipalPermission this was not a big deal, since LinkDemand operation for it was forbidden long time ago anyway. The only potentially affected code was custom non-CAS permissions.

    So far, we are not aware of any people who found good use of non-CAS custom permissions LinkDemands. Ironically, it seems like the only person suffered from this change was... me -- simply because my sample of non-CAS permission put on the Web about a year ago [http://blogs.msdn.com/eugene_bobukh/archive/2004/03/10/87645.aspx] will not work in FullTrust anymore :)

  • Some tips on testing managed code Security.

    <Disclaimer>

    The text below represents author's personal opinion and does not necessarily reflect Microsoft recommended best practices. Author does not assume any responsibility caused by the use of the following information.

    </Disclaimer>

    Some quick tips on testing managed code Security.

    So now you are developing or testing some managed application. You’ve heard lots about .NET Security, but unfortunately did not have much time to read enough about it. You are starting to worry if Security would cause you any troubles and what should you do to verify you are OK here. In this case, this article may help you. Well, it will not teach how to become a real Security expert; and the steps outlined below are definitely not enough to ensure that your code if fully secure, but at least it will help to make sure you’ve covered some mostly typical issues.

    Introduction:

    Managed code has Security built-in as inevitable part of it. Almost every API call goes through some of the Security API. This represent several essential differences from how old unmanaged code works; moreover, ensuring that Security is properly involved in right places and right times is really critical for any application.

    For non-experts in .NET Security there are quite a few things to keep in mind though.

    First, the idea of Security in general is that not everything what compiles is always allowed to run. Different restrictions may apply in different situations. For example, traditional Windows NT Security decides whether or not permit some execution based on who is current user.

    .NET Security [and this is the second thing] makes its' decisions based on the identity of the code, not the user [that's why it is named CAS -- Code Access Security]. That means, in order to determine what this or that piece of code is allowed to do, it looks at such things as: where from the code came; does it have anything to point to it's author [e.g., Authenticode certificate]; what is the hash value of the code, and so on. It does not mean though that Windows security gets overridden -- no, it still applies. .NET Security is just like another independent dimension of hedging against the attacks.

    There is virtually infinite number of real life Security configuration that may affect execution of managed code, and obviously we won't be able to cover all of them. However, there are several simple scenarios where Security most likely gets involved.

    Typical issues:

    "I built an application which updates Registry. It works fine when I run it from C:\, but fails if I start it from the network share. What am I supposed to do, my customers are waiting?"

    This is probably the most common issue. And unfortunately for the author, it is expected. Any code that comes from the network [and share is considered a network] gets less trust then the one starting from local machine. For instance, by default such code is not allowed to access the Registry, and this seems reasonable: why would you let somebody you don't even know to write to the sensitive part of your machine?

    Another kind of thing you might see is that some DLLs allow only callers that are digitally signed with the key known to them. So no matter what, you won't get access if you don't bear the evidence that you come from Microsoft, for example. This thing is harder to test, but at least it should be kept in mind.

    How to test:

    Here I assume that your primary area is not the Security itself but rather something else. In this case, we can outline the set of most basic things to verify.

    First, let's introduce the concept of Trust Level. Normally, the level of trust is applied to application or function, and is expressed in terms of what it is permitted to do. For example, some code may be allowed to do File IO to given file, do unlimited networking operations, read environment variables, but not allowed to write to the Registry.

    It is very important to realize that every API in the .NET has its requirements on the trust level needed in order to invoke them. For instance, if you are trying to open a file via FileStream class, before doing anything it will verify that the caller of the API can do it; otherwise, it will fail.

    The granularity of different trust level requirements is really tremendous. However, for the sake of simplicity we can split it into three major categories:

    1. FullTrust: with this trust, everything is permitted by CAS. If some API is protected by FullTrust requirement, it most likely may be doing something really dangerous. Example of such thing is Process class that needs FullTrust to be created. By default everything that runs from the local machine gets FullTrust, so if you run your application from C:\ only, you are not really testing the Security of it.

    2. Partial trust: This could be really granular, ranging from the ability to show UI to calling private methods through Reflection or doing File IO. Usually any function that does such kind of things asks for the caller about ability to do them [but not for the FullTrust, it's not "all or nothing" model]. Quite many functions in .NET libraries have different partial trust requirements on them. Normally, applications that come from Internet or Intranet run with various degrees of partial trust.

    3. No special requirements (Execution only): anybody can call this method as it does not represent any Security risk. Example of such API may be Math.Sqrt().

    Using the classification above, it should be quite easy to achieve good basic level of Security testing coverage even for non-Security experts in 3 easy steps:

    1. Figure out is what is the actual trust level required by the design of API, scenario or application you are testing. In most cases just finding which bucket of the 3 above corresponds to it is enough; however, if you have more specific idea of what is needed [e.g., File IO] -- it is even better.

    2. Run your target with the trust level required or higher. If it does not run -- you are having Security overenforcement bug.

    3. Run it with the trust level below the required level. If it runs -- this is a Security hole.

    The next question is: how do I change that trust level?

    Tools:

    There are several relatively simple ways to manipulate Security for testing purposes. In fact, these are more tips rather than some systematic approach, but for quick testing it should help:

    1. [Very simple]: suppose that your machine name is Box1, that its' IP address is 111.111.111.111, that your App.exe application lives on C:\ drive and that your .NET Security policy and IE Security settings are in default state. In this case, the following command

    \\Box1\C$\App.exe

    will effectively run App.exe in LocalIntranet zone, with trust level greatly reduced. This will give you an idea of how you application would behave if somebody runs it from the share -- very quick and cheap test.

    Further, the following line:

    \\111.111.111.111\C$\App.exe

    will start your application as if it was run from the Internet zone which has even lesser trust. In this situation, you'll get execution environment pretty close [although not exactly the same!] to what will be if application is run in Internet Explorer. Again, this is just a s nice way to check basic things right away.

    2. More complex, but more flexible: change Security policy using caspol.exe or .NET Framework Configuration tool. To get 100% out of it, you might need to know Security deeper, but some sandboxing scenarios are quite easy to accomplish.

    For example, the following steps will assign any permissions you'd want to any application that starts from C:\TMP directory:

    2.1. Start .NET Framework Configuration tool [either by running mmc.exe and adding the proper snap-in, or through the shortcut in Administrative tools];

    2.2 Expand "Console Root -> .NET Framework Configuration -> My Computer" nodes if they are not expanded.

    2.3. Expand "Runtime Security Policy -> Machine -> Code Groups" nodes.

    2.4. On the node "All_Code", rightclick "New...".

    2.5. Give some name for the code group in the wizard that comes up, press "Next".

    2.6. From drop-down menu, choose "URL" as membershipcondition.

    2.7. In "URL" box, type file://C:\TMP\*, press "Next".

    2.8. Either use some of the existing sets, or create a new one. I'd really encourage you to play with the new sets creation as it would let you to test your application with all the kinds of Security settings you might be interested and to learn what permssions are shippedn with .NET. Creating new set goes though the wizard and is really easy and self-descriptive process.

    2.9. After you've finished creating the group, find it in under "All_Code" hierarchy, rightclick for "Properties" and check on the box "This policy level will only have the permissions from the permission set associated with this group".

    Now, if your application starts from C:\TMP, it will get those -- and only those! -- permissions that you have granted to it.

    Don't forget to reset the policy back to default state after you finished your testing. It could be done either though .NET Framework Configuration tool or by running script "caspol.exe -pp off -all -reset".

    However, what if you need to test not the whole application but rather one DLL, or even one function? Is there any way you can easily tweak the trust level of it? The answer is definite Yes.

    3. Assembly level requests are our friends.

    These are constructions that live at the beginning of an assembly and look as follows:

    [assembly: {Some permission | PermissionSet}Attribute(SecurityAction.Request{Minimum|Optional|Refuse}, ...)]

    For testing, the most interesting is RequestRefuse which basically tells the Policy: "this application should never be granted the named permission". So, for example, if the following line is there:

    [assembly: RegistryPermissionAttribute(SecurityAction.RequestRefuse, Unrestricted = true)]

    that would mean that assembly with it will not be granted any form of Registry access, even if run from the environment that otherwise would let it.

    One interesting note here is that FullTrust with any, even the most little permission "subtracted" this way is not FullTrust anymore. So everything that requests FullTrust must start failing now -- good test!

    More useful technique is combining RequestMinimum with RequestOptional. If your assembly has such requests [say ReqMin for set A and ReqOptional for B] that would mean that:

    a) Assembly will NOT start if environment grants it less then A, AND

    b) It will NEVER be granted more than unioning of A and B [what, when run from local machine, ends up with permissions grant set equal to union of A and B].

    So for instance these lines will make sure your assembly runs with the smallest privileges ever possible in the Runtime -- with the right for execution only:

    [assembly: SecurityPermissionAttribute(SecurityAction.RequestMinimum, Execution = true)]

    [assembly: PermissionSetAttribute(SecurityAction.RequestOptional, Unrestricted = false)]

    The technique above is quite powerful and covers many of the Security testing scenarios you may encounter in real life. However, to use it one needs to be familiar with the syntax of assembly level requests and with syntax of permissions used with them. Fortunately, this is not a problem as MSDN has plenty of information on this, at least for most common cases.

    4. Stack walk modifiers.

    This is actually something more advanced that allows to alter the trust on class/level method. The modifiers useful for testing are Deny() and PermitOnly(), and they are methods that live on Permissions and PermissionSet classes. This example shows how to make sure that everything that executes inside Foo() method, and everything that is called from Foo(), gets the right only to execute and to pop up File Save/Open dialogs:

    // This is the method we control and use to call into Foo:

    public void Bar()

    {

       PermissionSet pSet = new PermissionSet(PermissionState.None);

       FileDialogPermission FP = new FileDialogPermission(PermissionState.Unrestricted);

       SecurityPermission SP = new SecurityPermission(SecurityPermissionFlag.Execution);

       pSet.AddPermission(FP);

       pSet.AddPermission(SP);

       pSet.PermitOnly();

       Foo();

    }

    // Method that we test

    public void Foo()

    {

    //...

    }

    However, there are several caveats to keep in mind here, such as:

    a) In some cases modifiers effect can be overridden by applying other modifiers;

    b) There are types of Security checks [like LinkDemand] that are not affected by modifiers;

    So to use it, some level of Security expertise is actually required, which could be achieved by reading such MSDN topics as "Code Access Security", "PermissionSet", "SecurityAction", and materials on various Permissions used.

  • Sample of non-CAS custom permission with declarative form supported.

    Why?

    Recently, I started seeing numerous requests regarding creation of custom permissions that do not inherit from CodeAccessPermission and thus do not perform stackwalk. There is nothing special about implementing such classes. In fact, it is easier then with CodeAccessPermission as a base. However, having a sample handy, I just decided to share it here along with my comments. So welcome

    WorkingTimePermission

    Trying to be at least somewhat close to real life, I implemented the permission object with the following Demand semantics:

    If Demand is performed during business hours, it passes.

    If it is done in other time, it throws the SecurityException.

    As you see, it’s straightforward. However, it might save you a good chunk of time when you use it declaratively, e.g.:

     

    [method:WorkingTimePermissionAttribute(SecurityAction.Demand)]

    private static void AccessibleDuringBusinessHoursOnly()

    {

                //…

    }

     

    …instead of performing time checks explicitly on each protected function entrance.

    The sample consists of 4 files: Permission DLL, Permission Attribute DLL, Client file that uses it and script that builds all together. My comments are in gray. Catches or important places are in dark red. Enjoy!

    1. WorkingTimePermission.cs:

    using System;

    using System.Security;

    using System.Security.Permissions;

     

    // Make sure the assembly is signed; otherwise it will fail some of the actions needed for build sequence.

    [assembly:System.Reflection.AssemblyKeyFile("SomeKey.snk")]

    [assembly:System.Security.AllowPartiallyTrustedCallersAttribute()]

     

    namespace CustomPermissions

    {

                [Flags,Serializable]

                public enum AccessType

                {

                            Common = 0x00,

                            VIP = 0x01

                }

     

                public sealed class WorkingTimePermission : IPermission

                {

                            /* I’ve implemented this permission having two access

                            modes just to show how the case of permission having

                            internal data should be dealt with. But in fact this code

                            could be simplified even further by removing these flags */

                            private AccessType m_Access;

     

                            public WorkingTimePermission()

                            {

                                       m_Access = AccessType.Common;

                            }

     

                            public WorkingTimePermission(AccessType access)

                            {

                                       VerifyAccess(access);

                                       m_Access = access;

                            }

     

                            /* Catch #1: although IPermission does not obligate you

                            to implement this kind of constructor, it must be specified

                            if you wish to have the declarative form of the permission,

                            because Security system will silently try to use it when decoding

                            the attribute, and fail if it's not found. */

                            public WorkingTimePermission(PermissionState State)

                            {

                                       m_Access = AccessType.Common;

                            }

     

                            private void VerifyAccess(AccessType access)

                            {

                                       if (0 != (((int) access) & ~1))

    throw new ArgumentException("Wrong access type value: " + (int) access);

                            }         

     

                            // This is required by IPermission interface.

                            public IPermission Copy()         

                            {

                                       return new WorkingTimePermission(this.m_Access);

                            }

     

                            private void VerifyType(IPermission Target, bool IsNullOK)

                            {

                                       if (null == Target)

                                       {

                                                   if (true == IsNullOK) return;

                                                   else throw new ArgumentException("Target is not WorkingTimePermission [it is null]");

                                       }

                                       if (this.GetType() != Target.GetType())

                                                   throw new ArgumentException("Target is not WorkingTimePermission");

                            }

     

                            public AccessType GetAccess()

                            {

                                       return m_Access;

                            }

     

                            // This is required by IPermission interface.

                            public IPermission Intersect(IPermission Target)

                            {

                                       if (null == Target) return null;

                                       VerifyType(Target, false);

                                       WorkingTimePermission P = (WorkingTimePermission) Target;

                                       return new WorkingTimePermission(this.GetAccess() & P.GetAccess());

                            }

     

                            // This is required by IPermission interface.

                            public IPermission Union(IPermission Target)

                            {

                                       VerifyType(Target, true);

                                       WorkingTimePermission P = (WorkingTimePermission) Target;

                                       return new WorkingTimePermission(this.GetAccess() | P.GetAccess());

                            }

     

                            // This is required by IPermission interface, too

                            /* Catch #2: if you work with V1.0 or V1.1, this method will be silently

                            called by the compiler during build time if permission is used

                            declaratively. If any error occurs inside the body of this method, it is not

                            propagated to the above, issuing instead quite a bogus message like

                            “failed to create the permission for this attribute”. If it sounds familiar,

                            double your attention to this place: inject more debugging output, etc.*/

                            public bool IsSubsetOf(IPermission Target)

                            {

                                       VerifyType(Target, true);

                                       if (null == Target) return false;

                                       WorkingTimePermission P = (WorkingTimePermission) Target;

                                       return (this.GetAccess() <= P.GetAccess());

                            }

     

                            /* This is the core logics part of the permission. I’ve implemented it this way:

                                                    For VIP access, Demand always passes.

                                                    For normal access, it passes during “usual” business hours only.

                            Of course, you are more then welcome to implement whatever logics you see suitable.

                            Note please that all exception messages are hardcoded in English here.

                            This is not a good style in general, and done only for the sake of simplicity. */

                            public void Demand()

                            {

                                       Console.WriteLine("WorkingTimePermission.Demand() called!");

                                       if (AccessType.VIP == this.GetAccess()) return;

                                       DateTime Curr = DateTime.Now;

                                       DayOfWeek CurrDay = Curr.DayOfWeek;

                                       if ((CurrDay == DayOfWeek.Saturday)||(CurrDay == DayOfWeek.Sunday))

                                                   throw new SecurityException("Request for WorkingTimePermission failed because current day of week is " + CurrDay + ".");

                                       int Hour = Curr.Hour;

                                       if ((Hour < 8)||(Hour > 17))

                                                   throw new SecurityException("Request for WorkingTimePermission failed because current time is " + Hour + " hours.");

                            }

     

                            // This is required by IPermission interface.

                            public SecurityElement ToXml()

                            {

                                       SecurityElement Ret = new SecurityElement("IPermission");

     

                                       String name = typeof(WorkingTimePermission).AssemblyQualifiedName;

                                       Ret.AddAttribute("class", name);

                                       Ret.AddAttribute("version", "1.0");

                                       Ret.AddAttribute("AccessType", this.GetAccess().ToString());

                                       return Ret;

                            }

     

                            // This is required by IPermission interface.

                            public void FromXml(SecurityElement e)

                            {

                                       String name = e.Attribute("class");

                                       // Make sure we are not converting “something else” to WorkingTimePermission:

                                       if (name != typeof(WorkingTimePermission).AssemblyQualifiedName) throw new ArgumentException("Wrong SecurityElement");

               

                                       String version = e.Attribute("version");

                                       if (version != "1.0") throw new ArgumentException("Version " + version + " does not match current version of the permission");

     

                                       String access = e.Attribute("AccessType");

       

                                       if (null != access) m_Access = (AccessType) Enum.Parse(typeof(AccessType), access);

                                       else m_Access = AccessType.Common;

                            }

                }

     

    }

     

    2. WorkingTimePermissionAttribute.cs:

    using System;

    using System.Security;

    using System.Security.Permissions;

     

    [assembly:System.Reflection.AssemblyKeyFile("SomeKey.snk")]

    [assembly:System.Security.AllowPartiallyTrustedCallersAttribute()]

     

    namespace CustomPermissions

    {

                [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false )]

                /*Catch #3: use CodeAccessSecurityAttribute as a base class, don’t be tempted by SecurityAttribute. */

                public sealed class WorkingTimePermissionAttribute : CodeAccessSecurityAttribute

                {

                            private AccessType m_Access;

     

                            public WorkingTimePermissionAttribute(SecurityAction action): base(action)

                            {

                                       m_Access = AccessType.Common;

                            }

     

                            // This is required by CodeAccessSecurityAttribute:

                            public override IPermission CreatePermission()

                            {

                                       return new WorkingTimePermission(m_Access);

                            }

     

                            /* Catch #4: Even if permission has some internal parameter named Data,

                            we don't need to implement  constructor(DataType Data). Attributes think that

                            Data is a property of the permission, so let’s implement it as a property: */

                            public AccessType Access

                            {

                                       get

                                       {

                                                   return m_Access;

                                       }

                                       set

                                       {

                                                   m_Access = value;

                                       }

                            }

                }

    }

     

    3. Client.cs

    This is the sample of the code that uses WorkingTimePermission:

    using System;

    using System.Security;

    using System.Security.Permissions;

    using CustomPermissions;

     

    // This class is created only to show the inheritance protection by the new permission:

    public class BBB : AAA

    {

                public override void Ozz()

                {

                            Console.WriteLine("BBB.Ozz() being called");

                }

    }

     

    // This is the second, and the main class, containing all the demo code.

    // It consists of several methods and calls to them, nothing more:

    public class AAA

    {

                /* This is the demo of explicit permission usage.

                Demand will pass during business hours and fail otherwise. */

                private static void Foo()

                {

                            WorkingTimePermission P = new WorkingTimePermission();

                            P.Demand();

                            Console.WriteLine("AAA.Foo() called!");

                }

                /* Here is the same thing as above, but used declaratively.

                The attribute ensures that method Bar() could be called during business time only.

                Isn’t it neat? :) */

                [method:WorkingTimePermissionAttribute(SecurityAction.Demand)]

                private static void Bar()

                {

                            Console.WriteLine("AAA.Bar() called!");

                }

     

                /* This is only to show how to use attribute with the property. It is a plain

                syntax demo, because our permission is implemented in such a way that VIP

                demand always passes thus making the permission useless. However, we

                can code some different schedule for VIPs, too */

                [method:WorkingTimePermissionAttribute(SecurityAction.Demand, Access = AccessType.VIP)]

                private static void BarVIP()

                {

                            Console.WriteLine("AAA.BarVIP() called!");

                }

     

                /* Here you go: we’ve implemented Demand only, but got LinkDemand for free, too.

                And it works, ensuring that Rug() method cannot be even instantiated during

                non-business hours: */

                [method:WorkingTimePermissionAttribute(SecurityAction.LinkDemand)]

                private static void Rug()

                {

                            Console.WriteLine("AAA.Rug() called!");

                }

     

                /* Catch #5: if you want to be able to see the actual SecurityException from the method

                protected by JIT-time action like LinkDemand, wrap a target method into another one

                and call the wrapper from inside a try...catch block. Without a wrapper, SecurityException

                will be thrown during JIT-time, when try...catch does not exist yet, thus going uncaught.

                So the sample below calls RugWrapper() instead of just Rug().*/

                private static void RugWrapper()

                {

                            Rug();

                }

     

                // And, yes, InheritanceDemand just works, too!

                // So, see, everyone who inherited from your AAA

                // class will loose the access to Ozz during non-business hours.

                [method:WorkingTimePermissionAttribute(SecurityAction.InheritanceDemand)]

                public virtual void Ozz()

                {

                            Console.WriteLine("AAA.Ozz() being called");

                }

     

                // Same notes about JIT-time actions as in RugWrapper() apply:

                private static void BBBOzzWrapper()

                {

                            (new BBB()).Ozz();

                }

     

                public static void Main()

                {

                            try

                            {

                                       Foo();

                            }

                            catch (SecurityException e)

                  &nb