From da3ad30dcfbb4770835c2b7e165bb719f76cfc16 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Thu, 3 May 2012 19:11:49 -0700 Subject: OpenSSL fixes and backwards compat break. Drop SSL_clear() after SSL_new() which causes protocol negotiation failures for TLS1.0 vs TLS1.1/1.2 in OpenSSL 1.0.1b. Remove SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS (+dont_insert_empty_fragments) from default of openssl_options. --- doc/doc-docbook/spec.xfpt | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'doc/doc-docbook') diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index e719855f8..016f3f075 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -14333,16 +14333,12 @@ harm. This option overrides the &%pipe_as_creator%& option of the &(pipe)& transport driver. -.option openssl_options main "string list" +dont_insert_empty_fragments +.option openssl_options main "string list" unset .cindex "OpenSSL "compatibility options" This option allows an administrator to adjust the SSL options applied by OpenSSL to connections. It is given as a space-separated list of items, -each one to be +added or -subtracted from the current value. The default -value is one option which happens to have been set historically. You can -remove all options with: -.code -openssl_options = -all -.endd +each one to be +added or -subtracted from the current value. + This option is only available if Exim is built against OpenSSL. The values available for this option vary according to the age of your OpenSSL install. The &"all"& value controls a subset of flags which are available, typically @@ -14354,12 +14350,19 @@ names lose the leading &"SSL_OP_"& and are lower-cased. Note that adjusting the options can have severe impact upon the security of SSL as used by Exim. It is possible to disable safety checks and shoot yourself in the foot in various unpleasant ways. This option should not be -adjusted lightly. An unrecognised item will be detected at by invoking Exim -with the &%-bV%& flag. +adjusted lightly. An unrecognised item will be detected at startup, by +invoking Exim with the &%-bV%& flag. + +.new +Historical note: prior to release 4.78, Exim defaulted this value to +"+dont_insert_empty_fragments", which may still be needed for compatibility +with some clients, but which lowers security by increasing exposure to +some now infamous attacks. +.wen An example: .code -openssl_options = -all +microsoft_big_sslv3_buffer +openssl_options = -all +microsoft_big_sslv3_buffer +dont_insert_empty_fragments .endd Possible options may include: -- cgit v1.2.3 From 7be682ca5ebd9571a01b762195b11c34cd231830 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Fri, 4 May 2012 04:39:01 -0700 Subject: TLS SNI support for OpenSSL ($tls_sni) --- doc/doc-docbook/spec.xfpt | 18 ++++ doc/doc-txt/ChangeLog | 3 + doc/doc-txt/NewStuff | 7 ++ src/src/daemon.c | 13 ++- src/src/expand.c | 3 + src/src/globals.c | 3 + src/src/globals.h | 3 + src/src/mytypes.h | 2 + src/src/spool_in.c | 7 ++ src/src/spool_out.c | 3 + src/src/tls-openssl.c | 233 ++++++++++++++++++++++++++++++++++++++-------- 11 files changed, 253 insertions(+), 42 deletions(-) (limited to 'doc/doc-docbook') diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 016f3f075..32e24ca80 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -11888,6 +11888,24 @@ the value of the Distinguished Name of the certificate is made available in the value is retained during message delivery, except during outbound SMTP deliveries. +.new +.vitem &$tls_sni$& +.vindex "&$tls_sni$&" +.cindex "TLS" "Server Name Indication" +When a TLS session is being established, if the client sends the Server +Name Indication extension, the value will be placed in this variable. +If the variable appears in &%tls_certificate%& then this option and +&%tls_privatekey%& will be re-expanded early in the TLS session, to permit +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. + +This is currently only available when using OpenSSL, built with support for +SNI. +.wen + .vitem &$tod_bsdinbox$& .vindex "&$tod_bsdinbox$&" The time of day and the date, in the format required for BSD-style mailbox diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index a491cf973..4ad79c28e 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -73,6 +73,9 @@ PP/16 Removed "dont_insert_empty_fragments" fron "openssl_options". Removed SSL_clear() after SSL_new() which led to protocol negotiation failures. We appear to now support TLS1.1+ with Exim. +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. + Exim version 4.77 ----------------- diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 0aee33cec..b788b45dc 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -42,6 +42,13 @@ Version 4.78 administrators can choose to make the trade-off themselves and restore compatibility at the cost of session security. + 7. Use of the new expansion variable $tls_sni in the main configuration option + tls_certificate will cause Exim to re-expand the option, if the client + 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. + Currently OpenSSL only. + Version 4.77 ------------ diff --git a/src/src/daemon.c b/src/src/daemon.c index 4ac34332b..27b4cb265 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -828,8 +828,17 @@ pid_t pid; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { int i; - DEBUG(D_any) debug_printf("child %d ended: status=0x%x\n", (int)pid, - status); + DEBUG(D_any) + { + debug_printf("child %d ended: status=0x%x\n", (int)pid, status); +#ifdef WCOREDUMP + if (WIFEXITED(status)) + debug_printf(" normal exit, %d\n", WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + debug_printf(" signal exit, signal %d%s\n", WTERMSIG(status), + WCOREDUMP(status) ? " (core dumped)" : ""); +#endif + } /* If it's a listening daemon for which we are keeping track of individual subprocesses, deal with an accepting process that has terminated. */ diff --git a/src/src/expand.c b/src/src/expand.c index 54501de0b..22f7d9a66 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -615,6 +615,9 @@ static var_entry var_table[] = { { "tls_certificate_verified", vtype_int, &tls_certificate_verified }, { "tls_cipher", vtype_stringptr, &tls_cipher }, { "tls_peerdn", vtype_stringptr, &tls_peerdn }, +#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) + { "tls_sni", vtype_stringptr, &tls_sni }, +#endif { "tod_bsdinbox", vtype_todbsdin, NULL }, { "tod_epoch", vtype_tode, NULL }, { "tod_full", vtype_todf, NULL }, diff --git a/src/src/globals.c b/src/src/globals.c index 6124cb585..7985cd3f0 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -116,6 +116,9 @@ BOOL tls_offered = FALSE; uschar *tls_privatekey = NULL; BOOL tls_remember_esmtp = FALSE; uschar *tls_require_ciphers = NULL; +#ifndef USE_GNUTLS +uschar *tls_sni = NULL; +#endif uschar *tls_try_verify_hosts = NULL; uschar *tls_verify_certificates= NULL; uschar *tls_verify_hosts = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index a51e3bc50..f9540785c 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -98,6 +98,9 @@ extern BOOL tls_offered; /* Server offered TLS */ extern uschar *tls_privatekey; /* Private key file */ extern BOOL tls_remember_esmtp; /* For YAEB */ extern uschar *tls_require_ciphers; /* So some can be avoided */ +#ifndef USE_GNUTLS +extern uschar *tls_sni; /* Server Name Indication */ +#endif extern uschar *tls_try_verify_hosts; /* Optional client verification */ extern uschar *tls_verify_certificates;/* Path for certificates to check */ extern uschar *tls_verify_hosts; /* Mandatory client verification */ diff --git a/src/src/mytypes.h b/src/src/mytypes.h index 5215777f8..ade294e5d 100644 --- a/src/src/mytypes.h +++ b/src/src/mytypes.h @@ -31,8 +31,10 @@ the arguments of printf-like functions. This is done by a macro. */ #if defined(__GNUC__) || defined(__clang__) #define PRINTF_FUNCTION(A,B) __attribute__((format(printf,A,B))) +#define ARG_UNUSED __attribute__((__unused__)) #else #define PRINTF_FUNCTION(A,B) +#define ARG_UNUSED /**/ #endif diff --git a/src/src/spool_in.c b/src/src/spool_in.c index e0d7fcffe..bdc3903c0 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -286,6 +286,9 @@ dkim_collect_input = FALSE; tls_certificate_verified = FALSE; tls_cipher = NULL; tls_peerdn = NULL; +#ifndef USE_GNUTLS +tls_sni = NULL; +#endif #endif #ifdef WITH_CONTENT_SCAN @@ -549,6 +552,10 @@ for (;;) tls_cipher = string_copy(big_buffer + 12); else if (Ustrncmp(p, "ls_peerdn", 9) == 0) tls_peerdn = string_unprinting(string_copy(big_buffer + 12)); + #ifndef USE_GNUTLS + else if (Ustrncmp(p, "ls_sni", 6) == 0) + tls_sni = string_unprinting(string_copy(big_buffer + 9)); + #endif break; #endif diff --git a/src/src/spool_out.c b/src/src/spool_out.c index 7b8229934..fa4f1b6e2 100644 --- a/src/src/spool_out.c +++ b/src/src/spool_out.c @@ -229,6 +229,9 @@ if (bmi_verdicts != NULL) fprintf(f, "-bmi_verdicts %s\n", bmi_verdicts); if (tls_certificate_verified) fprintf(f, "-tls_certificate_verified\n"); if (tls_cipher != NULL) fprintf(f, "-tls_cipher %s\n", tls_cipher); if (tls_peerdn != NULL) fprintf(f, "-tls_peerdn %s\n", string_printing(tls_peerdn)); +#ifndef USE_GNUTLS +if (tls_sni != NULL) fprintf(f, "-tls_sni %s\n", string_printing(tls_sni)); +#endif #endif /* To complete the envelope, write out the tree of non-recipients, followed by diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 5e8c804e5..8cc2457e5 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -34,6 +34,7 @@ static BOOL verify_callback_called = FALSE; static const uschar *sid_ctx = US"exim"; static SSL_CTX *ctx = NULL; +static SSL_CTX *ctx_sni = NULL; static SSL *ssl = NULL; static char ssl_errstring[256]; @@ -41,8 +42,26 @@ static char ssl_errstring[256]; static int ssl_session_timeout = 200; static BOOL verify_optional = FALSE; +static BOOL reexpand_tls_files_for_sni = FALSE; +typedef struct tls_ext_ctx_cb { + uschar *certificate; + uschar *privatekey; + uschar *dhparam; + /* these are cached from first expand */ + uschar *server_cipher_list; + /* only passed down to tls_error: */ + host_item *host; +} tls_ext_ctx_cb; + +/* should figure out a cleanup of API to handle state preserved per +implementation, for various reasons, which can be void * in the APIs. +For now, we hack around it. */ +tls_ext_ctx_cb *static_cbinfo = NULL; + +static int +setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional); /************************************************* @@ -201,8 +220,8 @@ return 1; /* accept */ *************************************************/ /* The SSL library functions call this from time to time to indicate what they -are doing. We copy the string to the debugging output when the level is high -enough. +are doing. We copy the string to the debugging output when TLS debugging has +been requested. Arguments: s the SSL connection @@ -279,6 +298,145 @@ return yield; +/************************************************* +* Expand key and cert file specs * +*************************************************/ + +/* Called once during tls_init and possibly againt during TLS setup, for a +new context, if Server Name Indication was used and tls_sni was seen in +the certificate string. + +Arguments: + sctx the SSL_CTX* to update + cbinfo various parts of session state + +Returns: OK/DEFER/FAIL +*/ + +static int +tls_expand_session_files(SSL_CTX *sctx, const tls_ext_ctx_cb *cbinfo) +{ +uschar *expanded; + +if (cbinfo->certificate == NULL) + return OK; + +if (Ustrstr(cbinfo->certificate, US"tls_sni")) + reexpand_tls_files_for_sni = TRUE; + +if (!expand_check(cbinfo->certificate, US"tls_certificate", &expanded)) + return DEFER; + +if (expanded != NULL) + { + DEBUG(D_tls) debug_printf("tls_certificate file %s\n", expanded); + if (!SSL_CTX_use_certificate_chain_file(sctx, CS expanded)) + return tls_error(string_sprintf( + "SSL_CTX_use_certificate_chain_file file=%s", expanded), + cbinfo->host, NULL); + } + +if (cbinfo->privatekey != NULL && + !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded)) + return DEFER; + +/* If expansion was forced to fail, key_expanded will be NULL. If the result +of the expansion is an empty string, ignore it also, and assume the private +key is in the same file as the certificate. */ + +if (expanded != NULL && *expanded != 0) + { + DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded); + if (!SSL_CTX_use_PrivateKey_file(sctx, CS expanded, SSL_FILETYPE_PEM)) + return tls_error(string_sprintf( + "SSL_CTX_use_PrivateKey_file file=%s", expanded), cbinfo->host, NULL); + } + +return OK; +} + + + + +/************************************************* +* Callback to handle SNI * +*************************************************/ + +/* Called when acting as server during the TLS session setup if a Server Name +Indication extension was sent by the client. + +API documentation is OpenSSL s_server.c implementation. + +Arguments: + s SSL* of the current session + ad unknown (part of OpenSSL API) (unused) + arg Callback of "our" registered data + +Returns: SSL_TLSEXT_ERR_{OK,ALERT_WARNING,ALERT_FATAL,NOACK} +*/ + +static int +tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg); +/* pre-declared for SSL_CTX_set_tlsext_servername_callback call within func */ + +static int +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; + +if (!servername) + return SSL_TLSEXT_ERR_OK; + +DEBUG(D_tls) debug_printf("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; + +if (!reexpand_tls_files_for_sni) + return SSL_TLSEXT_ERR_OK; + +/* Can't find an SSL_CTX_clone() or equivalent, so we do it manually; +not confident that memcpy wouldn't break some internal reference counting. +Especially since there's a references struct member, which would be off. */ + +ctx_sni = SSL_CTX_new(SSLv23_server_method()); +if (!ctx_sni) + { + ERR_error_string(ERR_get_error(), ssl_errstring); + DEBUG(D_tls) debug_printf("SSL_CTX_new() failed: %s\n", ssl_errstring); + return SSL_TLSEXT_ERR_NOACK; + } + +/* Not sure how many of these are actually needed, since SSL object +already exists. Might even need this selfsame callback, for reneg? */ + +SSL_CTX_set_info_callback(ctx_sni, SSL_CTX_get_info_callback(ctx)); +SSL_CTX_set_mode(ctx_sni, SSL_CTX_get_mode(ctx)); +SSL_CTX_set_options(ctx_sni, SSL_CTX_get_options(ctx)); +SSL_CTX_set_timeout(ctx_sni, SSL_CTX_get_timeout(ctx)); +SSL_CTX_set_tlsext_servername_callback(ctx_sni, tls_servername_cb); +SSL_CTX_set_tlsext_servername_arg(ctx_sni, cbinfo); +if (cbinfo->server_cipher_list) + SSL_CTX_set_cipher_list(ctx_sni, CS cbinfo->server_cipher_list); + +rc = tls_expand_session_files(ctx_sni, cbinfo); +if (rc != OK) return SSL_TLSEXT_ERR_NOACK; + +rc = setup_certs(ctx_sni, tls_verify_certificates, tls_crl, NULL, FALSE); +if (rc != OK) return SSL_TLSEXT_ERR_NOACK; + +DEBUG(D_tls) debug_printf("Switching SSL context.\n"); +SSL_set_SSL_CTX(s, ctx_sni); + +return SSL_TLSEXT_ERR_OK; +} + + + + /************************************************* * Initialize for TLS * *************************************************/ @@ -301,7 +459,15 @@ tls_init(host_item *host, uschar *dhparam, uschar *certificate, uschar *privatekey, address_item *addr) { long init_options; +int rc; BOOL okay; +tls_ext_ctx_cb *cbinfo; + +cbinfo = store_malloc(sizeof(tls_ext_ctx_cb)); +cbinfo->certificate = certificate; +cbinfo->privatekey = privatekey; +cbinfo->dhparam = dhparam; +cbinfo->host = host; SSL_load_error_strings(); /* basic set up */ OpenSSL_add_ssl_algorithms(); @@ -379,36 +545,16 @@ if (!init_dh(dhparam, host)) return DEFER; /* Set up certificate and key */ -if (certificate != NULL) - { - uschar *expanded; - if (!expand_check(certificate, US"tls_certificate", &expanded)) - return DEFER; - - if (expanded != NULL) - { - DEBUG(D_tls) debug_printf("tls_certificate file %s\n", expanded); - if (!SSL_CTX_use_certificate_chain_file(ctx, CS expanded)) - return tls_error(string_sprintf( - "SSL_CTX_use_certificate_chain_file file=%s", expanded), host, NULL); - } - - if (privatekey != NULL && - !expand_check(privatekey, US"tls_privatekey", &expanded)) - return DEFER; - - /* If expansion was forced to fail, key_expanded will be NULL. If the result - of the expansion is an empty string, ignore it also, and assume the private - key is in the same file as the certificate. */ +rc = tls_expand_session_files(ctx, cbinfo); +if (rc != OK) return rc; - if (expanded != NULL && *expanded != 0) - { - DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded); - if (!SSL_CTX_use_PrivateKey_file(ctx, CS expanded, SSL_FILETYPE_PEM)) - return tls_error(string_sprintf( - "SSL_CTX_use_PrivateKey_file file=%s", expanded), host, NULL); - } - } +/* 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); +#endif /* Set up the RSA callback */ @@ -418,6 +564,9 @@ SSL_CTX_set_tmp_rsa_callback(ctx, rsa_callback); SSL_CTX_set_timeout(ctx, ssl_session_timeout); DEBUG(D_tls) debug_printf("Initialized TLS\n"); + +static_cbinfo = cbinfo; + return OK; } @@ -496,6 +645,7 @@ DEBUG(D_tls) debug_printf("Cipher: %s\n", cipherbuf); /* Called by both client and server startup Arguments: + sctx SSL_CTX* to initialise certs certs file or NULL crl CRL file or NULL host NULL in a server; the remote host in a client @@ -506,7 +656,7 @@ Returns: OK/DEFER/FAIL */ static int -setup_certs(uschar *certs, uschar *crl, host_item *host, BOOL optional) +setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional) { uschar *expcerts, *expcrl; @@ -516,7 +666,7 @@ if (!expand_check(certs, US"tls_verify_certificates", &expcerts)) if (expcerts != NULL) { struct stat statbuf; - if (!SSL_CTX_set_default_verify_paths(ctx)) + if (!SSL_CTX_set_default_verify_paths(sctx)) return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL); if (Ustat(expcerts, &statbuf) < 0) @@ -539,12 +689,12 @@ if (expcerts != NULL) says no certificate was supplied.) But this is better. */ if ((file == NULL || statbuf.st_size > 0) && - !SSL_CTX_load_verify_locations(ctx, CS file, CS dir)) + !SSL_CTX_load_verify_locations(sctx, CS file, CS dir)) return tls_error(US"SSL_CTX_load_verify_locations", host, NULL); if (file != NULL) { - SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(CS file)); + SSL_CTX_set_client_CA_list(sctx, SSL_load_client_CA_file(CS file)); } } @@ -576,7 +726,7 @@ if (expcerts != NULL) { /* is it a file or directory? */ uschar *file, *dir; - X509_STORE *cvstore = SSL_CTX_get_cert_store(ctx); + X509_STORE *cvstore = SSL_CTX_get_cert_store(sctx); if ((statbufcrl.st_mode & S_IFMT) == S_IFDIR) { file = NULL; @@ -603,7 +753,7 @@ if (expcerts != NULL) /* If verification is optional, don't fail if no certificate */ - SSL_CTX_set_verify(ctx, + SSL_CTX_set_verify(sctx, SSL_VERIFY_PEER | (optional? 0 : SSL_VERIFY_FAIL_IF_NO_PEER_CERT), verify_callback); } @@ -641,6 +791,7 @@ tls_server_start(uschar *require_ciphers, uschar *require_mac, { int rc; uschar *expciphers; +tls_ext_ctx_cb *cbinfo; /* Check for previous activation */ @@ -656,6 +807,7 @@ the error. */ rc = tls_init(NULL, tls_dhparam, tls_certificate, tls_privatekey, NULL); if (rc != OK) return rc; +cbinfo = static_cbinfo; if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers)) return FAIL; @@ -671,6 +823,7 @@ if (expciphers != NULL) DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers); if (!SSL_CTX_set_cipher_list(ctx, CS expciphers)) return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL); + cbinfo->server_cipher_list = expciphers; } /* If this is a host for which certificate verification is mandatory or @@ -681,13 +834,13 @@ verify_callback_called = FALSE; if (verify_check_host(&tls_verify_hosts) == OK) { - rc = setup_certs(tls_verify_certificates, tls_crl, NULL, FALSE); + rc = setup_certs(ctx, tls_verify_certificates, tls_crl, NULL, FALSE); if (rc != OK) return rc; verify_optional = FALSE; } else if (verify_check_host(&tls_try_verify_hosts) == OK) { - rc = setup_certs(tls_verify_certificates, tls_crl, NULL, TRUE); + rc = setup_certs(ctx, tls_verify_certificates, tls_crl, NULL, TRUE); if (rc != OK) return rc; verify_optional = TRUE; } @@ -839,7 +992,7 @@ if (expciphers != NULL) return tls_error(US"SSL_CTX_set_cipher_list", host, NULL); } -rc = setup_certs(verify_certs, crl, host, FALSE); +rc = setup_certs(ctx, verify_certs, crl, host, FALSE); if (rc != OK) return rc; if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", host, NULL); -- cgit v1.2.3 From 3f0945ffae8acee547d11ae53d38fbdf9a2cc81f Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Fri, 4 May 2012 08:27:09 -0700 Subject: More tls_sni support: outbound, logging. 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). --- doc/doc-docbook/spec.xfpt | 36 ++++++++++++++++++++++++++++++++++-- doc/doc-txt/ChangeLog | 2 ++ doc/doc-txt/NewStuff | 7 +++++++ doc/doc-txt/OptionLists.txt | 1 + src/src/exipick.src | 6 ++++++ src/src/functions.h | 2 +- src/src/globals.c | 3 ++- src/src/local_scan.h | 2 +- src/src/macros.h | 3 ++- src/src/receive.c | 5 +++++ src/src/smtp_in.c | 5 +++++ src/src/string.c | 2 +- src/src/tls-gnu.c | 6 ++++-- src/src/tls-openssl.c | 37 ++++++++++++++++++++++++++++++------- src/src/transports/smtp.c | 10 ++++++++-- src/src/transports/smtp.h | 1 + 16 files changed, 110 insertions(+), 18 deletions(-) (limited to 'doc/doc-docbook') 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 &<>& 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; -- cgit v1.2.3 From f16be9794c6eec65385dca68e88978b30efd6012 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Fri, 4 May 2012 12:13:56 -0700 Subject: Doc build bug-fix. Had repeated .ilist instead of .next in the openssl_options value list. Old sdop: segfault. New sdop: memory exhaustion. Oops! --- doc/doc-docbook/spec.xfpt | 53 ++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 26 deletions(-) (limited to 'doc/doc-docbook') diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index ea4e040e1..7b8d3074d 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -11900,7 +11900,8 @@ 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. During outbound -SMTP deliveries, it reflects the value of the tls_sni option on the transport. +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. @@ -14386,55 +14387,55 @@ openssl_options = -all +microsoft_big_sslv3_buffer +dont_insert_empty_fragments Possible options may include: .ilist &`all`& -.ilist +.next &`allow_unsafe_legacy_renegotiation`& -.ilist +.next &`cipher_server_preference`& -.ilist +.next &`dont_insert_empty_fragments`& -.ilist +.next &`ephemeral_rsa`& -.ilist +.next &`legacy_server_connect`& -.ilist +.next &`microsoft_big_sslv3_buffer`& -.ilist +.next &`microsoft_sess_id_bug`& -.ilist +.next &`msie_sslv2_rsa_padding`& -.ilist +.next &`netscape_challenge_bug`& -.ilist +.next &`netscape_reuse_cipher_change_bug`& -.ilist +.next &`no_compression`& -.ilist +.next &`no_session_resumption_on_renegotiation`& -.ilist +.next &`no_sslv2`& -.ilist +.next &`no_sslv3`& -.ilist +.next &`no_ticket`& -.ilist +.next &`no_tlsv1`& -.ilist +.next &`no_tlsv1_1`& -.ilist +.next &`no_tlsv1_2`& -.ilist +.next &`single_dh_use`& -.ilist +.next &`single_ecdh_use`& -.ilist +.next &`ssleay_080_client_dh_bug`& -.ilist +.next &`sslref2_reuse_cert_type_bug`& -.ilist +.next &`tls_block_padding_bug`& -.ilist +.next &`tls_d5_bug`& -.ilist +.next &`tls_rollback_bug`& .endlist -- cgit v1.2.3 From 50aeabbc8bbe2c80d9503379b6613596fa826e02 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Fri, 4 May 2012 12:26:21 -0700 Subject: fix example line-length, add comment (openssl_options) --- doc/doc-docbook/spec.xfpt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'doc/doc-docbook') diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 7b8d3074d..582eb6072 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -14381,7 +14381,9 @@ some now infamous attacks. An example: .code -openssl_options = -all +microsoft_big_sslv3_buffer +dont_insert_empty_fragments +# Make both old MS and old Eudora happy: +openssl_options = -all +microsoft_big_sslv3_buffer \ + +dont_insert_empty_fragments .endd Possible options may include: -- cgit v1.2.3 From f85ae6bda4fced49950c61b29c27ab6aa5732f2f Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Fri, 4 May 2012 12:52:56 -0700 Subject: fix all sdop "line overflow" doc complaints --- doc/doc-docbook/spec.xfpt | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) (limited to 'doc/doc-docbook') diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 582eb6072..11381a5b8 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -9755,11 +9755,13 @@ dotted-quad decimal form, while for IPv6 addreses the result is in dotted-nibble hexadecimal form. In both cases, this is the "natural" form for DNS. For example, .code -${reverse_ip:192.0.2.4} and ${reverse_ip:2001:0db8:c42:9:1:abcd:192.0.2.3} +${reverse_ip:192.0.2.4} +${reverse_ip:2001:0db8:c42:9:1:abcd:192.0.2.3} .endd returns .code -4.2.0.192 and 3.0.2.0.0.0.0.c.d.c.b.a.1.0.0.0.9.0.0.0.2.4.c.0.8.b.d.0.1.0.0.2 +4.2.0.192 +3.0.2.0.0.0.0.c.d.c.b.a.1.0.0.0.9.0.0.0.2.4.c.0.8.b.d.0.1.0.0.2 .endd @@ -24163,9 +24165,10 @@ login: server_prompts = Username:: : Password:: server_condition = ${if and{{ \ !eq{}{$auth1} }{ \ - ldapauth{user="cn=${quote_ldap_dn:$auth1},ou=people,o=example.org" \ - pass=${quote:$auth2} \ - ldap://ldap.example.org/} }} } + ldapauth{\ + user="uid=${quote_ldap_dn:$auth1},ou=people,o=example.org" \ + pass=${quote:$auth2} \ + ldap://ldap.example.org/} }} } server_set_id = uid=$auth1,ou=people,o=example.org .endd We have to check that the username is not empty before using it, because LDAP @@ -27824,14 +27827,14 @@ in the MAIL ACL. Subsequent connections from the same client will check this new rate. .code acl_check_connect: - deny ratelimit = 100 / 5m / readonly - log_message = RATE CHECK: $sender_rate/$sender_rate_period \ - (max $sender_rate_limit) + deny ratelimit = 100 / 5m / readonly + log_message = RATE CHECK: $sender_rate/$sender_rate_period \ + (max $sender_rate_limit) # ... acl_check_mail: - warn ratelimit = 100 / 5m / strict - log_message = RATE UPDATE: $sender_rate/$sender_rate_period \ - (max $sender_rate_limit) + warn ratelimit = 100 / 5m / strict + log_message = RATE UPDATE: $sender_rate/$sender_rate_period \ + (max $sender_rate_limit) .endd If Exim encounters multiple &%ratelimit%& conditions with the same key when @@ -35289,10 +35292,15 @@ unqualified domain &'foundation'&. . //////////////////////////////////////////////////////////////////////////// . //////////////////////////////////////////////////////////////////////////// -.chapter "Support for DKIM (DomainKeys Identified Mail) - RFC4871" "CHID12" &&& +.chapter "Support for DKIM (DomainKeys Identified Mail)" "CHID12" &&& "DKIM Support" .cindex "DKIM" +DKIM is a mechanism by which messages sent by some entity can be provably +linked to a domain which that entity controls. It permits reputation to +be tracked on a per-domain basis, rather than merely upon source IP address. +DKIM is documented in RFC 4871. + Since version 4.70, DKIM support is compiled into Exim by default. It can be disabled by setting DISABLE_DKIM=yes in Local/Makefile. @@ -35313,9 +35321,12 @@ Exim's standard controls. Please note that verification of DKIM signatures in incoming mail is turned on by default for logging purposes. For each signature in incoming email, exim will log a line displaying the most important signature details, and the -signature status. Here is an example: +signature status. Here is an example (with line-breaks added for clarity): .code -2009-09-09 10:22:28 1MlIRf-0003LU-U3 DKIM: d=facebookmail.com s=q1-2009b c=relaxed/relaxed a=rsa-sha1 i=@facebookmail.com t=1252484542 [verification succeeded] +2009-09-09 10:22:28 1MlIRf-0003LU-U3 DKIM: + d=facebookmail.com s=q1-2009b + c=relaxed/relaxed a=rsa-sha1 + i=@facebookmail.com t=1252484542 [verification succeeded] .endd You might want to turn off DKIM verification processing entirely for internal or relay mail sources. To do that, set the &%dkim_disable_verify%& ACL @@ -35523,7 +35534,7 @@ for a match against the domain or identity that the ACL is currently verifying verb to a group of domains or identities. For example: .code -# Warn when message apparently from GMail has no signature at all +# Warn when Mail purportedly from GMail has no signature at all warn log_message = GMail sender without DKIM signature sender_domains = gmail.com dkim_signers = gmail.com @@ -35533,10 +35544,10 @@ warn log_message = GMail sender without DKIM signature .vitem &%dkim_status%& ACL condition that checks a colon-separated list of possible DKIM verification results agains the actual result of verification. This is typically used -to restrict an ACL verb to a list of verification outcomes, like: +to restrict an ACL verb to a list of verification outcomes, for example: .code -deny message = Message from Paypal with invalid or missing signature +deny message = Mail from Paypal with invalid/missing signature sender_domains = paypal.com:paypal.de dkim_signers = paypal.com:paypal.de dkim_status = none:invalid:fail -- cgit v1.2.3 From 2ff4a98a5fa50522b88c892318c46e769e0182cc Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Fri, 4 May 2012 13:06:38 -0700 Subject: fix sdop directive in filter.xfpt --- doc/doc-docbook/filter.xfpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'doc/doc-docbook') diff --git a/doc/doc-docbook/filter.xfpt b/doc/doc-docbook/filter.xfpt index b0e176205..4821ef173 100644 --- a/doc/doc-docbook/filter.xfpt +++ b/doc/doc-docbook/filter.xfpt @@ -21,7 +21,7 @@ -- cgit v1.2.3 From ee278e5a4369c214892af66c2bd003bd00899345 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Fri, 4 May 2012 15:52:30 -0700 Subject: New doc section explaining TLS SNI --- doc/doc-docbook/spec.xfpt | 111 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 102 insertions(+), 9 deletions(-) (limited to 'doc/doc-docbook') diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 11381a5b8..c4739a80f 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -11897,7 +11897,8 @@ deliveries. When a TLS session is being established, if the client sends the Server Name Indication extension, the value will be placed in this variable. If the variable appears in &%tls_certificate%& then this option and -&%tls_privatekey%& will be re-expanded early in the TLS session, to permit +some others, described in &<>&, +will be re-expanded early in the TLS session, to permit 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. @@ -15635,8 +15636,8 @@ 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. +Server Name Indication extension, then this option and others documented in +&<>& will be re-expanded. .wen .option tls_crl main string&!! unset @@ -15645,6 +15646,10 @@ will be re-expanded. This option specifies a certificate revocation list. The expanded value must be the name of a file that contains a CRL in PEM format. +.new +See &<>& for discussion of when this option might be re-expanded. +.wen + .option tls_dhparam main string&!! unset .cindex "TLS" "D-H parameters for server" @@ -15671,8 +15676,7 @@ key is assumed to be in the same file as the server's certificates. See chapter &<>& for further details. .new -See &%tls_certificate%& discussion of &$tls_sni$& for when this option may be -re-expanded. +See &<>& for discussion of when this option might be re-expanded. .wen @@ -15721,6 +15725,10 @@ connecting clients, defining the list of accepted certificate authorities. Thus the values defined should be considered public data. To avoid this, use OpenSSL with a directory. +.new +See &<>& for discussion of when this option might be re-expanded. +.wen + .option tls_verify_hosts main "host list&!!" unset .cindex "TLS" "client certificate verification" @@ -21803,12 +21811,15 @@ that are in force when the &%helo_data%&, &%hosts_try_auth%&, &%interface%&, .section "Use of $tls_cipher and $tls_peerdn" "usecippeer" +.vindex &$tls_bits$& .vindex &$tls_cipher$& .vindex &$tls_peerdn$& -At the start of a run of the &(smtp)& transport, the values of &$tls_cipher$& -and &$tls_peerdn$& are the values that were set when the message was received. +.vindex &$tls_sni$& +At the start of a run of the &(smtp)& transport, the values of &$tls_bits$&, +&$tls_cipher$&, &$tls_peerdn$& and &$tls_sni$& +are the values that were set when the message was received. These are the values that are used for options that are expanded before any -SMTP connections are made. Just before each connection is made, these two +SMTP connections are made. Just before each connection is made, these four variables are emptied. If TLS is subsequently started, they are set to the appropriate values for the outgoing connection, and these are the values that are in force when any authenticators are run and when the @@ -22396,6 +22407,8 @@ 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. +See &<>& for more information. + OpenSSL only, also requiring a build of OpenSSL that supports TLS extensions. .wen @@ -24926,6 +24939,13 @@ option). .next The &%tls_require_ciphers%& options operate differently, as described in the sections &<>& and &<>&. +.new +.next +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. +.wen .endlist @@ -25322,9 +25342,12 @@ All the TLS options in the &(smtp)& transport are expanded before use, with which the client is connected. Forced failure of an expansion causes Exim to behave as if the relevant option were unset. +.vindex &$tls_bits$& .vindex &$tls_cipher$& .vindex &$tls_peerdn$& -Before an SMTP connection is established, the &$tls_cipher$& and &$tls_peerdn$& +.vindex &$tls_sni$& +Before an SMTP connection is established, the +&$tls_bits$&, &$tls_cipher$&, &$tls_peerdn$& and &$tls_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 @@ -25332,6 +25355,76 @@ outgoing connection. +.new +.section "Use of TLS Server Name Indication" "SECTtlssni" +.cindex "TLS" "Server Name Indication" +.vindex "&$tls_sni$&" +.oindex "&%tls_sni%&" +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 analagous 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_sni$& variable will be set to this string +for the lifetime of the client connection (including during authentication). + +Except during SMTP client sessions, if &$tls_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_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: + +.ilist +.vindex "&%tls_certificate%&" +&%tls_certificate%& +.next +.vindex "&%tls_crl%&" +&%tls_crl%& +.next +.vindex "&%tls_privatekey%&" +&%tls_privatekey%& +.next +.vindex "&%tls_verify_certificates%&" +&%tls_verify_certificates%& +.endlist + +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_sni$& is +arbitrary unverified data provided prior to authentication. + +The Exim developers are proceeding cautiously and so far no other TLS options +are re-expanded. + +Currently SNI support is only available if using OpenSSL, with TLS Extensions +support enabled therein. +.wen + + + .section "Multiple messages on the same encrypted TCP/IP connection" &&& "SECTmulmessam" .cindex "multiple SMTP deliveries with TLS" -- cgit v1.2.3