SECURITY: fix SMTP verb option parsing
[exim.git] / src / src / smtp_in.c
index 14dd11498c74673848d29b631018d6654ffb0d52..4f16fd4b82f5de8e4fd1cc6fd072208882662e54 100644 (file)
@@ -347,7 +347,7 @@ wouldblock_reading(void)
 {
 int fd, rc;
 fd_set fds;
-struct timeval tzero;
+struct timeval tzero = {.tv_sec = 0, .tv_usec = 0};
 
 #ifndef DISABLE_TLS
 if (tls_in.active.sock >= 0)
@@ -360,8 +360,6 @@ if (smtp_inptr < smtp_inend)
 fd = fileno(smtp_in);
 FD_ZERO(&fds);
 FD_SET(fd, &fds);
-tzero.tv_sec = 0;
-tzero.tv_usec = 0;
 rc = select(fd + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL, &tzero);
 
 if (rc <= 0) return TRUE;     /* Not ready to read */
@@ -587,6 +585,8 @@ smtp_get_cache(void)
 {
 #ifndef DISABLE_DKIM
 int n = smtp_inend - smtp_inptr;
+if (chunking_state == CHUNKING_LAST && chunking_data_left < n)
+  n = chunking_data_left;
 if (n > 0)
   dkim_exim_verify_feed(smtp_inptr, n);
 #endif
@@ -1969,12 +1969,13 @@ extract_option(uschar **name, uschar **value)
 uschar *n;
 uschar *v = smtp_cmd_data + Ustrlen(smtp_cmd_data) - 1;
 while (isspace(*v)) v--;
-v[1] = 0;
+v[1] = '\0';
 while (v > smtp_cmd_data && *v != '=' && !isspace(*v))
   {
   /* Take care to not stop at a space embedded in a quoted local-part */
 
-  if (*v == '"') do v--; while (*v != '"' && v > smtp_cmd_data+1);
+  if ((*v == '"') && (v > smtp_cmd_data + 1))
+    do v--; while (*v != '"' && v > smtp_cmd_data+1);
   v--;
   }
 
@@ -2109,7 +2110,11 @@ while (acl_warn_logged)
   acl_warn_logged = acl_warn_logged->next;
   store_free(this);
   }
+
+message_tidyup();
 store_reset(reset_point);
+
+message_start();
 return store_mark();
 }
 
@@ -2186,7 +2191,7 @@ while (done <= 0)
 
     case MAIL_CMD:
       smtp_mailcmd_count++;              /* Count for no-mail log */
-      if (sender_address != NULL)
+      if (sender_address)
        /* The function moan_smtp_batch() does not return. */
        moan_smtp_batch(smtp_cmd_buffer, "503 Sender already given");
 
@@ -3843,12 +3848,35 @@ if (*user_msgp)
 else
   smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
 
-#ifndef DISABLE_TLS
+#ifdef SERVERSIDE_CLOSE_NOWAIT
+# ifndef DISABLE_TLS
 tls_close(NULL, TLS_SHUTDOWN_NOWAIT);
-#endif
+# endif
+
+log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
+  smtp_get_connection_info());
+#else
+
+# ifndef DISABLE_TLS
+tls_close(NULL, TLS_SHUTDOWN_WAIT);
+# endif
 
 log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
   smtp_get_connection_info());
+
+/* Pause, hoping client will FIN first so that they get the TIME_WAIT.
+The socket should become readble (though with no data) */
+
+  {
+  int fd = fileno(smtp_in);
+  fd_set fds;
+  struct timeval t_limit = {.tv_sec = 0, .tv_usec = 200*1000};
+
+  FD_ZERO(&fds);
+  FD_SET(fd, &fds);
+  (void) select(fd + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL, &t_limit);
+  }
+#endif /*!DAEMON_CLOSE_NOWAIT*/
 }
 
 
@@ -3862,6 +3890,13 @@ cmd_list[CMD_LIST_RSET].is_mail_cmd = FALSE;
 }
 
 
+static int
+expand_mailmax(const uschar * s)
+{
+if (!(s = expand_cstring(s)))
+  log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand smtp_accept_max_per_connection");
+return *s ? Uatoi(s) : 0;
+}
 
 /*************************************************
 *       Initialize for SMTP incoming message     *
@@ -3892,6 +3927,7 @@ int
 smtp_setup_msg(void)
 {
 int done = 0;
+int mailmax = -1;
 BOOL toomany = FALSE;
 BOOL discarded = FALSE;
 BOOL last_was_rej_mail = FALSE;
@@ -4249,6 +4285,9 @@ while (done <= 0)
       fl.smtputf8_advertised = FALSE;
 #endif
 
+      /* Expand the per-connection message count limit option */
+      mailmax = expand_mailmax(smtp_accept_max_per_connection);
+
       smtp_code = US"250 ";        /* Default response code plus space*/
       if (!user_msg)
        {
@@ -4308,6 +4347,19 @@ while (done <= 0)
          g = string_catn(g, US"-SIZE\r\n", 7);
          }
 
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+       if (  (mailmax > 0 || recipients_max)
+          && verify_check_host(&limits_advertise_hosts) == OK)
+         {
+         g = string_fmt_append(g, "%.3s-LIMITS", smtp_code);
+         if (mailmax > 0)
+           g = string_fmt_append(g, " MAILMAX=%d", mailmax);
+         if (recipients_max)
+           g = string_fmt_append(g, " RCPTMAX=%d", recipients_max);
+         g = string_catn(g, US"\r\n", 2);
+         }
+#endif
+
        /* Exim does not do protocol conversion or data conversion. It is 8-bit
        clean; if it has an 8-bit character in its hand, it just sends it. It
        cannot therefore specify 8BITMIME and remain consistent with the RFCs.
@@ -4520,16 +4572,20 @@ while (done <= 0)
     case MAIL_CMD:
       HAD(SCH_MAIL);
       smtp_mailcmd_count++;              /* Count for limit and ratelimit */
+      message_start();
       was_rej_mail = TRUE;               /* Reset if accepted */
       env_mail_type_t * mail_args;       /* Sanity check & validate args */
 
-      if (fl.helo_required && !fl.helo_seen)
-       {
-       smtp_printf("503 HELO or EHLO required\r\n", FALSE);
-       log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL from %s: no "
-         "HELO/EHLO given", host_and_ident(FALSE));
-       break;
-       }
+      if (!fl.helo_seen)
+       if (fl.helo_required)
+         {
+         smtp_printf("503 HELO or EHLO required\r\n", FALSE);
+         log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL from %s: no "
+           "HELO/EHLO given", host_and_ident(FALSE));
+         break;
+         }
+       else if (mailmax < 0)
+         mailmax = expand_mailmax(smtp_accept_max_per_connection);
 
       if (sender_address)
        {
@@ -4548,8 +4604,7 @@ while (done <= 0)
       /* Check to see if the limit for messages per connection would be
       exceeded by accepting further messages. */
 
-      if (smtp_accept_max_per_connection > 0 &&
-         smtp_mailcmd_count > smtp_accept_max_per_connection)
+      if (mailmax > 0 && smtp_mailcmd_count > mailmax)
        {
        smtp_printf("421 too many messages in this connection\r\n", FALSE);
        log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL command %s: too many "