From June 1 until September 1, 2023, Jake Freeland will intern with the Foundation to work on Capsicum, FreeBSD’s sandbox framework.  Capsicum was built to limit the capabilities given to applications and libraries. The Capsicum model is simple and secure, but progress and development surrounding the framework has died down in recent years. The core idea behind Capsicum is intuitive; once capability mode is entered, resource acquisition and external communication is severely curtailed. It is fairly easy to design a program around this principle, but the problems start when existing applications, that were not designed to be sandboxed, need to work in this environment. It is difficult to determine what actions cause Capsicum violations and it is impossible to pre-open resources that have not been requested or named yet. On top of that, the developer needs to be fully acquainted with the program before they can begin to implement Capsicum functionality. These reasons describe why Capsicumization efforts have faded.

The internship will involve working on a number of projects that, as a whole, aim to revitalize Capsicum. The primary goal is to enhance and ease the experience for developers who wish to Capsicumize their existing programs. The biggest enemy of Capsicum is its large learning curve. Refactoring a program to support capability mode often requires the developer to know what causes Capsicum violations and know how to restructure the given program to avoid violations. Sometimes this process is trivial, but larger programs often need resources on-demand and figuring out how to serve these needs can be difficult.  Extending the number of tools available to the developer for convenient program Capsicumization will decrease the aforementioned learning curve significantly. If Capsicumization is easy, then more developers will adopt it.


1. Tracing Capsicum Violations

At the time of writing, modifying a program to support Capsicum requires the developer to manually parse their code and find Capsicum violations. It would be convenient to have a utility that could trace the application during runtime and figure out where violations will occur. This functionality could be added to ktrace(1) under an options flag. The premise is to hook where ENOCAP would usually be returned, log that location, and continue execution normally. Differential revision by David Chisnall could also be used to log violations via signal notification.

2. Capsicumizing syslogd(8)

The syslogd daemon is responsible for reading and logging messages to the system console, log files, and other machines. Logging is an integral, and often sensitive, duty of any operating system, so syslogd should naturally be sandboxed with Capsicum. Using ktrace(1) Capsicum violation tracing, syslogd will be re-architected to run in capability mode. The syslog.conf configuration file is responsible for providing syslogd with facilities and programs to watch with corresponding log file locations. This convention allows us to parse syslog.conf and determine what resources syslogd requires for normal operation. It is not possible to sandbox syslogd by parsing syslog.conf once at the beginning of execution. If, at any time, a SIGHUP signal is received, syslogd will reprocess its configuration and may require new resources. To circumvent this issue, syslogd should be separated into two concurrent processes: one handles logging and the other listens for SIGHUP, reads the configuration file, and passes capabilities to the other process, if necessary.

3. Capsicumizing NFS Daemons

The NFS suite is composed of many daemons including nfsd(8), mountd(8), rpcbind(8), rpc.statd(8), rpc.lockd(8), and rpc.tlsservd(8). Focus in this project will be on Capsicumizing rpcbind. The rpcbind daemon is responsible for converting RPC program numbers into standard universal DARPA addresses. This program is an especially good candidate for Capsicumization since it commonly run by root and, as a result, is a valuable target for exploitation. A quick glance at usr.sbin/rpcbind/rpcbind.c:152 shows that rpcbind is limiting itself to at least 128 resources, which indicates that it will likely request to open abritrary files on demand. This is a glaring roadblock for Capsicumization since we’re unable to open arbitrary files inside of capability mode. We will probably need to use a mechanism like libcasper(3) (or similar) that can pass capabilities to rpcbind when required. Requests for these named assets have been noted: an rpc lock file, log file, netconfig file, a pipe used for thread wakeup, the rpcbind socket. There are undoubtedly more. Capsicumize the other NFS daemons may also be completed.

4. Capsicumizing ggatec(8) and ggated(8)

The GEOM Gate network facilities provide remote access to storage devices and are built on top of FreeBSD’s GEOM framework. Similar to NFS, device exports can be managed inside of an exports file, /etc/gg.exports. The ggate utilities are prime candidates for Capsicumization since they are run as root, process network requests, and have been subject to remote code execution in CVE-2021-29630. The ggatec client utility is used for the creation of ggate devices and communicating with ggated. Capsicumizing ggatec should be relatively simple since the remote host and device path are specified in the command line arguments. Simple file and socket pre-opening should suffice before entering capability mode. The ggated utility looks at /etc/gg.exports, or a specified alternative exports file, and services GEOM Gate requests from ggatec. It appears that all required resources are specified in command line arguments and the exports file, so pre-opening these resources should suffice before entering capability mode.

5. Capsicumizing tftpd(8)

The tftpd server implements the Internet Trivial File Transfer Protocol (RFC 1350) to allow remote reading of files specified in tftpd’s arguments. Since required resources are specified upfront, Capsicumization of tftpd should involve simple directory pre-opening.

6. Capsicumizing ntpd(8)

Unlike syslogd(8) and rpcbind(8), the ntpd codebase appears to be separated into around a hundred large files.  Based on the manual page, these files are named and will be easy to pre-open: a configuration file, drift file, network interface device, key file, log file, pid file, and statistics files. Assuming no arbitrary files are needed, the Capsicumization process should be relatively tame.

7. Capsicumizing libarchive(3)

The libarchive library specializes in compressing and decompressing a number of popular archive formats. It has been noted that shared library acquisition for iconv(3) is prompting a Capsicum violation. A temporary solution is to pre-open those iconv(3) files in unzip(1), but this should really be happening in libarchive. The goal of libarchive Capsicumization is to introduce archive agnostic creation and extraction while in capability mode. This will likely be done by adding Capsicum-specific interfaces alongside the standard API. Given this is feasible, the next step would be to Capsicumize tar(1). Given most tar files are extracting into the current directory, the Capsicumization process should involve opening the current directory file descriptor and changing all file system resource acquisition calls to their at() derivatives. Example: open() would be changed to openat().

8. Finalize the SIGCAP Violation Signal Implementation

Differential revision by David Chisnall proposes a SIGCAP signal that may optionally be delivered on Capsicum violation. Unfortunately, the review is unfinished and has not been updated for months. Finishing this review and adding the SIGCAP signal could make debugging easier for programs that use violation signals to trigger fall-back codepaths for Capsicum violations. Instead of waiting for a SIGTRAP, which may get intercepted by a debugger, we can use SIGCAP to tell the code to fall back to an alternative path. Additionally, having an explicit Capsicum violation signal would allow Capsicum violation tracing utilities to record Capsicum-specific failures. For example, when kern.trap enotcap=1 is set, any Capsicum violation will prompt a SIGTRAP with program termination. It is not transparent whether this program terminated because of a violation or because of an unrelated SIGTRAP signal. Altering kern.trap enotcap to deliver SIGCAP would eliminate this confusion. This SIGCAP signal could also introduce an alternative approach to tracing Capsicum violations in the aforementioned ktrace(1). The ktrace(1) program could intercept and record SIGCAP calls and send the original program back into execution using an appropriate signal handler.