Published on (
 See this if you're having trouble printing code examples

Big Scary Daemons Creating Systrace Policies

by Michael W. Lucas

In the last article, we examined basic systrace policies. This time we're going to learn how to create and use systrace policies. In a true paranoid's ideal world, sysadmins would read the source code for every application on their system and be able to build system call access policies by hand, relying only on their intimate understanding of every feature of the application. Most system administrators don't have that sort of time and would have better things to do with that sort of time if they did.

A second choice would be to take policies from a selection of publicly-audited policies. The Hairy Eyeball project contains systrace policies for almost two hundred programs. The policies are designed for use with OpenBSD, but could easily be edited for binaries on any operating system. I recommend that you download the entire Hairy Eyeball distribution to use as examples, if nothing else. Using a predefined policy can save you a lot of annoyance, but you still need to be able to edit these to fit your particular circumstances. Also, you'll have to generate policies from scratch for programs that are not in the repository.

systrace(1) includes an policy-generation tool that will generate a policy listing every system call the application wants to make. You can use that policy as a starting point to narrow down the access you will allow the application. We'll use this method to generate a policy for inetd(8). Use the -A flag to systrace, and the full path to the program you want to run.

systrace -A /usr/sbin/inetd

To pass flags to inetd, add them at the end of the command line.

Then use the program for which you're developing a policy. This system has ident, daytime, and time services open, so run programs that require those services. Fire up an IRC client to trigger ident requests, and telnet to port 13 and 37 to get time services. Once you have put inetd through its paces, shut it down. inetd has no control program, so you need to kill it by process ID. Checking the process list will show two processes:

# ps -ax | grep inet
24421 ??  Ixs     0:00.00 /usr/sbin/inetd 
12929 ??  Is      0:00.01 systrace -A /usr/sbin/inetd 

Do not kill the systrace process (pid 12929 in this example). That process has all the records of the system calls that inetd(8) has made. Just kill the inetd process, and the systrace process will exit normally.

Now check your home directory for a .systrace directory, which will contain systrace(1)'s first stab at an inetd(8) policy. Remember, policies are placed in files named after the full path to the program, replacing slashes with underscores.

# ls .systrace
usr_libexec_identd   usr_sbin_inetd

Also in Big Scary Daemons:

Running Commercial Linux Software on FreeBSD

Building Detailed Network Reports with Netflow

Visualizing Network Traffic with Netflow and FlowScan

Monitoring Network Traffic with Netflow

Information Security with Colin Percival

systrace created two policies, not one. In addition to the expected policy for /usr/sbin/inetd, there's one for /usr/libexec/identd. If you're familiar with inetd(8), you've probably already guessed why this happened. Those new to inetd should consult the man page, which explains that inetd implements time services internally while ident calls a separate program to service requests. As inetd spawned identd, systrace captured the identd system calls as well.

By reading the policy, you can improve your understanding of what the program actually does. Look up each system call the program uses, and see if you can restrict access further. An experienced system administrator can probably find ways to tighten access, but junior sysadmins can just use the autogenerated policy. If you run the autogenerated policy, you've already improved your systems' security. For example, the autogenerated policy contains rules that only allow it to listen on particular TCP/IP ports and only read certain files on the system.

Applying a policy to a program is much like creating the systrace policy itself; just run the program as an argument to systrace, using the -a option.

# systrace -a /usr/sbin/inetd

If the program tries to perform system calls not listed in the policy, they will fail. This may cause the program to behave unpredictably. systrace will log failed entries in /var/log/messages. For example, after running inetd under this particular systrace policy I found messages like this throughout /var/log/messages:

Dec  2 01:31:11 openbsdtest systrace: deny user: _identd, \
	prog: /usr/libexec/identd, pid: 27046(1)[12989], \
	policy: /usr/libexec/identd, filters: 24, syscall: native-fsread(5), \
	filename: /etc/spwd.db

Dec  2 01:31:11 openbsdtest identd[27046]: /etc/pwd.db: Operation not permitted

Dec  2 01:31:11 openbsdtest identd[27046]: \
	getpwuid() could not map uid (25) to name

We can trace this back and see what exactly happened. In the first log entry, identd tried to real /etc/spwd.db. This is the hash of the password file. identd(8) should certainly be able to look up system users--that's the service it provides, after all. I need to add an appropriate entry to the policy to allow this access.

Editing a policy is very simple: just add the desired statement to the end of the rule list, and it will be picked up. You could do this by hand, of course, but that's the hard way. systrace includes a tool to let you edit policies in real time, as the system call is made. This is excellent for use in a network operations center environment, where the person responsible for watching the network monitor can also be assigned to watch for system calls and bring them to the attention of the appropriate sysadmin. You can specify which program you wish to monitor by using systrace's -p flag. This is called "attaching" to the program.

For example, earlier we saw two processes containing inetd. One was the actual inetd process, the other was the systrace process managing inetd. Attach to the systrace process, not the actual program; in this case, process 12929. Also give the full path to the managed program as an argument.

# systrace -p 12929 /usr/sbin/inetd

At first nothing will happen. When the program attempts to make an unauthorized system call, however, a GUI will pop up. You will have the options to allow the system call, deny the system call, always permit the call, or always deny it. The program will hang until you make a decision, however, so decide quickly.

Related Reading

The Unix CD Bookshelf
By O'Reilly Media, Inc.

Note that these changes will only take effect so long as the current process is running. If you restart the program, you must also restart the attached systrace monitor, and any changes you set in the monitor are gone. You must add those rules to the policy if you want them to be permanent.

Once you've mastered the basics, check out gtk-systrace. It includes policy suggestions that can help you create a policy very quickly. We'll spend some time with gtk-systrace and other systrace features in a future article.

While systrace has a vast number of functions and abilities, this should be enough to get you started. Experiment with the tool, look at some existing policies, and be sure to read section 2 of the man pages when you're in doubt. systrace is starting to spread to other operating systems; not only is it on OpenBSD, but NetBSD and Linux, with ports underway to Mac OS X and FreeBSD.

(The author wishes to thank systrace's author, Neils Provos, for taking time out of his busy schedule to answer questions and review the previous two articles.)

Michael W. Lucas

Read more Big Scary Daemons columns.

Return to the BSD DevCenter.

Copyright © 2009 O'Reilly Media, Inc.