[exim] Future OpenSSL configuration: sketch 1

Top Page
Delete this message
Reply to this message
Author: Phil Pennock
Date:  
To: exim-users
Subject: [exim] Future OpenSSL configuration: sketch 1
Folks,

The way we configure OpenSSL and the amount of special stuff we have to
do is a bit of a mess. GnuTLS is a bit better, because you can put TLS
protocol versions into the Priority String, but with OpenSSL, we're
stuck trying to support every last thing and caught when some folks
stuck supporting bad clients hold us back from letting everyone else
move forward.

Several years ago, I added the openssl_options config knob to Exim,
which at least made things a bit better, but we're creaking now.

This is a rough proposal, with not a single line of code written to
support it, but I'm looking for considered informed feedback as to
whether it makes sense to postmasters out there.

We've said "we only support versions of OpenSSL supported by the
upstream project", so now it's time to take advantage of that. I'm
proposing a new configuration section, when built against OpenSSL, which
uses an API introduced with OpenSSL 1.0.2, the minimum version still
supported upstream. We would use SSL_CONF_cmd(3) and pass settings
through from Exim's configure file, straight to OpenSSL.

How it would look:

~~~~~~~~~~~~~~~~~~~~~~~~8< hooking: variant 1 >8~~~~~~~~~~~~~~~~~~~~~~~~
# Variant 1: main section config, handled like tls_require_ciphers now:

openssl = ${if eq{$received_port}{25}{server_mx}{server_submission}}
~~~~~~~~~~~~~~~~~~~~~~~~8< hooking: variant 1 >8~~~~~~~~~~~~~~~~~~~~~~~~

~~~~~~~~~~~~~~~~~~~~~~~~8< hooking: variant 2 >8~~~~~~~~~~~~~~~~~~~~~~~~
# main section:
acl_smtp_connect = acl_connect

begin acl

acl_connect:
  warn   condition = ${if eq{$received_port}{25}}
         control   = openssl/server_mx
  warn   condition = ${if !eq{$received_port}{25}}
         control   = openssl/server_submission
~~~~~~~~~~~~~~~~~~~~~~~~8< hooking: variant 2 >8~~~~~~~~~~~~~~~~~~~~~~~~


~~~~~~~~~~~~~~~~~~~~~8< hooking: smtp transport >8~~~~~~~~~~~~~~~~~~~~~~
begin transports:

remote_smtp:
driver = smtp
openssl = ${if dane{client_dane}{client_insecure}}
~~~~~~~~~~~~~~~~~~~~~8< hooking: smtp transport >8~~~~~~~~~~~~~~~~~~~~~~

~~~~~~~~~~~~~~~~~~~~~~~~8< new config section >8~~~~~~~~~~~~~~~~~~~~~~~~
# This section is ignored if built against GnuTLS
#
# Warning: no string expansion is performed here (but may be in future).
# Macros are allowed.

begin openssl

server_mx:
  CipherString   DEFAULT:!SSLv2:!LOW:aNULL:!eNULL
  MinProtocol    TLSv1    # requires OpenSSL 1.1.0.
  Certificate    /etc/exim/tls/mx.crt
  PrivateKey     /etc/exim/tlx/mx.key


server_submission:
  Ciphersuites   TLS13-CHACHA20-POLY1305-SHA256:...   # used for TLS1.3
  CipherString   ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:HIGH:!MD5:!RC4:!aNULL:!ADH:!DES:!EXP:!NULL
  MinProtocol    TLSv1.2
  Groups         X25519
  Options        -SessionTicket
  Certificate    /etc/exim/tls/submission.crt   # private CA, perhaps
  PrivateKey     /etc/exim/tls/submission.key


client_dane:
  MinProtocol    TLSv1.2
  CipherString   something modern-ish here
  # VerifyCAPath irrelevant here, DANE only supports usages 2/3 which
  # ignores this ... although if the remote server doesn't send a full
  # chain and uses fingerprint-based pinning, this might unbreak things
  # enough?


client_insecure:
  MinProtocol    TLSv1
  VerifyCAPath   /etc/ssl/certs     # Exim's require/try options still affect this
  CipherString   something broad here


# This isn't referenced above, but fancier config options could make it
# required for domains in a whitelist, eg to say "use better security
# for gmail.com":
client_secure:
  MinProtocol    TLSv1.2
  CipherString   something modern-ish here
  VerifyCAPath   /etc/ssl/certs
~~~~~~~~~~~~~~~~~~~~~~~~8< new config section >8~~~~~~~~~~~~~~~~~~~~~~~~


Some settings come in with newer versions of OpenSSL. We wouldn't have
to do anything special to support them, that's _why_ we'd just pass
things through. If someone shouts and wants MinProtocol with older
OpenSSL, we'd shrug and say "upgrade OpenSSL, we use what they export
and are not getting sucked into replicating their work."

We simply split on whitespace to a "key" and a "value", where the value
can have internal whitespace we don't touch, but we strip off leading
and trailing whitespace. Then we pass each key/value down.

The openssl main setting, or ACL control, and openssl SMTP transport
would all only be valid when built with OpenSSL and be parse errors with
GnuTLS. The "begin openssl" could be allowed and ignored with GnuTLS.

At daemon startup, before backgrounding, where we do validation right
now, we'd go through every defined config block in the openssl section,
and for each one we're create an OpenSSL context, call SSL_CONF_cmd() to
set each line, and make sure there's no error. If there's an error,
that's a fatal configuration error and is passed back. These contexts
are all discarded. This would then automatically be visible as an error
when running "exim -bV"/"exim --version".

Then at runtime, these values are just passed in when configuring the
context.

For sanity, at first we'd skip string expansion, but would allow macros.
If there's an argument to be made for allowing string expansion, we
could switch to allowing it but would need to figure out what startup
validation looks like in that case.

I've introduced a hypothetical expansion condition "dane" for dispatch.

Exim settings which would become obsolete for OpenSSL:
* tls_require_ciphers
* tls_certificate
* tls_privatekey
* tls_dhparam, tls_dh_max_bits, tls_dh_min_bits
* tls_verify_certificates
* tls_ocsp_file
* openssl_options
* smtp transport: tls_require_ciphers
* smtp transport: dane_require_tls_ciphers

To not break backwards compatibility, we'd allow all of those options to
exist and work as long as the "openssl" option is not set. Whether and
when to break them is a larger issue.

Regards,
-Phil