tls_sni as SMTP transport option.
Use correct storage pool for copying tls_sni, so survives for life of process.
Add +tls_sni log-selector, for inbound tls_sni.
Update exipick to handle -tls_sni in spool files.
Also reset tls_bits at start of outbound connection (was missing).
16 files changed:
a different certificate to be presented (and optionally a different key to be
used) to the client, based upon the value of the SNI extension.
a different certificate to be presented (and optionally a different key to be
used) to the client, based upon the value of the SNI extension.
-The value will be retained for the lifetime of the message, and not changed
-during outbound SMTP.
+The value will be retained for the lifetime of the message. During outbound
+SMTP deliveries, it reflects the value of the tls_sni option on the transport.
This is currently only available when using OpenSSL, built with support for
SNI.
This is currently only available when using OpenSSL, built with support for
SNI.
use when sending messages as a client, you must set the &%tls_certificate%&
option in the relevant &(smtp)& transport.
use when sending messages as a client, you must set the &%tls_certificate%&
option in the relevant &(smtp)& transport.
+.new
+If the option contains &$tls_sni$& and Exim is built against OpenSSL, then
+if the OpenSSL build supports TLS extensions and the TLS client sends the
+Server Name Indication extension, then this option and &%tls_privatekey%&
+will be re-expanded.
+.wen
.option tls_crl main string&!! unset
.cindex "TLS" "server certificate revocation list"
.option tls_crl main string&!! unset
.cindex "TLS" "server certificate revocation list"
key is assumed to be in the same file as the server's certificates. See chapter
&<<CHAPTLS>>& for further details.
key is assumed to be in the same file as the server's certificates. See chapter
&<<CHAPTLS>>& for further details.
+.new
+See &%tls_certificate%& discussion of &$tls_sni$& for when this option may be
+re-expanded.
+.wen
+
.option tls_remember_esmtp main boolean false
.cindex "TLS" "esmtp state; remembering"
.option tls_remember_esmtp main boolean false
.cindex "TLS" "esmtp state; remembering"
+.new
+.option tls_sni smtp string&!! unset
+.cindex "TLS" "Server Name Indication"
+.vindex "&$tls_sni$&"
+If this option is set then it sets the $tls_sni variable and causes any
+TLS session to pass this value as the Server Name Indication extension to
+the remote side, which can be used by the remote side to select an appropriate
+certificate and private key for the session.
+
+OpenSSL only, also requiring a build of OpenSSL that supports TLS extensions.
+.wen
+
+
+
.option tls_tempfail_tryclear smtp boolean true
.cindex "4&'xx'& responses" "to STARTTLS"
When the server host is not in &%hosts_require_tls%&, and there is a problem in
.option tls_tempfail_tryclear smtp boolean true
.cindex "4&'xx'& responses" "to STARTTLS"
When the server host is not in &%hosts_require_tls%&, and there is a problem in
&` tls_certificate_verified `& certificate verification status
&`*tls_cipher `& TLS cipher suite on <= and => lines
&` tls_peerdn `& TLS peer DN on <= and => lines
&` tls_certificate_verified `& certificate verification status
&`*tls_cipher `& TLS cipher suite on <= and => lines
&` tls_peerdn `& TLS peer DN on <= and => lines
+&` tls_sni `& TLS SNI on <= lines
&` unknown_in_list `& DNS lookup failed in list match
&` all `& all of the above
&` unknown_in_list `& DNS lookup failed in list match
&` all `& all of the above
connection, and a certificate is supplied by the remote host, the peer DN is
added to the log line, preceded by DN=.
.next
connection, and a certificate is supplied by the remote host, the peer DN is
added to the log line, preceded by DN=.
.next
+.cindex "log" "TLS SNI"
+.cindex "TLS" "logging SNI"
+&%tls_sni%&: When a message is received over an encrypted connection, and
+the remote host provided the Server Name Indication extension, the SNI is
+added to the log line, preceded by SNI=.
+.next
.cindex "log" "DNS failure in list"
&%unknown_in_list%&: This setting causes a log entry to be written when the
result of a list match is failure because a DNS lookup failed.
.cindex "log" "DNS failure in list"
&%unknown_in_list%&: This setting causes a log entry to be written when the
result of a list match is failure because a DNS lookup failed.
PP/17 OpenSSL: new expansion var $tls_sni, which if used in tls_certificate
lets Exim select keys and certificates based upon TLS SNI from client.
PP/17 OpenSSL: new expansion var $tls_sni, which if used in tls_certificate
lets Exim select keys and certificates based upon TLS SNI from client.
+ Also option tls_sni on SMTP Transports. Also clear $tls_bits correctly
+ before an outbound SMTP session. New log_selector, +tls_sni.
sends the TLS Server Name Indication extension, to permit choosing a
different certificate; tls_privatekey will also be re-expanded. You must
still set these options to expand to valid files when $tls_sni is not set.
sends the TLS Server Name Indication extension, to permit choosing a
different certificate; tls_privatekey will also be re-expanded. You must
still set these options to expand to valid files when $tls_sni is not set.
+
+ The SMTP Transport has gained the option tls_sni, which will set a hostname
+ for outbound TLS sessions, and set $tls_sni too.
+
+ A new log_selector, +tls_sni, has been added, to log received SNI values
+ for Exim as a server.
+
tls_remember_emstp boolean false main 4.21
tls_require_ciphers string* unset smtp 4.00 replaces tls_verify_ciphers
string* unset main 4.33
tls_remember_emstp boolean false main 4.21
tls_require_ciphers string* unset smtp 4.00 replaces tls_verify_ciphers
string* unset main 4.33
+tls_sni string* unset main 4.78
tls_tempfail_tryclear boolean true smtp 4.05
tls_try_verify_hosts host list unset main 4.00
tls_verify_certificates string* unset main 3.20
tls_tempfail_tryclear boolean true smtp 4.05
tls_try_verify_hosts host list unset main 4.00
tls_verify_certificates string* unset main 3.20
$self->{_vars}{tls_cipher} = $arg;
} elsif ($tag eq '-tls_peerdn') {
$self->{_vars}{tls_peerdn} = $arg;
$self->{_vars}{tls_cipher} = $arg;
} elsif ($tag eq '-tls_peerdn') {
$self->{_vars}{tls_peerdn} = $arg;
+ } elsif ($tag eq '-tls_sni') {
+ $self->{_vars}{tls_sni} = $arg;
} elsif ($tag eq '-host_address') {
$self->{_vars}{sender_host_port} = $self->_get_host_and_port(\$arg);
$self->{_vars}{sender_host_address} = $arg;
} elsif ($tag eq '-host_address') {
$self->{_vars}{sender_host_port} = $self->_get_host_and_port(\$arg);
$self->{_vars}{sender_host_address} = $arg;
The value of the Distinguished Name of the certificate if Exim is configured to request one
The value of the Distinguished Name of the certificate if Exim is configured to request one
+=item S . $tls_sni
+
+The value of the Server Name Indication TLS extension sent by a client, if one was sent.
+
=item N + $warning_count
The number of delay warnings which have been sent for this message.
=item N + $warning_count
The number of delay warnings which have been sent for this message.
#ifdef SUPPORT_TLS
extern int tls_client_start(int, host_item *, address_item *, uschar *,
uschar *, uschar *, uschar *, uschar *, uschar *, uschar *,
#ifdef SUPPORT_TLS
extern int tls_client_start(int, host_item *, address_item *, uschar *,
uschar *, uschar *, uschar *, uschar *, uschar *, uschar *,
- uschar *, uschar *, int);
+ uschar *, uschar *, uschar *, int);
extern void tls_close(BOOL);
extern int tls_feof(void);
extern int tls_ferror(void);
extern void tls_close(BOOL);
extern int tls_feof(void);
extern int tls_ferror(void);
/* Those log options with L_xxx identifiers have values less than 0x800000 and
are the ones that get put into log_write_selector. They can be used in calls to
log_write() to test for the bit. The options with LX_xxx identifiers have
/* Those log options with L_xxx identifiers have values less than 0x800000 and
are the ones that get put into log_write_selector. They can be used in calls to
log_write() to test for the bit. The options with LX_xxx identifiers have
-values greater than 0x80000000 and are put int log_extra_selector (without the
+values greater than 0x80000000 and are put into log_extra_selector (without the
top bit). They are never used in calls to log_write(), but are tested
independently. This separation became necessary when the number of log
selectors was getting close to filling a 32-bit word. */
top bit). They are never used in calls to log_write(), but are tested
independently. This separation became necessary when the number of log
selectors was getting close to filling a 32-bit word. */
{ US"tls_certificate_verified", LX_tls_certificate_verified },
{ US"tls_cipher", LX_tls_cipher },
{ US"tls_peerdn", LX_tls_peerdn },
{ US"tls_certificate_verified", LX_tls_certificate_verified },
{ US"tls_cipher", LX_tls_cipher },
{ US"tls_peerdn", LX_tls_peerdn },
+ { US"tls_sni", LX_tls_sni },
{ US"unknown_in_list", LX_unknown_in_list }
};
{ US"unknown_in_list", LX_unknown_in_list }
};
extern int smtp_fflush(void);
extern void smtp_printf(const char *, ...) PRINTF_FUNCTION(1,2);
extern void smtp_vprintf(const char *, va_list);
extern int smtp_fflush(void);
extern void smtp_printf(const char *, ...) PRINTF_FUNCTION(1,2);
extern void smtp_vprintf(const char *, va_list);
-extern uschar *string_copy(uschar *);
+extern uschar *string_copy(const uschar *);
extern uschar *string_copyn(uschar *, int);
extern uschar *string_sprintf(const char *, ...) PRINTF_FUNCTION(1,2);
extern uschar *string_copyn(uschar *, int);
extern uschar *string_sprintf(const char *, ...) PRINTF_FUNCTION(1,2);
#define LX_tls_certificate_verified 0x80100000
#define LX_tls_cipher 0x80200000
#define LX_tls_peerdn 0x80400000
#define LX_tls_certificate_verified 0x80100000
#define LX_tls_cipher 0x80200000
#define LX_tls_peerdn 0x80400000
-#define LX_unknown_in_list 0x80800000
+#define LX_tls_sni 0x80800000
+#define LX_unknown_in_list 0x81000000
#define L_default (L_connection_reject | \
L_delay_delivery | \
#define L_default (L_connection_reject | \
L_delay_delivery | \
if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_peerdn != NULL)
s = string_append(s, &size, &sptr, 3, US" DN=\"",
string_printing(tls_peerdn), US"\"");
if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_peerdn != NULL)
s = string_append(s, &size, &sptr, 3, US" DN=\"",
string_printing(tls_peerdn), US"\"");
+#ifndef USE_GNUTLS
+if ((log_extra_selector & LX_tls_sni) != 0 && tls_sni != NULL)
+ s = string_append(s, &size, &sptr, 3, US" SNI=\"",
+ string_printing(tls_sni), US"\"");
+#endif
#endif
if (sender_host_authenticated != NULL)
#endif
if (sender_host_authenticated != NULL)
if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_peerdn != NULL)
s = string_append(s, &size, &ptr, 3, US" DN=\"",
string_printing(tls_peerdn), US"\"");
if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_peerdn != NULL)
s = string_append(s, &size, &ptr, 3, US" DN=\"",
string_printing(tls_peerdn), US"\"");
+#ifndef USE_GNUTLS
+if ((log_extra_selector & LX_tls_sni) != 0 && tls_sni != NULL)
+ s = string_append(s, &size, &ptr, 3, US" SNI=\"",
+ string_printing(tls_sni), US"\"");
+#endif
#endif
sep = (smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE)?
#endif
sep = (smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE)?
+string_copy(const uschar *s)
{
int len = Ustrlen(s) + 1;
uschar *ss = store_get(len);
{
int len = Ustrlen(s) + 1;
uschar *ss = store_get(len);
dhparam DH parameter file
certificate certificate file
privatekey private key file
dhparam DH parameter file
certificate certificate file
privatekey private key file
+ sni TLS SNI to send to remote host
verify_certs file for certificate verify
verify_crl CRL for verify
require_ciphers list of allowed ciphers or NULL
verify_certs file for certificate verify
verify_crl CRL for verify
require_ciphers list of allowed ciphers or NULL
int
tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam,
int
tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam,
- uschar *certificate, uschar *privatekey, uschar *verify_certs,
- uschar *verify_crl, uschar *require_ciphers, uschar *require_mac,
+ uschar *certificate, uschar *privatekey, uschar *sni ARG_UNUSED,
+ uschar *verify_certs, uschar *verify_crl,
+ uschar *require_ciphers, uschar *require_mac,
uschar *require_kx, uschar *require_proto, int timeout)
{
const gnutls_datum *server_certs;
uschar *require_kx, uschar *require_proto, int timeout)
{
const gnutls_datum *server_certs;
const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
const tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg;
int rc;
const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
const tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg;
int rc;
+int old_pool = store_pool;
if (!servername)
return SSL_TLSEXT_ERR_OK;
if (!servername)
return SSL_TLSEXT_ERR_OK;
-DEBUG(D_tls) debug_printf("TLS SNI: %s%s\n", servername,
+DEBUG(D_tls) debug_printf("Received TLS SNI \"%s\"%s\n", servername,
reexpand_tls_files_for_sni ? "" : " (unused for certificate selection)");
/* Make the extension value available for expansion */
reexpand_tls_files_for_sni ? "" : " (unused for certificate selection)");
/* Make the extension value available for expansion */
+store_pool = POOL_PERM;
+tls_sni = string_copy(US servername);
+store_pool = old_pool;
if (!reexpand_tls_files_for_sni)
return SSL_TLSEXT_ERR_OK;
if (!reexpand_tls_files_for_sni)
return SSL_TLSEXT_ERR_OK;
/* If we need to handle SNI, do so */
#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
/* If we need to handle SNI, do so */
#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
-/* We always do this, so that $tls_sni is available even if not used in
-tls_certificate */
-SSL_CTX_set_tlsext_servername_callback(ctx, tls_servername_cb);
-SSL_CTX_set_tlsext_servername_arg(ctx, cbinfo);
+if (host == NULL)
+ {
+ /* We always do this, so that $tls_sni is available even if not used in
+ tls_certificate */
+ SSL_CTX_set_tlsext_servername_callback(ctx, tls_servername_cb);
+ SSL_CTX_set_tlsext_servername_arg(ctx, cbinfo);
+ }
#endif
/* Set up the RSA callback */
#endif
/* Set up the RSA callback */
dhparam DH parameter file
certificate certificate file
privatekey private key file
dhparam DH parameter file
certificate certificate file
privatekey private key file
+ sni TLS SNI to send to remote host
verify_certs file for certificate verify
crl file containing CRL
require_ciphers list of allowed ciphers
verify_certs file for certificate verify
crl file containing CRL
require_ciphers list of allowed ciphers
int
tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam,
int
tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam,
- uschar *certificate, uschar *privatekey, uschar *verify_certs, uschar *crl,
+ uschar *certificate, uschar *privatekey, uschar *sni,
+ uschar *verify_certs, uschar *crl,
uschar *require_ciphers, uschar *require_mac, uschar *require_kx,
uschar *require_proto, int timeout)
{
uschar *require_ciphers, uschar *require_mac, uschar *require_kx,
uschar *require_proto, int timeout)
{
SSL_set_fd(ssl, fd);
SSL_set_connect_state(ssl);
SSL_set_fd(ssl, fd);
SSL_set_connect_state(ssl);
+if (sni)
+ {
+ if (!expand_check(sni, US"tls_sni", &tls_sni))
+ return FAIL;
+ if (!Ustrlen(tls_sni))
+ tls_sni = NULL;
+ else
+ {
+ DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tls_sni);
+ SSL_set_tlsext_host_name(ssl, tls_sni);
+ }
+ }
+
/* There doesn't seem to be a built-in timeout on connection. */
DEBUG(D_tls) debug_printf("Calling SSL_connect\n");
/* There doesn't seem to be a built-in timeout on connection. */
DEBUG(D_tls) debug_printf("Calling SSL_connect\n");
SSL_free(ssl);
ssl = NULL;
tls_active = -1;
SSL_free(ssl);
ssl = NULL;
tls_active = -1;
tls_cipher = NULL;
tls_peerdn = NULL;
tls_cipher = NULL;
tls_peerdn = NULL;
(void *)offsetof(smtp_transport_options_block, tls_crl) },
{ "tls_privatekey", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_privatekey) },
(void *)offsetof(smtp_transport_options_block, tls_crl) },
{ "tls_privatekey", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_privatekey) },
- { "tls_require_ciphers", opt_stringptr,
+ { "tls_require_ciphers", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_require_ciphers) },
(void *)offsetof(smtp_transport_options_block, tls_require_ciphers) },
+ { "tls_sni", opt_stringptr,
+ (void *)offsetof(smtp_transport_options_block, tls_sni) },
{ "tls_tempfail_tryclear", opt_bool,
(void *)offsetof(smtp_transport_options_block, tls_tempfail_tryclear) },
{ "tls_verify_certificates", opt_stringptr,
{ "tls_tempfail_tryclear", opt_bool,
(void *)offsetof(smtp_transport_options_block, tls_tempfail_tryclear) },
{ "tls_verify_certificates", opt_stringptr,
NULL, /* gnutls_require_mac */
NULL, /* gnutls_require_proto */
NULL, /* tls_verify_certificates */
NULL, /* gnutls_require_mac */
NULL, /* gnutls_require_proto */
NULL, /* tls_verify_certificates */
- TRUE /* tls_tempfail_tryclear */
+ TRUE, /* tls_tempfail_tryclear */
+ NULL /* tls_sni */
#endif
#ifndef DISABLE_DKIM
,NULL, /* dkim_canon */
#endif
#ifndef DISABLE_DKIM
,NULL, /* dkim_canon */
/* Reset the parameters of a TLS session. */
/* Reset the parameters of a TLS session. */
tls_cipher = NULL;
tls_peerdn = NULL;
tls_cipher = NULL;
tls_peerdn = NULL;
/* If an authenticated_sender override has been specified for this transport
instance, expand it. If the expansion is forced to fail, and there was already
/* If an authenticated_sender override has been specified for this transport
instance, expand it. If the expansion is forced to fail, and there was already
NULL, /* No DH param */
ob->tls_certificate,
ob->tls_privatekey,
NULL, /* No DH param */
ob->tls_certificate,
ob->tls_privatekey,
ob->tls_verify_certificates,
ob->tls_crl,
ob->tls_require_ciphers,
ob->tls_verify_certificates,
ob->tls_crl,
ob->tls_require_ciphers,
uschar *gnutls_require_proto;
uschar *tls_verify_certificates;
BOOL tls_tempfail_tryclear;
uschar *gnutls_require_proto;
uschar *tls_verify_certificates;
BOOL tls_tempfail_tryclear;
#endif
#ifndef DISABLE_DKIM
uschar *dkim_domain;
#endif
#ifndef DISABLE_DKIM
uschar *dkim_domain;