Extra paranoia around STARTTLS-with-data-in-buffer.
[exim.git] / src / src / smtp_in.c
index 8edfe810424b4380e8d2bd39e29a8d2208c46cb2..500000be446a0c0dec2333d9675a42c819e25fca 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/smtp_in.c,v 1.66 2009/11/16 19:50:37 nm4 Exp $ */
+/* $Cambridge: exim/src/src/smtp_in.c,v 1.67 2010/06/12 15:21:26 jetmore Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -31,6 +31,7 @@ including that header, and restore its value afterwards. */
 
 int allow_severity = LOG_INFO;
 int deny_severity  = LOG_NOTICE;
+uschar *tcp_wrappers_name;
 #endif
 
 
@@ -48,7 +49,7 @@ the data can be quite long. */
 /* Structure for SMTP command list */
 
 typedef struct {
-  char *name;
+  const char *name;
   int len;
   short int cmd;
   short int has_arg;
@@ -375,7 +376,7 @@ Returns:      nothing
 */
 
 void
-smtp_printf(char *format, ...)
+smtp_printf(const char *format, ...)
 {
 va_list ap;
 
@@ -386,29 +387,28 @@ va_end(ap);
 
 /* This is split off so that verify.c:respond_printf() can, in effect, call
 smtp_printf(), bearing in mind that in C a vararg function can't directly
-call another vararg function, only a function which accepts a va_list.
-
-Note also that repeated calls to va_start()/va_end() pairs is claimed to be
-non-portable; meanwhile, va_copy() is also non-portable in that it's C99, so
-we end up needing OS support to define it for us. */
+call another vararg function, only a function which accepts a va_list. */
 
 void
-smtp_vprintf(char *format, va_list ap)
+smtp_vprintf(const char *format, va_list ap)
 {
-va_list ap_d;
+BOOL yield;
+
+yield = string_vformat(big_buffer, big_buffer_size, format, ap);
 
 DEBUG(D_receive)
   {
-  uschar *cr, *end;
-  va_copy(ap_d, ap);
-  (void) string_vformat(big_buffer, big_buffer_size, format, ap_d);
-  end = big_buffer + Ustrlen(big_buffer);
-  while ((cr = Ustrchr(big_buffer, '\r')) != NULL)   /* lose CRs */
-    memmove(cr, cr + 1, (end--) - cr);
-  debug_printf("SMTP>> %s", big_buffer);
+  void *reset_point = store_get(0);
+  uschar *msg_copy, *cr, *end;
+  msg_copy = string_copy(big_buffer);
+  end = msg_copy + Ustrlen(msg_copy);
+  while ((cr = Ustrchr(msg_copy, '\r')) != NULL)   /* lose CRs */
+  memmove(cr, cr + 1, (end--) - cr);
+  debug_printf("SMTP>> %s", msg_copy);
+  store_reset(reset_point);
   }
 
-if (!string_vformat(big_buffer, big_buffer_size, format, ap))
+if (!yield)
   {
   log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_printf()");
   smtp_closedown(US"Unexpected error");
@@ -1692,7 +1692,14 @@ if (!sender_host_unknown)
 
   #ifdef USE_TCP_WRAPPERS
   errno = 0;
-  if (!hosts_ctl("exim",
+  tcp_wrappers_name = expand_string(tcp_wrappers_daemon_name);
+  if (tcp_wrappers_name == NULL)
+    {
+    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Expansion of \"%s\" "
+      "(tcp_wrappers_name) failed: %s", string_printing(tcp_wrappers_name),
+        expand_string_message);
+    }
+  if (!hosts_ctl(tcp_wrappers_name,
          (sender_host_name == NULL)? STRING_UNKNOWN : CS sender_host_name,
          (sender_host_address == NULL)? STRING_UNKNOWN : CS sender_host_address,
          (sender_ident == NULL)? STRING_UNKNOWN : CS sender_ident))
@@ -3837,6 +3844,23 @@ while (done <= 0)
     toomany = FALSE;
     cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = FALSE;
 
+    /* There's an attack where more data is read in past the STARTTLS command
+    before TLS is negotiated, then assumed to be part of the secure session
+    when used afterwards; we use segregated input buffers, so are not
+    vulnerable, but we want to note when it happens and, for sheer paranoia,
+    ensure that the buffer is "wiped".
+    Pipelining sync checks will normally have protected us too, unless disabled
+    by configuration. */
+
+    if (receive_smtp_buffered())
+      {
+      DEBUG(D_any)
+        debug_printf("Non-empty input buffer after STARTTLS; naive attack?");
+      if (tls_active < 0)
+        smtp_inend = smtp_inptr = smtp_inbuffer;
+      /* and if TLS is already active, tls_server_start() should fail */
+      }
+
     /* Attempt to start up a TLS session, and if successful, discard all
     knowledge that was obtained previously. At least, that's what the RFC says,
     and that's what happens by default. However, in order to work round YAEB,