s6-networking: the stls library interface
Keywords: s6-networking net stls library TLS SSL LibreSSL OpenSSL libtls

s6-networking
Software
skarnet.org

# The `stls` library interface

## General information

`libstls` is a small support library for the s6-tlsc and s6-tlsd executables when they're built against the libtls API, whether that API is implemented via LibreSSL or via OpenSSL with the addition of LibreTLS. You can use it in your own programs, but since libtls is already relatively high-level, it's probably not very useful.

## Compiling

- Make sure the s6-networking headers, as well as the skalibs headers, and the `tls.h` header, are visible in your header search path.
- Use `#include <s6-networking/stls.h>`

## Linking

- Make sure the s6-networking libraries, as well as the skalibs libraries, and the libraries needed by libtls, are visible in your library search path.
- Link against `-lstls`, `-lskarnet`, `-ltls`, `-lssl`, `-lcrypto`, ``cat $sysdeps/socket.lib``, ``cat $sysdeps/spawn.lib``, and ``cat $sysdeps/sysclock.lib``, where `$sysdeps` is your skalibs sysdeps directory.

## Programming

### Utilities

#### `void stls_drop ()`

If the process is running as root, then this function drops its privileges (else it does nothing). The gid to drop to is read from the TLS_GID environment variable; the uid to drop to is read from the TLS_UID environment variable. If those variables are not given, then the uid, or gid, or both, are not changed. If they contain something else than numerical uid/gids, the process exits 111 with an error message.

#### `int stls_send_environment (struct tls *ctx, int fd)`

Writes a series of null-terminated strings of the form `key=value` to file descriptor fd; the series is terminated with an additional null character. The strings represent information about the TLS connection represented by context ctx; it is only valid to call this function after the handshake has completed. The exact keys used will change over time, but at least `SSL_PROTOCOL=value` and `SSL_CIPHER=value` are transmitted. The function returns 1 if it succeeds and 0 if it fails.

### Initializing the TLS engine

#### `struct tls *stls_client_init_and_handshake (int const *fds, uint32_t preoptions, char const *servername)`

This function initializes a TLS context for a client-side connection, then performs a TLS handshake. It then returns a non-null pointer to a `struct tls` context for the application to pass to the `stls_run` function when it wants to run the engine. If the context cannot be initialized or the handshake cannot be performed, the process exits (96 for configuration issues, 97 for context and handshake issues) with an appropriate error message.

If the `CADIR` environment variable is set, then it must contain the path of a directory containing the hashed names of the public certificates identifying the trust anchors. Else, if the `CAFILE` environment variable is set, then it must contain the path to a PEM file containing all the certificates for the trust anchors. Else, the process exits 100 with an error message.

The arguments are as follows:

- `fds` : an array of 2 file descriptors, that are in this order: the fd reading from the network, the fd writing to the network.
- `preoptions` : a bitfield.
  - Bit 0: if clear, no client authentication is performed. If set, the `CERTFILE` and `KEYFILE` environment variables are read, they must contain the path to a valid client certificate and private key (else the process exits 96); this certificate is then provided to the server for client authentication.
- `servername` : the server name used for SNI. If NULL, then no SNI is performed, which may be a security risk.

#### `struct tls *stls_server_init_and_handshake (int const *fds, uint32_t preoptions)`

Same as the previous function, but on the server side. No servername argument is required. The `CERTFILE` and `KEYFILE` environment variables are mandatory, they point to the server's certificate and private key. It is only necessary to set `CADIR` or `CAFILE` when bit 0 of preoptions is set, in which case client authentication will be requested, and a list of trust anchors (read from either the directory in `CADIR` or the PEM file in `CAFILE`) will be used to verify the client certificate.

### Running the TLS engine

#### `void stls_run (struct tls *ctx, int *fds, unsigned int verbosity, uint32_t options, tain_t const *tto)`

This function runs a full-duplex TLS/SSL engine, reading/writing clear text from/to two file descriptors, and writing/reading ciphertext to/from two other file descriptors, until the connection is closed both ways (either with a SSL close, or with EOF). It does not return.

- ctx is a pointer to a fully initialized context, connected to fds[2] and fds[3]. The TLS handshake must already be completed.
- fds is an array of 4 file descriptors, in this order: fd reading clear text, fd writing clear text, fd reading ciphertext, fd writing ciphertext.
- verbosity defines the engine's verbosity: the higher the more verbose. This parameter is currently ignored.
- options is a bitfield.
  - bit 0 tells the engine how to behave when the local application closes the connection (i.e. when the engine reads EOF on fds[0]). If the bit is clear, then the engine will perform as SSL close: it will send a SSL close_notify, and stop processing incoming records, waiting for a peer acknowledgement of the close_notify. If the bit is set, then the engine will not send a close_notify but simply transmit EOF to the peer, while continuing to process incoming records until it gets EOF back. close_notify is secure when handling protocols that are not auto-terminated (such as HTTP 0.9), but it does not permit separate closing of both ways. EOF allows full-duplex until the very end, but is insecure if the application protocol does not know in advance how many bytes it should get. Modern application protocols should all work with EOF.
  - bit 1 tells the engine whether (if set) or not (if clear) to be sensitive to close_notify if on the receiving side of them. If set, if the peer closes the connection without sending a close_notify, the process will exit 98 with a fatal error message. If clear, the EOF will be transmitted and the engine will continue serving the other half of the connection until it's closed as well, and the process wil exit 0.
- tto is a pointer to a tain_t containing a relative time (i.e. a timeout). If *tto time elapses with no application data being exchanged, the engine will forcibly close the connection (with the method defined by `options & 1`). You can use `&tain_infinite_relative` as a value for tto if you don't want the engine to ever timeout.

`stls_run` will make the process die with an appropriate error message and exit code if it encounters an unrecoverable error. If there were no problems and the SSL/TLS connection closed cleanly, the process exits 0.