*Intro This presentation aims to show what can be done in the kernel to help enforcing a security policy, and why it is a necessary approach to trust a security policy. This presentation is very PC-linux oriented, even if a lot of general ideas are treated. *Tr 1 As an introductin, we will first see the aim of this approach. Then we'll try to focus more on the way we could implement some concepts we would have seen in the first part. We will finish by the presentation of some existing projects for the linux kernel, Openwall, Medusa DS9, RSBAC (Rule Set Based Access Control), the Security Enhanced Linux by the NSA (National Security Agency), and LIDS (Linux Intrusion Detection System) which is co-authored by me. I'll conclude quickly on the GACI (general access control interface) project. *Tr 2 So, let's begin with the first part. I'll first present the context and give some definitions Then I'll speak about trustfulness To conclude on a very light specification *Tr 3 In the world of computer systems, not everything is pink. Systems, specially networked systems, are exposed to various dangers : some which just do nothing harmful, other that can cost money. *Tr 4 A good system administrator does not want its systems to be hacked or cracked. He must ensure three key points : the confidentiality : Information must not be either made accessible or divulgated to a non authorized user, entity or process the data integrity : Information must not be altered or destroyed in a non authorized maneer and the services avaiability : The access by an authorized user, entity or process to services of the system must always be possible The way he will guarentee these points is to define a set of rules describing the way information is handled, protected and distributed, which is called a security policy. In fact, we can include some less computer-related things like floodings or employee illness in a security policy, but this definition will be ok. *Tr 5 To enforce a security policy on a computer, some security tools must be used. Some can guarantee data integrity, like Tripwire or AIDE. Some helps for confidentiality, like cryptographic software. Access controls use password checks or other gadgets like biometrics. Are all these tools enough to guarantee the enforcement of the security policy ? Can we trust them ? *Tr 6 This question raises the problem of the Fortress Built upon Sand, which is the title of an article by D. Baker. Because user space (a space where the applications run, by opposition to the kernel space, where the code running is the kernel's one. The kernel space has all the control on the computer and the user space have no control on it and must ask kernel space) is untrusted (root intrusion is not so hard, for example), and because a root intruder can take control of the kerne space by poking into kernel memory or, simplier, by inserting a module, the kernel space is untrusted. So that every security application, on which rely our security policy, is built upon an untrusted level. *Tr 6bis What happen if someone break into sendmail ? He can take the control of the kernel, read, confidential data before encryption or after decryption by ssh, corrupt tripwire hash database. *Tr 7 This brings us to more general concepts of security and trustfulness. There are some good sense rules to use Security must be built layer by layer Each layer is built with the hypothesis the underlayer is trusted. It is not worth building security applications on untrusted layers, because it does not increase security. What about the first layer ? We must make hypothesis at some point. We'll trust hardware. We have no other choice anyway. We also have to trust compilers, this in reference to the Ken Thomson trojan and its "Reflections on Trusting Trust". The compiler added some trojaned code when it compiled the login program, and also added some trojaned code to do so when compiling itself. So that traces of this trojan disapeared in source code but existed in binaries. We need to trust kernel space, because we can't built any secure system without this. This is not the case of most of the kernel OS. We'll need to tune this up. Why don't we want user space to be trust ? *Tr8 To answer this, let's talk about the story of the mice and the cookies We have some cookies in a house and we want to prevent the mice from eating the cookies. *Tr9 We have two solutions that could work, in theory : to protect the house and to put the cookies in a metal box. The first can't be applied in a real case, because there are too many doors, windows, holes to cope with. What about floors and cells ? We can't know all the holes to lock them. Moreover we can t be sure there weren t any mice before we closed the holes! The second is a very simple human sized solution. The hypothesys is reasonable. The cookies don't care about mice in the house. If we do, we can also try the first solution. If it works, that's great. If not, too bad. But at least, cookies are safe. *Tr10 In conclusion, we saw that we did not aim to make the user space trusted but only the kernel space. This mean that we must add code to the kernel, to protect the kernel and the added code itself against user space. This is what I'll call untamperability. We then need to portect other code or data involved in the security policy, (or in every other thing we want to protect). A way to control everything (other than modifying its own memory) done by an application is to put code in a place where it becomes mandatory. This lead to what I'll call unbypassability. *Tr11 We're entering now in a more technical part, to see what can be done to reach the conclusion we just saw. I'll first speak about what must be done and about some justification of implementation designs we'll see later. Then we'll see the first big point of the design of this approach, which is untamperability. And we'll finish on the second big point, which is unbypassability. *Tr12 We saw previously that we wanted first to make the kernel trusted, which in fact does not mean more than the need add code to protect the kernel space and thus the code itself. This is achieved by blocking (or filtering) everything coming from user space. We'll see later what we must block. The second thing to do is to extend the control we get over user space to protect applications involved in the security policy, like for exemple preventing process memory peeking fo encryption applications, or enforcing access control for httpd. Because we design the code to be in kernel space and because we make the kernel space trusted, this code is also trusted. We just have to make it a mandatory way, that is to say it is unbybassable. *Tr12 Because of the design of the CPU (at least in the case of Intel's ones), the protected mode of the CPU give privileged powers to the kernel code. The particularities of this memory location is that we have few entry points, which help us to reach untamperability, and user space can do nothing without asking code in the kernel space. This will help us top reach unbypassability. *Tr13 Let's analyse what we must do to reach untamperability. The kernel space is unreachable by user space code. There are some ways to communicate. First, user space code can trigger the execution of some defined kernel code, by the mean of system calls, devices utilisation, /proc filesystem and (not exactly user space) hardware interruption (keystroke, network packet, ...) That mean that this kernel code must be of good quality (!). Second, there are very few entry points from user space to kernel space, and all of them are invitations by kernel code. We have the /dev/mem and kmem devices. They permit you to poke into the whole memory, if you have sufficient rights. The notion of "sufficient right" must be a kernel notion. Being root must not be enough to grant you this. TO simplify things and to add trustfulness, we'll disable this. (just for info, there exists some user space programs that, abusing their root privileges, can emulate module insertion/deletion just by playing with /dev/mem or kmem) We also have /dev/port, ioperm and iopl. They permit you th directly control hardware without asking the kernel. We can imagine that it should be possible to modify kernel space memory just with this. Anyway, it's bad for the unbypassability (we'll see this later). So, same judgement : we supress them. The module insertion and deletion must also be prohibited as they are the easiest way to insert code into the kernel and to tamper with kernel code/data. Last, but not least, the fact to reboot the computer can be considered as an entry point, as changing the kernel image on disk and rebooting means changing the code which lives in kernel space. All these points are wanted to be an exhaustive list, at least for the linux kernel. I hope I did not forget one. If you see any other ? *Tr 15 I previously said that a lot of attention must be paid to kernel code. The way the protected mode must be managed help programmer a lot to avoid some programming faults like buffer overflows. Each piece of data must be transfered from user space to kernel space. In the cazse of linux kernel, we must use a function called copy_from_user, which need the size of teh piece of data. Wel, this may be the reason why we don't see a lot of buffer overflows in kernel code... *Tr 16 We see here the function called when an application try to open /dev/port (in fact, it is also the one for /dev/mem and kmem). It is the place where we must patch to control these entry points, either by adding a new access control (here we can see teh capabilities access control, already implemented in the linux kernel). or by simply forbiding this operation and always return the premission denied error. *Tr 17 Here is the system call for module insertion. It is called by insmod to insert some code in the kernel. We also see how it is easy to add a different access control, or to simply forbid it. *Tr 18 The last point is the reboot problem. We must note that the reboot/halt system call can't be simply forbidden. First, because UPS need to reboot the machine. Well, we could consider that secured power supply with electrogen can be part of the security policy so that the machine doesn't need to reboot. But that's not all. In fact, the reboot system call just execute some special intructions to reboot the CPU, the big part of the reboot is only user space stuff (killing apps, stopping daemons,...) And, in fact, there is no difference with a runlevel change. The way we'll take is to accept that the machine can reboot and to guarantee the safety of the boot sequence, so that the next state after a reboot is still a state where the kernel space is trusted. It's in fact a little like a proof by induction. We have a set of states, some have a kernel space trusted, other not. If we proove that a valid transition from a secure state can't be an insecure state, and if the very first state is secure, then every states are secure. *Tr 19 We can see here the different parts of a boot sequence. First, the power on self test is issued, finishing by the "press del to enter setup". This step is console vulnerable (which mean physically, if some corrupted hardware is inserted (floppy, spy daughter card) by using the console, for example by modifying the bios setup). password protection and physical access (cages, locks) are generally used. Then the boot loader launching, which is also vulnerable to console access (need password protection) but also depends on the boot disk. The load of the kernel also rely on the kernel image on disk. All of this need to be protected during the working state to be rerun safely after each reboot. That was the interesting part. Then we have the booting process, where some initialization stuffs are done, when daemons are launched, etc. And we finally reach the working state, where the machine is ready to work. What must be protected is user space stuff. So it can be protected with same mechanisms as other user spaces stuff. Let's now see what we can do to protect user space, with a trusted kernel space. *Tr 20 First, lets enumerate the different kind of things we must protect. First, everything in memory must not be accessed without authorization. That means peeking into other processes memory or modifying the kernel behaviour altering firewall rules, rp_filters, etc. Second, every access to data stored on a hard disk, a tape must be controled. Data means files and metadata, that is to say filesystems, partition tables, boot loaders, etc. Third, hardware can also be vulnerable, everything that can be programmed can be a target. For example : CPU microcode, bios EPROM. *Tr 21 The good thing is that user space can't access any of these items without asking the kernel to do it, by the mean of syscalls or sometimes driver calls. SO that system calls are a place of choice for controlling accesses. *Tr 22 We'll use a modular architecture because it leads to more .. modularity. The code will be divided into 2 parts : an enforcer componenent, that will intercept system calls and ask a decider component wether the system call is authorized or not. If it is authorized, the enforcer component will let the system call resume, else, an error will be returned. The enforcer component must give any useful parameters for the decider to be able to decide knowing every element. The modularity enables us to change the decider component at will, so that we can have an access control policy for each day of the week, like - discretionnary access control, which is the usual unix fs rights, - the mandatory access control, a more complex one relying on subjects, objects and actions, and allowing or denying an action to be done by a subject on an object. -The access control list, with which you can control individual access for each file -Role Based access control where some roles (administrator, backup man, etc.) are used -Identity based control, where identity is used. I won't detail them. *Tr 23 Basically, there are two methods to add the enforcer code to syscalls : the interception, which is done in the dispatching code and can be compared to a firewall fo system calls, and the system call modification, which is done in each syscall. These two methods also apply do devices controlling. *Tr 24 Let's see what an interception can look like. This piece of code is extracted from a patched linux kernel with Medusa DS9. the entry.S file is the entry point, the dispatcher for system calls. We are in the middle of this function which, for at least linux on PCs, is called by a software interruption. Just before calling the real syscall, we can see the code inserted by the patch, with a call to the decider component. *Tr 25 THe advantages of this method are : it's a general method. You get every system calls even futures ones. The patch is really small. You just have to insert this code and everything else is code addition. But there are some drawbacks. It's like if every syscall is duplicated. For each syscall, you call a function, then another. Moreover, you have to know and interpret parameters for each syscal. You must say mmh, it's syscall number 10, it's an open request. Ok, so this register is the rights it wants and this one is a pointer to the file name. You must then check parameters are ok. WHat if you want to open a call that does not exist ? Must the decider deny it, returning a wrong error : permisssion denied instead of file not found ? Must it allow this for the syscall to return the correct error ? Must it implement the complete error checking of the system call ? An it is architecture dependant. System call calls are not issued the same way on different architectures and on different OSes. Each architecture/OS couple is a different case. *Tr 26 Here we can see the second method, used by LIDS, which is the syscall modification. We have a syscall taken from open.c file. All the parameters in the CPU registers are interpreted. Then they are tested, some useful information is retrieved, for example with the pathwalk. Becase these are needed operations, it is not added load. THen, when we have everything we need to decide, we call the decider componenent, right in the middle of the sytem call. The lids_check_base function is called with needed parameters. We can see the nd structure, which has been calculated, with no cost at charge of the enforcer or decider componenet. If the syscall is authorized, the enforcer code is transparent, like if nothing happened. If not, the enforcer code log this with personalized message and data are deallocated thanks to the system call code. *Tr 27 So, we see all the advantages brought by this method : parameters are already interpreted and checked, ready to be used. We have also a great tuning power. Each syscall can be treated as a particular case. The big drawback is that each syscall must be altered. There are near 200 syscalls for linux on a PC. In the case of driver controlling, we have the difficulty that there are a lot of drivers not all shipped with a vanilla kernel. And a not secured driver is enough to break the trusted state of the kernel space. *Tr 28 We are going no to see a brief description of some existing projects that add control policies into the kernel. *Tr 29 I speak about openwall, by solar designer, just because it is a well known and often used patch. It does not add any access control policy but just harden the kernel by adding a collection of security related features for the linux kernel, the most famous being the non-executable user stack.