AppArmor Detail
İçindekiler
AppArmor Design
AppArmor is designed to address the application security problem, which is to ensure that attackers cannot cause applications to do something undesirable. It sounds simple, but is actually very difficult; while you can test an application to ensure that it fulfills its functional specification, you cannot test an application to ensure that it never does anything else when fed strange inputs, because the list of possible strange inputs is usually infinite. Succinctly put by Ivan Arce, "Reliable software does what it is supposed to do. Secure software does what it is supposed to do... and nothing else."
There are compile-time tools that scan source code and/or binary files for potential vulnerabilities. However, for theoretical and practical reasons, compile-time solutions do an incomplete job and are difficult to apply. For economic and social reasons, tools and reasonable care are rarely applied. As a result, most software has many latent bugs, many of them exploitable by attackers to make software misbehave. Security would be simple if we could just get a stable supply of bug-free software, but that approach has a supply-side problem :)
AppArmor Goals
To defend against potentially mis-behaving software, we want to apply the principle of least privilege to applications. Whenever the application runs, restrict it to only access the resources it needs to get its job done. Thus if it ever is exploited, the amount of damage is minimized. However, we also need the privilege confinement to be secure, transparent, and fast.
- Secure - the application cannot escape or bypass the confinement.
- Fast - performance overhead is negligible.
- Transparent - software and users can do things the way they did before, with as few changes as possible.
Secure is vital so that the security is not a sham. Transparent and Fast are vital because the very worst security is that which is turned off because it was too troublesome or expensive to use. Here we describe how AppArmor achieves security, performance, and transparency.
Security Through LSM: Linux Security Modules Interface
To achieve security (non-bypassability) mediation methods like AppArmor need to be inside the kernel. AppArmor originally was a kernel patch, but that imposes major problems for distribution and deployment, especially for enterprise users. To achieve kernel-grade security without requiring a kernel patch, Immunix Inc. (who developed AppArmor prior to Novell's acquisition) developed a feature for Linux 2.6 called LSM: Linux Security Modules interface. LSM provides a kernel API for modules that allows loadable modules to do effective access control mediation. Other participants in LSM development included the SELinux community, IBM, and assorted other open source community developers.
Mediation at layers other than the kernel, e.g. in libraries, leads to bypassability: if an attacker can induce arbitrary code execution in a confined application (e.g. buffer overflow) then they can cause the program to call the kernel directly instead of through the library, thus bypassing the mediation. Moreover, such mediation is best done deep inside the kernel, rather than by intercepting system calls. Secure mediation can be done by replacing the syscall table (as is done in Systrace) but that leads to three significant problems:
- Code Embolism: The kernel has a lot of infrastructure to translate, say, a request to open a file named "foo" into a specific inode by considering the current working directory, etc. None of that information is available at the time a system call is intercepted, and so a mediation system has to duplicate the code to do the translation before it can make a meaningful decision about whether to grant access to "foo". Worse, this translation is now being done twice, imposing a performance penalty.
- SMP Safety: The name of the file being requested is in user-accessable memory, and thus vulnerable to change between the time the mediation module checks the access and the time the kernel grants the access. So the mediation module might see an access request for the file "foo", and then after it is approved the requestor changes the request to "../bar". This attack is especially likely on SMP machines, which will become most machines as multicore chips proliferate.
- Linus: For reasons involving the GPL, Linus has decided to remove the ability to reload the syscall table in Linux 2.6, so system call interposition is problematic on modern kernels without applying a kernel patch.
So instead of system call mediation, LSM inserts hooks deep into the kernel. Wherever a request by a user level process leads to access to an important deep kernel data structure, such as task descriptors or inodes, the LSM hook makes an up-call to whatever LSM module is loaded, asking "is this ok?" The LSM module considers the situation, makes its access control decision, and sends back a "yes" or "no" answer to the kernel, which then either performs the access or returns an error to the requestor. Thus access control technologies like SELinux and AppArmor can do their work as loadable modules, without requiring constant patching of the kernel.
Performance Through Simplicity
To achieve speed, AppArmor uses a very simple security model so that checking can be done quickly. In particular, security policies are very small, so that they can be fully loaded into kernel memory, eliminating the need for a 2-level caching architecture of policy storage.
Transparancy Through Familiarity
To achieve transparency, AppArmor uses all-classical UNIX security semantics, but applies them to programs. An AppArmor policy specifies the set of POSIX.1e capabilities that a program can have, and specifies the set of files it can access. The POSIX.1e capabilities are specified by name. The files are specified by absolute path names, including embedding shell-syntax wild cards, followed by access modes (R, W, and X, with a bit of embellishment). So an AppArmor policy to confine ntpd (the network time protocol daemon) looks like this:
/usr/sbin/ntpd { #include <abstractions/base> #include <abstractions/nameservice> #include <program-chunks/ntpd> capability ipc_lock, capability net_bind_service, capability sys_time, capability sys_chroot, capability setuid, /etc/ntp.conf r, /etc/ntp/drift* rwl, /etc/ntp/keys r, /etc/ntp/step-tickers r, /tmp/ntp* rwl, /usr/sbin/ntpd rix, /var/log/ntp w, /var/log/ntp.log w, /var/run/ntpd.pid w, /var/lib/ntp/drift rwl, /var/lib/ntp/drift.TEMP rwl, /var/lib/ntp/var/run/ntp/ntpd.pid w, /var/lib/ntp/drift/ntp.drift r, /drift/ntp.drift.TEMP rwl, /drift/ntp.drift rwl, }
From here, it would be relatively straightforward to hand-craft security policy for applications through a combination of expert knowledge and trial and error. However, that would be so tedious that the vast majority of developers and users would refuse to put in the work. To further improve transparency, AppArmor includes a "learning mode" to allow policy to be built by running the application and observing what it does. In learning mode, the rules are not actually enforced, but violations are logged, and process forking is tracked, so that the log of an application's execution builds up a characteristic description of the application's activities. AppArmor includes a log analysis program that scans the log, prompts the user with questions, and automatically creates a program profile. Learning mode and the log analyzer are also capable of incremental improvement of the profile if one already exists, but omits some necessary rules.
Application Security and System Security
An individual application can be secured using an individual profile, but how to secure a system? A vital question in securing any system is "against what threats?" We could profile all of the programs on the system, but that would be a lot of effort, and in most cases unnecessary. For instance, consider the network threat model: we want to prevent remote network attackers from gaining control of the system. To prevent such an attack, we need to ensure that all programs that communicate with the network have an AppArmor profile. If we profile all applications that connect to the network, then the AppArmor profiles completely control everything that a network attacker could do to the system.
Note: When an AppArmor profile grants permission to execute another program, it specifies whether the child executes in its own profile (denoted px), executes in the same profile as the parent inheriting the parent's profile, (denoted ix) or whether the child gets to execute unconstrained (denoted ux). The px permission should be used for major programs that broker access to data, such as Apache executing Sendmail to send some mail. The ix permission should be used for smaller, utility programs that operate on whatever data the parent has at hand, such as a shell script executing cp to copy a file. The ux permission is very dangerous, and should be used carefully to allow administrative access that is not regulated by AppArmor, such as the ultimate system administrator's shell executed from the SSH daemon.
Since the network threat model is so common a concern, AppArmor comes with a system analyzer called unconfined, which scans the machine for open network ports, finds the programs listening to those open network ports, and lists the profiles wrapped around those programs, if any. If unconfined reports that all open network ports lead to AppArmor profiles, then it is the case that these profiles fully define the worst case the attacker could impose on the machine.
Similarly, to protect a workstation against network attack, all of the programs that process network input should be profiled. Some of these programs have persistent open network ports, such as ssh clients. Some have transient open network ports, such as web browsers, mail clients, and IM clients. And some such programs have no network ports, but none the less process network input with considerable security risks, such as the OpenOffice suite, which is often asked to immediately open .doc files that are attached to incoming e-mail messages. However, one still need not profile all of the programs on the workstation, only those that process network input.
In a different situation, to protect a kiosk workstation against attacks from users, all of the programs that accept keyboard and mouse input should be profiled, as well as any other device readers such as bar codes and mag stripe readers. This "keyboard threat model" is very similar to the network threat model above, but with the threat coming from local IO devices like the keyboard and card reader, rather than the network interfaces.