Joshua Brindle

How to Win At Security

Top-down vs. Bottom-up Policy Development

I'll be the first to say I'm not a policy developer. The process of actually writing policy is not at all interesting to me, fortunately for me there are people like Chris PeBenito, Russell Coker, et al that seem to enjoy this. I am interested, however, in how policies are developed.

There seem to be two schools of thought on this subject: top-down policy development and bottom-up. Top-down policy development is very similar to status quo encapsulation, which I talked about in a previous post, in that it basically means you take a running system and look at it top-down, in its entirety, and develop a policy based on that perspective. Bottom-up policy development, which is historically what SELinux has done, is the opposite; you create policies for individual applications running on a system until the sum of those policies meets the security goals of the system. I'll try to talk about the advantages and issues with each of these.

Many proponents of access control systems like grsecurity seem to favor top-down development. This is encouraged by "full-system learning" mode which creates a status-quo encapsulation style policy for the entire system. For information on status-quo encapsulation see my previous post. The advocates of this method believe that the only way to create a policy for a particular configuration is to do it on that system. This is a reasonable belief, for example it might be popular to connect allow Apache to execute CGI scripts but if that isn't how the system is question is configured why should that be part of the policy? The problem is that it makes a fragile, rigid policy. If the system changes configuration the entire system must be re-profiled to see what is different, this incurs the additional issue of developing a policy on a system that is not in a known good state while re-profiling. One change in configuration can propagate changes to the entire system so trying to modify the system policy manually may be difficult or impossible. The policies are also very complex. Most system administrators and software developers would probably have a hard time looking at an entire running system and know what's going on, even fewer would be able to discern what is going on from what should be going on. Users complicate the matter quite a bit. Since it is hard to profile user behavior the policies are either far too complicated or unusable, or both. Adding users to the system could be problematic since the policy is inflexible and won't have rules that are applicable to new user home directories and so on. Like status-quo encapsulation this type of policy development lacks specific security goals, and only tries to keep the system running like it currently is.

In SELinux we take a different approach, bottom-up if you will. The SELinux reference policy has pre-written policies for many applications - 177 at the time of this writing. The policies are written and analyzed by security professionals and unexpected behavior is tracked down for the cause. If it turns out the permission was unnecessary for normal application usage the permission can be dontaudited instead of allowed. This is only possible if the policy development and analysis is done on a per-application basis where interactions with other applications as well as all resources used by that application can be studied and analyzed in detail. When application configurations change the effects can be observed and added to the affected application policies without disturbing the rest of the system. The Apache example earlier is a good example. Since the ability to execute CGI scripts is a common configuration the Apache policy has the rules required to do so. If this permission isn't necessary on a system the permissions can easily be tracked down in the Apache policy and removed. Better yet, policies that support multiple configurations can use conditional SELinux policy. In this case if the permission is not required the admin need only toggle the httpd_enable_cgi boolean and the permissions are no longer active on the system.

Even when applications don't yet have policies the end user should be much more able to profile a single application and write a policy for it rather than worrying about what every single thing on the machine is doing. Other systems like grsecurity and Apparmor allow per-application policy development, however without a strong foundation to stand on (the system policy) writing an application policy may be moot. Apparmor comes with many application policies - 80 at the time of this writing, but fail to provide the foundation. For example there is no policy for /bin/init, the first app to load on the system and the one that loads everything else. How can one trust the policy limiting an application if the application that loaded it has no restrictions? It is impossible to tell how an application may be affected by other applications if there isn't a policy restricting the init system, X or login. Apparmor is actually designed for application policies only. This severe limitation in its implementation makes it unable to implement many security models. Unfortunately this was a design decision in the mechanism itself, rather than building a general mechanism and making the policies flexible enough to implement this limited access control or if the user chooses, something more complex.

On the other hand, grsecurity provides no pre-written policies and leaves it up to the end user to develop/generate the entire thing. This is even encouraged by the main grsecurity developer. A user is free to write a grsecurity policy bottom up but that is a major undertaking. The SELinux policies have years of development in them and are being worked on every day, I have doubts that a single person has ever written a grsecurity policy in this way.

Policy writing is hard. Analyzing thousands of accesses by hundreds of applications on modern systems makes the task far harder if a good foundation and reference policies aren't provided. Top down policy development can fail in many ways and in the end provide less security than more lenient pre-written policies. Additionally providing application policies but ignoring the rest of the system can give a false sense of security and leave many attack vectors open. Bottom-up policy development allows one to be very specific about what their security goals are, and create a policy that meets those exact goals. It also provides a flexible way to handle changes in system configuration.

My next post may take a while longer than this one as it requires quite a bit of research. Hopefully, unless I get distracted and write something else, it will be about common security models that have been used and what their advantages and disadvantages are, stay tuned.