If only everyone would just play nice…

Continuing with my rebuttal of Shawn’s listing of reasons for forcing full trust of assemblies in the GAC…



4. “If an application is hosting the CLR, it has the ability to protect itself from assemblies it doesn’t trust to load. For instance, SQL Server 2005 does not allow the Windows Forms library to load. Applications can provide an AppDomainManager and HostSecurityManager in order to disallow some assemblies from loading, or to tweak their grant sets.”


That’s nice. Unfortunately, it does nothing to protect users against the majority of .NET applications, which are run outside of such custom hosts. I suppose this does mean that one could force stand-alone .NET apps to run in a host of one’s choosing, but I have a really hard time seeing this is a general workaround for reduced CAS policy flexibility. Amongst other things, who could be trusted by most users to build, test, and distribute such a host (keeping in mind that a buggy host might introduce security problems of its own)?



5. “Assembly-level declarative security still works to reduce the grant set, so if you really need it, there is a knob you can turn to reduce the granted permissions of an assembly stored in the GAC.”


This works for the assemblies we create, but not the ones we consume (either via our own code or applications authored by others). It would be naive not to recognize that some developers will be GACing assemblies just to acquire a guaranteed full trust grant. These are not folks who would be likely to be willing to reduce their permission grants in any way. They may also do some pretty silly things like blindly asserting any permission their own callers might need, thereby creating some rather ugly security holes on our machines.


If the platform allows developers to abuse the system with such ease, it should at least allow users to protect themselves with as little effort.

I’m so special…

Continuing with my rebuttal of Shawn’s listing of reasons for forcing full trust of assemblies in the GAC…



3.a) “Since you have to be an administrator in order to add an assembly to the GAC, it is already considered special from a security standpoint.”


Ouch. Got a few things to say about this one…


i. Joe User can’t write to the Program Files directory either. Does this make it special too? (Rhetorical question, I hope. ;))


ii. One absolutely does not need to be an administrator to add assemblies to the GAC. Even under default ACLs (at least on fully patched Windows XP and .NET Framework 1.1), power users can also do so. Although I haven’t tried this, I’m guessing that manipulation of ACLs might also allow any user to do so.


iii. Unfortunately, far too many enterprise users (particularly in smaller businesses) and almost all home users run as admins. In practice, there’s nothing special about a location to which all these folks can write. These are also the users who can most benefit from the protections offered by limitation of CAS permissions.


iv. Windows 98, Windows 98 Second Edition, Windows ME are all listed as supported versions for the beta version of the v. 2.0 Framework. Assuming this doesn’t change, the supposed “difficulty” of placing assemblies in the GAC shouldn’t be a factor in the decision to force full trust of GACed assemblies.



3.b) “For instance, strong name verification is skipped for assemblies that are loaded from the GAC.”


Well, I’ve never liked that one–surprise! 🙂 Amongst other things, consider those poor Win9x users and all those folks who run with sufficient privileges to alter the contents of the GAC. Also, given that the ACLs on the GAC can be altered, it seems ridiculous for the platform provider to be making decisions based on an assumption that the default ACLs will be in effect.

Secure by default?

Continuing with my rebuttal of Shawn’s listing of reasons for forcing full trust of assemblies in the GAC…



2.a) “By side-effect, assemblies in the GAC did already receive FullTrust.”


Under default policy only. I’d be one of the first to argue that this default policy is probably too permissive, but it’s a little late in the game for that. At least those of us who don’t like the default policy can alter it so that not all locally installed code is fully trusted. Forcing full trust of all assemblies in the GAC would deny us that possibility.



2.b) “The only way that you could change this would be to either not grant MyComputer FullTrust, or create an exclusive code group that matched the strong name of the assembly and granted less trust.”


Yup. I run almost all my machines (both desktops and servers) with only SecurityPermission\Execution granted to the My_Computer_Zone code group. It causes the occasional bit of pain, but I prefer it to the alternative.

I’m in the platform? Little old me?

After introducing a Microsoft plan to force full trust all assemblies in the GAC, Shawn Farkas posted follow-up inviting further feeback. Included in his post are six points explaining some of the reasoning behind the change. In my opinion, none of these reasons even begins to justify the change, and I’d like to present some counter-arguments to each. I’ll address each of Shawn’s points in a separate post here, with the individual points and subpoints labeled for ease of discussion.


Starting with point 1…



1.a) “Assemblies in the GAC build up the managed platform that all managed applications can run on.”


Not all assemblies in the GAC are meant to form part of the general .NET platform, just as not all DLLs in the Windows\System32 folder form part of the Windows platform. Microsoft lost the right to assume this the moment every Tom, Dick, and Harry were allowed to put their assemblies in the GAC.



1.b) “In order to build up a platform where any application, trusted or not, can run safely, it makes sense that you need to trust the assemblies making up that platform.”


But that trust need not be blind. See my earlier post on trust decisions for why one might not want to grant full trust to even the most trustworthy assemblies.



1.c) “If you don’t trust an assembly enough for any code to be able to call into it, then the best place for it is probably not the GAC”


i. On inappropriate GACing:


By and large, the decision to place any given assembly in the GAC or not is made by the folks who author and distribute applications, not the folks who run their installation packages. I might decide not to install an app once I find out that it puts assemblies in the GAC in a manner that I find unnecessary (and I’ll be far more likely to make this decision if the change goes through), but I won’t be adjusting someone else’s app or installer to avoid putting their assemblies in the GAC.


ii. On trusting GACed assemblies:


Under v. 1.x rules, one could set policy to grant only partial trust to assemblies in the GAC, so allowing other folks’ apps to put assemblies in the GAC did not necessarily cause a trust problem. The problem is introduced by eliminating the possibility to the restrict GACed assemblies to the permission one feels they actually merit.

Keep it simple, smarty

This post is in response to a Microsoft plan to force full trust all assemblies in the GAC regardless of CAS policy settings.



CAS


Imagine for a moment that you could find an “intro to CAS” document from Microsoft that gives a simple, clear statement of the purpose of CAS. What would that statement be?


Unfortunately, my own search for such a document failed to turn up anything that didn’t jump directly from the “why” into the “how” of CAS, leaving the reader to infer the purpose from the “why”. In the absence of a clear statement of from Microsoft (unless someone else has seen one?), I’m going to propose the following:


The purpose of code access security is to permit restriction of the activities available to managed code beyond the limitations imposed by permissions (not) granted to the user executing the code.


If one agrees with the above statement, then it should be very difficult to accept a modification to CAS that makes it impossible to control permissions for any managed code. After all, if the purpose of CAS is to allow restriction of code permissions, how does eliminating the restrictability of permissions for some code fit in?



The GAC


What’s the purpose of the GAC? I had a little better luck this time around and found the following statement in the Global Assembly Cache topic in the .NET Framework Developer’s Guide:


“The global assembly cache stores assemblies specifically designated to be shared by several applications on the computer.”


One could argue that the above isn’t necessarily a definitive statement of purpose, so let’s also consider the following instructions from the same document:


“You should share assemblies by installing them into the global assembly cache only when you need to. As a general guideline, keep assembly dependencies private, and locate assemblies in the application directory unless sharing an assembly is explicitly required.”


CAS isn’t mentioned at all. There’s nothing about placing assemblies in the GAC in order to obtain a full trust grant. Unfortunately, if the plan to force full trust for GACed assemblies goes through, folks will violate the above instructions simply to ensure their code is granted full trust. The worst abusers of the GAC will probably be those developers who are least familiar with (and/or least willing to abide by) the principles and practices of secure development. Do you really want their assemblies being guaranteed full trust on your machine?


UPDATE: Microsoft is already recommending that folks place assemblies in the GAC solely in order to obtain a full trust grant.



CAS and the GAC


I actually happen to think that the introduction of GacMembershipCondition (assuming it sticks around) and the associated easing of assignment of special permissions to GACed assemblies is quite reasonable. If .NET 2.0 were to ship with a code group granting full trust to assemblies in the GAC (redundant though that may be if all local code is already granted full trust), I wouldn’t complain despite the apparent violation of the “secure by default” principle. After all, those of us who care about running code with least privilege would still be free to modify CAS policy in order to reduce the permission grants, and ignorant and/or lazy developers wouldn’t be able to grab full trust simply by GACing their assemblies on our machines.


However, enforcing full trust for GACed assemblies has some potential consequences beyond simply removing our immediate ability to limit the permissions of potentially “bad” code in the GAC. I would imagine that quite a few very smart people spent rather a lot of time mapping out the goals for CAS and the GAC. If one accepts that their visions for CAS and the GAC were reasonable, then corruption of those goals should not be irrevocably built into the framework. Of course, no software feature is ever strictly final, but Microsoft has repeatedly demonstrated an unwillingness to make breaking changes. Unfortunately, if this change goes through, the rather large segment of the developer population that will want to run their code as fully trusted will likely complain very loudly if the change is ever reversed.


What do you think will happen a few years down the road if folks at Microsoft decide that the original vision was right after all?

Do I trust you? Well, sort of…

This post is in response to a Microsoft plan to force full trust all assemblies in the GAC regardless of CAS policy settings.



For some time now, I’ve been rather disappointed with the view of code trustworthiness that seems to be generally espoused at Microsoft. IMO, there are at least two main issues to address when evaluating the trustworthiness of code (and/or its source):



  1. Do I trust it to not be deliberately malicious?
  2. Do I trust it to not contain any exploitable flaws?

As far as I can tell, Microsoft seems to be concentrating mostly on #1* (and not just with respect to CAS and the .NET Framework). However, I worry at least as much, if not more, about #2. Unfortunately, even the most well intentioned of developers are not necessarily all that competent, particularly when it comes to security.


Even when developers are competent and careful, there’s every reason to expect that their code will contain at least some exploitable flaws since bugs related to security will likely be at least as frequent as problems in any other area. Therefore, even if I trust the developers of a given assembly to be both non-malicious and competent, I would still want to run their code with least possible privilege. This is simple defense in depth.


If all assemblies in the GAC are to become fully trusted regardless of policy settings, administrators will have no way of enforcing least privilege for these assemblies. Is the loss of this ability really worth any trade-off with respect to possible gains that might result in other areas?




*Interestingly enough, the Code Access Security topic in the .NET Framework Developer’s Guide does mention #2 as one of the reasons for the limitation of code permissions under CAS. Unfortunately, it would seem that someone has forgotten about this along the way.