[exim-cvs] OpenSSL: in server, detect TCP RST from client a…

Startseite
Nachricht löschen
Nachricht beantworten
Autor: Exim Git Commits Mailing List
Datum:  
To: exim-cvs
Betreff: [exim-cvs] OpenSSL: in server, detect TCP RST from client after QUIT under SSL,
Gitweb: https://git.exim.org/exim.git/commitdiff/afaf5a50b05810d75c1f7ae9d1cd83697815a997
Commit:     afaf5a50b05810d75c1f7ae9d1cd83697815a997
Parent:     fd3b39c16b3040cc7f12dbc3afdfc4e9e6e0807b
Author:     Jeremy Harris <jgh146exb@???>
AuthorDate: Thu Jul 23 16:32:29 2020 +0100
Committer:  Jeremy Harris <jgh146exb@???>
CommitDate: Fri Jul 24 15:00:57 2020 +0100


      OpenSSL: in server, detect TCP RST from client after QUIT under SSL,
      and log different message (under new log_selector)
---
 doc/doc-txt/experimental-spec.txt | 14 ++++++++++++++
 src/src/acl.c                     |  6 ++++--
 src/src/globals.c                 |  6 ++++--
 src/src/globals.h                 |  1 +
 src/src/macros.h                  |  2 ++
 src/src/smtp_in.c                 | 18 ++++++++++--------
 src/src/tls-openssl.c             | 15 +++++++++++----
 7 files changed, 46 insertions(+), 16 deletions(-)


diff --git a/doc/doc-txt/experimental-spec.txt b/doc/doc-txt/experimental-spec.txt
index 68366a4..e4a9301 100644
--- a/doc/doc-txt/experimental-spec.txt
+++ b/doc/doc-txt/experimental-spec.txt
@@ -666,6 +666,20 @@ and a whitespace-separated port number must be given.



+
+Logging protocol unusual states
+---------------------------------------------------------------
+An extra log_selector, "protocol_detail" has been added in the default build.
+The name may change in future, hence the Experimenal status.
+
+Currrently the only effect is to enable logging, under OpenSSL,
+of a TCP RST received directly after a QUIT (in server mode).
+
+Outlook is consistently doing this; not waiting for the SMTP response
+to its QUIT, not properly closing the TLS session and not properly closing
+the TCP connection.  Previously this resulted is an error from SSL_write
+being logged.
+
 --------------------------------------------------------------
 End of file
 --------------------------------------------------------------
diff --git a/src/src/acl.c b/src/src/acl.c
index 185a39d..934112c 100644
--- a/src/src/acl.c
+++ b/src/src/acl.c
@@ -1944,7 +1944,8 @@ if (quota)
       {
       if (!*user_msgptr && *log_msgptr)
         *user_msgptr = string_sprintf("Rejected after %s: %s",
-        smtp_names[smtp_connection_had[smtp_ch_index-1]], *log_msgptr);
+        smtp_names[smtp_connection_had[SMTP_HBUFF_PREV(smtp_ch_index)]],
+        *log_msgptr);
       if (rc == DEFER) f.acl_temp_details = TRUE;
       }
     }
@@ -4542,7 +4543,8 @@ switch (where)
     /* Drop cutthrough conns, and drop heldopen verify conns if
     the previous was not DATA */
     {
-    uschar prev = smtp_connection_had[smtp_ch_index-2];
+    uschar prev =
+      smtp_connection_had[SMTP_HBUFF_PREV(SMTP_HBUFF_PREV(smtp_ch_index))];
     BOOL dropverify = !(prev == SCH_DATA || prev == SCH_BDAT);


     cancel_cutthrough_connection(dropverify, US"quit or conndrop");
diff --git a/src/src/globals.c b/src/src/globals.c
index 654c39b..a5df06d 100644
--- a/src/src/globals.c
+++ b/src/src/globals.c
@@ -307,6 +307,7 @@ struct global_flags f =
 #endif
     .smtp_in_pipelining_advertised = FALSE,
     .smtp_in_pipelining_used = FALSE,
+    .smtp_in_quit        = FALSE,
     .spool_file_wireformat  = FALSE,
     .submission_mode        = FALSE,
     .suppress_local_fixups  = FALSE,
@@ -1073,6 +1074,7 @@ bit_table log_options[]        = { /* must be in alphabetical order,
   BIT_TABLE(L, outgoing_port),
   BIT_TABLE(L, pid),
   BIT_TABLE(L, pipelining),
+  BIT_TABLE(L, protocol_detail),
 #if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS)
   BIT_TABLE(L, proxy),
 #endif
@@ -1464,6 +1466,8 @@ uschar *smtp_etrn_command      = NULL;
 int     smtp_max_synprot_errors= 3;
 int     smtp_max_unknown_commands = 3;
 uschar *smtp_notquit_reason    = NULL;
+unsigned smtp_peer_options     = 0;
+unsigned smtp_peer_options_wrap= 0;
 uschar *smtp_ratelimit_hosts   = NULL;
 uschar *smtp_ratelimit_mail    = NULL;
 uschar *smtp_ratelimit_rcpt    = NULL;
@@ -1479,8 +1483,6 @@ int     smtp_rlr_base          = 0;
 double  smtp_rlr_factor        = 0.0;
 int     smtp_rlr_limit         = 0;
 int     smtp_rlr_threshold     = INT_MAX;
-unsigned smtp_peer_options     = 0;
-unsigned smtp_peer_options_wrap= 0;
 #ifdef SUPPORT_I18N
 uschar *smtputf8_advertise_hosts = US"*";    /* overridden under test-harness */
 #endif
diff --git a/src/src/globals.h b/src/src/globals.h
index 0c85c11..ea32695 100644
--- a/src/src/globals.h
+++ b/src/src/globals.h
@@ -269,6 +269,7 @@ extern struct global_flags {
 #endif
  BOOL   smtp_in_pipelining_advertised    :1; /* server advertised PIPELINING */
  BOOL   smtp_in_pipelining_used        :1; /* server noted client using PIPELINING */
+ BOOL   smtp_in_quit            :1; /* server noted QUIT command */
  BOOL   spool_file_wireformat        :1; /* current -D file has CRLF rather than NL */
  BOOL   submission_mode            :1; /* Can be forced from ACL */
  BOOL   suppress_local_fixups        :1; /* Can be forced from ACL */
diff --git a/src/src/macros.h b/src/src/macros.h
index b5221c7..4c5279f 100644
--- a/src/src/macros.h
+++ b/src/src/macros.h
@@ -148,6 +148,7 @@ enough to hold all the headers from a normal kind of message. */
 /* The size of the circular buffer that remembers recent SMTP commands */


 #define SMTP_HBUFF_SIZE 20
+#define SMTP_HBUFF_PREV(n)    ((n) ? (n)-1 : SMTP_HBUFF_SIZE-1)


 /* The initial size of a big buffer for use in various places. It gets put
 into big_buffer_size and in some circumstances increased. It should be at least
@@ -480,6 +481,7 @@ enum logbit {
   Li_outgoing_port,
   Li_pid,
   Li_pipelining,
+  Li_protocol_detail,
   Li_proxy,
   Li_queue_time,
   Li_queue_time_overall,
diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
index ef3964f..3325d54 100644
--- a/src/src/smtp_in.c
+++ b/src/src/smtp_in.c
@@ -1733,6 +1733,7 @@ for (;;) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED))
     return;


   case QUIT_CMD:
+    f.smtp_in_quit = TRUE;
     smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
     mac_smtp_fflush();
     return;
@@ -2349,8 +2350,9 @@ while (done <= 0)
       break;



-    case EOF_CMD:
     case QUIT_CMD:
+      f.smtp_in_quit = TRUE;
+    case EOF_CMD:
       done = 2;
       break;


@@ -3835,14 +3837,13 @@ static void
 smtp_quit_handler(uschar ** user_msgp, uschar ** log_msgp)
 {
 HAD(SCH_QUIT);
+f.smtp_in_quit = TRUE;
 incomplete_transaction_log(US"QUIT");
-if (acl_smtp_quit)
-  {
-  int rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, user_msgp, log_msgp);
-  if (rc == ERROR)
+if (  acl_smtp_quit
+   && acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, user_msgp, log_msgp)
+    == ERROR)
     log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s",
       *log_msgp);
-  }


 #ifdef TCP_CORK
 (void) setsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_CORK, US &on, sizeof(on));
@@ -5284,10 +5285,10 @@ while (done <= 0)
       }
     if (f.smtp_in_pipelining_advertised && last_was_rcpt)
       smtp_printf("503 Valid RCPT command must precede %s\r\n", FALSE,
-        smtp_names[smtp_connection_had[smtp_ch_index-1]]);
+        smtp_names[smtp_connection_had[SMTP_HBUFF_PREV(smtp_ch_index)]]);
     else
       done = synprot_error(L_smtp_protocol_error, 503, NULL,
-        smtp_connection_had[smtp_ch_index-1] == SCH_DATA
+        smtp_connection_had[SMTP_HBUFF_PREV(smtp_ch_index)] == SCH_DATA
         ? US"valid RCPT command must precede DATA"
         : US"valid RCPT command must precede BDAT");


@@ -5551,6 +5552,7 @@ while (done <= 0)
     some sense is perhaps "right". */


     case QUIT_CMD:
+      f.smtp_in_quit = TRUE;
       user_msg = NULL;
       if (  acl_smtp_quit
          && ((rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, &user_msg,
diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
index 50384cb..64f60b7 100644
--- a/src/src/tls-openssl.c
+++ b/src/src/tls-openssl.c
@@ -3673,7 +3673,7 @@ context for the stashed information. */
 a store reset there, so use POOL_PERM. */
 /* + if CHUNKING, cmds EHLO,MAIL,RCPT(s),BDAT */


-if ((more || corked))
+if (more || corked)
   {
   if (!len) buff = US &error;    /* dummy just so that string_catn is ok */


@@ -3722,9 +3722,16 @@ for (int left = len; left > 0;)
       return -1;


     case SSL_ERROR_SYSCALL:
-      log_write(0, LOG_MAIN, "SSL_write: (from %s) syscall: %s",
-    sender_fullhost ? sender_fullhost : US"<unknown>",
-    strerror(errno));
+      if (ct_ctx || errno != ECONNRESET || !f.smtp_in_quit)
+    log_write(0, LOG_MAIN, "SSL_write: (from %s) syscall: %s",
+      sender_fullhost ? sender_fullhost : US"<unknown>",
+      strerror(errno));
+      else if (LOGGING(protocol_detail))
+    log_write(0, LOG_MAIN, "[%s] after QUIT, client reset TCP before"
+      " SMTP response and TLS close\n", sender_host_address);
+      else
+    DEBUG(D_tls) debug_printf("[%s] SSL_write: after QUIT,"
+      " client reset TCP before TLS close\n", sender_host_address);
       return -1;


     default: