Support REQUIRETLS
[exim.git] / src / src / smtp_in.c
index 498cfab5e2d6513d6915d372c24140fec53178f3..d1c19ea0d6b15d76c3c4ffd20221e2266856fe04 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2017 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for handling an incoming SMTP call. */
@@ -135,6 +135,9 @@ static auth_instance *authenticated_by;
 static BOOL auth_advertised;
 #ifdef SUPPORT_TLS
 static BOOL tls_advertised;
+# ifdef EXPERIMENTAL_REQUIRETLS
+static BOOL requiretls_advertised;
+# endif
 #endif
 static BOOL dsn_advertised;
 static BOOL esmtp;
@@ -255,6 +258,9 @@ enum {
   ENV_MAIL_OPT_RET, ENV_MAIL_OPT_ENVID,
 #ifdef SUPPORT_I18N
   ENV_MAIL_OPT_UTF8,
+#endif
+#ifdef EXPERIMENTAL_REQUIRETLS
+  ENV_MAIL_OPT_REQTLS,
 #endif
   };
 typedef struct {
@@ -274,6 +280,10 @@ static env_mail_type_t env_mail_type_list[] = {
     { US"ENVID",  ENV_MAIL_OPT_ENVID,  TRUE },
 #ifdef SUPPORT_I18N
     { US"SMTPUTF8",ENV_MAIL_OPT_UTF8,  FALSE },                /* rfc6531 */
+#endif
+#ifdef EXPERIMENTAL_REQUIRETLS
+    /* https://tools.ietf.org/html/draft-ietf-uta-smtp-require-tls-03 */
+    { US"REQUIRETLS",ENV_MAIL_OPT_REQTLS,  FALSE },
 #endif
     /* keep this the last entry */
     { US"NULL",   ENV_MAIL_OPT_NULL,   FALSE },
@@ -340,7 +350,7 @@ fd_set fds;
 struct timeval tzero;
 
 #ifdef SUPPORT_TLS
-if (tls_in.active >= 0)
+if (tls_in.active.sock >= 0)
  return !tls_could_read();
 #endif
 
@@ -359,16 +369,13 @@ rc = smtp_getc(GETC_BUFFER_UNLIMITED);
 if (rc < 0) return TRUE;      /* End of file or error */
 
 smtp_ungetc(rc);
-rc = smtp_inend - smtp_inptr;
-if (rc > 150) rc = 150;
-smtp_inptr[rc] = 0;
 return FALSE;
 }
 
 static BOOL
 check_sync(void)
 {
-if (!smtp_enforce_sync || sender_host_address == NULL || sender_host_notsocket)
+if (!smtp_enforce_sync || !sender_host_address || sender_host_notsocket)
   return TRUE;
 
 return wouldblock_reading();
@@ -427,6 +434,53 @@ log_write(L_smtp_incomplete_transaction, LOG_MAIN|LOG_SENDER|LOG_RECIPIENTS,
 
 
 
+void
+smtp_command_timeout_exit(void)
+{
+log_write(L_lost_incoming_connection,
+         LOG_MAIN, "SMTP command timeout on%s connection from %s",
+         tls_in.active.sock >= 0 ? " TLS" : "", host_and_ident(FALSE));
+if (smtp_batched_input)
+  moan_smtp_batch(NULL, "421 SMTP command timeout"); /* Does not return */
+smtp_notquit_exit(US"command-timeout", US"421",
+  US"%s: SMTP command timeout - closing connection",
+  smtp_active_hostname);
+exim_exit(EXIT_FAILURE, US"receiving");
+}
+
+void
+smtp_command_sigterm_exit(void)
+{
+log_write(0, LOG_MAIN, "%s closed after SIGTERM", smtp_get_connection_info());
+if (smtp_batched_input)
+  moan_smtp_batch(NULL, "421 SIGTERM received");  /* Does not return */
+smtp_notquit_exit(US"signal-exit", US"421",
+  US"%s: Service not available - closing connection", smtp_active_hostname);
+exim_exit(EXIT_FAILURE, US"receiving");
+}
+
+void
+smtp_data_timeout_exit(void)
+{
+log_write(L_lost_incoming_connection,
+  LOG_MAIN, "SMTP data timeout (message abandoned) on connection from %s F=<%s>",
+  sender_fullhost ? sender_fullhost : US"local process", sender_address);
+receive_bomb_out(US"data-timeout", US"SMTP incoming data timeout");
+/* Does not return */
+}
+
+void
+smtp_data_sigint_exit(void)
+{
+log_write(0, LOG_MAIN, "%s closed after %s",
+  smtp_get_connection_info(), had_data_sigint == SIGTERM ? "SIGTERM":"SIGINT");
+receive_bomb_out(US"signal-exit",
+  US"Service not available - SIGTERM or SIGINT received");
+/* Does not return */
+}
+
+
+
 /* Refill the buffer, and notify DKIM verification code.
 Return false for error or EOF.
 */
@@ -439,22 +493,33 @@ if (!smtp_out) return FALSE;
 fflush(smtp_out);
 if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
 
-/* Limit amount read, so non-message data is not fed to DKIM */
+/* Limit amount read, so non-message data is not fed to DKIM.
+Take care to not touch the safety NUL at the end of the buffer. */
 
-rc = read(fileno(smtp_in), smtp_inbuffer, MIN(IN_BUFFER_SIZE, lim));
+rc = read(fileno(smtp_in), smtp_inbuffer, MIN(IN_BUFFER_SIZE-1, lim));
 save_errno = errno;
-alarm(0);
+if (smtp_receive_timeout > 0) alarm(0);
 if (rc <= 0)
   {
   /* Must put the error text in fixed store, because this might be during
   header reading, where it releases unused store above the header. */
   if (rc < 0)
     {
+    if (had_command_timeout)           /* set by signal handler */
+      smtp_command_timeout_exit();     /* does not return */
+    if (had_command_sigterm)
+      smtp_command_sigterm_exit();
+    if (had_data_timeout)
+      smtp_data_timeout_exit();
+    if (had_data_sigint)
+      smtp_data_sigint_exit();
+
     smtp_had_error = save_errno;
     smtp_read_error = string_copy_malloc(
       string_sprintf(" (error: %s)", strerror(save_errno)));
     }
-  else smtp_had_eof = 1;
+  else
+    smtp_had_eof = 1;
   return FALSE;
   }
 #ifndef DISABLE_DKIM
@@ -540,7 +605,7 @@ uschar * log_msg;
 for(;;)
   {
 #ifndef DISABLE_DKIM
-  BOOL dkim_save;
+  unsigned dkim_save;
 #endif
 
   if (chunking_data_left > 0)
@@ -551,7 +616,7 @@ for(;;)
   receive_ungetc = lwr_receive_ungetc;
 #ifndef DISABLE_DKIM
   dkim_save = dkim_collect_input;
-  dkim_collect_input = FALSE;
+  dkim_collect_input = 0;
 #endif
 
   /* Unless PIPELINING was offered, there should be no next command
@@ -653,7 +718,7 @@ next_cmd:
          }
 
       receive_getc = bdat_getc;
-      receive_getbuf = bdat_getbuf;
+      receive_getbuf = bdat_getbuf;    /* r~getbuf is never actually used */
       receive_ungetc = bdat_ungetc;
 #ifndef DISABLE_DKIM
       dkim_collect_input = dkim_save;
@@ -684,7 +749,7 @@ bdat_flush_data(void)
 while (chunking_data_left)
   {
   unsigned n = chunking_data_left;
-  (void) bdat_getbuf(&n);
+  if (!bdat_getbuf(&n)) break;
   }
 
 receive_getc = lwr_receive_getc;
@@ -867,9 +932,9 @@ if (rcpt_in_progress)
 /* Now write the string */
 
 #ifdef SUPPORT_TLS
-if (tls_in.active >= 0)
+if (tls_in.active.sock >= 0)
   {
-  if (tls_write(TRUE, big_buffer, Ustrlen(big_buffer), more) < 0)
+  if (tls_write(NULL, big_buffer, Ustrlen(big_buffer), more) < 0)
     smtp_write_error = -1;
   }
 else
@@ -896,7 +961,7 @@ Returns:    0 for no error; -1 after an error
 int
 smtp_fflush(void)
 {
-if (tls_in.active < 0 && fflush(smtp_out) != 0) smtp_write_error = -1;
+if (tls_in.active.sock < 0 && fflush(smtp_out) != 0) smtp_write_error = -1;
 return smtp_write_error;
 }
 
@@ -916,16 +981,7 @@ Returns:  nothing
 static void
 command_timeout_handler(int sig)
 {
-sig = sig;    /* Keep picky compilers happy */
-log_write(L_lost_incoming_connection,
-          LOG_MAIN, "SMTP command timeout on%s connection from %s",
-          (tls_in.active >= 0)? " TLS" : "",
-          host_and_ident(FALSE));
-if (smtp_batched_input)
-  moan_smtp_batch(NULL, "421 SMTP command timeout");  /* Does not return */
-smtp_notquit_exit(US"command-timeout", US"421",
-  US"%s: SMTP command timeout - closing connection", smtp_active_hostname);
-exim_exit(EXIT_FAILURE, US"receiving");
+had_command_timeout = sig;
 }
 
 
@@ -943,13 +999,7 @@ Returns:  nothing
 static void
 command_sigterm_handler(int sig)
 {
-sig = sig;    /* Keep picky compilers happy */
-log_write(0, LOG_MAIN, "%s closed after SIGTERM", smtp_get_connection_info());
-if (smtp_batched_input)
-  moan_smtp_batch(NULL, "421 SIGTERM received");  /* Does not return */
-smtp_notquit_exit(US"signal-exit", US"421",
-  US"%s: Service not available - closing connection", smtp_active_hostname);
-exim_exit(EXIT_FAILURE, US"receiving");
+had_command_sigterm = sig;
 }
 
 
@@ -1509,6 +1559,7 @@ int ptr = 0;
 smtp_cmd_list *p;
 BOOL hadnull = FALSE;
 
+had_command_timeout = 0;
 os_non_restarting_signal(SIGALRM, command_timeout_handler);
 
 while ((c = (receive_getc)(buffer_lim)) != '\n' && c != EOF)
@@ -1614,11 +1665,11 @@ if (proxy_session && proxy_session_failed)
 
 /* Enforce synchronization for unknown commands */
 
-if (smtp_inptr < smtp_inend &&                     /* Outstanding input */
-    check_sync &&                                  /* Local flag set */
-    smtp_enforce_sync &&                           /* Global flag set */
-    sender_host_address != NULL &&                 /* Not local input */
-    !sender_host_notsocket)                        /* Really is a socket */
+if (  smtp_inptr < smtp_inend          /* Outstanding input */
+   && check_sync                       /* Local flag set */
+   && smtp_enforce_sync                        /* Global flag set */
+   && sender_host_address              /* Not local input */
+   && !sender_host_notsocket)          /* Really is a socket */
   return BADSYN_CMD;
 
 return OTHER_CMD;
@@ -1838,7 +1889,7 @@ BOOL yield = helo_accept_junk;
 
 /* Discard any previous helo name */
 
-if (sender_helo_name != NULL)
+if (sender_helo_name)
   {
   store_free(sender_helo_name);
   sender_helo_name = NULL;
@@ -1947,13 +1998,13 @@ return TRUE;
 *************************************************/
 
 /* This function is called whenever the SMTP session is reset from
-within either of the setup functions.
+within either of the setup functions; also from the daemon loop.
 
 Argument:   the stacking pool storage reset point
 Returns:    nothing
 */
 
-static void
+void
 smtp_reset(void *reset_point)
 {
 recipients_list = NULL;
@@ -1980,8 +2031,8 @@ active_local_sender_retain = local_sender_retain;    /* Can be set by ACL */
 sending_ip_address = NULL;
 return_path = sender_address = NULL;
 sender_data = NULL;                                 /* Can be set by ACL */
-deliver_localpart_orig = NULL;
-deliver_domain_orig = NULL;
+deliver_localpart_parent = deliver_localpart_orig = NULL;
+deliver_domain_parent = deliver_domain_orig = NULL;
 callout_address = NULL;
 submission_name = NULL;                              /* Can be set by ACL */
 raw_sender = NULL;                  /* After SMTP rewrite, before qualifying */
@@ -1996,10 +2047,27 @@ bmi_run = 0;
 bmi_verdicts = NULL;
 #endif
 dnslist_domain = dnslist_matched = NULL;
+#ifdef SUPPORT_SPF
+spf_header_comment = spf_received = spf_result = spf_smtp_comment = NULL;
+spf_result_guessed = FALSE;
+#endif
 #ifndef DISABLE_DKIM
-dkim_signers = NULL;
+dkim_cur_signer = dkim_signers =
+dkim_signing_domain = dkim_signing_selector = dkim_signatures = NULL;
+dkim_cur_signer = dkim_signers = dkim_signing_domain = dkim_signing_selector = NULL;
 dkim_disable_verify = FALSE;
-dkim_collect_input = FALSE;
+dkim_collect_input = 0;
+dkim_verify_overall = dkim_verify_status = dkim_verify_reason = NULL;
+dkim_key_length = 0;
+dkim_verify_signers = US"$dkim_signers";
+#endif
+#ifdef EXPERIMENTAL_DMARC
+dmarc_has_been_checked = dmarc_disable_verify = dmarc_enable_forensic = FALSE;
+dmarc_domain_policy = dmarc_status = dmarc_status_text =
+dmarc_used_domain = NULL;
+#endif
+#ifdef EXPERIMENTAL_ARC
+arc_state = arc_state_reason = NULL;
 #endif
 dsn_ret = 0;
 dsn_envid = NULL;
@@ -2007,12 +2075,6 @@ deliver_host = deliver_host_address = NULL;      /* Can be set by ACL */
 #ifndef DISABLE_PRDR
 prdr_requested = FALSE;
 #endif
-#ifdef SUPPORT_SPF
-spf_header_comment = NULL;
-spf_received = NULL;
-spf_result = NULL;
-spf_smtp_comment = NULL;
-#endif
 #ifdef SUPPORT_I18N
 message_smtputf8 = FALSE;
 #endif
@@ -2375,7 +2437,8 @@ smtp_exit_function_called = FALSE;    /* For avoiding loop in not-quit exit */
 /* If receiving by -bs from a trusted user, or testing with -bh, we allow
 authentication settings from -oMaa to remain in force. */
 
-if (!host_checking && !sender_host_notsocket) sender_host_authenticated = NULL;
+if (!host_checking && !sender_host_notsocket)
+  sender_host_auth_pubname = sender_host_authenticated = NULL;
 authenticated_by = NULL;
 
 #ifdef SUPPORT_TLS
@@ -2384,6 +2447,9 @@ tls_in.ourcert = tls_in.peercert = NULL;
 tls_in.sni = NULL;
 tls_in.ocsp = OCSP_NOT_REQ;
 tls_advertised = FALSE;
+# ifdef EXPERIMENTAL_REQUIRETLS
+requiretls_advertised = FALSE;
+# endif
 #endif
 dsn_advertised = FALSE;
 #ifdef SUPPORT_I18N
@@ -2419,10 +2485,12 @@ else
     (sender_host_address ? protocols : protocols_local) [pnormal];
 
 /* Set up the buffer for inputting using direct read() calls, and arrange to
-call the local functions instead of the standard C ones. */
+call the local functions instead of the standard C ones.  Place a NUL at the
+end of the buffer to safety-stop C-string reads from it. */
 
 if (!(smtp_inbuffer = US malloc(IN_BUFFER_SIZE)))
   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "malloc() failed for SMTP input buffer");
+smtp_inbuffer[IN_BUFFER_SIZE-1] = '\0';
 
 receive_getc = smtp_getc;
 receive_getbuf = smtp_getbuf;
@@ -3522,7 +3590,7 @@ else
 
     HDEBUG(D_receive) debug_printf("getting IP address for %s\n",
       sender_helo_name);
-    rc = host_find_bydns(&h, NULL, HOST_FIND_BY_A,
+    rc = host_find_bydns(&h, NULL, HOST_FIND_BY_A | HOST_FIND_BY_AAAA,
                          NULL, NULL, NULL, &d, NULL, NULL);
     if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL)
       for (hh = &h; hh; hh = hh->next)
@@ -3625,12 +3693,13 @@ switch(rc)
     {
     if (set_id) authenticated_id = string_copy_malloc(set_id);
     sender_host_authenticated = au->name;
+    sender_host_auth_pubname  = au->public_name;
     authentication_failed = FALSE;
     authenticated_fail_id = NULL;   /* Impossible to already be set? */
 
     received_protocol =
       (sender_host_address ? protocols : protocols_local)
-       [pextend + pauthed + (tls_in.active >= 0 ? pcrpted:0)];
+       [pextend + pauthed + (tls_in.active.sock >= 0 ? pcrpted:0)];
     *s = *ss = US"235 Authentication succeeded";
     authenticated_by = au;
     break;
@@ -3724,7 +3793,7 @@ else
   smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
 
 #ifdef SUPPORT_TLS
-tls_close(TRUE, TRUE);
+tls_close(NULL, TLS_SHUTDOWN_NOWAIT);
 #endif
 
 log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
@@ -3800,6 +3869,7 @@ cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE;
 
 /* Set the local signal handler for SIGTERM - it tries to end off tidily */
 
+had_command_sigterm = 0;
 os_non_restarting_signal(SIGTERM, command_sigterm_handler);
 
 /* Batched SMTP is handled in a different function. */
@@ -3834,7 +3904,7 @@ while (done <= 0)
 
 #ifdef AUTH_TLS
   /* Check once per STARTTLS or SSL-on-connect for a TLS AUTH */
-  if (  tls_in.active >= 0
+  if (  tls_in.active.sock >= 0
      && tls_in.peercert
      && tls_in.certificate_verified
      && cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd
@@ -4040,9 +4110,9 @@ while (done <= 0)
       /* Force a reverse lookup if HELO quoted something in helo_lookup_domains
       because otherwise the log can be confusing. */
 
-      if (sender_host_name == NULL &&
-           (deliver_domain = sender_helo_name,  /* set $domain */
-            match_isinlist(sender_helo_name, CUSS &helo_lookup_domains, 0,
+      if (  !sender_host_name
+         && (deliver_domain = sender_helo_name,  /* set $domain */
+             match_isinlist(sender_helo_name, CUSS &helo_lookup_domains, 0,
               &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) == OK)
         (void)host_name_lookup();
 
@@ -4051,7 +4121,7 @@ while (done <= 0)
 
       host_build_sender_fullhost();  /* Rebuild */
       set_process_info("handling%s incoming connection from %s",
-        (tls_in.active >= 0)? " TLS" : "", host_and_ident(FALSE));
+        (tls_in.active.sock >= 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. If helo_required is set,
@@ -4095,7 +4165,11 @@ while (done <= 0)
                &user_msg, &log_msg)) != OK)
         {
         done = smtp_handle_acl_fail(ACL_WHERE_HELO, rc, user_msg, log_msg);
-        sender_helo_name = NULL;
+       if (sender_helo_name)
+         {
+         store_free(sender_helo_name);
+         sender_helo_name = NULL;
+         }
         host_build_sender_fullhost();  /* Rebuild */
         break;
         }
@@ -4111,6 +4185,9 @@ while (done <= 0)
     pipelining_advertised = FALSE;
 #ifdef SUPPORT_TLS
     tls_advertised = FALSE;
+# ifdef EXPERIMENTAL_REQUIRETLS
+    requiretls_advertised = FALSE;
+# endif
 #endif
     dsn_advertised = FALSE;
 #ifdef SUPPORT_I18N
@@ -4303,13 +4380,24 @@ while (done <= 0)
       secure connection. */
 
 #ifdef SUPPORT_TLS
-      if (tls_in.active < 0 &&
+      if (tls_in.active.sock < 0 &&
           verify_check_host(&tls_advertise_hosts) != FAIL)
         {
         g = string_catn(g, smtp_code, 3);
         g = string_catn(g, US"-STARTTLS\r\n", 11);
         tls_advertised = TRUE;
         }
+
+# ifdef EXPERIMENTAL_REQUIRETLS
+      /* Advertise REQUIRETLS only once we are in a secure connection */
+      if (  tls_in.active.sock >= 0
+         && verify_check_host(&tls_advertise_requiretls) != FAIL)
+       {
+       g = string_catn(g, smtp_code, 3);
+       g = string_catn(g, US"-REQUIRETLS\r\n", 13);
+       requiretls_advertised = TRUE;
+       }
+# endif
 #endif
 
 #ifndef DISABLE_PRDR
@@ -4341,7 +4429,7 @@ while (done <= 0)
     has been seen. */
 
 #ifdef SUPPORT_TLS
-    if (tls_in.active >= 0) (void)tls_write(TRUE, g->s, g->ptr, FALSE); else
+    if (tls_in.active.sock >= 0) (void)tls_write(NULL, g->s, g->ptr, FALSE); else
 #endif
 
       {
@@ -4364,7 +4452,7 @@ while (done <= 0)
        [ (esmtp
          ? pextend + (sender_host_authenticated ? pauthed : 0)
          : pnormal)
-       + (tls_in.active >= 0 ? pcrpted : 0)
+       + (tls_in.active.sock >= 0 ? pcrpted : 0)
        ];
     cancel_cutthrough_connection(TRUE, US"sent EHLO response");
     smtp_reset(reset_point);
@@ -4392,14 +4480,14 @@ while (done <= 0)
       break;
       }
 
-    if (sender_address != NULL)
+    if (sender_address)
       {
       done = synprot_error(L_smtp_protocol_error, 503, NULL,
         US"sender already given");
       break;
       }
 
-    if (smtp_cmd_data[0] == 0)
+    if (!*smtp_cmd_data)
       {
       done = synprot_error(L_smtp_protocol_error, 501, NULL,
         US"MAIL must have an address operand");
@@ -4496,7 +4584,7 @@ while (done <= 0)
             /* Check if RET has already been set */
             if (dsn_ret > 0)
              {
-              synprot_error(L_smtp_syntax_error, 501, NULL,
+              done = synprot_error(L_smtp_syntax_error, 501, NULL,
                 US"RET can be specified once only");
               goto COMMAND_LOOP;
              }
@@ -4509,7 +4597,7 @@ while (done <= 0)
             /* Check for invalid invalid value, and exit with error */
             if (dsn_ret == 0)
              {
-              synprot_error(L_smtp_syntax_error, 501, NULL,
+              done = synprot_error(L_smtp_syntax_error, 501, NULL,
                 US"Value for RET is invalid");
               goto COMMAND_LOOP;
              }
@@ -4519,9 +4607,9 @@ while (done <= 0)
           if (dsn_advertised)
            {
             /* Check if the dsn envid has been already set */
-            if (dsn_envid != NULL)
+            if (dsn_envid)
              {
-              synprot_error(L_smtp_syntax_error, 501, NULL,
+              done = synprot_error(L_smtp_syntax_error, 501, NULL,
                 US"ENVID can be specified once only");
               goto COMMAND_LOOP;
              }
@@ -4552,10 +4640,10 @@ while (done <= 0)
                 US"invalid data for AUTH");
               goto COMMAND_LOOP;
               }
-            if (acl_smtp_mailauth == NULL)
+            if (!acl_smtp_mailauth)
               {
               ignore_msg = US"client not authenticated";
-              rc = (sender_host_authenticated != NULL)? OK : FAIL;
+              rc = sender_host_authenticated ? OK : FAIL;
               }
             else
               {
@@ -4608,18 +4696,50 @@ while (done <= 0)
 
 #ifdef SUPPORT_I18N
         case ENV_MAIL_OPT_UTF8:
-         if (smtputf8_advertised)
+         if (!smtputf8_advertised)
            {
-           int old_pool = store_pool;
+           done = synprot_error(L_smtp_syntax_error, 501, NULL,
+             US"SMTPUTF8 used when not advertised");
+           goto COMMAND_LOOP;
+           }
 
-           DEBUG(D_receive) debug_printf("smtputf8 requested\n");
-           message_smtputf8 = allow_utf8_domains = TRUE;
+         DEBUG(D_receive) debug_printf("smtputf8 requested\n");
+         message_smtputf8 = allow_utf8_domains = TRUE;
+         if (Ustrncmp(received_protocol, US"utf8", 4) != 0)
+           {
+           int old_pool = store_pool;
            store_pool = POOL_PERM;
            received_protocol = string_sprintf("utf8%s", received_protocol);
            store_pool = old_pool;
            }
          break;
 #endif
+
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+        case ENV_MAIL_OPT_REQTLS:
+         {
+         const uschar * list = value;
+         int sep = ',';
+         const uschar * opt;
+         uschar * r, * t;
+
+         if (!requiretls_advertised)
+           {
+           done = synprot_error(L_smtp_syntax_error, 555, NULL,
+             US"unadvertised MAIL option: REQUIRETLS");
+           goto COMMAND_LOOP;
+           }
+
+         DEBUG(D_receive) debug_printf("requiretls requested\n");
+         tls_requiretls = REQUIRETLS_MSG;
+
+         r = string_copy_malloc(received_protocol);
+         if ((t = Ustrrchr(r, 's'))) *t = 'S';
+         received_protocol = r;
+         }
+         break;
+#endif
+
         /* No valid option. Stick back the terminator characters and break
         the loop.  Do the name-terminator second as extract_option sets
         value==name when it found no equal-sign.
@@ -4637,6 +4757,17 @@ while (done <= 0)
       if (arg_error) break;
       }
 
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+    if (tls_requiretls & REQUIRETLS_MSG)
+      {
+      /* Ensure headers-only bounces whether a RET option was given or not. */
+
+      DEBUG(D_receive) if (dsn_ret == dsn_ret_full)
+       debug_printf("requiretls override: dsn_ret_full -> dsn_ret_hdrs\n");
+      dsn_ret = dsn_ret_hdrs;
+      }
+#endif
+
     /* If we have passed the threshold for rate limiting, apply the current
     delay, and update it for next time, provided this is a limited host. */
 
@@ -4713,8 +4844,7 @@ while (done <= 0)
     in which case just qualify the address. The flag is set above at the start
     of the SMTP connection. */
 
-    if (sender_domain == 0 && sender_address[0] != 0)
-      {
+    if (!sender_domain && *sender_address)
       if (allow_unqualified_sender)
         {
         sender_domain = Ustrlen(sender_address) + 1;
@@ -4735,7 +4865,6 @@ while (done <= 0)
         sender_address = NULL;
         break;
         }
-      }
 
     /* Apply an ACL check if one is defined, before responding. Afterwards,
     when pipelining is not advertised, do another sync check in case the ACL
@@ -4840,7 +4969,7 @@ while (done <= 0)
         /* Check whether orcpt has been already set */
         if (orcpt)
          {
-          synprot_error(L_smtp_syntax_error, 501, NULL,
+          done = synprot_error(L_smtp_syntax_error, 501, NULL,
             US"ORCPT can be specified once only");
           goto COMMAND_LOOP;
           }
@@ -4853,7 +4982,7 @@ while (done <= 0)
         /* Check if the notify flags have been already set */
         if (flags > 0)
          {
-          synprot_error(L_smtp_syntax_error, 501, NULL,
+          done = synprot_error(L_smtp_syntax_error, 501, NULL,
               US"NOTIFY can be specified once only");
           goto COMMAND_LOOP;
           }
@@ -4885,7 +5014,7 @@ while (done <= 0)
             else
              {
               /* Catch any strange values */
-              synprot_error(L_smtp_syntax_error, 501, NULL,
+              done = synprot_error(L_smtp_syntax_error, 501, NULL,
                 US"Invalid value for NOTIFY parameter");
               goto COMMAND_LOOP;
               }
@@ -5152,7 +5281,7 @@ while (done <= 0)
       ACL may have delayed.  To handle cutthrough delivery enforce a dummy call
       to get the DATA command sent. */
 
-      if (acl_smtp_predata == NULL && cutthrough.fd < 0)
+      if (acl_smtp_predata == NULL && cutthrough.cctx.sock < 0)
        rc = OK;
       else
        {
@@ -5306,7 +5435,7 @@ while (done <= 0)
       {
       DEBUG(D_any)
         debug_printf("Non-empty input buffer after STARTTLS; naive attack?\n");
-      if (tls_in.active < 0)
+      if (tls_in.active.sock < 0)
         smtp_inend = smtp_inptr = smtp_inbuffer;
       /* and if TLS is already active, tls_server_start() should fail */
       }
@@ -5336,7 +5465,7 @@ while (done <= 0)
       cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE;
       cmd_list[CMD_LIST_AUTH].is_mail_cmd = TRUE;
       cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE;
-      if (sender_helo_name != NULL)
+      if (sender_helo_name)
         {
         store_free(sender_helo_name);
         sender_helo_name = NULL;
@@ -5349,10 +5478,10 @@ while (done <= 0)
          [ (esmtp
            ? pextend + (sender_host_authenticated ? pauthed : 0)
            : pnormal)
-         + (tls_in.active >= 0 ? pcrpted : 0)
+         + (tls_in.active.sock >= 0 ? pcrpted : 0)
          ];
 
-      sender_host_authenticated = NULL;
+      sender_host_auth_pubname = sender_host_authenticated = NULL;
       authenticated_id = NULL;
       sync_cmd_limit = NON_SYNC_CMD_NON_PIPELINING;
       DEBUG(D_tls) debug_printf("TLS active\n");
@@ -5409,7 +5538,7 @@ while (done <= 0)
        smtp_printf("554 Security failure\r\n", FALSE);
        break;
       }
-    tls_close(TRUE, TRUE);
+    tls_close(NULL, TLS_SHUTDOWN_NOWAIT);
     break;
     #endif
 
@@ -5451,7 +5580,7 @@ while (done <= 0)
       buffer[0] = 0;
       Ustrcat(buffer, " AUTH");
       #ifdef SUPPORT_TLS
-      if (tls_in.active < 0 &&
+      if (tls_in.active.sock < 0 &&
           verify_check_host(&tls_advertise_hosts) != FAIL)
         Ustrcat(buffer, " STARTTLS");
       #endif
@@ -5497,7 +5626,7 @@ while (done <= 0)
 
     case ETRN_CMD:
     HAD(SCH_ETRN);
-    if (sender_address != NULL)
+    if (sender_address)
       {
       done = synprot_error(L_smtp_protocol_error, 503, NULL,
         US"ETRN is not permitted inside a transaction");
@@ -5523,7 +5652,7 @@ while (done <= 0)
     since that is strictly the only kind of ETRN that can be implemented
     according to the RFC. */
 
-    if (smtp_etrn_command != NULL)
+    if (smtp_etrn_command)
       {
       uschar *error;
       BOOL rc;
@@ -5664,8 +5793,8 @@ while (done <= 0)
 
     case BADCHAR_CMD:
     done = synprot_error(L_smtp_syntax_error, 0, NULL,       /* Just logs */
-      US"NULL character(s) present (shown as '?')");
-    smtp_printf("501 NULL characters are not allowed in SMTP commands\r\n", FALSE);
+      US"NUL character(s) present (shown as '?')");
+    smtp_printf("501 NUL characters are not allowed in SMTP commands\r\n", FALSE);
     break;
 
 
@@ -5674,7 +5803,7 @@ while (done <= 0)
     if (smtp_inend >= smtp_inbuffer + IN_BUFFER_SIZE)
       smtp_inend = smtp_inbuffer + IN_BUFFER_SIZE - 1;
     c = smtp_inend - smtp_inptr;
-    if (c > 150) c = 150;
+    if (c > 150) c = 150;      /* limit logged amount */
     smtp_inptr[c] = 0;
     incomplete_transaction_log(US"sync failure");
     log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol synchronization error "
@@ -5739,6 +5868,30 @@ while (done <= 0)
 return done - 2;  /* Convert yield values */
 }
 
+
+
+gstring *
+authres_smtpauth(gstring * g)
+{
+if (!sender_host_authenticated)
+  return g;
+
+g = string_append(g, 2, US";\n\tauth=pass (", sender_host_auth_pubname);
+
+if (Ustrcmp(sender_host_auth_pubname, "tls") != 0)
+  g = string_append(g, 2, US") smtp.auth=", authenticated_id);
+else if (authenticated_id)
+  g = string_append(g, 2, US") x509.auth=", authenticated_id);
+else
+  g = string_catn(g, US") reason=x509.auth", 17);
+
+if (authenticated_sender)
+  g = string_append(g, 2, US" smtp.mailfrom=", authenticated_sender);
+return g;
+}
+
+
+
 /* vi: aw ai sw=2
 */
 /* End of smtp_in.c */