s6: the s6-sudod program
Keywords: s6 s6-sudod sudo setuid suid unix privilege gain getpeereid server

s6
Software
skarnet.org

# The `s6-sudod` program

`s6-sudod` receives command-line arguments, environment variables and standard descriptors from a peer s6-sudoc program over a Unix socket, then forks another program.

## Interface

         s6-sudod [ -0 ] [ -1 ] [ -2 ] [ -t timeout ] [ sargv... ]

- s6-sudod gets 3 file descriptors via fd-passing over a Unix socket that must be open on its descriptors 0 and 1. (The received descriptors will be the stdin, stdout and stderr of the server program.) It expects a s6-sudoc process to be sending them on the client side.
- It also receives a list of command-line arguments cargv..., and an environment clientenv.
- s6-sudod forks and executes sargv... cargv... The client command line is appended to the server command line.
- s6-sudod waits for its child to exit and transmits its exit code to the peer s6-sudoc process. It then exits 0.

## Environment

s6-sudod transmits its own environment to its child, plus the environment sent by s6-sudoc, filtered in the following manner: for every variable sent by s6-sudoc, if the variable is present but empty in s6-sudod's environment, then its value is overriden by the value given by s6-sudoc. A variable that is already nonempty, or that doesn't exist, in s6-sudod's environment, will not be transmitted to the child.

## Options

- `-0` : do not inherit stdin from s6-sudoc. The child will be run with its stdin pointing to `/dev/null` instead.
- `-1` : do not inherit stdout from s6-sudoc. The child will be run with its stdout pointing to `/dev/null` instead.
- `-2` : do not inherit stderr from s6-sudoc. The child will be run with its stderr being a copy of s6-sudod's stderr instead. (This is useful to still log the child's error messages without sending them to the client.)
- `-t timeout` : if s6-sudod has not received all the needed data from the client after timeout milliseconds, it will exit without spawning a child. By default, timeout is 0, meaning infinite. This mechanism exists to protect the server from malicious or buggy clients that would uselessly consume resources.

## Usage example

The typical use of s6-sudod is in a local service with a s6-ipcserver process listening on a Unix socket, a s6-ipcserver-access process performing client authentication and access control, and possibly a s6-envdir process setting up the environment variables that will be accepted by s6-sudod. The following script, meant to be a run script in a service directory, will set up a privileged program:

    #!/command/execlineb -P
    fdmove -c 2 1
    fdmove 1 3
    s6-envuidgid serveruser
    s6-ipcserver -U -1 -- serversocket
    s6-ipcserver-access -v2 -l0 -i rules --
    exec -c
    s6-envdir env
    s6-sudod
    sargv

- execlineb executes the script.
- fdmove makes sure the script's error messages are sent to the service's logger.
- fdmove redirects the script's stdout to file descriptor 3. This is useful if the service directory contains a `notification-fd` file containing `3`, so the daemon can perform readiness notification by writing a newline to its stdout. (The `-1` option to s6-ipcserver tells it to do this.)
- s6-envuidgid sets the UID, GID and GIDLIST environment variables for s6-ipcserver to interpret.
- s6-ipcserver binds to serversocket, drops its privileges to those of serveruser, and announces its readiness. Then, for every client connecting to serversocket:
  - s6-ipcserver-access checks the client's credentials according to the rules in directory rules.
  - exec -c clears the environment.
  - s6-envdir sets environment variables according to the directory env. You can make sure that a variable VAR will be present but empty by performing `echo > env/VAR`. (A single newline is interpreted by s6-envdir as an empty variable; whereas if `env/VAR` is totally empty, then the VAR variable will be removed from the environment.)
  - s6-sudod reads a command line cargv, a client environment and file descriptors over the socket.
  - s6-sudod spawns `sargv cargv`.
  (Actually, s6-ipcserver does not do this itself: it executes into other programs that each do one of the tasks. But for our example, it does not matter.)

This means that user clientuser running `s6-sudo serversocket cargv` will be able, if authorized by the configuration in rules, to run `sargv cargv` as user serveruser, with stdin, stdout, stderr and the environment variables properly listed in env transmitted to sargv.

## Notes

- If s6-sudoc is killed, or exits after timeoutrun milliseconds, while the server program is still running, s6-sudod will send a SIGTERM and a SIGCONT to its child, then exit 1. However, sending a SIGTERM to the child does not guarantee that it will die; and if it keeps running, it might still read from the file that was s6-sudoc's stdin, or write to the files that were s6-sudoc's stdout or stderr. This is a potential security risk. Administrators should audit their server programs to make sure this does not happen.
- More generally, anything using signals or terminals will not be handled transparently by the s6-sudoc + s6-sudod mechanism. The mechanism was designed to allow programs to gain privileges in specific situations: short-lived, simple, noninteractive processes. It was not designed to emulate the full suid functionality and will not go out of its way to do so.
- sargv may be empty. In that case, the client is in complete control of the command line executed as serveruser. This setup is permitted by s6-sudod, but it is very dangerous, and extreme attention should be paid to the construction of the s6-ipcserver-access rules.