Overhaul of GnuTLS code.
[exim.git] / src / src / smtp_in.c
index 6c66a5634240c2e7ab718673f0f44c14011211fb..5d241d960b3d7c35e3ef989e2573e9fa55c8e343 100644 (file)
@@ -1,10 +1,8 @@
-/* $Cambridge: exim/src/src/smtp_in.c,v 1.61 2007/08/22 14:20:28 ph10 Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2007 */
+/* Copyright (c) University of Cambridge 1995 - 2009 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for handling an incoming SMTP call. */
@@ -31,15 +29,21 @@ including that header, and restore its value afterwards. */
 
 int allow_severity = LOG_INFO;
 int deny_severity  = LOG_NOTICE;
+uschar *tcp_wrappers_name;
 #endif
 
 
 /* Size of buffer for reading SMTP commands. We used to use 512, as defined
 by RFC 821. However, RFC 1869 specifies that this must be increased for SMTP
 commands that accept arguments, and this in particular applies to AUTH, where
-the data can be quite long. */
+the data can be quite long.  More recently this value was 2048 in Exim; 
+however, RFC 4954 (circa 2007) recommends 12288 bytes to handle AUTH.  Clients
+such as Thunderbird will send an AUTH with an initial-response for GSSAPI. 
+The maximum size of a Kerberos ticket under Windows 2003 is 12000 bytes, and 
+we need room to handle large base64-encoded AUTHs for GSSAPI.
+*/
 
-#define smtp_cmd_buffer_size  2048
+#define smtp_cmd_buffer_size  16384
 
 /* Size of buffer for reading SMTP incoming packets */
 
@@ -48,7 +52,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;
@@ -264,6 +268,9 @@ if (smtp_inptr >= smtp_inend)
     else smtp_had_eof = 1;
     return EOF;
     }
+#ifndef DISABLE_DKIM
+  dkim_exim_verify_feed(smtp_inbuffer, rc);
+#endif
   smtp_inend = smtp_inbuffer + rc;
   smtp_inptr = smtp_inbuffer;
   }
@@ -372,30 +379,44 @@ Returns:      nothing
 */
 
 void
-smtp_printf(char *format, ...)
+smtp_printf(const char *format, ...)
 {
 va_list ap;
 
+va_start(ap, format);
+smtp_vprintf(format, ap);
+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. */
+
+void
+smtp_vprintf(const char *format, va_list ap)
+{
+BOOL yield;
+
+yield = string_vformat(big_buffer, big_buffer_size, format, ap);
+
 DEBUG(D_receive)
   {
-  uschar *cr, *end;
-  va_start(ap, format);
-  (void) string_vformat(big_buffer, big_buffer_size, format, ap);
-  va_end(ap);
-  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);
   }
 
-va_start(ap, format);
-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");
   exim_exit(EXIT_FAILURE);
   }
-va_end(ap);
 
 /* If this is the first output for a (non-batch) RCPT command, see if all RCPTs
 have had the same. Note: this code is also present in smtp_respond(). It would
@@ -820,6 +841,9 @@ if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
 if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_peerdn != NULL)
   s = string_append(s, &size, &ptr, 3, US" DN=\"",
     string_printing(tls_peerdn), US"\"");
+if ((log_extra_selector & LX_tls_sni) != 0 && tls_sni != NULL)
+  s = string_append(s, &size, &ptr, 3, US" SNI=\"",
+    string_printing(tls_sni), US"\"");
 #endif
 
 sep = (smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE)?
@@ -962,7 +986,8 @@ if (*v != '=') return FALSE;
 n = v;
 while(isalpha(n[-1])) n--;
 
-if (n[-1] != ' ') return FALSE;
+/* RFC says SP, but TAB seen in wild and other major MTAs accept it */
+if (!isspace(n[-1])) return FALSE;
 
 n[-1] = 0;
 *name = n;
@@ -1022,8 +1047,10 @@ authenticated_sender = NULL;
 bmi_run = 0;
 bmi_verdicts = NULL;
 #endif
-#ifdef EXPERIMENTAL_DOMAINKEYS
-dk_do_verify = 0;
+#ifndef DISABLE_DKIM
+dkim_signers = NULL;
+dkim_disable_verify = FALSE;
+dkim_collect_input = FALSE;
 #endif
 #ifdef EXPERIMENTAL_SPF
 spf_header_comment = NULL;
@@ -1372,6 +1399,7 @@ smtp_cmd_buffer = (uschar *)malloc(2*smtp_cmd_buffer_size + 2);
 if (smtp_cmd_buffer == NULL)
   log_write(0, LOG_MAIN|LOG_PANIC_DIE,
     "malloc() failed for SMTP command buffer");
+smtp_cmd_buffer[0] = 0;
 smtp_data_buffer = smtp_cmd_buffer + smtp_cmd_buffer_size + 1;
 
 /* For batched input, the protocol setting can be overridden from the
@@ -1646,8 +1674,7 @@ if (!sender_host_unknown)
 
   #ifdef SUPPORT_TLS
   if (tls_on_connect &&
-      tls_server_start(tls_require_ciphers,
-        gnutls_require_mac, gnutls_require_kx, gnutls_require_proto) != OK)
+      tls_server_start(tls_require_ciphers) != OK)
     return FALSE;
   #endif
 
@@ -1672,7 +1699,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))
@@ -2170,8 +2204,13 @@ if (where == ACL_WHERE_RCPT || where == ACL_WHERE_DATA)
 if (where == ACL_WHERE_RCPT || where == ACL_WHERE_DATA || where == ACL_WHERE_MIME)
 #endif
   {
-  sender_info = string_sprintf("F=<%s> ", (sender_address_unrewritten != NULL)?
-    sender_address_unrewritten : sender_address);
+  sender_info = string_sprintf("F=<%s>%s%s%s%s ",
+    sender_address_unrewritten ? sender_address_unrewritten : sender_address,
+    sender_host_authenticated ? US" A="                                    : US"",
+    sender_host_authenticated ? sender_host_authenticated                  : US"",
+    sender_host_authenticated && authenticated_id ? US":"                  : US"",
+    sender_host_authenticated && authenticated_id ? authenticated_id       : US""
+    );
   }
 
 /* If there's been a sender verification failure with a specific message, and
@@ -3817,6 +3856,32 @@ 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 */
+      }
+
+    /* There is nothing we value in the input buffer and if TLS is succesfully
+    negotiated, we won't use this buffer again; if TLS fails, we'll just read
+    fresh content into it.  The buffer contains arbitrary content from an
+    untrusted remote source; eg: NOOP <shellcode>\r\nSTARTTLS\r\n
+    It seems safest to just wipe away the content rather than leave it as a
+    target to jump to. */
+
+    memset(smtp_inbuffer, 0, in_buffer_size);
+
     /* 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,
@@ -3825,8 +3890,7 @@ while (done <= 0)
     We must allow for an extra EHLO command and an extra AUTH command after
     STARTTLS that don't add to the nonmail command count. */
 
-    if ((rc = tls_server_start(tls_require_ciphers, gnutls_require_mac,
-           gnutls_require_kx, gnutls_require_proto)) == OK)
+    if ((rc = tls_server_start(tls_require_ciphers)) == OK)
       {
       if (!tls_remember_esmtp)
         helo_seen = esmtp = auth_advertised = pipelining_advertised = FALSE;