Exim Internet Mailer

<-previousnext->

Chapter 43 - Encrypted SMTP connections using TLS/SSL

Support for TLS (Transport Layer Security), formerly known as SSL (Secure Sockets Layer), is implemented by making use of the OpenSSL library or the GnuTLS library (Exim requires GnuTLS release 1.0 or later). There is no cryptographic code in the Exim distribution itself for implementing TLS. In order to use this feature you must install OpenSSL or GnuTLS, and then build a version of Exim that includes TLS support (see section 4.7). You also need to understand the basic concepts of encryption at a managerial level, and in particular, the way that public keys, private keys, and certificates are used.

RFC 3207 defines how SMTP connections can make use of encryption. Once a connection is established, the client issues a STARTTLS command. If the server accepts this, the client and the server negotiate an encryption mechanism. If the negotiation succeeds, the data that subsequently passes between them is encrypted.

Exim’s ACLs can detect whether the current SMTP session is encrypted or not, and if so, what cipher suite is in use, whether the client supplied a certificate, and whether or not that certificate was verified. This makes it possible for an Exim server to deny or accept certain commands based on the encryption state.

Warning: Certain types of firewall and certain anti-virus products can disrupt TLS connections. You need to turn off SMTP scanning for these products in order to get TLS to work.

1. Support for the submissions (aka ssmtp and smtps) protocol

The history of port numbers for TLS in SMTP is a little messy and has been contentious. As of RFC 8314, the common practice of using the historically allocated port 465 for "email submission but with TLS immediately upon connect instead of using STARTTLS" is officially blessed by the IETF, and recommended by them in preference to STARTTLS.

The name originally assigned to the port was “ssmtp” or “smtps”, but as clarity emerged over the dual roles of SMTP, for MX delivery and Email Submission, nomenclature has shifted. The modern name is now “submissions”.

This approach was, for a while, officially abandoned when encrypted SMTP was standardized, but many clients kept using it, even as the TCP port number was reassigned for other use. Thus you may encounter guidance claiming that you shouldn’t enable use of this port. In practice, a number of mail-clients have only ever supported submissions, not submission with STARTTLS upgrade. Ideally, offer both submission (587) and submissions (465) service.

Exim supports TLS-on-connect by means of the tls_on_connect_ports global option. Its value must be a list of port numbers; the most common use is expected to be:

tls_on_connect_ports = 465

The port numbers specified by this option apply to all SMTP connections, both via the daemon and via inetd. You still need to specify all the ports that the daemon uses (by setting daemon_smtp_ports or local_interfaces or the -oX command line option) because tls_on_connect_ports does not add an extra port – rather, it specifies different behaviour on a port that is defined elsewhere.

There is also a -tls-on-connect command line option. This overrides tls_on_connect_ports; it forces the TLS-only behaviour for all ports.

2. OpenSSL vs GnuTLS

TLS is supported in Exim using either the OpenSSL or GnuTLS library. To build Exim to use OpenSSL you need to set

USE_OPENSSL=yes

in Local/Makefile.

To build Exim to use GnuTLS, you need to set

USE_GNUTLS=yes

in Local/Makefile.

You must also set TLS_LIBS and TLS_INCLUDE appropriately, so that the include files and libraries for GnuTLS can be found.

There are some differences in usage when using GnuTLS instead of OpenSSL:

  • The tls_verify_certificates option cannot be the path of a directory for GnuTLS versions before 3.3.6 (for later versions, or OpenSSL, it can be either).

  • The default value for tls_dhparam differs for historical reasons.

  • Distinguished Name (DN) strings reported by the OpenSSL library use a slash for separating fields; GnuTLS uses commas, in accordance with RFC 2253. This affects the value of the $tls_in_peerdn and $tls_out_peerdn variables.

  • OpenSSL identifies cipher suites using hyphens as separators, for example: DES-CBC3-SHA. GnuTLS historically used underscores, for example: RSA_ARCFOUR_SHA. What is more, OpenSSL complains if underscores are present in a cipher list. To make life simpler, Exim changes underscores to hyphens for OpenSSL and passes the string unchanged to GnuTLS (expecting the library to handle its own older variants) when processing lists of cipher suites in the tls_require_ciphers options (the global option and the smtp transport option).

  • The tls_require_ciphers options operate differently, as described in the sections 43.4 and 43.5.

  • The tls_dh_min_bits SMTP transport option is only honoured by GnuTLS. When using OpenSSL, this option is ignored. (If an API is found to let OpenSSL be configured in this way, let the Exim Maintainers know and we’ll likely use it).

  • With GnuTLS, if an explicit list is used for the tls_privatekey main option, it must be ordered to match the tls_certificate list.

  • Some other recently added features may only be available in one or the other. This should be documented with the feature. If the documentation does not explicitly state that the feature is infeasible in the other TLS implementation, then patches are welcome.

  • The output from "exim -bV" will show which (if any) support was included in the build. Also, the macro "_HAVE_OPENSSL" or "_HAVE_GNUTLS" will be defined.

3. GnuTLS parameter computation

This section only applies if tls_dhparam is set to historic or to an explicit path; if the latter, then the text about generation still applies, but not the chosen filename. By default, as of Exim 4.80 a hard-coded D-H prime is used. See the documentation of tls_dhparam for more information.

GnuTLS uses D-H parameters that may take a substantial amount of time to compute. It is unreasonable to re-compute them for every TLS session. Therefore, Exim keeps this data in a file in its spool directory, called gnutls-params-NNNN for some value of NNNN, corresponding to the number of bits requested. The file is owned by the Exim user and is readable only by its owner. Every Exim process that start up GnuTLS reads the D-H parameters from this file. If the file does not exist, the first Exim process that needs it computes the data and writes it to a temporary file which is renamed once it is complete. It does not matter if several Exim processes do this simultaneously (apart from wasting a few resources). Once a file is in place, new Exim processes immediately start using it.

For maximum security, the parameters that are stored in this file should be recalculated periodically, the frequency depending on your paranoia level. If you are avoiding using the fixed D-H primes published in RFCs, then you are concerned about some advanced attacks and will wish to do this; if you do not regenerate then you might as well stick to the standard primes.

Arranging this is easy in principle; just delete the file when you want new values to be computed. However, there may be a problem. The calculation of new parameters needs random numbers, and these are obtained from /dev/random. If the system is not very active, /dev/random may delay returning data until enough randomness (entropy) is available. This may cause Exim to hang for a substantial amount of time, causing timeouts on incoming connections.

The solution is to generate the parameters externally to Exim. They are stored in gnutls-params-N in PEM format, which means that they can be generated externally using the certtool command that is part of GnuTLS.

To replace the parameters with new ones, instead of deleting the file and letting Exim re-create it, you can generate new parameters using certtool and, when this has been done, replace Exim’s cache file by renaming. The relevant commands are something like this:

# ls
[ look for file; assume gnutls-params-2236 is the most recent ]
# rm -f new-params
# touch new-params
# chown exim:exim new-params
# chmod 0600 new-params
# certtool --generate-dh-params --bits 2236 >>new-params
# openssl dhparam -noout -text -in new-params | head
[ check the first line, make sure it's not more than 2236;
  if it is, then go back to the start ("rm") and repeat
  until the size generated is at most the size requested ]
# chmod 0400 new-params
# mv new-params gnutls-params-2236

If Exim never has to generate the parameters itself, the possibility of stalling is removed.

The filename changed in Exim 4.80, to gain the -bits suffix. The value which Exim will choose depends upon the version of GnuTLS in use. For older GnuTLS, the value remains hard-coded in Exim as 1024. As of GnuTLS 2.12.x, there is a way for Exim to ask for the "normal" number of bits for D-H public-key usage, and Exim does so. This attempt to remove Exim from TLS policy decisions failed, as GnuTLS 2.12 returns a value higher than the current hard-coded limit of the NSS library. Thus Exim gains the tls_dh_max_bits global option, which applies to all D-H usage, client or server. If the value returned by GnuTLS is greater than tls_dh_max_bits then the value will be clamped down to tls_dh_max_bits. The default value has been set at the current NSS limit, which is still much higher than Exim historically used.

The filename and bits used will change as the GnuTLS maintainers change the value for their parameter GNUTLS_SEC_PARAM_NORMAL, as clamped by tls_dh_max_bits. At the time of writing (mid 2012), GnuTLS 2.12 recommends 2432 bits, while NSS is limited to 2236 bits.

In fact, the requested value will be *lower* than tls_dh_max_bits, to increase the chance of the generated prime actually being within acceptable bounds, as GnuTLS has been observed to overshoot. Note the check step in the procedure above. There is no sane procedure available to Exim to double-check the size of the generated prime, so it might still be too large.

4. Requiring specific ciphers in OpenSSL

There is a function in the OpenSSL library that can be passed a list of cipher suites before the cipher negotiation takes place. This specifies which ciphers are acceptable for TLS versions prior to 1.3. The list is colon separated and may contain names like DES-CBC3-SHA. Exim passes the expanded value of tls_require_ciphers directly to this function call. Many systems will install the OpenSSL manual-pages, so you may have ciphers(1) available to you. The following quotation from the OpenSSL documentation specifies what forms of item are allowed in the cipher string:

  • It can consist of a single cipher suite such as RC4-SHA.

  • It can represent a list of cipher suites containing a certain algorithm, or cipher suites of a certain type. For example SHA1 represents all ciphers suites using the digest algorithm SHA1 and SSLv3 represents all SSL v3 algorithms.

  • Lists of cipher suites can be combined in a single cipher string using the + character. This is used as a logical and operation. For example SHA1+DES represents all cipher suites containing the SHA1 and the DES algorithms.

Each cipher string can be optionally preceded by one of the characters !, - or +.

  • If ! is used, the ciphers are permanently deleted from the list. The ciphers deleted can never reappear in the list even if they are explicitly stated.

  • If - is used, the ciphers are deleted from the list, but some or all of the ciphers can be added again by later options.

  • If + is used, the ciphers are moved to the end of the list. This option does not add any new ciphers; it just moves matching existing ones.

If none of these characters is present, the string is interpreted as a list of ciphers to be appended to the current preference list. If the list includes any ciphers already present they will be ignored: that is, they will not be moved to the end of the list.

The OpenSSL ciphers(1) command may be used to test the results of a given string:

# note single-quotes to get ! past any shell history expansion
$ openssl ciphers 'HIGH:!MD5:!SHA1'

This example will let the library defaults be permitted on the MX port, where there’s probably no identity verification anyway, but ups the ante on the submission ports where the administrator might have some influence on the choice of clients used:

# OpenSSL variant; see man ciphers(1)
tls_require_ciphers = ${if =={$received_port}{25}\
                           {DEFAULT}\
                           {HIGH:!MD5:!SHA1}}

This example will prefer ECDSA-authenticated ciphers over RSA ones:

tls_require_ciphers = ECDSA:RSA:!COMPLEMENTOFDEFAULT

For TLS version 1.3 the control available is less fine-grained and Exim does not provide access to it at present. The value of the tls_require_ciphers option is ignored when TLS version 1.3 is negotiated.

As of writing the library default cipher suite list for TLSv1.3 is

TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256

5. Requiring specific ciphers or other parameters in GnuTLS

The GnuTLS library allows the caller to provide a "priority string", documented as part of the gnutls_priority_init function. This is very similar to the ciphersuite specification in OpenSSL.

The tls_require_ciphers option is treated as the GnuTLS priority string and controls both protocols and ciphers.

The tls_require_ciphers option is available both as an global option, controlling how Exim behaves as a server, and also as an option of the smtp transport, controlling how Exim behaves as a client. In both cases the value is string expanded. The resulting string is not an Exim list and the string is given to the GnuTLS library, so that Exim does not need to be aware of future feature enhancements of GnuTLS.

Documentation of the strings accepted may be found in the GnuTLS manual, under "Priority strings". This is online as https://www.gnutls.org/manual/html_node/Priority-Strings.html, but beware that this relates to GnuTLS 3, which may be newer than the version installed on your system. If you are using GnuTLS 3, then the example code https://www.gnutls.org/manual/gnutls.html#Listing-the-ciphersuites-in-a-priority-string on that site can be used to test a given string.

For example:

# Disable older versions of protocols
tls_require_ciphers = NORMAL:%LATEST_RECORD_VERSION:-VERS-SSL3.0

Prior to Exim 4.80, an older API of GnuTLS was used, and Exim supported three additional options, "gnutls_require_kx", "gnutls_require_mac" and "gnutls_require_protocols". tls_require_ciphers was an Exim list.

This example will let the library defaults be permitted on the MX port, where there’s probably no identity verification anyway, and lowers security further by increasing compatibility; but this ups the ante on the submission ports where the administrator might have some influence on the choice of clients used:

# GnuTLS variant
tls_require_ciphers = ${if =={$received_port}{25}\
                           {NORMAL:%COMPAT}\
                           {SECURE128}}

6. Configuring an Exim server to use TLS

When Exim has been built with TLS support, it advertises the availability of the STARTTLS command to client hosts that match tls_advertise_hosts, but not to any others. The default value of this option is *, which means that STARTTLS is always advertised. Set it to blank to never advertise; this is reasonable for systems that want to use TLS only as a client.

If STARTTLS is to be used you need to set some other options in order to make TLS available.

If a client issues a STARTTLS command and there is some configuration problem in the server, the command is rejected with a 454 error. If the client persists in trying to issue SMTP commands, all except QUIT are rejected with the error

554 Security failure

If a STARTTLS command is issued within an existing TLS session, it is rejected with a 554 error code.

To enable TLS operations on a server, the tls_advertise_hosts option must be set to match some hosts. The default is * which matches all hosts.

If this is all you do, TLS encryption will be enabled but not authentication - meaning that the peer has no assurance it is actually you he is talking to. You gain protection from a passive sniffer listening on the wire but not from someone able to intercept the communication.

Further protection requires some further configuration at the server end.

To make TLS work you need to set, in the server,

tls_certificate = /some/file/name
tls_privatekey = /some/file/name

These options are, in fact, expanded strings, so you can make them depend on the identity of the client that is connected if you wish. The first file contains the server’s X509 certificate, and the second contains the private key that goes with it. These files need to be PEM format and readable by the Exim user, and must always be given as full path names. The key must not be password-protected. They can be the same file if both the certificate and the key are contained within it. If tls_privatekey is not set, or if its expansion is forced to fail or results in an empty string, this is assumed to be the case. The certificate file may also contain intermediate certificates that need to be sent to the client to enable it to authenticate the server’s certificate.

For dual-stack (eg. RSA and ECDSA) configurations, these options can be colon-separated lists of file paths. Ciphers using given authentication algorithms require the presence of a suitable certificate to supply the public-key. The server selects among the certificates to present to the client depending on the selected cipher, hence the priority ordering for ciphers will affect which certificate is used.

If you do not understand about certificates and keys, please try to find a source of this background information, which is not Exim-specific. (There are a few comments below in section 43.10.)

Note: These options do not apply when Exim is operating as a client – they apply only in the case of a server. If you need to use a certificate in an Exim client, you must set the options of the same names in an smtp transport.

With just these options, an Exim server will be able to use TLS. It does not require the client to have a certificate (but see below for how to insist on this). There is one other option that may be needed in other situations. If

tls_dhparam = /some/file/name

is set, the SSL library is initialized for the use of Diffie-Hellman ciphers with the parameters contained in the file. Set this to none to disable use of DH entirely, by making no prime available:

tls_dhparam = none

This may also be set to a string identifying a standard prime to be used for DH; if it is set to default or, for OpenSSL, is unset, then the prime used is ike23. There are a few standard primes available, see the documentation for tls_dhparam for the complete list.

See the command

openssl dhparam

for a way of generating file data.

The strings supplied for these three options are expanded every time a client host connects. It is therefore possible to use different certificates and keys for different hosts, if you so wish, by making use of the client’s IP address in $sender_host_address to control the expansion. If a string expansion is forced to fail, Exim behaves as if the option is not set.

The variable $tls_in_cipher is set to the cipher suite that was negotiated for an incoming TLS connection. It is included in the Received: header of an incoming message (by default – you can, of course, change this), and it is also included in the log line that records a message’s arrival, keyed by “X=”, unless the tls_cipher log selector is turned off. The encrypted condition can be used to test for specific cipher suites in ACLs.

Once TLS has been established, the ACLs that run for subsequent SMTP commands can check the name of the cipher suite and vary their actions accordingly. The cipher suite names vary, depending on which TLS library is being used. For example, OpenSSL uses the name DES-CBC3-SHA for the cipher suite which in other contexts is known as TLS_RSA_WITH_3DES_EDE_CBC_SHA. Check the OpenSSL or GnuTLS documentation for more details.

For outgoing SMTP deliveries, $tls_out_cipher is used and logged (again depending on the tls_cipher log selector).

6.1. Requesting and verifying client certificates

If you want an Exim server to request a certificate when negotiating a TLS session with a client, you must set either tls_verify_hosts or tls_try_verify_hosts. You can, of course, set either of them to * to apply to all TLS connections. For any host that matches one of these options, Exim requests a certificate as part of the setup of the TLS session. The contents of the certificate are verified by comparing it with a list of expected trust-anchors or certificates. These may be the system default set (depending on library version), an explicit file or, depending on library version, a directory, identified by tls_verify_certificates.

A file can contain multiple certificates, concatenated end to end. If a directory is used (OpenSSL only), each certificate must be in a separate file, with a name (or a symbolic link) of the form <hash>.0, where <hash> is a hash value constructed from the certificate. You can compute the relevant hash by running the command

openssl x509 -hash -noout -in /cert/file

where /cert/file contains a single certificate.

There is no checking of names of the client against the certificate Subject Name or Subject Alternate Names.

The difference between tls_verify_hosts and tls_try_verify_hosts is what happens if the client does not supply a certificate, or if the certificate does not match any of the certificates in the collection named by tls_verify_certificates. If the client matches tls_verify_hosts, the attempt to set up a TLS session is aborted, and the incoming connection is dropped. If the client matches tls_try_verify_hosts, the (encrypted) SMTP session continues. ACLs that run for subsequent SMTP commands can detect the fact that no certificate was verified, and vary their actions accordingly. For example, you can insist on a certificate before accepting a message for relaying, but not when the message is destined for local delivery.

When a client supplies a certificate (whether it verifies or not), the value of the Distinguished Name of the certificate is made available in the variable $tls_in_peerdn during subsequent processing of the message.

Because it is often a long text string, it is not included in the log line or Received: header by default. You can arrange for it to be logged, keyed by “DN=”, by setting the tls_peerdn log selector, and you can use received_header_text to change the Received: header. When no certificate is supplied, $tls_in_peerdn is empty.

6.2. Caching of static server configuration items

If any of the main configuration options tls_certificate, tls_privatekey, tls_crl and tls_ocsp_file have values with no expandable elements, then the associated information is loaded at daemon startup. It is made available to child processes forked for handling received SMTP connections.

This caching is currently only supported under Linux and FreeBSD.

If caching is not possible, for example if an item has to be dependent on the peer host so contains a $sender_host_name expansion, the load of the associated information is done at the startup of the TLS connection.

The cache is invalidated and reloaded after any changes to the directories containing files specified by these options.

The information specified by the main option tls_verify_certificates is similarly cached so long as it specifies files explicitly or (under GnuTLS) is the string “system,cache”. The latter case is not automatically invalidated; it is the operator’s responsibility to arrange for a daemon restart any time the system certificate authority bundle is updated. A HUP signal is sufficient for this. The value “system” results in no caching under GnuTLS.

The macro _HAVE_TLS_CA_CACHE will be defined if the suffix for "system" is acceptable in configurations for the Exim executable.

Caching of the system Certificate Authorities bundle can save significant time and processing on every TLS connection accepted by Exim.

7. Configuring an Exim client to use TLS

The tls_cipher and tls_peerdn log selectors apply to outgoing SMTP deliveries as well as to incoming, the latter one causing logging of the server certificate’s DN. The remaining client configuration for TLS is all within the smtp transport.

It is not necessary to set any options to have TLS work in the smtp transport. If Exim is built with TLS support, and TLS is advertised by a server, the smtp transport always tries to start a TLS session. However, this can be prevented by setting hosts_avoid_tls (an option of the transport) to a list of server hosts for which TLS should not be used.

If you do not want Exim to attempt to send messages unencrypted when an attempt to set up an encrypted connection fails in any way, you can set hosts_require_tls to a list of hosts for which encryption is mandatory. For those hosts, delivery is always deferred if an encrypted connection cannot be set up. If there are any other hosts for the address, they are tried in the usual way.

When the server host is not in hosts_require_tls, Exim may try to deliver the message unencrypted. It always does this if the response to STARTTLS is a 5xx code. For a temporary error code, or for a failure to negotiate a TLS session after a success response code, what happens is controlled by the tls_tempfail_tryclear option of the smtp transport. If it is false, delivery to this host is deferred, and other hosts (if available) are tried. If it is true, Exim attempts to deliver unencrypted after a 4xx response to STARTTLS, and if STARTTLS is accepted, but the subsequent TLS negotiation fails, Exim closes the current connection (because it is in an unknown state), opens a new one to the same host, and then tries the delivery unencrypted.

The tls_certificate and tls_privatekey options of the smtp transport provide the client with a certificate, which is passed to the server if it requests it. This is an optional thing for TLS connections, although either end may insist on it. If the server is Exim, it will request a certificate only if tls_verify_hosts or tls_try_verify_hosts matches the client.

Note: Do not use a certificate which has the OCSP-must-staple extension, for client use (they are usable for server use). As the TLS protocol has no means for the client to staple before TLS 1.3 it will result in failed connections.

If the tls_verify_certificates option is set on the smtp transport, it specifies a collection of expected server certificates. These may be the system default set (depending on library version), a file, or (depending on library version) a directory. The client verifies the server’s certificate against this collection, taking into account any revoked certificates that are in the list defined by tls_crl. Failure to verify fails the TLS connection unless either of the tls_verify_hosts or tls_try_verify_hosts options are set.

The tls_verify_hosts and tls_try_verify_hosts options restrict certificate verification to the listed servers. Verification either must or need not succeed respectively.

The tls_verify_cert_hostnames option lists hosts for which additional name checks are made on the server certificate. The match against this list is, as per other Exim usage, the IP for the host. That is most closely associated with the name on the DNS A (or AAAA) record for the host. However, the name that needs to be in the certificate is the one at the head of any CNAME chain leading to the A record. The option defaults to always checking.

The smtp transport has two OCSP-related options: hosts_require_ocsp; a host-list for which a Certificate Status is requested and required for the connection to proceed. The default value is empty. hosts_request_ocsp; a host-list for which (additionally) a Certificate Status is requested (but not necessarily verified). The default value is "*" meaning that requests are made unless configured otherwise.

The host(s) should also be in hosts_require_tls, and tls_verify_certificates configured for the transport, for OCSP to be relevant.

If tls_require_ciphers is set on the smtp transport, it must contain a list of permitted cipher suites. If either of these checks fails, delivery to the current host is abandoned, and the smtp transport tries to deliver to alternative hosts, if any.

Note: These options must be set in the smtp transport for Exim to use TLS when it is operating as a client. Exim does not assume that a server certificate (set by the global options of the same name) should also be used when operating as a client.

All the TLS options in the smtp transport are expanded before use, with $host and $host_address containing the name and address of the server to which the client is connected. Forced failure of an expansion causes Exim to behave as if the relevant option were unset.

Before an SMTP connection is established, the $tls_out_bits, $tls_out_cipher, $tls_out_peerdn and $tls_out_sni variables are emptied. (Until the first connection, they contain the values that were set when the message was received.) If STARTTLS is subsequently successfully obeyed, these variables are set to the relevant values for the outgoing connection.

7.1. Caching of static client configuration items

If any of the transport configuration options tls_certificate, tls_privatekey and tls_crl have values with no expandable elements, then the associated information is loaded per smtp transport at daemon startup, at the start of a queue run, or on a command-line specified message delivery. It is made available to child processes forked for handling making SMTP connections.

This caching is currently only supported under Linux.

If caching is not possible, the load of the associated information is done at the startup of the TLS connection.

The cache is invalidated in the daemon and reloaded after any changes to the directories containing files specified by these options.

The information specified by the main option tls_verify_certificates is similarly cached so long as it specifies files explicitly or (under GnuTLS) is the string “system,cache”. The latter case is not automatically invaludated; it is the operator’s responsibility to arrange for a daemon restart any time the system certificate authority bundle is updated. A HUP signal is sufficient for this. The value “system” results in no caching under GnuTLS.

The macro _HAVE_TLS_CA_CACHE will be defined if the suffix for "system" is acceptable in configurations for the Exim executable.

Caching of the system Certificate Authorities bundle can save significant time and processing on every TLS connection initiated by Exim.

8. Use of TLS Server Name Indication

With TLS1.0 or above, there is an extension mechanism by which extra information can be included at various points in the protocol. One of these extensions, documented in RFC 6066 (and before that RFC 4366) is “Server Name Indication”, commonly “SNI”. This extension is sent by the client in the initial handshake, so that the server can examine the servername within and possibly choose to use different certificates and keys (and more) for this session.

This is analogous to HTTP’s “Host:” header, and is the main mechanism by which HTTPS-enabled web-sites can be virtual-hosted, many sites to one IP address.

With SMTP to MX, there are the same problems here as in choosing the identity against which to validate a certificate: you can’t rely on insecure DNS to provide the identity which you then cryptographically verify. So this will be of limited use in that environment.

With SMTP to Submission, there is a well-defined hostname which clients are connecting to and can validate certificates against. Thus clients can choose to include this information in the TLS negotiation. If this becomes wide-spread, then hosters can choose to present different certificates to different clients. Or even negotiate different cipher suites.

The tls_sni option on an SMTP transport is an expanded string; the result, if not empty, will be sent on a TLS session as part of the handshake. There’s nothing more to it. Choosing a sensible value not derived insecurely is the only point of caution. The $tls_out_sni variable will be set to this string for the lifetime of the client connection (including during authentication).

If DANE validated the connection attempt then the value of the tls_sni option is forced to the name of the destination host, after any MX- or CNAME-following.

Except during SMTP client sessions, if $tls_in_sni is set then it is a string received from a client. It can be logged with the log_selector item +tls_sni.

If the string tls_in_sni appears in the main section’s tls_certificate option (prior to expansion) then the following options will be re-expanded during TLS session handshake, to permit alternative values to be chosen:

  • tls_certificate

  • tls_crl

  • tls_privatekey

  • tls_verify_certificates

  • tls_ocsp_file

Great care should be taken to deal with matters of case, various injection attacks in the string (../ or SQL), and ensuring that a valid filename can always be referenced; it is important to remember that $tls_in_sni is arbitrary unverified data provided prior to authentication. Further, the initial certificate is loaded before SNI has arrived, so an expansion for tls_certificate must have a default which is used when $tls_in_sni is empty.

The Exim developers are proceeding cautiously and so far no other TLS options are re-expanded.

When Exim is built against OpenSSL, OpenSSL must have been built with support for TLS Extensions. This holds true for OpenSSL 1.0.0+ and 0.9.8+ with enable-tlsext in EXTRACONFIGURE. If you invoke openssl s_client -h and see -servername in the output, then OpenSSL has support.

When Exim is built against GnuTLS, SNI support is available as of GnuTLS 0.5.10. (Its presence predates the current API which Exim uses, so if Exim built, then you have SNI support).

8.1. ALPN

There is a TLS feature related to SNI called Application Layer Protocol Name (ALPN). This is intended to declare, or select, what protocol layer will be using a TLS connection. The client for the connection proposes a set of protocol names, and the server responds with a selected one. It is not, as of 2021, commonly used for SMTP connections. However, to guard against misdirected or malicious use of web clients (which often do use ALPN) against MTA ports, Exim by default check that there is no incompatible ALPN specified by a client for a TLS connection. If there is, the connection is rejected.

As a client Exim does not supply ALPN by default. The behaviour of both client and server can be configured using the options tls_alpn and hosts_require_alpn. There are no variables providing observability. Some feature-specific logging may appear on denied connections, but this depends on the behaviour of the peer (not all peers can send a feature-specific TLS Alert).

This feature is available when Exim is built with OpenSSL 1.1.0 or later or GnuTLS 3.2.0 or later; the macro _HAVE_TLS_ALPN will be defined when this is so.

9. Multiple messages on the same encrypted TCP/IP connection

Exim sends multiple messages down the same TCP/IP connection by starting up an entirely new delivery process for each message, passing the socket from one process to the next. This implementation does not fit well with the use of TLS, because there is quite a lot of state information associated with a TLS connection, not just a socket identification. Passing all the state information to a new process is not feasible. Consequently, for sending using TLS Exim starts an additional proxy process for handling the encryption, piping the unencrypted data stream from and to the delivery processes.

An older mode of operation can be enabled on a per-host basis by the hosts_noproxy_tls option on the smtp transport. If the host matches this list the proxy process described above is not used; instead Exim shuts down an existing TLS session being run by the delivery process before passing the socket to a new process. The new process may then try to start a new TLS session, and if successful, may try to re-authenticate if AUTH is in use, before sending the next message.

The RFC is not clear as to whether or not an SMTP session continues in clear after TLS has been shut down, or whether TLS may be restarted again later, as just described. However, if the server is Exim, this shutdown and reinitialization works. It is not known which (if any) other servers operate successfully if the client closes a TLS session and continues with unencrypted SMTP, but there are certainly some that do not work. For such servers, Exim should not pass the socket to another process, because the failure of the subsequent attempt to use it would cause Exim to record a temporary host error, and delay other deliveries to that host.

To test for this case, Exim sends an EHLO command to the server after closing down the TLS session. If this fails in any way, the connection is closed instead of being passed to a new delivery process, but no retry information is recorded.

There is also a manual override; you can set hosts_nopass_tls on the smtp transport to match those hosts for which Exim should not pass connections to new processes if TLS has been used.

10. Certificates and all that

In order to understand fully how TLS works, you need to know about certificates, certificate signing, and certificate authorities. This is a large topic and an introductory guide is unsuitable for the Exim reference manual, so instead we provide pointers to existing documentation.

The Apache web-server was for a long time the canonical guide, so their documentation is a good place to start; their SSL module’s Introduction document is currently at

and their FAQ is at

Eric Rescorla’s book, SSL and TLS, published by Addison-Wesley (ISBN 0-201-61598-3) in 2001, contains both introductory and more in-depth descriptions. More recently Ivan Ristić’s book Bulletproof SSL and TLS, published by Feisty Duck (ISBN 978-1907117046) in 2013 is good. Ivan is the author of the popular TLS testing tools at https://www.ssllabs.com/.

10.1. Certificate chains

A file named by tls_certificate may contain more than one certificate. This is useful in the case where the certificate that is being sent is validated by an intermediate certificate which the other end does not have. Multiple certificates must be in the correct order in the file. First the host’s certificate itself, then the first intermediate certificate to validate the issuer of the host certificate, then the next intermediate certificate to validate the issuer of the first intermediate certificate, and so on, until finally (optionally) the root certificate. The root certificate must already be trusted by the recipient for validation to succeed, of course, but if it’s not preinstalled, sending the root certificate along with the rest makes it available for the user to install if the receiving end is a client MUA that can interact with a user.

Note that certificates using MD5 are unlikely to work on today’s Internet; even if your libraries allow loading them for use in Exim when acting as a server, increasingly clients will not accept such certificates. The error diagnostics in such a case can be frustratingly vague.

10.2. Self-signed certificates

You can create a self-signed certificate using the req command provided with OpenSSL, like this:

openssl req -x509 -newkey rsa:1024 -keyout file1 -out file2 \
            -days 9999 -nodes

file1 and file2 can be the same file; the key and the certificate are delimited and so can be identified independently. The -days option specifies a period for which the certificate is valid. The -nodes option is important: if you do not set it, the key is encrypted with a passphrase that you are prompted for, and any use that is made of the key causes more prompting for the passphrase. This is not helpful if you are going to use this certificate and key in an MTA, where prompting is not possible.

NB: we are now past the point where 9999 days takes us past the 32-bit Unix epoch. If your system uses unsigned time_t (most do) and is 32-bit, then the above command might produce a date in the past. Think carefully about the lifetime of the systems you’re deploying, and either reduce the duration of the certificate or reconsider your platform deployment. (At time of writing, reducing the duration is the most likely choice, but the inexorable progression of time takes us steadily towards an era where this will not be a sensible resolution).

A self-signed certificate made in this way is sufficient for testing, and may be adequate for all your requirements if you are mainly interested in encrypting transfers, and not in secure identification.

However, many clients require that the certificate presented by the server be a user (also called “leaf” or “site”) certificate, and not a self-signed certificate. In this situation, the self-signed certificate described above must be installed on the client host as a trusted root certification authority (CA), and the certificate used by Exim must be a user certificate signed with that self-signed certificate.

For information on creating self-signed CA certificates and using them to sign user certificates, see the General implementation overview chapter of the Open-source PKI book, available online at https://sourceforge.net/projects/ospkibook/.

10.3. Revoked certificates

There are three ways for a certificate to be made unusable before its expiry.

  • Certificate issuing authorities issue Certificate Revocation Lists (CRLs) when certificates are revoked. If you have such a list, you can pass it to an Exim server using the global option called tls_crl and to an Exim client using an identically named option for the smtp transport. In each case, the value of the option is expanded and must then be the name of a file that contains a CRL in PEM format. The downside is that clients have to periodically re-download a potentially huge file from every certificate authority they know of.

  • The way with most moving parts at query time is Online Certificate Status Protocol (OCSP), where the client verifies the certificate against an OCSP server run by the CA. This lets the CA track all usage of the certs. It requires running software with access to the private key of the CA, to sign the responses to the OCSP queries. OCSP is based on HTTP and can be proxied accordingly.

    The only widespread OCSP server implementation (known to this writer) comes as part of OpenSSL and aborts on an invalid request, such as connecting to the port and then disconnecting. This requires re-entering the passphrase each time some random client does this.

  • The third way is OCSP Stapling; in this, the server using a certificate issued by the CA periodically requests an OCSP proof of validity from the OCSP server, then serves it up inline as part of the TLS negotiation. This approach adds no extra round trips, does not let the CA track users, scales well with number of certs issued by the CA and is resilient to temporary OCSP server failures, as long as the server starts retrying to fetch an OCSP proof some time before its current proof expires. The downside is that it requires server support.

    Unless Exim is built with the support disabled, or with GnuTLS earlier than version 3.3.16 / 3.4.8 support for OCSP stapling is included.

    There is a global option called tls_ocsp_file. The file specified therein is expected to be in DER format, and contain an OCSP proof. Exim will serve it as part of the TLS handshake. This option will be re-expanded for SNI, if the tls_certificate option contains tls_in_sni, as per other TLS options.

    Exim does not at this time implement any support for fetching a new OCSP proof. The burden is on the administrator to handle this, outside of Exim. The file specified should be replaced atomically, so that the contents are always valid. Exim will expand the tls_ocsp_file option on each connection, so a new file will be handled transparently on the next connection.

    When built with OpenSSL Exim will check for a valid next update timestamp in the OCSP proof; if not present, or if the proof has expired, it will be ignored.

    For the client to be able to verify the stapled OCSP the server must also supply, in its stapled information, any intermediate certificates for the chain leading to the OCSP proof from the signer of the server certificate. There may be zero or one such. These intermediate certificates should be added to the server OCSP stapling file named by tls_ocsp_file.

    Note that the proof only covers the terminal server certificate, not any of the chain from CA to it.

    There is no current way to staple a proof for a client certificate.

      A helper script "ocsp_fetch.pl" for fetching a proof from a CA
      OCSP server is supplied.  The server URL may be included in the
      server certificate, if the CA is helpful.
    
      One failure mode seen was the OCSP Signer cert expiring before the end
      of validity of the OCSP proof. The checking done by Exim/OpenSSL
      noted this as invalid overall, but the re-fetch script did not.
    

11. TLS Resumption

TLS Session Resumption for TLS 1.2 and TLS 1.3 connections can be used (defined in RFC 5077 for 1.2). The support for this requires GnuTLS 3.6.3 or OpenSSL 1.1.1 (or later).

Session resumption (this is the "stateless" variant) involves the server sending a "session ticket" to the client on one connection, which can be stored by the client and used for a later session. The ticket contains sufficient state for the server to reconstruct the TLS session, avoiding some expensive crypto calculation and (on TLS1.2) one full packet roundtrip time.

  • Operational cost/benefit:

    The extra data being transmitted costs a minor amount, and the client has extra costs in storing and retrieving the data.

    In the Exim/Gnutls implementation the extra cost on an initial connection which is TLS1.2 over a loopback path is about 6ms on 2017-laptop class hardware. The saved cost on a subsequent connection is about 4ms; three or more connections become a net win. On longer network paths, two or more connections will have an average lower startup time thanks to the one saved packet roundtrip. TLS1.3 will save the crypto cpu costs but not any packet roundtrips.

    Since a new hints DB is used on the TLS client, the hints DB maintenance should be updated to additionally handle "tls".

  • Security aspects:

    The session ticket is encrypted, but is obviously an additional security vulnarability surface. An attacker able to decrypt it would have access all connections using the resumed session. The session ticket encryption key is not committed to storage by the server and is rotated regularly (OpenSSL: 1hr, and one previous key is used for overlap; GnuTLS 6hr but does not specify any overlap). Tickets have limited lifetime (2hr, and new ones issued after 1hr under OpenSSL. GnuTLS 2hr, appears to not do overlap).

    There is a question-mark over the security of the Diffie-Helman parameters used for session negotiation.

  • Observability:

    The log_selector "tls_resumption" appends an asterisk to the tls_cipher "X=" element.

    The variables $tls_in_resumption and $tls_out_resumption have bits 0-4 indicating respectively support built, client requested ticket, client offered session, server issued ticket, resume used. A suitable decode list is provided in the builtin macro _RESUME_DECODE for in listextract expansions.

  • Control:

    The tls_resumption_hosts main option specifies a hostlist for which exim, operating as a server, will offer resumption to clients. Current best practice is to not offer the feature to MUA connection. Commonly this can be done like this:

    tls_resumption_hosts = ${if inlist {$received_port}{587:465} {:}{*}}
    

    If the peer host matches the list after expansion then resumption is offered and/or accepted.

    The tls_resumption_hosts smtp transport option performs the equivalent function for operation as a client. If the peer host matches the list after expansion then resumption is attempted (if a stored session is available) or the information stored (if supplied by the peer).

  • Issues:

    In a resumed session:

    • The variables $tls_{in,out}_cipher will have values different to the original (under GnuTLS).

    • The variables $tls_{in,out}_ocsp will be "not requested" or "no response", and the hosts_require_ocsp smtp trasnport option will fail.

12. DANE

DNS-based Authentication of Named Entities, as applied to SMTP over TLS, provides assurance to a client that it is actually talking to the server it wants to rather than some attacker operating a Man In The Middle (MITM) operation. The latter can terminate the TLS connection you make, and make another one to the server (so both you and the server still think you have an encrypted connection) and, if one of the "well known" set of Certificate Authorities has been suborned - something which *has* been seen already (2014), a verifiable certificate (if you’re using normal root CAs, eg. the Mozilla set, as your trust anchors).

What DANE does is replace the CAs with the DNS as the trust anchor. The assurance is limited to a) the possibility that the DNS has been suborned, b) mistakes made by the admins of the target server. The attack surface presented by (a) is thought to be smaller than that of the set of root CAs.

It also allows the server to declare (implicitly) that connections to it should use TLS. An MITM could simply fail to pass on a server’s STARTTLS.

DANE scales better than having to maintain (and communicate via side-channel) copies of server certificates for every possible target server. It also scales (slightly) better than having to maintain on an SMTP client a copy of the standard CAs bundle. It also means not having to pay a CA for certificates.

DANE requires a server operator to do three things:

  1. Run DNSSEC. This provides assurance to clients that DNS lookups they do for the server have not been tampered with. The domain MX record applying to this server, its A record, its TLSA record and any associated CNAME records must all be covered by DNSSEC.

  2. Add TLSA DNS records. These say what the server certificate for a TLS connection should be.

  3. Offer a server certificate, or certificate chain, in TLS connections which is anchored by one of the TLSA records.

There are no changes to Exim specific to server-side operation of DANE. Support for client-side operation of DANE can be included at compile time by defining SUPPORT_DANE=yes in Local/Makefile. If it has been included, the macro "_HAVE_DANE" will be defined.

12.1. DNS records

A TLSA record consist of 4 fields, the "Certificate Usage", the "Selector", the "Matching type", and the "Certificate Association Data". For a detailed description of the TLSA record see RFC 7671.

The TLSA record for the server may have "Certificate Usage" (1st) field of DANE-TA(2) or DANE-EE(3). These are the "Trust Anchor" and "End Entity" variants. The latter specifies the End Entity directly, i.e. the certificate involved is that of the server (and if only DANE-EE is used then it should be the sole one transmitted during the TLS handshake); this is appropriate for a single system, using a self-signed certificate. DANE-TA usage is effectively declaring a specific CA to be used; this might be a private CA or a public, well-known one. A private CA at simplest is just a self-signed certificate (with certain attributes) which is used to sign server certificates, but running one securely does require careful arrangement. With DANE-TA, as implemented in Exim and commonly in other MTAs, the server TLS handshake must transmit the entire certificate chain from CA to server-certificate. DANE-TA is commonly used for several services and/or servers, each having a TLSA query-domain CNAME record, all of which point to a single TLSA record. DANE-TA and DANE-EE can both be used together.

Our recommendation is to use DANE with a certificate from a public CA, because this enables a variety of strategies for remote clients to verify your certificate. You can then publish information both via DANE and another technology, "MTA-STS", described below.

When you use DANE-TA to publish trust anchor information, you ask entities outside your administrative control to trust the Certificate Authority for connections to you. If using a private CA then you should expect others to still apply the technical criteria they’d use for a public CA to your certificates. In particular, you should probably try to follow current best practices for CA operation around hash algorithms and key sizes. Do not expect other organizations to lower their security expectations just because a particular profile might be reasonable for your own internal use.

When this text was last updated, this in practice means to avoid use of SHA-1 and MD5; if using RSA to use key sizes of at least 2048 bits (and no larger than 4096, for interoperability); to use keyUsage fields correctly; to use random serial numbers. The list of requirements is subject to change as best practices evolve. If you’re not already using a private CA, or it doesn’t meet these requirements, then we encourage you to avoid all these issues and use a public CA such as Let’s Encrypt instead.

The TLSA record should have a "Selector" (2nd) field of SPKI(1) and a "Matching Type" (3rd) field of SHA2-512(2).

For the "Certificate Authority Data" (4th) field, commands like

  openssl x509 -pubkey -noout <certificate.pem \
  | openssl rsa -outform der -pubin 2>/dev/null \
  | openssl sha512 \
  | awk '{print $2}'

are workable to create a hash of the certificate’s public key.

An example TLSA record for DANE-EE(3), SPKI(1), and SHA-512 (2) looks like

  _25._tcp.mail.example.com. TLSA 3 1 2 8BA8A336E...

At the time of writing, https://www.huque.com/bin/gen_tlsa is useful for quickly generating TLSA records.

For use with the DANE-TA model, server certificates must have a correct name (SubjectName or SubjectAltName).

The Certificate issued by the CA published in the DANE-TA model should be issued using a strong hash algorithm. Exim, and importantly various other MTAs sending to you, will not re-enable hash algorithms which have been disabled by default in TLS libraries. This means no MD5 and no SHA-1. SHA2-256 is the minimum for reliable interoperability (and probably the maximum too, in 2018).

12.2. Interaction with OCSP

The use of OCSP-stapling should be considered, allowing for fast revocation of certificates (which would otherwise be limited by the DNS TTL on the TLSA records). However, this is likely to only be usable with DANE-TA. NOTE: the default of requesting OCSP for all hosts is modified iff DANE is in use, to:

  hosts_request_ocsp = ${if or { {= {0}{$tls_out_tlsa_usage}} \
				 {= {4}{$tls_out_tlsa_usage}} } \
                         {*}{}}

The (new) variable $tls_out_tlsa_usage is a bitfield with numbered bits set for TLSA record usage codes. The zero above means DANE was not in use, the four means that only DANE-TA usage TLSA records were found. If the definition of hosts_request_ocsp includes the string "tls_out_tlsa_usage", they are re-expanded in time to control the OCSP request.

This modification of hosts_request_ocsp is only done if it has the default value of "*". Admins who change it, and those who use hosts_require_ocsp, should consider the interaction with DANE in their OCSP settings.

12.3. Client configuration

For client-side DANE there are three new smtp transport options, hosts_try_dane, hosts_require_dane and dane_require_tls_ciphers. The “require” variant will result in failure if the target host is not DNSSEC-secured. To get DNSSEC-secured hostname resolution, use the dnssec_request_domains router or transport option.

DANE will only be usable if the target host has DNSSEC-secured MX, A and TLSA records.

A TLSA lookup will be done if either of the above options match and the host-lookup succeeded using DNSSEC. If a TLSA lookup is done and succeeds, a DANE-verified TLS connection will be required for the host. If it does not, the host will not be used; there is no fallback to non-DANE or non-TLS.

If DANE is requested and usable, then the TLS cipher list configuration prefers to use the option dane_require_tls_ciphers and falls back to tls_require_ciphers only if that is unset. This lets you configure "decent crypto" for DANE and "better than nothing crypto" as the default. Note though that while GnuTLS lets the string control which versions of TLS/SSL will be negotiated, OpenSSL does not and you’re limited to ciphersuite constraints.

If DANE is requested and useable (see above) the following transport options are ignored:

  hosts_require_tls
  tls_verify_hosts
  tls_try_verify_hosts
  tls_verify_certificates
  tls_crl
  tls_verify_cert_hostnames
  tls_sni

If DANE is not usable, whether requested or not, and CA-anchored verification evaluation is wanted, the above variables should be set appropriately.

The router and transport option dnssec_request_domains must not be set to “never”, and dnssec_require_domains is ignored.

12.4. Observability

If verification was successful using DANE then the "CV" item in the delivery log line will show as "CV=dane".

There is a new variable $tls_out_dane which will have "yes" if verification succeeded using DANE and "no" otherwise (only useful in combination with events; see 61), and a new variable $tls_out_tlsa_usage (detailed above).

An event (see 61) of type "dane:fail" will be raised on failures to achieve DANE-verified connection, if one was either requested and offered, or required. This is intended to support TLS-reporting as defined in https://tools.ietf.org/html/draft-ietf-uta-smtp-tlsrpt-17. The $event_data will be one of the Result Types defined in Section 4.3 of that document.

12.5. General

Under GnuTLS, DANE is only supported from version 3.0.0 onwards.

DANE is specified in published RFCs and decouples certificate authority trust selection from a "race to the bottom" of "you must trust everything for mail to get through". There is an alternative technology called MTA-STS, which instead publishes MX trust anchor information on an HTTPS website. At the time this text was last updated, MTA-STS was still a draft, not yet an RFC. Exim has no support for MTA-STS as a client, but Exim mail server operators can choose to publish information describing their TLS configuration using MTA-STS to let those clients who do use that protocol derive trust information.

The MTA-STS design requires a certificate from a public Certificate Authority which is recognized by clients sending to you. That selection of which CAs are trusted by others is outside your control.

The most interoperable course of action is probably to use Let’s Encrypt, with automated certificate renewal; to publish the anchor information in DNSSEC-secured DNS via TLSA records for DANE clients (such as Exim and Postfix) and to publish anchor information for MTA-STS as well. This is what is done for the exim.org domain itself (with caveats around occasionally broken MTA-STS because of incompatible specification changes prior to reaching RFC status).

<-previousTable of Contentsnext->