SHA256
1
0
forked from pool/latex2html
latex2html/testfile.tex
Dr. Werner Fink 83c9b7c8a1 Accepting request 180837 from openSUSE:Factory:Staging:perl518
- add latex2html-fix-defined.patch from debian bug tracker to avoid
  a warning
- add apparmor's tex docu for better %check: testfile.tex

OBS-URL: https://build.opensuse.org/request/show/180837
OBS-URL: https://build.opensuse.org/package/show/Publishing/latex2html?expand=0&rev=11
2013-06-25 11:12:33 +00:00

1304 lines
50 KiB
TeX

\documentclass[a4paper]{article}
%\usepackage{graphicx}
%\usepackage{subfigure}
\usepackage[utf8]{inputenc}
\usepackage{url}
%\usepackage{times}
\usepackage[pdftex,
pdfauthor={Andreas Gruenbacher and Seth Arnold},
pdftitle={AppArmor Technical Documentation},%
\ifx\fixedpdfdate\@empty\else
pdfcreationdate={\fixedpdfdate},
pdfmoddate={\fixedpdfdate},
\fi
pdfsubject={AppArmor},
pdfkeywords={AppArmor}
]{hyperref}
\hyphenation{App-Armor}
\hyphenation{name-space}
\renewcommand{\H}{\hspace{0pt}}
\title{AppArmor Technical Documentation}
\author{Andreas Gruenbacher and Seth Arnold \\
\url{{agruen,seth.arnold}@suse.de} \\
SUSE Labs / Novell}
% don't include the (build!) date
\date{}
\begin{document}
\maketitle
\tableofcontents
\newpage
%\begin{abstract}
%\end{abstract}
\section{Introduction}
In this paper we describe AppArmor from a technical point of view,
introduce its concepts, and explain the design decisions taken. This
text is intended for people interested in understanding why AppArmor
works the way it does. You may be looking for less detailed, low-level,
or kernel centric documentation; in that case, please refer to the
AppArmor documentation web site~\cite{apparmor}.
Sections~\ref{sec:overview} and ~\ref{sec:model} discuss the AppArmor
security model, while Section~\ref{sec:walk-through} shows how to use it
from a low-level point of view. Please be aware that lots of details
are discussed here which the higher-level tools hide from the average
user.
\section{Overview}
\label{sec:overview}
AppArmor protects systems from insecure or untrusted processes by
running them in confinement, still allowing them to share files with
other parts of the system, exercising privilege, and communicating with
other processes, but with some restrictions. These restrictions are
mandatory; they are not bound to identity, group membership, or object
ownership. In particular, the restrictions also apply to processes
running with superuser privileges. AppArmor achieves this by plugging
into the Linux Security Module (LSM) framework. The protections
provided are in addition to the kernel's regular access control
mechanisms.
The AppArmor kernel module and accompanying user-space tools are
available under the GPL license. (The exception is the libapparmor
library, available under the LGPL license, which allows change\_hat(2)
to be used by non-GPL binaries.)
At the moment, AppArmor knows about two types of resources: files, and
POSIX.1e (draft) capabilities. By controlling access to these
resources, AppArmor can effectively prevent confined processes from
accessing files in unwanted ways, from executing binaries which they are
not meant to execute, and from exercising privileges such as acting on
behalf of another user (which are traditionally restricted to the
superuser).
One use case for this kind of protection is a network daemon: even if
the daemon is broken into, the additional restrictions imposed by
AppArmor will prevent the attacker from attaining additional privileges
beyond what the daemon is normally allowed to do. Because AppArmor
controls which files a process can access in which ways down to the
individual file level, the potential damage is much limited.
There is work going on for teaching AppArmor about additional resources
like ulimits, and interprocess and network communication, but at this
time, these resource types are not covered. This is less severe than it
might initially seem: in order to attack another process from a
broken-into process like a network daemon, that other process has to
actively listen. The set of actively listening processes is relatively
small, and this sort of interprocess communication is a natural security
boundary, so listening processes should be validating all their input
already. For protection against bugs in the input validation of those
processes, they should also be confined by AppArmor though, thus further
limiting the potential damage.
AppArmor protection is selective: it only confines processes for which
policies (referred to as profiles) have been defined. All other
processes will continue to run unrestricted by AppArmor.
To confine a process, all it takes is to write a profile for it, take an
existing profile, or automatically generate a profile: for the latter,
the process can be run in \textit{learning} or \textit{complain} mode in
which AppArmor allows all accesses, and logs all accesses that are not
allowed by the current profile already. This log can then be used to
automatically generate a suitable new profile, or refine an existing
one. The application does not need to be modified.
An example profile together with a complete low-level walk-through of
AppArmor can be found in Section~\ref{sec:walk-through}. The
apparmor.d(5) manual page contains further details.
AppArmor is not based on labeling or label-based access and transition
rules, so it does not stick a label on each each file in the file system
(or more generally, on each object). It identifies files by name rather
than by label, so if a process is granted read access to /etc/shadow and
the system administrator renames /etc/shadow to /etc/shadow.old and
replaces it with a copy (that may have an additional user in it, for
example), the process will have access to the new /etc/shadow, and not
to /etc/shadow.old.
\section{The AppArmor Security Model}
\label{sec:model}
When a file is accessed by name with open(2), mkdir(2), etc., the kernel
looks up the location of the object associated with the specified
pathname in the file system hierarchy. The lookup is relative to the
root directory for pathnames starting with a slash, and to the current
working directory otherwise. Different processes can have have different
working directories as well as different root directories. See
path\_resolution(2) for a detailed discussion of how pathname resolution
works.
Either way, the result of the lookup is a pair of (dentry, vfsmount)
kernel-internal objects that uniquely identify the location of the file
in the file system hierarchy. The dentry points to the object if the
object already exists, and is a placeholder for the object to be created
otherwise.
AppArmor uses the (dentry, vfsmount) pair to compute the pathname of the
file within a process's filesystem namespace. The resulting pathname
contains no relative pathname components (``.'' or ``..''), or symlinks.
AppArmor checks if the current profile contains rules that match this
pathname, and if those rules allow the requested access. Accesses
that are not explicitly allowed are denied.
\subsection{Symbolic Links}
When looking up the (dentry, vfsmount) pair of a file, the kernel
resolves symlinks where appropriate (and fails the lookup where symlink
resolution is inappropriate).
The pathname that AppArmor computes from a (dentry, vfsmount) pair never
contains symlinks. This also means that if symlinks are used instead of
directories for paths like /tmp, profiles need to be adjusted
accordingly. A future version of AppArmor may have built-in support
for this kind of pathname rewriting.
\subsection{Namespaces}
Linux allows different processes to live in separate namespaces, each of
which forms an independent file system hierarchy. A recent paper by Al
Viro and Ram Pai~\cite{ols06-pai} discusses all the intricate things
possible with namespaces in recent 2.6 kernels.
From the point of view of a process, an absolute path is a path that
goes all the way up to the root directory of that process. This is
ambiguous if processes have different root directories. Therefore,
instead of paths relative to process root directories, AppArmor uses
paths relative to the namespace root.
Pathnames are meaningful only within a namespace. Each namespace has a
root where all the files, directories, and mount points are hanging off
from.
The privilege of creating new namespaces is bound to the
CAP\_{\H}SYS\_{\H}ADMIN capability, which grants a multitude of other
things that would allow a process to break out of AppArmor confinement,
so confined processes are not supposed to have this privilege, and
processes with this capability need to be considered trusted.
In this setup, privileged processes can still create separate namespaces
and start processes in those namespaces; processes confinement will be
relative to whatever namespace a process ends up in. It is unclear
at this point how AppArmor should support separate namespaces --- either
by computing all pathnames relative to one particular namespace
considered global (assuming that such a globally meaningful namespace
will exist in all setups in which AppArmor is relevant), or by allowing
different sets of profiles to be associated with different namespaces.
\subsection{Disconnected Files and Pseudo File Systems}
In some situations, a process can end up with a file descriptor or
working directory that was looked up by name at some point, but is not
connected to the process's namespace anymore (and hasn't been deleted,
either). This can happen when file descriptors are passed between
processes that do not share the same namespace, or when a file system
has been lazily unmounted (see the MNT\_DETACH flag of umount2(2)). Such
files may still be visible to other processes, and they may become
reconnected. AppArmor cannot compute the pathnames of such files.
Granting unrestricted access would be insecure, and so AppArmor denies
access to disconnected files.
As a special case, the kernel supports a number of file systems that
users can have file descriptors open for, but that can never be mounted.
Those files are by definition disconnected. Anonymous pipes, futexes,
inotify, and epoll are all examples of that. Accesses to those files
is always allowed.
Future versions of AppArmor will have better control over disconnected
files by controlling file descriptor passing between processes.
\subsection{Mount}
Mounting can change a process's namespace in almost arbitrary ways.
This is a problem because AppArmor's file access control is pathname
based, and granting a process the right to arbitrarily change its
namespace would subvert this protection mechanism. AppArmor therefore
denies confined processes access to the mount(2), umount(2), and
umount2(2) system calls.
Future versions of AppArmor may offer fine-grained control over mount,
and may grant confined processes specific mount operations.
\subsection{The Kernel NFS Daemon}
The security model of the various versions of NFS is that files are
looked up by name as usual, but after that lookup, each file is only
identified by a file handle in successive acesses. The file handle at a
minimum includes some sort of filesystem identifier and the file's inode
number. In Linux, the file handles used by most filesystems also
include the inode number of the parent directory; this may change in the
future. File handles are persistent across server restarts.
This means that when the NFS daemon is presented with a file handle,
clients must get access without having specified a pathname. A pathname
can be computed from a (parent, child) inode pair that identifies the
file down to the directory level if the dentry is properly connected to
the dcache, but multiple hardlinks to the same file within the same
directory cannot be distinguished, and properly connecting dentries
comes at a cost in the NFS daemon. Because of this overhead and the
questionable benefit, most setups do not guarantee that dentries will be
connected, and so pathnames cannot always be computed. (See the
no\_subtree\_check option in exports(5).)
In addition, the NFS daemon is implemented in the kernel rather than as
a user space process. There is no memory separation or other protection
between the daemon and the rest of the kernel. This means that at best,
the NFS daemon could cooperate with an additional access control
mechanism like AppArmor --- but there would be no enforcement.
Because of all of this, it makes little sense to put the kernel NFS
daemon under AppArmor control. Administrators are advised to not assign
profiles to the kernel nfsd daemons.
\subsection{Why are the computed pathnames meaningful?}
Whenever a process performs a name-based file access, the pathname or
pathname component always refers to a specific path to that file: the
path is either relative to the chroot if an absolute path is used, or
else relative to the current working directory. The chroot or current
working directory always has a unique pathname up to the namespace root
(even if the process itself has no direct access above the chroot).
This means that each name-based file access maps to a unique, canonical,
absolute pathname. There may be additional paths pointing to the same
file, but a particular name-based access still always refers to only one
of them. These are the pathnames that AppArmor uses for permission
checks.
If directories along the path get renamed after a process changes into
them (either with chroot(2) or with chdir(2)), the resulting pathname
will differ from the pathnames that the process used. Consider the
following sequence of operations for example:
\begin{tabbing}
\begin{tabular}{ll}
\textbf{Process 1} & \textbf{Process 2} \\
chdir("/var/tmp/foo"); & \\
& rename("/var/tmp/foo", "/var/tmp/bar"); \\
creat("baz", 0666); & \\
\end{tabular}
\end{tabbing}
The creat operation will check against the path
/var/tmp/\textit{bar}/baz, even though Process~1 never used
\textit{bar.} This is the expected behavior; we are interested in the
names of the objects along the path at the time of the access, not in
their previous names.
As already mentioned, a path lookup results in a pair of (dentry,
vfsmount) kernel-internal objects. The pathname that AppArmor checks
against is computed from these two objects after these objects have been
looked up. The lookup and the pathname computation are not atomic, which
means that pathname components could even be renamed after the lookup
but before the pathname has been computed.
It matters that the AppArmor access check is performed between the lookup and
the actual access, but atomicity between the lookup and that access
check is not necessary: there is no difference between a rename before
the lookup and a rename after the lookup from AppArmor's point of view;
all we care about is the current pathname at some point between the
lookup and the access.
A special case occurs when the lookup succeeds, but the file is deleted
before the AppArmor access check. In this case the access is denied and
errno is set to ENOENT, the same behavior as if the lookup had failed.
\subsection{Path Permission Checking}
On UNIX systems, when files are looked up by name, the lookup starts
either at the root or the current working directory of a process. From
there, each directory reached is checked for search permission (x). The
permissions on the directories leading to the current working directory
are not checked. When a file is being created or deleted, the parent
directory of that file is checked for write and search access (wx).
When a file is being accessed, the permissions of that file are checked
for r, w, or x access, or a combination thereof. Each check can result
in a failure with errno set to EACCES (Permission denied).
In contrast, AppArmor first computes the pathname to a file. If a file
is being created, the name being looked up is the name of the new file
and not the name of the parent directory.
If the file being looked up is a directory, AppArmor appends a slash to
the pathname so that directory pathnames always end in a slash;
otherwise the pathname will not end in a slash.
It then checks for file access rules in the process's profile that match
that pathname, and decides based on that. With some exceptions for
execute modes as described in Section~\ref{sec:merging}, the permissions
granted are the union of permissions of all matching rules.
\subsection{Profile Permissions}
\label{sec:permissions}
AppArmor differentiates between slightly more permissions than UNIX
does, as shown in Table~\ref{tab:permissions}: file access rules in
AppArmor support the read (r), write (w), execute (x), memory map as
executable (m), and link (l) permissions. The execute permission
requires a modifier that further specifies which kind of execution is
being granted: inherit the current profile (ix), use the profile defined
for that executable (px), or execute unconfined without a profile (ux).
In addition, the px and ux permissions have Px and Ux forms that will
trigger Secure Execution (see Section~\ref{sec:secure-exec} below).
The different permissions are used as follows:
\begin{table}[tb]
\center
\begin{tabular}{|l|l|}
\hline
r & Read. \\
w & Write. \\
ix & Execute and inherit the current profile. \\
px & Execute under a specific profile. \\
Px & Execute secure and under a specific profile. \\
ux & Execute unconfined. \\
Ux & Execute secure and unconfined. \\
m & Memory map as executable. \\
l & Link. \\
\hline
\end{tabular}
\caption{File Access Permissions in Profiles}
\label{tab:permissions}
\end{table}
\begin{description}
\item[Read.]
The profile read permission is required by all system calls that
require the UNIX read permission. This includes open with
O\_RDONLY, getdents (i.e., readdir), listxattr, getxattr, and
mmap with PROT\_READ.
\item[Write.]
The profile write permission is required by all system calls
that require the UNIX write permission, except for operations
that create or remove files: while UNIX requires write access to
the parent directory, AppArmor requires write access on the new
file in this case (which does not exist at the time of the
permission check for file creates). Operations that create
files include open with O\_CREAT, creat, mkdir, symlink, and
mknod. Operations that remove files include rename, unlink and
rmdir.
Operations that require write access in UNIX as well as AppArmor
include open with O\_WRONLY (O\_RDWR requires read and write),
setxattr, removexattr, and mmap with PROT\_WRITE.
Other system calls such as chmod, chown, utime, and utimes are
bound to file ownership or the respective capabilities in UNIX.
AppArmor also requires profile write access for those operations.
\item[Execute.]
As mentioned above, AppArmor distinguishes a few different ways
how files may be executed as described above.
For directories, the UNIX execute permission maps to search
access. AppArmor does not control directory search access.
Traversing directories is always granted.
\item[Memory map as executable.]
The Linux kernel only requires read access to files in order
to memory map them for execution with the PROT\_EXEC flag.
AppArmor makes a distinction here, and requires the m profile
permission in order for files to be mapped as executable. That way,
it is more obvious in profiles what applications are allowed to do even
if from a security point of view, the m permission provides a similar
level of protection as the ix permission --- execute under the current
profile.
\item[Link.]
Creating a hardlink requires the profile link permission (l) on the new
path. In addition, the new path must have a subset of the r, w, x, and
m permissions of the old path, and if the new path has the x permission,
the execute flags (i, u, U, p, and P) of the old and the new path must
be equal.
\item[Rename.]
A rename requires profile read and write access for the source
file, and profile write access for the target file.
\item[Stat.]
Retrieving information about files is always allowed. We believe
that providing policy for file information retrieval is more
troublesome than the benefit it would provide.
\end{description}
\subsection{System Calls Taking File Handles, At System Calls}
A number of system calls take file descriptors instead of pathnames as
their parameters (ftruncate, fchmod, etc.), or take directory file
descriptors, and resolve pathnames relative to those directories
(openat, mkdirat, etc.). These system calls are treated like their
non-f and non-at equivalents, and the same access checks are performed.
At the point where AppArmor is asked to validate those file accesses, it
is passed a (dentry, vfsmount) pair no matter which system call variant
is used.
\subsection{File Descriptor Passing and Revalidation}
After a file descriptor has been obtained, the permitted accesses (read
and/or write) are encoded in the file descriptor, and reads and writes
are not revalidated against the profile for each access. This is
consistent with how access checks are done in UNIX; such access checks
would have a severe performance impact.
The picture changes when a file descriptor is passed between processes
and the other process is running under a different profile, or when a
process switches profiles: in that case, read and write accesses are
revalidated under the new profile. If the new profile does not allow
them, the access is denied and errno is set to EACCES (Permission
denied).
File descriptors opened by unconfined processes are exempt from this
rule. This is so that processes will still have access to their stdin,
stdout, and stderr without having to list all possible sources of input
and output in all profiles.
\subsection{Deleted Files}
Revalidation is problematic for deleted files for which a process still
has an open file descriptor --- after all, the idea of the pathname of a
deleted file is somewhat peculiar: the file is no longer reachable by
any pathname, and it also cannot become re-attached to the filesystem
namespace again.
The traditional UNIX behavior is to determine access upon file access,
and to never check again. Applications depend on this, particularly for
temporary files. In addition to temporary files, deleted files can be
used as an interprocess communication mechanism if the file descriptor
is shared among multiple processes.
AppArmor grants access to deleted files, just like it grants access to
files opened by unconfined processes. It may control interprocess
communication, including file descriptor passing, in a future version.
\subsection{The access System Call}
This system call determines whether a process has a given mode of access
to a file in terms of the read, write, and execute permissions. This is
not a sufficient replacement for performing the access check at the time
of access even under traditional UNIX, because the access system call
and the subsequent access are not atomic, and the permissions might
change between the two operations. Applications are not supposed to rely
on access(2).
AppArmor introduces additional restrictions, some of which cannot be
modeled in terms of read, write, and execute: for example, an AppArmor
profile may allow a process to create files /tmp/foo-*, but not any
other files in /tmp.
There is no way to express this with access(2); in traditional UNIX, all
that is required for creating files is write access to the parent
directory. Access(2) will indicate that some accesses are allowed even
when AppArmor will eventually deny them.
\subsection{The ptrace System Call}
The ability to ptrace allows a process to look up information about
another process, read and write the memory of that process, and attach
to (or trace) that process in order to debug it, or analyze its
behavior. This gives total control over the process being traced, and
so the kernel employs some restrictions over which processes may ptrace
with other processes.
In addition to these restrictions, AppArmor requires that if the tracing
task is confined, it must either have the CAP\_{\H}SYS\_{\H}PTRACE capability,
or be confined by the same profile and sub-profile as the process being
traced. Attempts to switch to another profile or sub-profile by a
process being traced is denied.
\subsection{Secure Execution}
\label{sec:secure-exec}
In this mode, the kernel passes a flag to user space. When glibc finds
this flag set, it unsets environment variables that are considered
dangerous, and it prevents the dynamic loader from loading libraries
controlled by the environment. With non-secure exec, the
LD\_LIBRARY\_PATH environment variable can be used to switch to a
different set of libraries, for example. The secure exec mechanism is
not specific to AppArmor: set-user-id and set-group-id executables also
use it, as well as SELinux, which introduced this glibc feature.
\subsection{Exec Mode Merging in Profiles, Exact Matches}
\label{sec:merging}
When more than one rule in a profile matches a given path, all the
permissions accumulate except for ix, px, Px, ux, and Ux: those
permissions would conflict with each other; it would be unclear how to
execute the new binary if more than one of these flags was set. To deal
with this situation, AppArmor differentiates between rules that define
exact matches and wildcard rules (see Table~\ref{tab:globbing} on
page~\pageref{tab:globbing}). Execute flags in exact matches override
execute flags in wildcard matches.
If the execute flags of multiple rules still disagree, the profile is
rejected at profile load time.
\subsection{Capabilities}
AppArmor uses the standard Linux capability mechanism. When the kernel
checks if a certain capability can be exercised, AppArmor additionally
checks if the current profile allows the requested capability, and
rejects the use of the capability otherwise.
\subsection{The sysctl System Call and /proc/sys}
The sysctl system call and files below /proc/sys
can be used to read and modify various kernel parameters. Root processes
can easily bring the system down by setting kernel parameters to invalid
values. To prevent against that, AppArmor denies confined processes
that do not have the CAP\_{\H}SYS\_{\H}ADMIN capability write access to kernel
parameters.
\subsection{Subprofiles aka. Hats}
Profiles can contain subprofiles that processes may switch to from the
main profile. Switching from a subprofile into a sibling subprofile or
back to the parent profile is allowed depending on how the subprofile
was entered, and provided that the child knows a magic cookie.\footnote{
\textbf{A word of warning about change\_hat(2):} When used with a
non-zero magic cookie for changing into a subprofile, that magic
cookie can be used to change back out of the subprofile; in this
mode, change\_hat(2) is not a strong confinement mechanism. If the
code running in the subprofile can guess the magic cookie, it can
break out of the subprofile. Likewise, if that code can manipulate
the processes' behavior beyond the point where the process returns
from the subprofile, it can influence what is done under the parent
profile. Therefore, change\_hat(2) with a non-zero magic cookie is
only safe in combination with restricted code environments, such as
when the subprofile is used for executing Safe Perl (see Safe(3pm)),
etc.
} See the change\_hat(2) manual page for details.
Each process may consist of multiple tasks. Each task may only change
its own subprofile. The superuser cannot put a task into a different
hat, but he can replace the entire profile and its subprofiles, or he
can put a process in a different top-level profile (see
Section~\ref{sec:association}).
Internally, change\_hat(2) is implemented by writing to a special
kernel-provided file. This is equivalent to a command like:
\begin{small}
\begin{verbatim}
$ echo "changehat 123^hat_name" > /proc/$PID/attr/current
\end{verbatim}
\end{small}
Here, the number is the magic cookie value, and hat\_name obviously is
the name of the hat; either may be replaced by the empty string (but not
both).
\subsection{Association of Profiles with Processes}
\label{sec:association}
Profiles are associated with kernel tasks, which roughly correspond to
threads in user space (see clone(2) for details). Currently there are
two ways how a profile can be associated with a task: when an executable
is started and a profile is defined for that executable, or when the
administrator assigns a profile to a task explicitly.
In addition to that, once a task is confined by a profile, that profile
determines which other executables may be executed, and under which
profile they may run (under the profile defined for that executable, the
same profile as the current task, or unconfined; see
Section~\ref{sec:permissions}).
A process will consist of a single task after an exec, so in the exec
case, the entire process will be confined. New tasks (threads as well
as processes) inherit the same profile and subprofile as their parent
task.
Unconfined processes with the CAP\_{\H}SYS\_{\H}ADMIN privilege may assign a
profile to a task with a command like this:
\begin{small}
\begin{verbatim}
$ echo "setprofile /name/of/profile" > \
/proc/$PID/attr/current
\end{verbatim}
\end{small}
After that, the task will be in the new top-level profile, even if the
process was in a subprofile before.
Processes with the CAP\_{\H}SYS\_{\H}ADMIN privilege as well as the process itself
can query the profile a process is in by reading from that file:
\begin{small}
\begin{verbatim}
$ cat /proc/$PID/attr/current
unconfined
$ cat /proc/$PID/attr/current
/name/of/profile (complain)
$ cat /proc/$PID/attr/current
/name/of/profile^hat_name (enforce)
\end{verbatim}
\end{small}
The output includes the name of the profile and subprofile as well as
the mode the active profile is in. (When a task is in a subprofile,
the subprofile is the active profile.)
\subsection{Profile Loading, Replacement, and Removal}
Before the kernel can use any profiles, they must be loaded. The
profile sources consist of plain text. This text representation is
converted into in a binary representation that the kernel can more
easily deal with by the user-space profile loader.
Profiles contain potentially long lists of file access rules that may
include wildcards. In order to make the lookup efficient, the AppArmor
kernel module does not actually go through all the file access rules
when checking for access. Instead, the profile loader takes those rules
and compiles them into transition tables. Pathnames are then looked up
in those tables with a simple and efficient algorithm, the theory behind
which is explained in the Lexical Analysis section of the Dragon
Book~\cite{dragon86}.
An init script loads all the known profiles into the kernel at an early
boot stage. This happens automatically and the system administrator
tools will take care of loading, reloading, or removing profiles after
they manipulate them, so end users will not usually notice this step.
Profiles can be replaced at any time during runtime, and all processes
running under old profiles will transparently be switched to the updated
versions. Profiles can also be removed. All processes running under a
profile that is removed will become unconfined.
Profiles are always replaced together with all their subprofiles. It
may be that an updated profile no longer contains a specific subprofile.
If that happens while processes are using that subprofile, those
processes will be put in a profile that denies all accesses. Such
processes may still change to sibling subprofiles or back to the parent
profile subject to the change\_hat(2) semantics.
\section{AppArmor Walk-Through}
\label{sec:walk-through}
AppArmor consists of a set of kernel patches and accompanying
user-space tools, both of which are available at
\url{http://developer.novell.com/wiki/index.php/Apparmor}.
\subsection{Kernel Patches and Configuration}
The AppArmor kernel patches are provided in a format convenient for use
with quilt,\footnote{
\url{http://savannah.nongnu.org/projects/quilt}
} however, other tools for applying the patches can be used, too. The
patches are supposed to apply against recent kernel.org git kernels. A
copy of the current git tree can be obtained from
\url{git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git}
with \textit{git clone} (see the \hbox{git-clone(1)} manual page). In
case the the differences between the latest git tree and the tree the
AppArmor patches are based on is too big, the patches won't apply
cleanly. In this case, trying an older git tree may work better.
After obtaining the AppArmor patches tarball and the git tree which will
end up in the linux-2.6 directory by default, the AppArmor patches can
be applied to the git tree as follows:
\begin{small}
\begin{verbatim}
$ tar zxvf apparmor.tar.gz
$ cd linux-2.6/
$ ln -s ../apparmor patches
$ quilt push -a
\end{verbatim}
\end{small}
When configuring the kernel, make sure that AppArmor is built in or as a
module (CONFIG\_{\H}SECURITY\_{\H}APPARMOR must be 'y' or 'm'). AppArmor
cannot be used together with other Linux Security Modules, so if
CONFIG\_{\H}SECURITY\_{\H}CAPABILITIES or CONFIG\_{\H}SECURITY\_{\H}SELINUX is
set to 'y', they must be disabled by adding \texttt{selinux=0} and/or
\texttt{capability.disable=1} to the kernel command line (grub, lilo,
yaboot, etc.). It is not sufficient to put SELinux into permissive
mode --- at this time, AppArmor cannot be combined with other LSMs.
\subsection{The securityfs file system}
AppArmor uses securityfs for configuration and to report information.
The usual mountpoint for securityfs is /sys/{\H}kernel/{\H}security.
Unless your distribution automatically does so, you can mount securityfs
with:
\begin{small}
\begin{verbatim}
$ mount securityfs -t securityfs /sys/kernel/security
\end{verbatim}
\end{small}
Once securityfs has been mounted and the apparmor module loaded,
/sys/{\H}kernel/{\H}security/{\H}apparmor/{\H}profiles will show the
profiles loaded into the kernel, as well as mark if the profiles are in
enforcement mode or in learning mode:
\begin{small}
\begin{verbatim}
$ cat /sys/kernel/security/apparmor/profiles
/usr/bin/opera (complain)
/usr/lib/firefox/firefox.sh (complain)
/sbin/lspci (enforce)
...
\end{verbatim}
\end{small}
Profile loading, replacement, and unloading, as well as configuration of
AppArmor is also done via securityfs.
%/sys/kernel/security/apparmor/control/ control seldom-used features of AppArmor:
% audit if '1', audit all actions by confined processes
% complain if '1', allow all actions by confined processes, report
% accesses not granted by policy
% debug if '1', emit copius debugging
% logsyscall if '1', use audit framework's syscall debugging, if audit
% has been instructed to create per-task contexts.[2]
\subsection{Profile Loading}
Profile loading, replacement, and removal is performed by the
apparmor\_parser utility from the apparmor-parser package. The
package can easily be built by running make in the package's top-level
directory. Once that is done and the AppArmor module loaded, you may
use the parser to load profiles with:
\begin{small}
\begin{verbatim}
$ echo "/tmp/ls { /tmp/ls rm, }" | apparmor_parser
\end{verbatim}
\end{small}
Once a profile for a program has been loaded into the kernel, you must
use the --replace option for replacing the existing profile with a new
one (this option may be used even if no profile by that name exists):
\begin{small}
\begin{verbatim}
$ echo "/tmp/ls { /tmp/ls rm, }" | apparmor_parser --replace
\end{verbatim}
\end{small}
\subsection{Anatomy of a Profile}
AppArmor profiles use a simple declaritive language, fully described in
the apparmor.d(5) manual page. By convention, profiles are stored in
/etc/{\H}apparmor.d/. The AppArmor parser supports a simple cpp-style
include mechanism to allow sharing pieces of policy. A simple profile
looks like this:
\begin{small}
\begin{verbatim}
/bin/ls flags=(complain) {
/bin/ls rm,
/lib/ld-2.5.so rmix,
/etc/ld.so.cache rm,
/lib/lib*.so* rm,
/dev/pts/* w,
/proc/meminfo r,
/var/run/nscd/socket w,
/var/run/nscd/passwd r,
/var/run/nscd/group r,
/tmp/ r,
}
\end{verbatim}
\end{small}
Here, the first /bin/ls is the name of the profile. This profile will be
automatically used whenever an unconfined process executes /bin/ls. The
flags instruct AppArmor to put the profile in complain (aka. learning)
mode: in this mode, all operations are allowed, and any events that
would have been denied are logged. This helps users to incrementally
deploy AppArmor in production environments. The default if no flags are
specified is enforcement mode, in which all operations not allowed by
the profile are logged and denied.
Complain mode can be enabled individually for profiles as shown above
(followed by reloading the profile), or by globally putting all profiles
in complain mode with:
\begin{small}
\begin{verbatim}
$ echo 1 > /sys/kernel/security/apparmor/control/complain
\end{verbatim}
\end{small}
The user-space tools also include two small utilities, enforce and
complain, which will put profiles into enforce or complain mode:
\begin{small}
\begin{verbatim}
$ enforce firefox
Setting /usr/lib/firefox/firefox.sh to enforce mode.
\end{verbatim}
\end{small}
Inside the body of the profile are any number of rules consisting of a
pathname expression that may include globbing, and a set of permissions.
Table~\ref{tab:globbing} shows the supported shell-inspired globbing
constructs; Section~\ref{sec:permissions} on
page~\pageref{sec:permissions} describes the permissions.
\begin{table}[tb]
\center
\begin{tabular}{|l|l|}
\hline
{?} & Any single character except ``/''. \\
{*} & Any number of characters except ``/''. \\
{**} & Any number of characters including ``/''. \\
{[ab]} & One of ``a'' or ``b''. \\
{[a-c]} & One of ``a'', ``b'', or ``c''. \\
\{ab,cd\} & Alternation: either ``ab'' or ``cd''. \\
\hline
\end{tabular}
\caption{Globbing in File Access Rules. Alternation counts as an exact
match in file access rules; all others count as wildcards (see
Section~\ref{sec:merging}).}
\label{tab:globbing}
\end{table}
When AppArmor looks up a directory the pathname being looked up will end
with a slash (e.g., /var/tmp/), otherwise it will not. Only rules
that match that trailing slash will match directories. Some examples,
none matching the /tmp directory itself, are:
\begin{tabbing}
\begin{tabular}{ll}
{/tmp/*} & Files directly in /tmp. \\
{/tmp/*/} & Directories directly in /tmp. \\
{/tmp/**} & Files and directories anywhere underneath /tmp. \\
{/tmp/**/} & Directories anywhere underneath /tmp. \\
\end{tabular}
\end{tabbing}
As explained in Section~\ref{sec:model}, AppArmor does not require
execute access to allow directory traversal, or write access on a
directory to create or rename files inside the directory. Instead, write
access is required on the specific files that a confined process
attempts to create, remove, rename, etc. Read access is required for
reading the contents of a directory.
AppArmor also mediates the use of POSIX 1003.1e draft capabilities;
capabilities that a process is allowed to use are listed in the
profile by their name in lower-case (with ``CAP\_'' stripped off), e.g.,
\begin{small}
\begin{verbatim}
#include <tunables/global>
/sbin/lspci {
#include <abstractions/base>
#include <abstractions/consoles>
capability sys_admin,
/sbin/lspci mr,
/sys/bus/pci/ r,
/sys/bus/pci/devices/ r,
/sys/devices/** r,
/usr/share/pci.ids r,
}
\end{verbatim}
\end{small}
This profile uses predefined include files which are part of the
apparmor-profiles package.
\subsection{Logging}
AppArmor uses the kernel standard audit facility for reporting. When a
profile is in complain mode, the log messages look like this:
\begin{small}
\begin{verbatim}
type=APPARMOR msg=audit(1174506429.573:1789): PERMITTING r access
to /home/sarnold/ (ls(16504) profile /tmp/ls active /tmp/ls)
\end{verbatim}
\end{small}
When a profile is in enforcement mode, the log messages look like this:
\begin{small}
\begin{verbatim}
type=APPARMOR msg=audit(1174508205.298:1791): REJECTING r access
to /bin/ (ls(16552) profile /tmp/ls active /tmp/ls)
\end{verbatim}
\end{small}
These log messages are sent to the kernel auditing facility; if auditd
is not running, the kernel will forward these messages to printk for
collection by klogd. Auditd must be configured with --with-apparmor to
enable the \#defines to handle AppArmor's message type correctly.
AppArmor also logs some important events in the process lifecycle,
such as when processes in learning mode fork and change domain via
exec. These other events, while not strictly related to permissions
requested by the process, help the genprof profile generation tool
reconstruct when specific accesses are required by processes --- this
allows the tool to make more relevant and meaningful policy suggestions.
\subsection{Generating Profiles By Hand}
While the majority of our users are expected to generate profiles with
the help of our profile tools, it is possible to write policy by hand.
This final section gives a very quick walkthrough generating a simple
profile for firefox.
Since the kernel resolves symlinks to their ``final destinations'' before
presenting AppArmor with policy questions, we first must see if
/usr/{\H}bin/{\H}firefox is a symlink or the shell script that starts firefox;
on our system, it is a symlink:
\begin{small}
\begin{verbatim}
$ ls -l /usr/bin/firefox
lrwxrwxrwx 1 root root 25 Mar 21 13:36 /usr/bin/firefox ->
../lib/firefox/firefox.sh
\end{verbatim}
\end{small}
So we will start a profile for /usr/{\H}lib/{\H}firefox/{\H}firefox.sh. This shell
script will execute firefox-bin, as we will see later; when it does so,
we will tell AppArmor to inherit this profile. Thus, firefox-bin will
be executing under the profile for /usr/{\H}lib/{\H}firefox/{\H}firefox.sh.
To get started, we can make some assumptions about the privileges that
firefox will need (both as a shell script and as a fairly complex GUI
application):
\begin{small}
\begin{verbatim}
$ cat /etc/apparmor.d/usr.lib.firefox.firefox.sh
/usr/lib/firefox/firefox.sh flags=(complain) {
/usr/lib/firefox/firefox.sh r,
/bin/bash rmix,
/lib/ld-2.5.so rmix,
/etc/ld.so.cache rm,
/lib/lib*.so* rm,
/usr/lib/lib*.so* rm,
}
$ apparmor_parser --reload < \
/etc/apparmor.d/usr.lib.firefox.firefox.sh
Replacement succeeded for "/usr/lib/firefox/firefox.sh".
\end{verbatim}
\end{small}
The easiest way to see what accesses AppArmor allows, start a tail -F
/var/{\H}log/{\H}audit/{\H}audit.log (or /var/{\H}log/{\H}messages, or wherever your audit
messages are being sent). In another terminal, start firefox. tail will
show a few hundred PERMITTING audit events like these:
\begin{small}
\begin{verbatim}
type=APPARMOR msg=audit(1174512269.026:1804): PERMITTING rw access
to /dev/tty (firefox(16950) profile /usr/lib/firefox/firefox.sh
active /usr/lib/firefox/firefox.sh)
type=APPARMOR msg=audit(1174512269.026:1805): PERMITTING r access
to /usr/share/locale/locale.alias (firefox(16950) profile
/usr/lib/firefox/firefox.sh active /usr/lib/firefox/firefox.sh)
type=APPARMOR msg=audit(1174512269.026:1806): PERMITTING r access
to /usr/lib/locale/en_US.utf8/LC_IDENTIFICATION (firefox(16950)
profile /usr/lib/firefox/firefox.sh active
/usr/lib/firefox/firefox.sh)
\end{verbatim}
\end{small}
Because we want this profile to be fairly simple we'll be fairly
permissive, add a few more rules to the profile and reload:
\begin{small}
\begin{verbatim}
/dev/tty rw,
/usr/share/locale/** r,
/usr/lib/locale/** r,
\end{verbatim}
\end{small}
Now re-run firefox. There is no need to handle all log entries at once.
In complain mode, AppArmor will only report accesses that are not in the
profile. This makes it fairly easy to add a few rules and re-run the
application to determine what privileges are still necessary. We get a
few more messages:
\begin{small}
\begin{verbatim}
type=APPARMOR msg=audit(1174512791.236:5356): PERMITTING r access
to /usr/lib/gconv/gconv-modules.cache (firefox(17031) profile
/usr/lib/firefox/firefox.sh active /usr/lib/firefox/firefox.sh)
type=APPARMOR msg=audit(1174512791.236:5357): PERMITTING r access
to /proc/meminfo (firefox(17031) profile
/usr/lib/firefox/firefox.sh active /usr/lib/firefox/firefox.sh)
type=APPARMOR msg=audit(1174512791.240:5358): PERMITTING x access
to /bin/basename (firefox(17032) profile
/usr/lib/firefox/firefox.sh active /usr/lib/firefox/firefox.sh)
type=APPARMOR msg=audit(1174512791.240:5359): LOGPROF-HINT
changing_profile pid=17032
type=APPARMOR msg=audit(1174512791.240:5360): PERMITTING r access
to /bin/basename (firefox(17032) profile
null-complain-profile active null-complain-profile)
...
type=APPARMOR msg=audit(1174512791.240:5364): PERMITTING mr access
to /bin/basename (basename(17032) profile
null-complain-profile active null-complain-profile)
\end{verbatim}
\end{small}
So now, we add a few more rules:
\begin{small}
\begin{verbatim}
/usr/lib/gconv/** r,
/proc/meminfo r,
/bin/basename rmix,
\end{verbatim}
\end{small}
We selected ``rmix'' for /bin/basename --- most small shell utilities
should not have a profile for themselves. There's nothing wrong with
giving basename a profile, but the value of such a profile would be very
limited. Giving other shell utilities their own profiles would be worse:
the profile would need read access to the whole filesystem for shell
scripts to function reliably. In our case, basename simply inherits
privileges from another profile, then it has no more and no fewer
privileges than the calling program --- which is often a fine tradeoff.
The loader will need r and m access to execute basename, and we use ix
to execute basename in the same profile. The kernel logs only reported
r, m and x access; we have to choose the execute mode ourselves. Again,
the standard user tools would prompt users for this decision and give
consequences of decisions.
We continue in this fashion, iteratively adding and changing rules as
needed by the logs. Some of the logs report attribute modifications,
such as:
\begin{small}
\begin{verbatim}
type=APPARMOR msg=audit(1174519157.851:10357): PERMITTING
attribute (mode,ctime,) change to
/home/sarnold/.gnome2_private/ (firefox-bin(17338) profile
/usr/lib/firefox/firefox.sh active /usr/lib/firefox/firefox.sh)
\end{verbatim}
\end{small}
These need to be represented in the profile with simple w access.
\begin{small}
\begin{verbatim}
/home/*/.gnome2_private/ w,
\end{verbatim}
\end{small}
After nine iterations, the profile looks like this --- we have inserted
blank lines between each iteration:
\begin{small}
\begin{verbatim}
/usr/lib/firefox/firefox.sh flags=(complain) {
/usr/lib/firefox/firefox.sh r,
/bin/bash rmix,
/lib/ld-2.5.so rmix,
/etc/ld.so.cache rm,
/lib/lib*.so* rm,
/usr/lib/lib*.so* rm,
/dev/tty rw,
/usr/share/locale/** r,
/usr/lib/locale/** r,
/usr/lib/gconv/** r,
/proc/meminfo r,
/bin/basename rmix,
/usr/bin/file rmix,
/etc/magic r,
/usr/share/misc/magic.mgc r,
/bin/gawk rmix,
/usr/lib/firefox/firefox-bin rmix,
/usr/lib/firefox/lib*so rm,
/opt/gnome/lib/lib*so* rm,
/usr/share/X11/locale/* r,
/var/run/nscd/socket w,
/var/run/nscd/passwd r,
/usr/share/X11/locale/** r,
/home/*/.Xauthority r,
/usr/lib/gconv/*so m,
/home/*/.mozilla/** rw,
/etc/resolv.conf r,
/usr/lib/firefox/**.so rm,
/usr/lib/firefox/** r,
/etc/opt/gnome/** r,
/var/run/dbus/system_bus_socket w,
/etc/localtime r,
/opt/gnome/lib/**.so rm,
/var/cache/libx11/compose/* r,
/tmp/orbit-*/ w,
/dev/urandom r,
/tmp/ r,
/dev/null rw,
/opt/gnome/lib/GConf/2/gconfd-2 rmix,
/dev/log w,
/tmp/orbit-*/* w,
/tmp/gconfd-*/ r,
/tmp/gconfd-*/** rwl,
/home/*/.gconf/ r,
/home/*/.gconf/* rw,
/etc/fonts/** r,
/var/cache/fontconfig/* r,
/home/*/.fontconfig/** r,
/usr/share/ghostscript/fonts/** r,
/etc/passwd r,
/var/tmp/ r,
/bin/netstat rmix,
/home/*/.gnome2_private/ w,
/home/*/.gconfd/* rw,
/proc/net/ r,
/proc/net/* r,
/usr/share/fonts/** r,
/usr/lib/browser-plugins/ r,
/usr/lib/browser-plugins/** rm,
}
\end{verbatim}
\end{small}
Sorting the entries in the profile can help show areas that can be
collapsed with even more generic rules. After doing that and making a
few rules slightly more generic, we end up with:
\begin{small}
\begin{verbatim}
/usr/lib/firefox/firefox.sh {
/bin/basename rmix,
/bin/bash rmix,
/bin/gawk rmix,
/bin/netstat rmix,
/dev/log w,
/dev/null rw,
/dev/tty rw,
/dev/urandom r,
/etc/fonts/** r,
/etc/ld.so.cache rm,
/etc/localtime r,
/etc/magic r,
/etc/opt/gnome/** r,
/etc/passwd r,
/etc/resolv.conf r,
/home/*/.fontconfig/** r,
/home/*/.gconfd/* rw,
/home/*/.gconf/ r,
/home/*/.gconf/* rw,
/home/*/.gnome2_private/ w,
/home/*/.mozilla/** rw,
/home/*/.Xauthority r,
/lib/ld-2.5.so rmix,
/lib/lib*.so* rm,
/opt/gnome/lib/GConf/2/gconfd-2 rmix,
/opt/gnome/lib/**.so* rm,
/proc/meminfo r,
/proc/net/ r,
/proc/net/* r,
/tmp/gconfd-*/ r,
/tmp/gconfd-*/** rwl,
/tmp/orbit-*/ w,
/tmp/orbit-*/* w,
/tmp/ r,
/usr/bin/file rmix,
/usr/lib/browser-plugins/ r,
/usr/lib/browser-plugins/** rm,
/usr/lib/firefox/firefox-bin rmix,
/usr/lib/firefox/firefox.sh r,
/usr/lib/firefox/** r,
/usr/lib/firefox/**.so rm,
/usr/lib/gconv/** r,
/usr/lib/gconv/*so m,
/usr/lib/lib*.so* rm,
/usr/lib/locale/** r,
/usr/share/** r,
/var/cache/fontconfig/* r,
/var/cache/libx11/compose/* r,
/var/run/dbus/system_bus_socket w,
/var/run/nscd/passwd r,
/var/run/nscd/socket w,
/var/tmp/ r,
}
\end{verbatim}
\end{small}
\begin{thebibliography}{XX}
\bibitem{apparmor}
AppArmor documentation,
\url{http://www.novell.com/documentation/apparmor/}
\bibitem{ols06-pai}
Al Viro and Ram Pai:
{\em Shared-Subtree Concept, Implementation and Applications in Linux,}
Ottawa Linux Symposium, July 19-22, 2006,
\url{http://www.linuxsymposium.org/2006/}
\bibitem{dragon86}
Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullman:
{\em Compilers: Principles, Techniques, and Tools}
(The ``Dragon Book''), Addison-Wesley, 1986, ISBN 0-201-10088-6.
A second edition of this classic is available since August 2006 as
ISBN 0-321-48681-1.
\end{thebibliography}
\end{document}
% vim:textwidth=72