From ec2a355fdc961aba7ace27c624c01accd58dc1ed Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Fri, 15 Apr 2022 10:36:56 +0100 Subject: TLS resumption: support Outlook hosts-behind-loadbalancer --- src/src/structs.h | 4 +++- src/src/tls.c | 9 +++++--- src/src/transports/smtp.c | 52 ++++++++++++++++++++++++++++++++++++++++------- src/src/transports/smtp.h | 3 ++- 4 files changed, 56 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/src/structs.h b/src/src/structs.h index 9bf3aebe2..087683c49 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -830,8 +830,10 @@ typedef struct { host_item * host; int host_af; uschar * interface; - uschar * sending_ip_address; /* used for TLS resumption */ + int sock; /* used for a bound but not connected socket */ + uschar * sending_ip_address; /* used for TLS resumption */ + const uschar * host_lbserver; /* ditto, for server-behind LB */ #ifdef SUPPORT_DANE BOOL dane:1; /* connection must do dane */ diff --git a/src/src/tls.c b/src/src/tls.c index c9bc556fc..e80dd9aaf 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -804,16 +804,19 @@ hctx * h = &tlsp->resume_hctx; blob b; gstring * g; +DEBUG(D_tls) if (conn_args->host_lbserver) + debug_printf("TLS: lbserver '%s'\n", conn_args->host_lbserver); + #ifdef EXIM_HAVE_SHA2 exim_sha_init(h, HASH_SHA2_256); #else exim_sha_init(h, HASH_SHA1); #endif - -// TODO: word from server EHLO resp /* how, fer gossakes? Add item to conn_args or tls_support? */ - +exim_sha_update_string(h, conn_args->host_lbserver); +#ifdef SUPPORT_DANE if (conn_args->dane) exim_sha_update(h, CUS &conn_args->tlsa_dnsa, sizeof(dns_answer)); +#endif exim_sha_update_string(h, conn_args->host->address); exim_sha_update(h, CUS &conn_args->host->port, sizeof(conn_args->host->port)); exim_sha_update_string(h, conn_args->sending_ip_address); diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index f9e319c79..e2368da13 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -64,6 +64,9 @@ optionlist smtp_transport_options[] = { { "final_timeout", opt_time, LOFF(final_timeout) }, { "gethostbyname", opt_bool, LOFF(gethostbyname) }, { "helo_data", opt_stringptr, LOFF(helo_data) }, +#if !defined(DISABLE_TLS) && !defined(DISABLE_TLS_RESUME) + { "host_name_extract", opt_stringptr, LOFF(host_name_extract) }, +# endif { "hosts", opt_stringptr, LOFF(hosts) }, { "hosts_avoid_esmtp", opt_stringptr, LOFF(hosts_avoid_esmtp) }, { "hosts_avoid_pipelining", opt_stringptr, LOFF(hosts_avoid_pipelining) }, @@ -199,6 +202,9 @@ smtp_transport_options_block smtp_transport_option_defaults = { .tls_tempfail_tryclear = TRUE, .tls_try_verify_hosts = US"*", .tls_verify_cert_hostnames = US"*", +# ifndef DISABLE_TLS_RESUME + .host_name_extract = US"${if and {{match{$host}{.outlook.com\\$}} {match{$item}{\\N^250-([\\w.]+)\\s\\N}}} {$1}}", +# endif #endif #ifdef SUPPORT_I18N .utf8_downconvert = US"-1", @@ -1066,6 +1072,7 @@ if (pending_EHLO) if (tls_out.active.sock >= 0 || !(peer_offered & OPTION_TLS)) ehlo_response_limits_read(sx); #endif +/*XXX RESUMP - EHLO-resp avail here int sx->buffer */ if ( peer_offered != sx->peer_offered || (authbits = study_ehlo_auths(sx)) != *ap) { @@ -1874,6 +1881,28 @@ return checks; +/* Grab a string differentiating server behind a loadbalancer, for TLS +resumption when such servers do not share a session-cache */ + +static const uschar * +ehlo_response_lbserver(uschar * buffer, smtp_transport_options_block * ob) +{ +#if !defined(DISABLE_TLS) && !defined(DISABLE_TLS_RESUME) +/* want to make this a main-section option */ +const uschar * s; +uschar * save_item = iterate_item; + +iterate_item = buffer; +s = expand_cstring(ob->host_name_extract); +iterate_item = save_item; +return s && !*s ? NULL : s; +#else +return NULL; +#endif +} + + + /* Callback for emitting a BDAT data chunk header. If given a nonzero size, first flush any buffered SMTP commands @@ -2516,6 +2545,8 @@ goto SEND_QUIT; : 0 ) #endif +/*XXX RESUMP - sx->buffer has the EHLO-resp, but only if not early-pipe and not continued-connection */ +/* maybe disable resump on cont? */ ); #ifdef EXPERIMENTAL_ESMTP_LIMITS if (tls_out.active.sock >= 0 || !(sx->peer_offered & OPTION_TLS)) @@ -2538,6 +2569,7 @@ goto SEND_QUIT; } } #endif + sx->conn_args.host_lbserver = ehlo_response_lbserver(sx->buffer, ob); } /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */ @@ -2629,14 +2661,19 @@ if ( smtp_peer_options & OPTION_TLS the response for the STARTTLS we just sent alone. On fail, assume wrong cached capability and retry with the pipelining disabled. */ - if (sx->early_pipe_active && sync_responses(sx, 2, 0) != 0) + if (sx->early_pipe_active) { - HDEBUG(D_transport) - debug_printf("failed reaping pipelined cmd responses\n"); - close(sx->cctx.sock); - sx->cctx.sock = -1; - sx->early_pipe_active = FALSE; - goto PIPE_CONNECT_RETRY; + if (sync_responses(sx, 2, 0) != 0) + { + HDEBUG(D_transport) + debug_printf("failed reaping pipelined cmd responses\n"); + close(sx->cctx.sock); + sx->cctx.sock = -1; + sx->early_pipe_active = FALSE; + goto PIPE_CONNECT_RETRY; + } +/*XXX RESUMP - does this leave the EHLO-resp anywhere? Yes, sx->buffer */ + sx->conn_args.host_lbserver = ehlo_response_lbserver(sx->buffer, ob); } #endif @@ -2666,6 +2703,7 @@ if ( smtp_peer_options & OPTION_TLS TLS_NEGOTIATE: { sx->conn_args.sending_ip_address = sending_ip_address; + /*XXX RESUMP want LB-server info here */ if (!tls_client_start(&sx->cctx, &sx->conn_args, sx->addrlist, &tls_out, &tls_errstr)) { /* TLS negotiation failed; give an error. From outside, this function may diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 2ed6cfd51..8dbd1fcf3 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -83,7 +83,7 @@ typedef struct { int size_addition; int hosts_max_try; int hosts_max_try_hardlimit; - int message_linelength_limit; + int message_linelength_limit; BOOL address_retry_include_sender; BOOL allow_localhost; BOOL authenticated_sender_force; @@ -108,6 +108,7 @@ typedef struct { uschar *tls_privatekey; uschar *tls_require_ciphers; # ifndef DISABLE_TLS_RESUME + uschar *host_name_extract; uschar *tls_resumption_hosts; # endif const uschar *tls_sni; -- cgit v1.2.1