February 2021: nooSMTPD libtls-conversion, ciphers, curves and protocols

TL;DR: I converted nooSMTPD to libtls and implemented SMTP ciphers, curves and protocols selection.

Shout outs to my patrons !

As usual, a huge thanks goes to the people sponsoring me on patreon, github or liberaPay, the work in this post was made possible by my sponsorship.

Let’s start with some LoFi


I have a youtube channel (subscribe ! now !)

Nothing new in OpenSMTPD-portable

eric@ sent a libtls-conversion diff to tech@ last month but there hasn’t been much progress since then.

I tested the diff extensively and I’m currently waiting for it to be merged to OpenBSD so I can bring it to OpenSMTPD-portable.

nooSMTPD is libtls-enabled

In nooSMTPD, I did the libtls-conversion by applying the diff and making the appropriate changes to the libtls compat layer.

I switched my machines from my custom FrankenOpenSMTPD to nooSMTPD and been running it since late January, with no regressions.

It’s interesting to note that while I ran OpenSMTPD from upstream repository without the autoconf build, I’m running nooSMTPD from autoconf which led to a lot of swearing as it was never meant to be used on OpenBSD. I fixed a few unpleasant things which I’ll be able to push to OpenSMTPD-portable as well.

The smtp tls global configuration

OpenSMTPD allows setting up SMTP global configuration with the smtp global keyword:

smtp max-message-size "30M"
smtp sub-addr-delim "+"

Because it may be desireable to setup an alternate list of ciphers, the ciphers keyword was introduced a long time ago and a global option allows overriding the default ciphers:

smtp ciphers "HIGH:!aNULL:!MD5"

In nooSMTPD, I decided to introduce the tls keyword for TLS related options.

The libtls conversion unlocks several TLS-related features that may be confusing below smtp, without any reference to TLS. For instance, I have implemented TLS protocol selection and unless tls appears in the configuration this will result in:

smtp protocols [...]

which is much more confusing than:

smtp tls protocols [...]

as it is unclear that the protocols are TLS related.

As a result, I moved ciphers below smtp tls so in nooSMTPD it reads as:

smtp tls ciphers "HIGH:!aNULL:!MD5"

I’ll suggest this change to OpenSMTPD too.

TLS ciphers selection

As mentionned above, I have changed the grammar to introduce a tls namespace to the smtp keyword:

smtp tls ciphers "HIGH:!aNULL:!MD5"

Because libtls supports cipher suite groups, the secure, compat, legacy or insecure groups can be used in place of a cipher suite, and since secure is the default, the global cipher selection only needs to be specified for other cases:

smtp tls ciphers "compat"

With this done, I also added the ability to setup the ciphers at the listener level:

listen on tls ciphers "compat"
listen on tls ciphers "legacy"  # use on legacy subnet

This was committed to nooSMTPD and I’ll submit a diff to OpenSMPTD when libtls-conversion is done upstream.

TLS curves selection

The selection of curves was never merged in OpenSMTPD, as a result it is not possible to select specific curves at either one of the global or listener level. The default curves (X25519,P-256,P-384) in libtls are sensible choices but some use-cases require restricting to a specific curve, or even an alternate one.

I introduced a curves option which allows specifying curves to override the default:

smtp tls curves "X25519"

While at it, I added the ability to setup the curves at the listener level:

listen on tls ciphers "X25519"
listen on tls curves "P-256,P-384"

This was committed to nooSMTPD and I’ll also submit a diff to OpenSMPTD when libtls-conversion is done upstream.

TLS protocols selection

Just like for curves, the selection of a TLS protocol was never merged in OpenSMTPD.

I introduce a protocols option which allows specifiying protocols to override the default:

smtp tls protocols "tlsv1.3:tlsv1.2"

And again, I added the ability to setup the protocol at the listener level:

listen on tls protocols "tlsv1.3"

Guess what… Yes, this was also committed to nooSMTPD and I’ll submit a diff to OpenSMPTD when libtls-conversion is done upstream.

Global smtp tls grammar needs to be reworked

The smtp tls keyword was implemented as a placeholder for the ciphers, curves and protocols options, but it was not implemented the way it should.

Basically, ciphers, curves and protocols should be options to tls so that it is possible to write:

smtp tls ciphers "secure" protocols "secure"

At the time being, this is not possible and it must be expressed as follows:

smtp tls ciphers "secure"
smtp tls protocols "secure"

This requires a bit of grammar refactor which I’ll be working on in March.

Listener tls grammar was reworked

The listener tls keyword suffered from the same issue as the global smtp tls keyword.

Initially, it was implemented in such a way that ciphers, curves and protocols were standalone options. This allowed avoiding the need to repeat tls before every option, you needed one tls on the listener so it was flagged as TLS-enabled and the options would check for the flag. This worked but it was not the right way to do it.

I reworked the grammar for tls in listeners so that ciphers, curves and protocols are tls options. This avoids having to repeat tls before every keyword, and it allows relying on the grammar rather than on a flag to determine if the configuration is valid:

listen on tls ciphers "secure" curves "P-256,P-384" protocols "secure"

I’m happy with the result.

What’s next ?

I will rework the grammar for global settings and will implement these options for the MTA layer, as these options were done only for the incoming path.

I intend to publish a technical article in March regarding software design and data.

I might unveil a project that’s unrelated to computer programming too :-)

Comments: https://github.com/poolpOrg/poolp.org/issues/85