Make verify=helo do the verification on the fly it if was not done
[exim.git] / src / src / smtp_in.c
index 063d74a9691a502a88a644409da308ce210e480d..9a736a4b7bb68af059af192b7a0beaffd1283905 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/smtp_in.c,v 1.9 2005/01/13 16:15:53 ph10 Exp $ */
+/* $Cambridge: exim/src/src/smtp_in.c,v 1.22 2005/08/02 15:19:20 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -34,9 +34,12 @@ int deny_severity  = LOG_NOTICE;
 #endif
 
 
-/* Size of buffer for reading SMTP commands */
+/* 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. */
 
-#define cmd_buffer_size  512      /* Ref. RFC 821 */
+#define cmd_buffer_size  2048
 
 /* Size of buffer for reading SMTP incoming packets */
 
@@ -801,11 +804,12 @@ store_reset(reset_point);
 recipients_list = NULL;
 rcpt_count = rcpt_defer_count = rcpt_fail_count =
   raw_recipients_count = recipients_count = recipients_list_max = 0;
+message_linecount = 0;
 message_size = -1;
 acl_warn_headers = NULL;
 queue_only_policy = FALSE;
 deliver_freeze = FALSE;                              /* Can be set by ACL */
-fake_reject = FALSE;                                 /* Can be set by ACL */
+fake_response = OK;                                  /* Can be set by ACL */
 #ifdef WITH_CONTENT_SCAN
 no_mbox_unspool = FALSE;                             /* Can be set by ACL */
 #endif
@@ -823,14 +827,21 @@ authenticated_sender = NULL;
 bmi_run = 0;
 bmi_verdicts = NULL;
 #endif
+#ifdef EXPERIMENTAL_DOMAINKEYS
+dk_do_verify = 0;
+#endif
 #ifdef EXPERIMENTAL_SPF
 spf_header_comment = NULL;
 spf_received = NULL;
-spf_result = NULL;  
+spf_result = NULL;
 spf_smtp_comment = NULL;
 #endif
 body_linecount = body_zerocount = 0;
 
+sender_rate = sender_rate_limit = sender_rate_period = NULL;
+ratelimiters_mail = NULL;           /* Updated by ratelimit ACL condition */
+                   /* Note that ratelimiters_conn persists across resets. */
+
 for (i = 0; i < ACL_M_MAX; i++) acl_var[ACL_C_MAX + i] = NULL;
 
 /* The message body variables use malloc store. They may be set if this is
@@ -1121,11 +1132,13 @@ int size = 256;
 int i, ptr;
 uschar *p, *s, *ss;
 
-/* If we are running in the test harness, and the incoming call is from 
-127.0.0.2 (sic), have a short delay. This makes it possible to test handling of 
+/* If we are running in the test harness, and the incoming call is from
+127.0.0.2 (sic), have a short delay. This makes it possible to test handling of
 input sent too soon (before the banner is output). */
 
-if (running_in_test_harness && Ustrcmp(sender_host_address, "127.0.0.2") == 0)
+if (running_in_test_harness &&
+    sender_host_address != NULL &&
+    Ustrcmp(sender_host_address, "127.0.0.2") == 0)
   sleep(1);
 
 /* Default values for certain variables */
@@ -1257,16 +1270,16 @@ if (!sender_host_unknown)
   if (!host_checking && !sender_host_notsocket)
     {
     #if OPTSTYLE == 1
-    SOCKLEN_T optlen = sizeof(struct ip_options) + MAX_IPOPTLEN;
+    EXIM_SOCKLEN_T optlen = sizeof(struct ip_options) + MAX_IPOPTLEN;
     struct ip_options *ipopt = store_get(optlen);
     #elif OPTSTYLE == 2
     struct ip_opts ipoptblock;
     struct ip_opts *ipopt = &ipoptblock;
-    SOCKLEN_T optlen = sizeof(ipoptblock);
+    EXIM_SOCKLEN_T optlen = sizeof(ipoptblock);
     #else
     struct ipoption ipoptblock;
     struct ipoption *ipopt = &ipoptblock;
-    SOCKLEN_T optlen = sizeof(ipoptblock);
+    EXIM_SOCKLEN_T optlen = sizeof(ipoptblock);
     #endif
 
     /* Occasional genuine failures of getsockopt() have been seen - for
@@ -1608,14 +1621,17 @@ if (smtp_enforce_sync && sender_host_address != NULL && !sender_host_notsocket)
       &tzero) > 0)
     {
     int rc = read(fileno(smtp_in), smtp_inbuffer, in_buffer_size);
-    if (rc > 150) rc = 150;
-    smtp_inbuffer[rc] = 0; 
-    log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol violation: "
-      "synchronization error (input sent without waiting for greeting): "
-      "rejected connection from %s input=\"%s\"", host_and_ident(TRUE),
-      string_printing(smtp_inbuffer));
-    smtp_printf("554 SMTP synchronization error\r\n");
-    return FALSE;
+    if (rc > 0)
+      {
+      if (rc > 150) rc = 150;
+      smtp_inbuffer[rc] = 0;
+      log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol "
+        "synchronization error (input sent without waiting for greeting): "
+        "rejected connection from %s input=\"%s\"", host_and_ident(TRUE),
+        string_printing(smtp_inbuffer));
+      smtp_printf("554 SMTP synchronization error\r\n");
+      return FALSE;
+      }
     }
   }
 
@@ -1803,7 +1819,7 @@ uschar *sender_info = US"";
 uschar *what = (where == ACL_WHERE_PREDATA)? US"DATA" :
 #ifdef WITH_CONTENT_SCAN
                (where == ACL_WHERE_MIME)? US"during MIME ACL checks" :
-#endif  
+#endif
                (where == ACL_WHERE_DATA)? US"after DATA" :
   string_sprintf("%s %s", acl_wherenames[where], smtp_data);
 
@@ -1916,6 +1932,130 @@ return 2;
 
 
 
+/*************************************************
+*             Verify HELO argument               *
+*************************************************/
+
+/* This function is called if helo_verify_hosts or helo_try_verify_hosts is
+matched. It is also called from ACL processing if verify = helo is used and
+verification was not previously tried (i.e. helo_try_verify_hosts was not
+matched). The result of its processing is to set helo_verified and
+helo_verify_failed. These variables should both be FALSE for this function to
+be called.
+
+Note that EHLO/HELO is legitimately allowed to quote an address literal. Allow
+for IPv6 ::ffff: literals.
+
+Argument:   none
+Returns:    TRUE if testing was completed;
+            FALSE on a temporary failure
+*/
+
+BOOL
+smtp_verify_helo(void)
+{
+BOOL yield = TRUE;
+
+HDEBUG(D_receive) debug_printf("verifying EHLO/HELO argument \"%s\"\n",
+  sender_helo_name);
+
+if (sender_helo_name == NULL)
+  {
+  HDEBUG(D_receive) debug_printf("no EHLO/HELO command was issued\n");
+  }
+
+else if (sender_helo_name[0] == '[')
+  {
+  helo_verified = Ustrncmp(sender_helo_name+1, sender_host_address,
+    Ustrlen(sender_host_address)) == 0;
+
+  #if HAVE_IPV6
+  if (!helo_verified)
+    {
+    if (strncmpic(sender_host_address, US"::ffff:", 7) == 0)
+      helo_verified = Ustrncmp(sender_helo_name + 1,
+        sender_host_address + 7, Ustrlen(sender_host_address) - 7) == 0;
+    }
+  #endif
+
+  HDEBUG(D_receive)
+    { if (helo_verified) debug_printf("matched host address\n"); }
+  }
+
+/* Do a reverse lookup if one hasn't already given a positive or negative
+response. If that fails, or the name doesn't match, try checking with a forward
+lookup. */
+
+else
+  {
+  if (sender_host_name == NULL && !host_lookup_failed)
+    yield = host_name_lookup() != DEFER;
+
+  /* If a host name is known, check it and all its aliases. */
+
+  if (sender_host_name != NULL)
+    {
+    helo_verified = strcmpic(sender_host_name, sender_helo_name) == 0;
+
+    if (helo_verified)
+      {
+      HDEBUG(D_receive) debug_printf("matched host name\n");
+      }
+    else
+      {
+      uschar **aliases = sender_host_aliases;
+      while (*aliases != NULL)
+        {
+        helo_verified = strcmpic(*aliases++, sender_helo_name) == 0;
+        if (helo_verified) break;
+        }
+      HDEBUG(D_receive)
+        {
+        if (helo_verified)
+          debug_printf("matched alias %s\n", *(--aliases));
+        }
+      }
+    }
+
+  /* Final attempt: try a forward lookup of the helo name */
+
+  if (!helo_verified)
+    {
+    int rc;
+    host_item h;
+    h.name = sender_helo_name;
+    h.address = NULL;
+    h.mx = MX_NONE;
+    h.next = NULL;
+    HDEBUG(D_receive) debug_printf("getting IP address for %s\n",
+      sender_helo_name);
+    rc = host_find_byname(&h, NULL, NULL, TRUE);
+    if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL)
+      {
+      host_item *hh = &h;
+      while (hh != NULL)
+        {
+        if (Ustrcmp(hh->address, sender_host_address) == 0)
+          {
+          helo_verified = TRUE;
+          HDEBUG(D_receive)
+            debug_printf("IP address for %s matches calling address\n",
+              sender_helo_name);
+          break;
+          }
+        hh = hh->next;
+        }
+      }
+    }
+  }
+
+if (!helo_verified) helo_verify_failed = FALSE;  /* We've tried ... */
+return yield;
+}
+
+
+
+
 /*************************************************
 *       Initialize for SMTP incoming message     *
 *************************************************/
@@ -2102,6 +2242,14 @@ while (done <= 0)
     if (au->set_id != NULL) set_id = expand_string(au->set_id);
     expand_nmax = -1;        /* Reset numeric variables */
 
+    /* The value of authenticated_id is stored in the spool file and printed in
+    log lines. It must not contain binary zeros or newline characters. In
+    normal use, it never will, but when playing around or testing, this error
+    can (did) happen. To guard against this, ensure that the id contains only
+    printing characters. */
+
+    if (set_id != NULL) set_id = string_printing(set_id);
+
     /* For the non-OK cases, set up additional logging data if set_id
     is not empty. */
 
@@ -2258,104 +2406,16 @@ while (done <= 0)
         (tls_active >= 0)? " TLS" : "", host_and_ident(FALSE));
 
       /* Verify if configured. This doesn't give much security, but it does
-      make some people happy to be able to do it. Note that HELO is legitimately
-      allowed to quote an address literal. Allow for IPv6 ::ffff: literals. */
+      make some people happy to be able to do it. If helo_required is set,
+      (host matches helo_verify_hosts) failure forces rejection. If helo_verify
+      is set (host matches helo_try_verify_hosts), it does not. This is perhaps
+      now obsolescent, since the verification can now be requested selectively
+      at ACL time. */
 
-      helo_verified = FALSE;
+      helo_verified = helo_verify_failed = FALSE;
       if (helo_required || helo_verify)
         {
-        BOOL tempfail = FALSE;
-
-        HDEBUG(D_receive) debug_printf("verifying %s %s\n", hello,
-          sender_helo_name);
-        if (sender_helo_name[0] == '[')
-          {
-          helo_verified = Ustrncmp(sender_helo_name+1, sender_host_address,
-            Ustrlen(sender_host_address)) == 0;
-
-          #if HAVE_IPV6
-          if (!helo_verified)
-            {
-            if (strncmpic(sender_host_address, US"::ffff:", 7) == 0)
-              helo_verified = Ustrncmp(sender_helo_name + 1,
-                sender_host_address + 7, Ustrlen(sender_host_address) - 7) == 0;
-            }
-          #endif
-
-          HDEBUG(D_receive)
-            { if (helo_verified) debug_printf("matched host address\n"); }
-          }
-
-        /* Do a reverse lookup if one hasn't already given a positive or
-        negative response. If that fails, or the name doesn't match, try
-        checking with a forward lookup. */
-
-        else
-          {
-          if (sender_host_name == NULL && !host_lookup_failed)
-            tempfail = host_name_lookup() == DEFER;
-
-          /* If a host name is known, check it and all its aliases. */
-
-          if (sender_host_name != NULL)
-            {
-            helo_verified = strcmpic(sender_host_name, sender_helo_name) == 0;
-
-            if (helo_verified)
-              {
-              HDEBUG(D_receive) debug_printf("matched host name\n");
-              }
-            else
-              {
-              uschar **aliases = sender_host_aliases;
-              while (*aliases != NULL)
-                {
-                helo_verified = strcmpic(*aliases++, sender_helo_name) == 0;
-                if (helo_verified) break;
-                }
-              HDEBUG(D_receive)
-                {
-                if (helo_verified)
-                  debug_printf("matched alias %s\n", *(--aliases));
-                }
-              }
-            }
-
-          /* Final attempt: try a forward lookup of the helo name */
-
-          if (!helo_verified)
-            {
-            int rc;
-            host_item h;
-            h.name = sender_helo_name;
-            h.address = NULL;
-            h.mx = MX_NONE;
-            h.next = NULL;
-            HDEBUG(D_receive) debug_printf("getting IP address for %s\n",
-              sender_helo_name);
-            rc = host_find_byname(&h, NULL, NULL, TRUE);
-            if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL)
-              {
-              host_item *hh = &h;
-              while (hh != NULL)
-                {
-                if (Ustrcmp(hh->address, sender_host_address) == 0)
-                  {
-                  helo_verified = TRUE;
-                  HDEBUG(D_receive)
-                    debug_printf("IP address for %s matches calling address\n",
-                      sender_helo_name);
-                  break;
-                  }
-                hh = hh->next;
-                }
-              }
-            }
-          }
-
-        /* Verification failed. A temporary lookup failure gives a temporary
-        error. */
-
+        BOOL tempfail = !smtp_verify_helo();
         if (!helo_verified)
           {
           if (helo_required)
@@ -2569,7 +2629,7 @@ while (done <= 0)
     if (tls_active >= 0) (void)tls_write(s, ptr); else
     #endif
 
-    fwrite(s, 1, ptr, smtp_out);
+    (void)fwrite(s, 1, ptr, smtp_out);
     DEBUG(D_receive) debug_printf("SMTP>> %s", s);
     helo_seen = TRUE;
     break;   /* HELO/EHLO */
@@ -3073,11 +3133,11 @@ while (done <= 0)
       smtp_printf("554 Too many recipients\r\n");
       break;
       }
-      
+
     if (acl_smtp_predata == NULL) rc = OK; else
-      { 
+      {
       enable_dollar_recipients = TRUE;
-      rc = acl_check(ACL_WHERE_PREDATA, NULL, acl_smtp_predata, &user_msg, 
+      rc = acl_check(ACL_WHERE_PREDATA, NULL, acl_smtp_predata, &user_msg,
         &log_msg);
       enable_dollar_recipients = FALSE;
       }
@@ -3155,7 +3215,7 @@ while (done <= 0)
       BOOL save_log_testing_mode = log_testing_mode;
       address_test_mode = log_testing_mode = TRUE;
       (void) verify_address(deliver_make_addr(smtp_data, FALSE), smtp_out,
-        vopt_is_recipient | vopt_qualify | vopt_expn, -1, -1, -1, NULL, NULL, 
+        vopt_is_recipient | vopt_qualify | vopt_expn, -1, -1, -1, NULL, NULL,
         NULL);
       address_test_mode = FALSE;
       log_testing_mode = save_log_testing_mode;    /* true for -bh */
@@ -3459,9 +3519,9 @@ while (done <= 0)
 
     if ((pid = fork()) == 0)
       {
-      smtp_input = FALSE;    /* This process is not associated with the */
-      fclose(smtp_in);       /* SMTP call any more. */
-      fclose(smtp_out);
+      smtp_input = FALSE;       /* This process is not associated with the */
+      (void)fclose(smtp_in);    /* SMTP call any more. */
+      (void)fclose(smtp_out);
 
       signal(SIGCHLD, SIG_DFL);      /* Want to catch child */
 
@@ -3538,8 +3598,7 @@ while (done <= 0)
     if (c > 150) c = 150;
     smtp_inptr[c] = 0;
     incomplete_transaction_log(US"sync failure");
-    log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol violation: "
-      "synchronization error "
+    log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol synchronization error "
       "(next input sent too soon: pipelining was%s advertised): "
       "rejected \"%s\" %s next input=\"%s\"",
       pipelining_advertised? "" : " not",