This post originally started out life as a three-paragraph thing, but then I realised the topic really merits a lot of background, so I thought I’d get with the cool gang and do a multi-part post.

First, I’m going to cover a bit of background about Medium Trust, and how to understand what it actually means for your code in terms of permissions.

The second post covers a solution I’ve produced over the past week and in the latest build of Umbraco.Framework.Testing which allows you to easily ensure all your NUnit tests run in partial trust.

The third post has the download and some instructions, so jump there if you want to cut to the chase.

In these posts I’ll be talking about .NET 3.5 and above – just to be clear from the outset.

Medium Nightmare

image

Medium Trust is probably one of the most infamous parts of an ASP.NET developer’s coding life. If you work on your own applications, hosted in Full Trust environments under the control of you or your team, you’re fine. You may have never even come across an issue. It’s this phenomenon which has created such a painful situation: so many developers write code which they reasonably expect can be deployed anywhere; it passes its tests, and works on their machine.

But if you’ve ever put together a website with some cool fandangled bit of code that runs fine locally, and then you’ve deployed it to your hosting account, only to find your beautiful creation misses you dearly and is just sat there screaming complaints of SecurityPermission this, and FieldAccessPermission that, you’ll know how tough it can be.

For many it’s the beginning of a very long and arduous process of re-engineering and debugging in what can seem like an infinite loop, and many assumptions about the way the application works (such as lazy-loading proxies in NHibernate) often need to be entirely revisited. Or not, depending on how much luck you have with your Bing search. It’s another large part of the problem: it’s such a minefield, even searching for solutions can be spotty.

It’s not all in your hands either; it’s an easy mistake to make precisely because it’s so difficult to test in a reliable way and it’s so easy to begin developing in the default setting for many ASP.Net project templates, which is: Full Trust. Fluent NHibernate, AutoMapper, certain setups of Castle DynamicProxy, of NHibernate – these are Big Name Libraries, and they all have problems. We’ve all been there: Umbraco included up until the past year or so. It’s a long list – and unpicking the problems one by one, rebuilding, and pressing F5 in your browser is a pain in the arse.

I don’t like pains in my arse, and since we’re busy building Umbraco 5 right now, I’d rather we don’t become one in yours either. I figured the other features we’re coding have test coverage, let’s get the Medium Trust issue covered too. And since it’s all open source, everyone can do it. Yeay!

Security background – what exactly is Medium Trust and its permissions?

“Medium Trust” is a loose term for a set of Code Access Security policy settings that offer less freedom to bend the server to your every will. It’s complicated by the fact that what constitutes “medium trust” is actually a set of permissions listed in configuration, and some hosting companies tailor the settings, so you don’t quite know what you’re dealing with. But in general the policy means at least the following permissions:

  • AspNetHostingPermission of “medium” – For example, a minimum requirement that calls to public types in the System.Web namespace must be coming from code that is granted at least a certain “minimal” trust level.
  • A DnsPermission which is unrestricted. Go mad on those Dns lookups.
  • An EnvironmentPermission which grants access only to the following system environment variables by default:
    • TEMP; TMP; USERNAME; OS; COMPUTERNAME
  • A FileIOPermission that says you may only Read, Write, Append and browse the filesystem that starts with the root of your website and nowhere else. Shocker.
  • An IsolatedStorageFilePermission which permits isolation of storage first by user, and then by code assembly. It allows you a data store for the assembly that is accessible in any domain context.
  • Bizarrely, a PrintingPermission which permits programmatic printing to the default printer.
  • A SecurityPermission which allows the following flags:
    • Execution (to allow managed code to execute – yeay!)
    • ControlPrincipal (to manipulate the Principal object i.e. set the current logged-on user)
    • ControlThread (to allow the totally unambiguous certain advanced operations on threads, thanks MSDN!)
    • RemotingConfiguration (to configure Remoting types and channels)
  • An SmtpPermission which allows you to connect only to port 25
  • An unrestricted SqlClientPermission
  • An unrestricted TypeDescriptorPermission
  • An unrestricted WebPermission (confused? This actually signifies unrestricted access to make HTTP requests to other services)
  • And finally the one which crops up most often, a ReflectionPermission with flags of RestrictedMemberAccess. This signifies “partially trusted code can access nonpublic types and members, but only if the grant set of the partially trusted code includes all permissions in the grant set of the assembly that contains the nonpublic types and members being accessed”. Again, thanks MSDN! In practice, this means you can’t access private members of someone else’s assemblies – and this is what brings down most libraries.

What’s important about this set of permissions is actually what it doesn’t give you. For example, let’s look at some of the SecurityPermissionFlags you could have won:

  • Calling unmanaged code
  • Ability to provide serialization services
  • Ability to create and manipulate AppDomains

Or the “MemberAccess” ReflectionPermissionFlag, which would have allowed you to use reflection to operate on all members of a class (even its privates).

Also, no OleDbClientPermission or OdbcClientPermissions are in there by default – most hosting companies add these to their Medium Trust configurations.

And certainly no RegistryPermission.

How do you know this and where is it configured?

Well, perhaps the wider point is I don’t know this by reading one handy straightforward piece of simple documentation. The top Google result for me is a piece of “retired” MSDN content left there for reference or for anyone who has run out of everything else to read in the world ever. The top Bing result is an MSDN video from 2005. I’m sure it’s very good, but it’s an exe file download. From 2005.

90% of what you need to know about Medium Trust’s permission set is stored in the web_mediumtrust.config file for the .NET version on which your application is running. On my machine, for .NET 4, that’s C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\web_mediumtrust.config.

Web_MediumTrust.config explained

The config file has a simple job: declare named sets of permissions. It does this by first declaring a list of SecurityClass elements to predefine the type names of the permissions and give them a key, and then goes on to declare PermissionSet elements which refer back to those types and pass in arguments. Here is the one installed on my dev machine in full:

  1. <configuration>
  2. <mscorlib>
  3. <security>
  4. <policy>
  5. <PolicyLevel version=“1″>
  6. <SecurityClasses>
  7. <SecurityClass Name=“AllMembershipCondition” Description=“System.Security.Policy.AllMembershipCondition, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  8. <SecurityClass Name=“AspNetHostingPermission” Description=“System.Web.AspNetHostingPermission, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  9. <SecurityClass Name=“DnsPermission” Description=“System.Net.DnsPermission, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  10. <SecurityClass Name=“EnvironmentPermission” Description=“System.Security.Permissions.EnvironmentPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  11. <SecurityClass Name=“FileIOPermission” Description=“System.Security.Permissions.FileIOPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  12. <SecurityClass Name=“FirstMatchCodeGroup” Description=“System.Security.Policy.FirstMatchCodeGroup, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  13. <SecurityClass Name=“IsolatedStorageFilePermission” Description=“System.Security.Permissions.IsolatedStorageFilePermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  14. <SecurityClass Name=“NamedPermissionSet” Description=“System.Security.NamedPermissionSet”/>
  15. <SecurityClass Name=“PrintingPermission” Description=“System.Drawing.Printing.PrintingPermission, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”/>
  16. <SecurityClass Name=“SecurityPermission” Description=“System.Security.Permissions.SecurityPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  17. <SecurityClass Name=“SmtpPermission” Description=“System.Net.Mail.SmtpPermission, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  18. <SecurityClass Name=“SqlClientPermission” Description=“System.Data.SqlClient.SqlClientPermission, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  19. <SecurityClass Name=“StrongNameMembershipCondition” Description=“System.Security.Policy.StrongNameMembershipCondition, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  20. <SecurityClass Name=“TypeDescriptorPermission” Description=“System.Security.Permissions.TypeDescriptorPermission, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  21. <SecurityClass Name=“UIPermission” Description=“System.Security.Permissions.UIPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  22. <SecurityClass Name=“UnionCodeGroup” Description=“System.Security.Policy.UnionCodeGroup, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  23. <SecurityClass Name=“UrlMembershipCondition” Description=“System.Security.Policy.UrlMembershipCondition, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  24. <SecurityClass Name=“WebPermission” Description=“System.Net.WebPermission, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  25. <SecurityClass Name=“ZoneMembershipCondition” Description=“System.Security.Policy.ZoneMembershipCondition, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  26. <SecurityClass Name=“ReflectionPermission” Description=“System.Security.Permissions.ReflectionPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
  27. </SecurityClasses>
  28. <NamedPermissionSets>
  29. <PermissionSet
  30. class=“NamedPermissionSet”
  31. version=“1″
  32. Unrestricted=“true”
  33. Name=“FullTrust”
  34. Description=“Allows full access to all resources”
  35. />
  36. <PermissionSet
  37. class=“NamedPermissionSet”
  38. version=“1″
  39. Name=“Nothing”
  40. Description=“Denies all resources, including the right to execute”
  41. />
  42. <PermissionSet
  43. class=“NamedPermissionSet”
  44. version=“1″
  45. Name=“ASP.Net”>
  46. <IPermission
  47. class=“AspNetHostingPermission”
  48. version=“1″
  49. Level=“Medium”
  50. />
  51. <IPermission
  52. class=“DnsPermission”
  53. version=“1″
  54. Unrestricted=“true”
  55. />
  56. <IPermission
  57. class=“EnvironmentPermission”
  58. version=“1″
  59. Read=“TEMP;TMP;USERNAME;OS;COMPUTERNAME”
  60. />
  61. <IPermission
  62. class=“FileIOPermission”
  63. version=“1″
  64. Read=“$AppDir$”
  65. Write=“$AppDir$”
  66. Append=“$AppDir$”
  67. PathDiscovery=“$AppDir$”
  68. />
  69. <IPermission
  70. class=“IsolatedStorageFilePermission”
  71. version=“1″
  72. Allowed=“AssemblyIsolationByUser”
  73. UserQuota=“9223372036854775807″
  74. />
  75. <IPermission
  76. class=“PrintingPermission”
  77. version=“1″
  78. Level=“DefaultPrinting”
  79. />
  80. <IPermission
  81. class=“SecurityPermission”
  82. version=“1″
  83. Flags=“Execution, ControlThread, ControlPrincipal, RemotingConfiguration”
  84. />
  85. <IPermission
  86. class=“SmtpPermission”
  87. version=“1″
  88. Access=“Connect”
  89. />
  90. <IPermission
  91. class=“SqlClientPermission”
  92. version=“1″
  93. Unrestricted=“true”
  94. />
  95. <IPermission
  96. class=“TypeDescriptorPermission”
  97. version=“1″
  98. Unrestricted=“true”
  99. />
  100. <IPermission
  101. class=“WebPermission”
  102. version=“1″
  103. Unrestricted=“true”
  104. />
  105. <IPermission
  106. class=“ReflectionPermission”
  107. version=“1″
  108. Flags=“RestrictedMemberAccess”/>
  109. </PermissionSet>
  110. </NamedPermissionSets>
  111. <CodeGroup
  112. class=“FirstMatchCodeGroup”
  113. version=“1″
  114. PermissionSetName=“Nothing”>
  115. <IMembershipCondition
  116. class=“AllMembershipCondition”
  117. version=“1″
  118. />
  119. <CodeGroup
  120. class=“UnionCodeGroup”
  121. version=“1″
  122. PermissionSetName=“ASP.Net”>
  123. <IMembershipCondition
  124. class=“UrlMembershipCondition”
  125. version=“1″
  126. Url=“$AppDirUrl$/*”
  127. />
  128. </CodeGroup>
  129. <CodeGroup
  130. class=“UnionCodeGroup”
  131. version=“1″
  132. PermissionSetName=“ASP.Net”>
  133. <IMembershipCondition
  134. class=“UrlMembershipCondition”
  135. version=“1″
  136. Url=“$CodeGen$/*”
  137. />
  138. </CodeGroup>
  139. </CodeGroup>
  140. </PolicyLevel>
  141. </policy>
  142. </security>
  143. </mscorlib>
  144. </configuration>



When ASP.Net spins up the AppDomain for your website, it checks the <trust> element of your web.config file (which may be inherited from a parent config that the hoster has set), and then goes to the appropriate config file to load the right set of permissions needed for the AppDomain. After that, it’s all down to security which is built-in to the CLR which transparently checks the caller’s permissions vs the callee’s permission demands when running managed code.

Can you see where I’m going with this?

Let’s get on to how and why this matters to us. In the next post I’ll outline how we can use this information to restrict tests to a very similar set of permissions.

Wow, what a rollercoaster so far. I thought I’d put up a quick update on here, as although Fed has been updating our team blog, I have limited Azure / Twitter / programming access (more on that in a minute) and I know this will go out via Feedburner automatically to anyone who is interested in our progress, so given the current set of news we have to share, this is the place to let you know. Luckily I managed to remember the password!

So, first of all it’s day 18 and we’re still not in Russia. This was not really part of the plan; we should have been in Russia a while ago in order to be “on track”. But things got hairy pretty soon after we departed England. Our convoy of six ambulances had headed south-east through Europe in order to get to Budapest, at the time a relatively small diversion in order to meet with the GoHelp charity and other teams outside of our convoy, for an annual event to share a few drinks with everyone who embarks on this mission.

Budapest is a lovely, lovely place, but unfortunately as Fed wrote at the time, it stole more than just our hearts: someone forced the back doors of our ambulance overnight and stole my suitcase, my hidden rucksack containing my laptop which I’d intended to use for blogging (and some Umbraco 5 work) and our camera chargers, Fed’s Kindle, and perhaps the most devastating thing given the context: our vehicle documentation.

Given that all these things except perhaps the suitcase had been hidden, we didn’t actually discover the theft until we were about 300 miles away from Budapest near the Ukranian border. At the time, it was a whirlwind of stress. Any theft is always earth-shattering but the full extent didn’t actually dawn on us until we had attempted a few border crossings: that vehicle documentation, a “V5C” in the UK or “auto-passport” in local parlance, was the key to us leaving the EU.

As it happened, the entire convoy spent about 2 days bouncing between the EU and Ukranian border in no-man’s land because the remainder of the convoy had “temporary V5″ documents whilst our permanent ones were being mailed to catch us up in Volgograd, and we had been given advice that this (and our hastily-obtained printout of a V5C scan we had on file) would not be a problem. But with clarity of hindsight we now know that actually the Ukraine was never going to be easy: it levies a 3000EUR fee per vehicle for crossing with temporary documents. Well, I say a “fee”: they claim we would have received a refund when getting to Russia in exchange for proving that we weren’t going to dump the vehicles in the Ukraine, but we just didn’t have that kind of cash or trust in chance to continue. Plus, it was probably a lucky break: we now know that our single ambulance would never have made it from the Ukraine into Russia without the original V5C and Fed and I would have been stuck having to turn back.

So the whole convoy chose a new route, after some deliberation between going north or south of the Ukraine to a Turkish ferry. We decided to travel north through Slovakia, Poland, Lithuania and finally into Latvia where there are several border crossings with Russia that avoid Belarus. Here the other five ambulances successfully made it through after a painful 9-hour queue, but Fed and I in ambulance #5 were turned away. It was an incredibly gut-wrenching moment, and everyone was incredibly supportive – although clearly we were all upset. Since that time, Fed and I have been traveling on our own with determination, to a few of the other Latvian crossings. At one border we even made it to the very last step (luckily after only a 30-minute queue), but still got turned away.

Since Saturday when the others made it through the Russian border and beyond we’ve tried all sorts, but finally on Monday my mum and dad managed to make a breakthrough with the DVLA in the UK who kindly agreed to get a new V5C sent to our London home first-class, where Fed’s mum and sister have been staying. Thanks to them all it’s now with DHL and we expect to receive it before 12pm tomorrow.

Through all of this we’ve had our ups and downs. Of course, the original robbery has always been at the back of our minds, but perhaps if we’d had the permanent V5C on us it might have been that which was stolen. But there have been other things, the largest of which is probably that our Russian visas expire on the 12th of August. Looking at the mileage, it’s still do-able if we enter Russia tomorrow night, but there’s no doubt that it will be a case of doing a lot of driving in a short amount of time. Which is why it’s so incredibly kind that one ambulance has stayed to wait for us in Samara. One of their team, Scott, will fly back to Moscow in order to jump in our van and help with the driving to get to Samara as quick as possible, and everything else being well (we are definitely due a lucky break!) we will then continue as a convoy of two vehicles and six people – a much more reassuring prospect. Together we probably won’t catch the other four, but the idea that it’s still at least possible to get all six vehicles there is a glimmer of hope to which we are all clinging right now.

It’s been our intense desire throughout all of this that we make sure that we get this vehicle to Ulaanbaatar, to deliver it but also to visit the places where the donations to our and the other teams’ fundraising websites will be spent. We’ve still got other backup options in case DHL lets us down, for example donating the vehicle to a needy beneficiary in the EU and then traveling to Mongolia via train or plane, but of course none of them quite lives up to the visions we’ve had for the past 18 months. It’s about the journey as well as the destination, and – whilst there are still hurdles we must cross – potentially Team 2 and our parents back in the UK scrabbling to help us out will have made this current hurdle conquerable.

And with that I’ll bring this post to a close. Since I’ve been doing all of the driving, and had my laptop stolen, I’ve not been able to blog or tweet at all. But hopefully the next time I get chance, it’ll be with good news. Fed has also been doing sterling work taking lots of great pictures for our team blog which is here, which she updates from the passenger seat whenever there is a sniff of open Wifi.

Until the next update, all the best and thanks again to everyone who has donated funds, to our amazing convoy-mates and our parents for their help, to all those kind folks we’ve met along the way, and to my incredibly supportive employer Umbraco to make this mission so worthwhile.