From 4243a209fd9499f30bebd58ceaa2d0d9845407ae Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Sat, 10 Dec 2022 10:47:05 +0000 Subject: [PATCH] Move connect ACL before TLS-on-connect --- doc/doc-docbook/spec.xfpt | 15 +++++--- doc/doc-txt/ChangeLog | 9 +++++ src/src/EDITME | 2 +- src/src/smtp_in.c | 52 +++++++++++++++++++-------- test/confs/5711 | 17 +++++++-- test/confs/5721 | 17 +++++++-- test/log/5711 | 30 +++++++++------- test/log/5721 | 28 +++++++++------ test/rejectlog/5711 | 4 +++ test/rejectlog/5721 | 4 +++ test/scripts/5710-GnuTLS-events/5711 | 12 ++++++- test/scripts/5720-OpenSSL-events/5721 | 12 ++++++- test/stdout/5711 | 12 +++++++ test/stdout/5721 | 12 +++++++ 14 files changed, 177 insertions(+), 49 deletions(-) create mode 100644 test/rejectlog/5711 create mode 100644 test/rejectlog/5721 diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 9d3813e5a..1b3c2b454 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -16193,11 +16193,13 @@ case. That is why the default tries a DNS lookup first. .cindex "host" "rejecting connections from" If this option is set, incoming SMTP calls from the hosts listed are rejected as soon as the connection is made. -This option is mostly obsolete, retained for backward compatibility because +This option is obsolete, and retained only for backward compatibility, because nowadays the ACL specified by &%acl_smtp_connect%& can also reject incoming -connections immediately +connections immediately. + .new -(except for tls-on-connect connections). +If the connection is on a TLS-on-connect port then the TCP connection is +just dropped. Otherwise, an SMTP error is sent first. .wen The ability to give an immediate rejection (either by this option or using an @@ -30487,8 +30489,11 @@ accepted by an &%accept%& verb that has a &%message%& modifier, the contents of the message override the banner message that is otherwise specified by the &%smtp_banner%& option. -For tls-on-connect connections, the ACL is run after the TLS connection -is accepted (however, &%host_reject_connection%& is tested before). +.new +For tls-on-connect connections, the ACL is run before the TLS connection +is accepted; if the ACL does not accept then the TCP connection is dropped without +any TLS startup attempt and without any SMTP response being transmitted. +.wen .subsection "The EHLO/HELO ACL" SECID192 diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 5ac91dc99..f8ab5da0c 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -66,6 +66,15 @@ JH/15 Fix argument parsing for ${run } expansion. Previously, when an argument included a close-brace character (eg. it itself used an expansion) an error occurred. +JH/16 Move running the smtp connect ACL to before, for TLS-on-connect ports, + starting TLS. Previously it was after, meaning that attackers on such + ports had to be screened using the host_reject_connection main config + option. The new sequence aligns better with the STARTTLS behaviour, and + permits defences against crypto-processing load attacks, even though it + is strictly an incompatible change. + Also, avoid sending any SMTP fail response for either the connect ACL + or host_reject_connection, for TLS-on-connect ports. + Exim version 4.96 ----------------- diff --git a/src/src/EDITME b/src/src/EDITME index 625df18f5..4fcaeda5b 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -401,7 +401,7 @@ TRANSPORT_SMTP=yes # For Redis you need to have hiredis installed on your system # (https://github.com/redis/hiredis). # Depending on where it is installed you may have to edit the CFLAGS -# (often += -I/usr/local/include) and LDFLAGS (-lhiredis) lines. +# (often += -I/usr/local/include) and LOOKUP_LIBS (-lhiredis) lines. # If your system has pkg-config then the _INCLUDE/_LIBS setting can be # handled for you automatically by also defining the _PC variable to reference diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 9b60702c1..b161f362d 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2505,6 +2505,22 @@ else DEBUG(D_receive) #endif +static void +log_connect_tls_drop(const uschar * what, const uschar * log_msg) +{ +gstring * g = s_tlslog(NULL); +uschar * tls = string_from_gstring(g); + +log_write(L_connection_reject, + log_reject_target, "%s%s%s dropped by %s%s%s", + LOGGING(dnssec) && sender_host_dnssec ? US" DS" : US"", + host_and_ident(TRUE), + tls ? tls : US"", + what, + log_msg ? US": " : US"", log_msg); +} + + /************************************************* * Start an SMTP session * *************************************************/ @@ -2857,7 +2873,10 @@ if (!f.sender_host_unknown) { log_write(L_connection_reject, LOG_MAIN|LOG_REJECT, "refused connection " "from %s (host_reject_connection)", host_and_ident(FALSE)); - smtp_printf("554 SMTP service not available\r\n", FALSE); +#ifndef DISABLE_TLS + if (!tls_in.on_connect) +#endif + smtp_printf("554 SMTP service not available\r\n", FALSE); return FALSE; } @@ -2983,18 +3002,6 @@ if (check_proxy_protocol_host()) setup_proxy_protocol_host(); #endif -/* Start up TLS if tls_on_connect is set. This is for supporting the legacy -smtps port for use with older style SSL MTAs. */ - -#ifndef DISABLE_TLS -if (tls_in.on_connect) - { - if (tls_server_start(&user_msg) != OK) - return smtp_log_tls_fail(user_msg); - cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; - } -#endif - /* Run the connect ACL if it exists */ user_msg = NULL; @@ -3004,11 +3011,28 @@ if (acl_smtp_connect) if ((rc = acl_check(ACL_WHERE_CONNECT, NULL, acl_smtp_connect, &user_msg, &log_msg)) != OK) { - (void) smtp_handle_acl_fail(ACL_WHERE_CONNECT, rc, user_msg, log_msg); +#ifndef DISABLE_TLS + if (tls_in.on_connect) + log_connect_tls_drop(US"'connect' ACL", log_msg); + else +#endif + (void) smtp_handle_acl_fail(ACL_WHERE_CONNECT, rc, user_msg, log_msg); return FALSE; } } +/* Start up TLS if tls_on_connect is set. This is for supporting the legacy +smtps port for use with older style SSL MTAs. */ + +#ifndef DISABLE_TLS +if (tls_in.on_connect) + { + if (tls_server_start(&user_msg) != OK) + return smtp_log_tls_fail(user_msg); + cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; + } +#endif + /* Output the initial message for a two-way SMTP connection. It may contain newlines, which then cause a multi-line response to be given. */ diff --git a/test/confs/5711 b/test/confs/5711 index d66935645..57a9fef08 100644 --- a/test/confs/5711 +++ b/test/confs/5711 @@ -6,7 +6,7 @@ primary_hostname = myhost.test.ex # ----- Main settings ----- -acl_smtp_connect = accept logwrite = ACL conn +acl_smtp_connect = check_conn acl_smtp_quit = accept logwrite = ACL quit acl_smtp_notquit = accept logwrite = ACL notquit @@ -16,13 +16,26 @@ tls_certificate = DIR/aux-fixed/cert1 host_reject_connection = ${acl {hrc}} event_action = ${acl {tls_fail}} +log_selector = +pid + # ------ ACL ------ begin acl hrc: - accept logwrite = eval host_reject_connection + warn logwrite = eval host_reject_connection + accept condition = ${if eq {$received_port}{PORT_D}} # no mesage= hence host_reject_connection should be empty + deny condition = ${if eq {$received_port}{PORT_D2}} + message = * + # PORT_D2 gets a host_reject_connection + +check_conn: + warn logwrite = ACL conn + deny condition = ${if eq {$received_port}{PORT_D3}} + log_message = we dislike you + # PORT_D3 gets a conn ACL fail + accept tls_fail: warn logwrite = EV $event_name diff --git a/test/confs/5721 b/test/confs/5721 index d156b1bf5..84c7785d9 100644 --- a/test/confs/5721 +++ b/test/confs/5721 @@ -6,7 +6,7 @@ primary_hostname = myhost.test.ex # ----- Main settings ----- -acl_smtp_connect = accept logwrite = ACL conn +acl_smtp_connect = check_conn acl_smtp_quit = accept logwrite = ACL quit acl_smtp_notquit = accept logwrite = ACL notquit @@ -16,13 +16,26 @@ tls_certificate = DIR/aux-fixed/cert1 host_reject_connection = ${acl {hrc}} event_action = ${acl {tls_fail}} +log_selector = +pid + # ------ ACL ------ begin acl hrc: - accept logwrite = eval host_reject_connection + warn logwrite = eval host_reject_connection + accept condition = ${if eq {$received_port}{PORT_D}} # no mesage= hence host_reject_connection should be empty + deny condition = ${if eq {$received_port}{PORT_D2}} + message = * + # PORT_D2 gets a host_reject_connection + +check_conn: + warn logwrite = ACL conn + deny condition = ${if eq {$received_port}{PORT_D3}} + log_message = we dislike you + # PORT_D3 gets a conn ACL fail + accept tls_fail: warn logwrite = EV $event_name diff --git a/test/log/5711 b/test/log/5711 index 32556a618..baf38b97d 100644 --- a/test/log/5711 +++ b/test/log/5711 @@ -1,14 +1,20 @@ ******** SERVER ******** -1999-03-02 09:44:33 exim x.yz daemon started: pid=p1234, no queue runs, listening for SMTPS on port PORT_D -1999-03-02 09:44:33 eval host_reject_connection -1999-03-02 09:44:33 ACL conn -1999-03-02 09:44:33 ACL quit -1999-03-02 09:44:33 eval host_reject_connection -1999-03-02 09:44:33 ACL conn -1999-03-02 09:44:33 TLS error on connection from [127.0.0.1] (recv): The TLS connection was non-properly terminated. -1999-03-02 09:44:33 ACL notquit -1999-03-02 09:44:33 eval host_reject_connection -1999-03-02 09:44:33 EV tls:fail:connect -1999-03-02 09:44:33 EVDATA: (gnutls_handshake): The TLS connection was non-properly terminated. -1999-03-02 09:44:33 TLS error on connection from [127.0.0.1] (tls lib accept fn): TCP connection closed by peer +1999-03-02 09:44:33 [1237] exim x.yz daemon started: pid=p1236, no queue runs, listening for SMTPS on port PORT_D port PORT_D2 port PORT_D3 +1999-03-02 09:44:33 [1238] eval host_reject_connection +1999-03-02 09:44:33 [1238] ACL conn +1999-03-02 09:44:33 [1238] ACL quit +1999-03-02 09:44:33 [1239] eval host_reject_connection +1999-03-02 09:44:33 [1239] ACL conn +1999-03-02 09:44:33 [1239] TLS error on connection from [127.0.0.1] (recv): The TLS connection was non-properly terminated. +1999-03-02 09:44:33 [1239] ACL notquit +1999-03-02 09:44:33 [1234] eval host_reject_connection +1999-03-02 09:44:33 [1234] refused connection from [127.0.0.1] (host_reject_connection) +1999-03-02 09:44:33 [1235] eval host_reject_connection +1999-03-02 09:44:33 [1235] ACL conn +1999-03-02 09:44:33 [1235] H=[127.0.0.1] dropped by 'connect' ACL: we dislike you +1999-03-02 09:44:33 [1240] eval host_reject_connection +1999-03-02 09:44:33 [1240] ACL conn +1999-03-02 09:44:33 [1240] EV tls:fail:connect +1999-03-02 09:44:33 [1240] EVDATA: (gnutls_handshake): The TLS connection was non-properly terminated. +1999-03-02 09:44:33 [1240] TLS error on connection from [127.0.0.1] (tls lib accept fn): TCP connection closed by peer diff --git a/test/log/5721 b/test/log/5721 index a1c9f9e37..41583c55a 100644 --- a/test/log/5721 +++ b/test/log/5721 @@ -1,13 +1,19 @@ ******** SERVER ******** -1999-03-02 09:44:33 exim x.yz daemon started: pid=p1234, no queue runs, listening for SMTPS on port PORT_D -1999-03-02 09:44:33 eval host_reject_connection -1999-03-02 09:44:33 ACL conn -1999-03-02 09:44:33 ACL quit -1999-03-02 09:44:33 eval host_reject_connection -1999-03-02 09:44:33 ACL conn -1999-03-02 09:44:33 ACL notquit -1999-03-02 09:44:33 eval host_reject_connection -1999-03-02 09:44:33 EV tls:fail:connect -1999-03-02 09:44:33 EVDATA: SSL_accept: TCP connection closed by peer -1999-03-02 09:44:33 TLS error on connection from [127.0.0.1] (tls lib accept fn): TCP connection closed by peer +1999-03-02 09:44:33 [1237] exim x.yz daemon started: pid=p1236, no queue runs, listening for SMTPS on port PORT_D port PORT_D2 port PORT_D3 +1999-03-02 09:44:33 [1238] eval host_reject_connection +1999-03-02 09:44:33 [1238] ACL conn +1999-03-02 09:44:33 [1238] ACL quit +1999-03-02 09:44:33 [1239] eval host_reject_connection +1999-03-02 09:44:33 [1239] ACL conn +1999-03-02 09:44:33 [1239] ACL notquit +1999-03-02 09:44:33 [1234] eval host_reject_connection +1999-03-02 09:44:33 [1234] refused connection from [127.0.0.1] (host_reject_connection) +1999-03-02 09:44:33 [1235] eval host_reject_connection +1999-03-02 09:44:33 [1235] ACL conn +1999-03-02 09:44:33 [1235] H=[127.0.0.1] dropped by 'connect' ACL: we dislike you +1999-03-02 09:44:33 [1240] eval host_reject_connection +1999-03-02 09:44:33 [1240] ACL conn +1999-03-02 09:44:33 [1240] EV tls:fail:connect +1999-03-02 09:44:33 [1240] EVDATA: SSL_accept: TCP connection closed by peer +1999-03-02 09:44:33 [1240] TLS error on connection from [127.0.0.1] (tls lib accept fn): TCP connection closed by peer diff --git a/test/rejectlog/5711 b/test/rejectlog/5711 new file mode 100644 index 000000000..e9945c13e --- /dev/null +++ b/test/rejectlog/5711 @@ -0,0 +1,4 @@ + +******** SERVER ******** +1999-03-02 09:44:33 [1234] refused connection from [127.0.0.1] (host_reject_connection) +1999-03-02 09:44:33 [1235] H=[127.0.0.1] dropped by 'connect' ACL: we dislike you diff --git a/test/rejectlog/5721 b/test/rejectlog/5721 new file mode 100644 index 000000000..e9945c13e --- /dev/null +++ b/test/rejectlog/5721 @@ -0,0 +1,4 @@ + +******** SERVER ******** +1999-03-02 09:44:33 [1234] refused connection from [127.0.0.1] (host_reject_connection) +1999-03-02 09:44:33 [1235] H=[127.0.0.1] dropped by 'connect' ACL: we dislike you diff --git a/test/scripts/5710-GnuTLS-events/5711 b/test/scripts/5710-GnuTLS-events/5711 index 7c276229d..725703f2a 100644 --- a/test/scripts/5710-GnuTLS-events/5711 +++ b/test/scripts/5710-GnuTLS-events/5711 @@ -1,6 +1,6 @@ # smtp-on-connect drop-before-tls-accept # -exim -DSERVER=server -tls-on-connect -bd -oX PORT_D +exim -DSERVER=server -tls-on-connect -bd -oX PORT_D:PORT_D2:PORT_D3 **** # # Normal, full connect and quit @@ -15,6 +15,16 @@ client-anytls -tls-on-connect 127.0.0.1 PORT_D ??? 220 **** # +# server rejects using host_reject_connection option +client-anytls -tls-on-connect 127.0.0.1 PORT_D2 +???* +**** +# +# server rejects using conn ACL +client-anytls -tls-on-connect 127.0.0.1 PORT_D3 +???* +**** +# # client disconnects before server TLS accept completes client 127.0.0.1 PORT_D +++ 1 diff --git a/test/scripts/5720-OpenSSL-events/5721 b/test/scripts/5720-OpenSSL-events/5721 index 0f72c17d2..19f977c7b 100644 --- a/test/scripts/5720-OpenSSL-events/5721 +++ b/test/scripts/5720-OpenSSL-events/5721 @@ -1,6 +1,6 @@ # smtp-on-connect drop-before-tls-accept # -exim -DSERVER=server -tls-on-connect -bd -oX PORT_D +exim -DSERVER=server -tls-on-connect -bd -oX PORT_D:PORT_D2:PORT_D3 **** # # Normal, full connect and quit @@ -15,6 +15,16 @@ client-anytls -tls-on-connect 127.0.0.1 PORT_D ??? 220 **** # +# server rejects using host_reject_connection option +client-anytls -tls-on-connect 127.0.0.1 PORT_D2 +???* +**** +# +# server rejects using conn ACL +client-anytls -tls-on-connect 127.0.0.1 PORT_D3 +???* +**** +# # client disconnects before server TLS accept completes client 127.0.0.1 PORT_D +++ 1 diff --git a/test/stdout/5711 b/test/stdout/5711 index d3bf62e95..f96f81b96 100644 --- a/test/stdout/5711 +++ b/test/stdout/5711 @@ -13,6 +13,18 @@ Succeeded in starting TLS ??? 220 <<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 End of script +Connecting to 127.0.0.1 port 1226 ... connected +Attempting to start TLS +Failed to start TLS +???* +Expected EOF read +End of script +Connecting to 127.0.0.1 port 1227 ... connected +Attempting to start TLS +Failed to start TLS +???* +Expected EOF read +End of script Connecting to 127.0.0.1 port 1225 ... connected +++ 1 End of script diff --git a/test/stdout/5721 b/test/stdout/5721 index d3bf62e95..f96f81b96 100644 --- a/test/stdout/5721 +++ b/test/stdout/5721 @@ -13,6 +13,18 @@ Succeeded in starting TLS ??? 220 <<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 End of script +Connecting to 127.0.0.1 port 1226 ... connected +Attempting to start TLS +Failed to start TLS +???* +Expected EOF read +End of script +Connecting to 127.0.0.1 port 1227 ... connected +Attempting to start TLS +Failed to start TLS +???* +Expected EOF read +End of script Connecting to 127.0.0.1 port 1225 ... connected +++ 1 End of script -- 2.30.2