guest - flak


At the g2k14 hackathon in July, I thought about a new interface for SSL connections. One of the most frequent complaints from OpenSSL users was that it was too much work to do anything, and one of the most frequent complaints from advanced users was that it was too much work to do anything correctly. Notably, failure to check the hostname in the cert against the hostname of the network connection is an unfortunately common mistake. And so was born the ressl (reimagined SSL) interface. Joel Sing (jsing) ended up implementing it first, putting the libressl in LibreSSL.

One of our strict guidelines was that we would not expose any OpenSSL data structures to the user. The biggest problem with the existing libssl API was that the underlying X.509 data structures poked through. Nobody cares about ASN.1 or X.509. Nobody wants to care. As one can see by reviewing the <ressl.h> header, only ressl types are exposed. Only the implementation knows about libssl and libcrypto, and in fact, it’s not even guaranteed that the implementation does know about them.

As an experiment to test the degree to which the ressl interface has been properly abstracted, I built a prototype second implementation. (The first implementation was also a prototype, but it will probably grow up into serious business soon.) My implementation is actually the TLS implementation from Go, bridged to C via a pipe. Spoiler: it kinda works.

goreSSL consists of two parts. A built from C and a goressl proxy built from Go.

The guts of the operation are in the ressl_connect_socket function.

ressl_connect_socket(struct ressl *ctx, int s, const char *hostname)
        char snum[16], pnum[16];
        int p[2];


        if (fork() == 0) {
                fcntl(s, F_SETFD, 0);
                snprintf(snum, sizeof(snum), "%d", s);
                snprintf(pnum, sizeof(pnum), "%d", p[0]);
                execlp("./goressl", "goressl", snum, pnum, hostname, NULL);
        ctx->fd = p[1];

        return 0;

Not a lot of code. We create a pipe and then fork a new process which will turn into the goressl proxy. It takes three command line arguments: the pipe fd, the socket fd, and the hostname (for validation). Assuming all goes according to plan, I/O on the ressl context is very simple from the C side. Just read from the pipe:

ressl_read(struct ressl *ctx, void *buf, size_t buflen, size_t *outlen)
        size_t x;

        x = read(ctx->fd, buf, buflen);
        if (x == -1)
                return -1;
        *outlen = x;
        return 0;

Who knew writing a TLS implementation could be so easy? The main function for the goressl process is also pretty simple.

func main() {
        snum, _ := strconv.Atoi(os.Args[1])
        pnum, _ := strconv.Atoi(os.Args[2])
        hostname := os.Args[3]

        log.Println(snum, pnum)

        socket, serr := net.FileConn(os.NewFile(uintptr(snum), "socket"))
        if serr != nil {
                panic("failed to create socket: " + serr.Error())
        pipe := os.NewFile(uintptr(pnum), "pipe")

        config := &tls.Config {
                ServerName: hostname,
        conn := tls.Client(socket, config)
        go readloop(conn, pipe)
        go writeloop(conn, pipe)
        <- done

Turn the command line arguments back into files, slap a TLS client around the socket, and go.

Mission accomplished? Once the library has been built and installed, the OpenBSD ftp client (which was rewritten to use the ressl API) picks it up and starts using it. I didn’t implement all the functions necessary for the ressl server API, but once that is done, it could also be used for the new httpd server.

Performance wise, copying data through a pipe adds overhead, but we’ve also magically made our TLS implementation multi-core. One process does encryption while another process does whatever else it does. The pipe overhead is probably less that.

Security wise, we’ve traded out some C code with a questionable history for some Go code with likely fewer flaws. The Go TLS stack doesn’t have all the same features, so it’s not quite an even matchup, but theoretically now you have a choice. If the server side were done, we could also successfully separate private keys from the bulk of our server code.

I haven’t done anything that people haven’t already done using stunnel (or ssh tunnels), although that’s usually setup by hand. This allows the proxy to be transparent to the user and program with no configuration, and even supports STARTTLS operations. If you don’t like goreSSL, you could cook up something similar with the OCaml TLS stack from Mirage OS, but then you’d have moreSSL and nobody wants that.

libressl has since been renamed to libtls. The goreSSL experiment is over.

The Cambridge folk actually went and built moreSSL although they called it the much cooler sounding not quite so bad TLS.

Posted 2014-09-10 15:45:23 by tedu Updated: 2016-08-04 03:43:47
Tagged: c go openbsd programming