summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/doc-docbook/spec.xfpt36
-rw-r--r--doc/doc-txt/ChangeLog2
-rw-r--r--doc/doc-txt/NewStuff7
-rw-r--r--doc/doc-txt/OptionLists.txt1
-rw-r--r--src/src/exipick.src6
-rw-r--r--src/src/functions.h2
-rw-r--r--src/src/globals.c3
-rw-r--r--src/src/local_scan.h2
-rw-r--r--src/src/macros.h3
-rw-r--r--src/src/receive.c5
-rw-r--r--src/src/smtp_in.c5
-rw-r--r--src/src/string.c2
-rw-r--r--src/src/tls-gnu.c6
-rw-r--r--src/src/tls-openssl.c37
-rw-r--r--src/src/transports/smtp.c10
-rw-r--r--src/src/transports/smtp.h1
16 files changed, 110 insertions, 18 deletions
diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index 32e24ca80..ea4e040e1 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -11899,8 +11899,8 @@ If the variable appears in &%tls_certificate%& then this option and
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.
@@ -15627,6 +15627,12 @@ receiving incoming messages as a server. If you want to supply certificates for
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"
@@ -15659,6 +15665,11 @@ the expansion is forced to fail, or the result is an empty string, the private
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"
@@ -22371,6 +22382,20 @@ ciphers is a preference order.
+.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
@@ -33155,6 +33180,7 @@ selection marked by asterisks:
&` 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
@@ -33450,6 +33476,12 @@ connection, the cipher suite used is added to the log line, preceded by X=.
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.
diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index 4ad79c28e..55cde6dcf 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -75,6 +75,8 @@ PP/16 Removed "dont_insert_empty_fragments" fron "openssl_options".
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.
Exim version 4.77
diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff
index b788b45dc..2872d241f 100644
--- a/doc/doc-txt/NewStuff
+++ b/doc/doc-txt/NewStuff
@@ -47,6 +47,13 @@ Version 4.78
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.
+
Currently OpenSSL only.
diff --git a/doc/doc-txt/OptionLists.txt b/doc/doc-txt/OptionLists.txt
index b10f3f1aa..52a24b198 100644
--- a/doc/doc-txt/OptionLists.txt
+++ b/doc/doc-txt/OptionLists.txt
@@ -554,6 +554,7 @@ tls_privatekey string* unset main
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
diff --git a/src/src/exipick.src b/src/src/exipick.src
index 811092dc1..ed3b66154 100644
--- a/src/src/exipick.src
+++ b/src/src/exipick.src
@@ -955,6 +955,8 @@ sub _parse_header {
$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;
@@ -1793,6 +1795,10 @@ The cipher suite that was negotiated for encrypted SMTP connections.
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.
diff --git a/src/src/functions.h b/src/src/functions.h
index f1af42ee5..220235235 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -23,7 +23,7 @@ extern uschar *init_perl(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);
diff --git a/src/src/globals.c b/src/src/globals.c
index 7985cd3f0..f11c7c2db 100644
--- a/src/src/globals.c
+++ b/src/src/globals.c
@@ -697,7 +697,7 @@ uschar *log_file_path = US LOG_FILE_PATH
/* 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. */
@@ -746,6 +746,7 @@ bit_table log_options[] = {
{ 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 }
};
diff --git a/src/src/local_scan.h b/src/src/local_scan.h
index 25b194407..aedfc9f92 100644
--- a/src/src/local_scan.h
+++ b/src/src/local_scan.h
@@ -186,7 +186,7 @@ extern uschar *rfc2047_decode(uschar *, BOOL, uschar *, int, int *, uschar **);
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);
diff --git a/src/src/macros.h b/src/src/macros.h
index c1c4cc33f..9b41226e5 100644
--- a/src/src/macros.h
+++ b/src/src/macros.h
@@ -407,7 +407,8 @@ set all the bits in a multi-word selector. */
#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 | \
diff --git a/src/src/receive.c b/src/src/receive.c
index 71052657c..aaaf64ce6 100644
--- a/src/src/receive.c
+++ b/src/src/receive.c
@@ -3488,6 +3488,11 @@ if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
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)
diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
index 23bc5315e..d1c10f00f 100644
--- a/src/src/smtp_in.c
+++ b/src/src/smtp_in.c
@@ -841,6 +841,11 @@ if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
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)?
diff --git a/src/src/string.c b/src/src/string.c
index 0a321ee1f..3fea7c048 100644
--- a/src/src/string.c
+++ b/src/src/string.c
@@ -415,7 +415,7 @@ Returns: copy of string in new store
*/
uschar *
-string_copy(uschar *s)
+string_copy(const uschar *s)
{
int len = Ustrlen(s) + 1;
uschar *ss = store_get(len);
diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
index 2f952e47b..7e87dded0 100644
--- a/src/src/tls-gnu.c
+++ b/src/src/tls-gnu.c
@@ -1055,6 +1055,7 @@ Arguments:
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
@@ -1069,8 +1070,9 @@ Returns: OK/DEFER/FAIL (because using common functions),
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;
diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
index 8cc2457e5..e609670ee 100644
--- a/src/src/tls-openssl.c
+++ b/src/src/tls-openssl.c
@@ -385,15 +385,18 @@ tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg)
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;
-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 */
-tls_sni = servername;
+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;
@@ -550,10 +553,13 @@ if (rc != OK) return rc;
/* 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 */
@@ -944,6 +950,7 @@ Argument:
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
@@ -961,7 +968,8 @@ Returns: OK on success
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)
{
@@ -1000,6 +1008,19 @@ SSL_set_session_id_context(ssl, sid_ctx, Ustrlen(sid_ctx));
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");
@@ -1078,8 +1099,10 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm)
SSL_free(ssl);
ssl = NULL;
tls_active = -1;
+ tls_bits = 0;
tls_cipher = NULL;
tls_peerdn = NULL;
+ tls_sni = NULL;
return smtp_getc();
}
diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
index c571d874c..b1fedd2d4 100644
--- a/src/src/transports/smtp.c
+++ b/src/src/transports/smtp.c
@@ -128,8 +128,10 @@ optionlist smtp_transport_options[] = {
(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) },
+ { "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,
@@ -191,7 +193,8 @@ smtp_transport_options_block smtp_transport_option_defaults = {
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 */
@@ -889,8 +892,10 @@ outblock.authenticating = FALSE;
/* Reset the parameters of a TLS session. */
+tls_bits = 0;
tls_cipher = NULL;
tls_peerdn = NULL;
+tls_sni = 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
@@ -1122,6 +1127,7 @@ if (tls_offered && !suppress_tls &&
NULL, /* No DH param */
ob->tls_certificate,
ob->tls_privatekey,
+ ob->tls_sni,
ob->tls_verify_certificates,
ob->tls_crl,
ob->tls_require_ciphers,
diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h
index a2ea4ff0a..605be4800 100644
--- a/src/src/transports/smtp.h
+++ b/src/src/transports/smtp.h
@@ -54,6 +54,7 @@ typedef struct {
uschar *gnutls_require_proto;
uschar *tls_verify_certificates;
BOOL tls_tempfail_tryclear;
+ uschar *tls_sni;
#endif
#ifndef DISABLE_DKIM
uschar *dkim_domain;