systrace in OpenBSD
29.1 Introduction
The OpenBSD default system comes with a policy enforcement tool named systrace, which provides a way to monitor, intercept, and restrict system calls. The systrace facility acts as a wrapper to the executables, shepherding their traversal of the system call table. The systrace facility then intercepts the system calls and, using the systrace device, processes them through the kernel and handles the system calls.
Getting started with systrace is quite easy. You can run your programs under systrace, generate policies based on the observed behavior, and then enforce this policy on the program in subsequent runs. There are, however, two problems with this approach:
-
This approach assumes that the executable behaves entirely correctly and within the expected bounds. Violations of this assumption can include the use of a modified executable that has been reconfigured by an attacker. Subsequent uses of systrace will allow the malicious behavior to continue. To remedy this problem, policies should be reviewed after their generation to ensure that the anticipated behavior is observed, and trusted executables should be used in policy generation. Generally, automated policy generation should be undertaken only with trusted applications. Unknown applications can be used with interactive policy generation, so that decisions can be made before any damage is done by a rogue application.
-
This approach assumes that the initial runs of the systrace policy generator fully exercise the range of actions for which the policy is intended. In reality, the ls executable, for example, may not know that it is allowed to list the files in a publicly allowed directory that was skipped in the original run.
To remedy this situation, it is possible to bootstrap the policy that systrace knows about by using arbitrary external policies and the -f flag. In this scenario, a base policy can be built and extended. Furthermore, one can generatefilters that use wildcards, which eliminate the need for fully itemized lists:
native-open: filename match "$HOME/*" and oflags sub "ro" then permit
In this example, one can read (but not write to) any files and directories under the current user’s home directory. This filter is forward adaptable and condensed.
Executables run under the systrace facility can pass policies on to their children and inherit policies from their parents. This is useful for login shells, for example, where you may wish to restrict a user’s behavior using systrace. Any children from this shell will have a policy that has been inherited from their parent. A simple systrace login shell would look like the following:1
#include#include #include int main(int argc, char *argv) { char *args[4]; args[0] = "-Ua"; /* system policies, auto enforce */ args[1] = "/bin/ksh"; /* run ksh */ args[2] = "-l"; /* login shell */ args[3] = NULL; if (execv("/bin/systrace", args) <0) { fprintf(stderr, "loging in failed."); exit(-1); } /* NOTREACHED */ return (0); }
The series of steps to enable this system for users would look like the following:
$ mkdir -p /usr/local/src/bin/stsh/ $ vi /usr/local/src/bin/stsh/stsh.c enter above code $ gcc -o /usr/local/src/bin/stsh/stsh /usr/local/src/bin/stsh/stsh.c $ sudo cp /usr/local/src/bin/stsh/stsh /bin/stsh $ sudo vi /etc/login.conf edit the variable ‘‘shells’’ to be /bin/stsh
This can be applied in the default class for all users or just to users in a particular login class.
Before you begin, make sure you have a policy for /bin/sh in the directory /etc/systrace (saved as bin sh). Now test this setup (leaving at least one user with a normal shell for login purposes). Also, this method disables the utility chsh, which users can use to change their shells. They cannot disable their use of stsh and use a non–systrace-wrapped /bin/ksh, for example. Instead, their parent shell will always be a systrace-wrapped /bin/ksh. Newer versions of stsh can spawn any shell the user chooses, wrapped in systrace.
The target uses of systrace are threefold. First, it is designed for untrusted data paths, such as executables from potentially untrusted sources or applications that handle untrustworthy data. These can include daemon processes, for example, which are open to the world. By using systrace, an administrator can restrict the arbitrary execution of commands. Second, this program is very useful for machines with untrusted users operating in their shells. By spawning the login shell under the control of systrace and then forcing children to inherit this policy, transparent sandboxing of the system can occur. Third, systrace can protect users from their own processes. Some applications are untrusted or otherwise potentially damaging to the system or accept untrusted data from the network. Cradeling their execution by using systrace can help mitigate any damage they may cause.
Global system policies live in the system directory /etc/systrace. Examples policies exist for two daemons, lpd and named, which provide robust sandboxing for the executables. These examples show what can be done to secure a system using systrace.
User-specific policies are found in /home/username/.systrace. If a user modifies a global policy, the modified version is saved in his or her home directory. This prevents one user from modifying the execution environment of other users’ applications.
Note that systrace does require a modest level of understanding regarding system calls and their consequences. It is easy to write a policy that is impossible to use by ignoring fundamental actions, which is why it is advisable to start with automatically generated policies. Also, some large, complex applications may be difficult to run under systrace due to the large number of system calls they make. In these situations, it may be wise to attempt to allow nearly everything except a subset of commands. For example, your Web browser may be allowed to open arbitrary sockets above 1024 but not allowed to spawn a child shell.
29.1.1 Example Use
As described previously, it is possible to use systrace to automatically generate a policy for an executable. With the -A flag set, systrace will accept all actions as permitted and use them to build a policy. The following example shows the geneation of a simple policy allowing /bin/ ls to read the user’s home directory:
$ cd $ systrace -A ls
By default, systrace will store the generated policies in the directory /home/username/ .systrace. For our example run of ls, a policy named bin ls will appear with the contents of the policy for that executable:
Policy: /bin/ls, Emulation: native native-_sysctl: permit native-mmap: permit native-mprotect: permit native-ioctl: permit native-getuid: permit native-fsread: filename eq "/etc/malloc.conf" then permit native-issetugid: permit native-break: permit native-fsread: filename eq "/home/jose" then permit native-fchdir: permit native-fstat: permit native-fcntl: permit native-fstatfs: permit native-getdirentries: permit native-lseek: permit native-close: permit native-write: permit native-munmap: permit native-exit: permit
This simple, minimalistic policy is nevertheless very restrictive. When we try to use this policy to enforce actions, we can see the effect. Using the command systrace -a,we can automatically enforce the policy we have installed:
$ systrace -a ls /etc/ ls: /etc/: Operation not permitted
Additionally, a message is logged to the central system logs via the syslog mechanism. By default, these messages will appear in the file /var/ log/messages. Reading these messages can be useful for security monitoring or policy review and adjustment purposes:
May 30 07:12:11 superfly systrace: deny user: jose, prog: /bin/ls, pid: 1664(0)[17057], policy: /bin/ls, filters: 129, syscall: native-dup2(90), args: 8
The denial of system calls can be controlled by using the specific signal sent to the executable making the request. For example, it may be advisable to send ls a “permission denied” signal when it attempts to show the files in the /etc directory. The ls command gracefully reports the error to the user and exits.
More complicated policies can be generated interactively. When the X11 environment is available, the application xsystrace is used to provide responses to policy queries. In a text-only environment, the responses are handled on the command line. Responses are “permit” and “deny” with options that match those found in the policy file. For example, generating a policy for tcpdump would look like the following:
# systrace tcpdump /usr/sbin/tcpdump, pid: 8159(0)[0], policy: /usr/sbin/tcpdump, filters: 0, syscall: native-issetugid(253), args: 0 Answer: permit
Note that systrace uses the shell from which it was started to make these policy queries. If the shell has been closed, the application will hang while waiting for a policy decision.
The systrace system understands the following environmental variables and expands them as macros: These variables can be substituted in the systrace policy and allowed to expand. An example setup using such macros would appear as follows:
- HOME The user’s home directory (e.g., /home/jose).
- USER The user’s name (e.g., jose).
- CWD The current working directory (also known as .).
These variables can be substituted in the systrace policy and allowed to expand. An
example setup using such macros would appear as follows:
native-fsread: filename eq "$HOME/.gaim" then permit native-fswrite: filename eq "$HOME/.gaimrc" then permit
These examples were taken from a policy for the IM chat client gaim, generated automatically by systrace -A, and then smoothed over by manual editing.