|
Helping ordinary people create extraordinary websites! |
Secure Programmer: Minimizing PrivilegesBy David A. Wheeler2005-05-27
Newer mechanisms The principles we've discussed up to this point are actually true for just about any operating system, and the general mechanisms have been very similar between just about all UNIX-like systems since the 1970s. That doesn't mean they're useless; simplicity and the test of time have their own advantages. But some newer UNIX-like systems have added mechanisms to support least privilege that are worth knowing about. While it's easy to find out about the time-tested mechanisms, information about the newer mechanisms isn't as widely known. So, here I'll discuss a few selected worthies: the FreeBSD jail(), the Linux Security Modules (LSM) framework, and Security-Enhanced Linux (SELinux). FreeBSD jail() The Linux Security Modules (LSM) Conceptually, the LSM framework is very simple. The Linux kernel still does its normal security checks; for example, if you want to write to a file, you still need write permission to it. However, any time that the Linux kernel needs to decide if access should be granted, it also checks -- asks a security module via a "hook" -- to determine whether or not the action is okay. This way, an administrator can simply pick the security module he wants to use and insert it like any other Linux kernel module. From then on, that security module decides what's allowed. The LSM framework was designed to be so flexible that it can implement many different kinds of security policies. In fact, several different projects worked together to make sure that the LSM framework is sufficient for real work. For example, the LSM framework includes several calls when internal objects are created and deleted -- not because those operations might get stopped, but so that the security module can keep track of critical data. Several different analysis tools have been used to make sure that the LSM framework didn't miss any important hooks for its purposes. This project turned out to be harder than many imagined, and its success was hard-won. The LSM made a fundamental design decision that's worth understanding. Fundamentally, the LSM framework was intentionally designed so that almost all of its hooks would be restrictive, not authoritative. An authoritative hook makes the absolute final decision: if the hook says a request should be granted, then it's granted no matter what. In contrast, a restrictive hook can only add additional restrictions; it can't grant new permissions. In theory, if all LSM hooks were authoritative, the LSM framework would be more flexible. One hook, named There were also many concerns that even the smallest bugs would be disastrous if most hooks were authoritative; while making the hooks restrictive meant that users would be unsurprised (no matter what, the original UNIX permissions would still normally work). So the LSM framework developers intentionally chose the restrictive approach, and most of its developers decided that they could work within the framework. It's important to understand some of the LSM framework's other limitations, too. The LSM framework is designed to support only access control, not other security issues such as auditing. By themselves LSM modules can't log all requests or their results, because they won't see them all. Why? One reason is because the kernel might reject a request without even calling an LSM module; a problem if you wanted to audit the rejection. Also, due to concerns about performance, some proposed LSM hooks and data fields for networks were rejected for the mainline kernel. It's possible to control some network accesses, but it's not enough to support "labelled" network flows (where different packets have different security labels handled by the operating system). These are unfortunate limitations, and not fundamental to the general idea; hopefully the LSM framework will be extended someday to eliminate these limitations. Still, even with these limitations, the LSM framework can be very useful for adding limits to privileges. Torvalds' goals were essentially met by the LSM framework: "I'm not interested in the fight between different security people. I want the indirection that gets me out of that picture, and then the market can fight out which policy and implementation actually ends up getting used." So, if you want to limit the privileges you give your programs on Linux, you could create your very own Linux security module. If you want to impose truly exotic limitations, that may be necessary -- and the nice thing is that it's possible. However, this isn't trivial; no matter what, you're still writing kernel code. If possible, you're better off using one of the existing Linux security modules than trying to write your own. There are several LSM modules available, but one of the most mature of the Linux security modules is the Security-Enhanced Linux (SELinux) module, so let's look at that. History of Security-Enhanced Linux (SELinux) MAC mechanisms make it possible for a system administrator to define a system-wide security policy, which could limit what programs can do based on other factors like the role of the user, the trustworthiness and expected use of the program, and the kind of data the program will use. A trivial example is that with MAC, users can't easily turn "Secret" into "Unclassified" data. However, MAC can actually do much more than that. The NSA has worked with operating system vendors over the years, but many of the vendors with the biggest markets haven't been interested in incorporating MAC. Even the vendors who have incorporated MAC often do it as "separate products," not their normal product. Part of the problem was that old-style MAC just wasn't flexible enough. NSA's research arm then worked to try to make MAC more flexible and easier to include in operating systems. They developed prototypes of their ideas using the Mach operating system, and later sponsored work extending the "Fluke" research operating system. However, it was hard to convince people that the ideas would work on "real" operating systems since all this work was based on tiny "toy" research projects. Few could even try out the prototypes, to see how well the ideas worked out with real applications. NSA couldn't convince proprietary vendors to add these ideas, and NSA didn't have the right to modify proprietary operating systems. This isn't a new problem; years ago DARPA tried to force its operating system researchers to use the proprietary operating system Windows, but encountered many problems (see the Resources resources below). So, NSA hit upon an idea that seems obvious in retrospect: take an open source operating system that's not a toy, and implement their security ideas to show that (1) it can work and (2) exactly how it can work (by revealing the source code for all). They picked the market-leading open source kernel (Linux) and implemented their ideas in it as "security-enhanced Linux" (SELinux). Not surprisingly, using a real system (Linux) made the NSA researchers deal with problems they hadn't had to deal with in toys. For example, on most Linux-based systems, almost everything is dynamically linked, so they had to do some subtle analysis about how programs are executed (see their documentation about the "entrypoint" and "execute" permissions for more information). This has been a far more successful approach; far more people are using SELinux than the previous prototypes. How SELinux works System administrators then create a "security policy" that specifies what privileges are granted for which security contexts. When a system call is made, SELinux checks if all of the necessary privileges are granted -- and if not, it rejects the request. For example, to create a file, the current process' security context has to have the privileges "search" and "add_name" for the parent directory's security context, and it needs the privilege "create" for the (to be created) file's security context. Also, that file's security context must be privileged to be "associated" with that filesystem (so for example, a "Top secret" file can't be written to an "Unclassified" disk). There are also network access controls for sockets, network interfaces, hosts, and ports. If the security policy grants all of those permissions, then the request is allowed by SELinux. Otherwise, it's forbidden. All of this checking would be slow if done naively, but numerous optimizations (based on years of research) make it extremely quick. This checking is completely separate from the usual permission bits in UNIX-like systems; you have to have both the standard UNIX-like permissions and the SELinux permissions to do something on an SELinux system. But the SELinux checks can do many things that are hard to do with traditional UNIX-like permissions. With SELinux, you could easily make a Web server that could only run specific programs and could only write to files with specific security contexts. More interestingly, if an attacker breaks into the Web server and becomes root, the attacker won't gain control over the whole system -- given a good security policy. And there's the rub: to use SELinux effectively, you need to have a good security policy for SELinux to enforce. Most users will need a useful starting policy that they can easily tailor. I began experimenting with SELinux several years ago; at that time, the starting policies were rudimentary and had many problems. For example, I found in those early days that the early sample policy didn't let the system update the hardware clock (I ended up submitting a patch to fix this). Devising good starting security policies is the kind of productizing that NSA is hoping the commercial world will do, and it looks like that's coming to pass. Red Hat, some Debian developers, Gentoo, and others are using the basic SELinux framework and creating initial security policies so users can immediately start using it. Indeed, Red Hat plans to have SELinux enabled for all users in their Fedora core, with simple tools to allow non-experts to tailor their security policies by selecting a few common options. Gentoo has a bootable SELinux LiveCD. These groups should make it much easier to minimize program privileges without requiring a lot of coding. Here's where we come full circle. SELinux permits security transitions to occur only upon program execution, and it controls process' permissions (not portions of a process). So to use SELinux to its full potential, you need to decompose your application into separate processes and programs, with only a few small privileged components -- which is exactly how to develop secure programs without SELinux. Tools like SELinux give you finer control over the privileges granted, and thus create a stronger defense, but you still need to break your program into smaller components so those controls can be at their most effective. Tutorial Pages: » Taking the fangs out of bugs » Basics of minimizing privileges » Minimize privileged modules » Minimize privileges granted » Minimize privileges' time » Newer mechanisms » Conclusions » Resources First published by IBM DeveloperWorks
|
|