Debug: indent builtin-DB operations
[exim.git] / src / src / smtp_in.c
index 0afb97ca67a4aa77c926d19ffb6e600a1f5cc886..aa85add3e356af43314d8ff10337e6afe8e1f0a1 100644 (file)
@@ -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;
@@ -143,7 +146,6 @@ static BOOL helo_verify = FALSE;
 static BOOL helo_seen;
 static BOOL helo_accept_junk;
 static BOOL count_nonmail;
-static BOOL pipelining_advertised;
 static BOOL rcpt_smtp_response_same;
 static BOOL rcpt_in_progress;
 static int  nonmail_command_count;
@@ -255,6 +257,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 +279,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 },
@@ -379,7 +388,7 @@ static BOOL
 pipeline_response(void)
 {
 if (  !smtp_enforce_sync || !sender_host_address
-   || sender_host_notsocket || !pipelining_advertised)
+   || sender_host_notsocket || !smtp_in_pipelining_advertised)
   return FALSE;
 
 return !wouldblock_reading();
@@ -612,7 +621,7 @@ for(;;)
   /* Unless PIPELINING was offered, there should be no next command
   until after we ack that chunk */
 
-  if (!pipelining_advertised && !check_sync())
+  if (!smtp_in_pipelining_advertised && !check_sync())
     {
     unsigned n = smtp_inend - smtp_inptr;
     if (n > 32) n = 32;
@@ -2419,7 +2428,7 @@ count_nonmail = TRUE_UNSET;
 synprot_error_count = unknown_command_count = nonmail_command_count = 0;
 smtp_delay_mail = smtp_rlm_base;
 auth_advertised = FALSE;
-pipelining_advertised = FALSE;
+smtp_in_pipelining_advertised = FALSE;
 pipelining_enable = TRUE;
 sync_cmd_limit = NON_SYNC_CMD_NON_PIPELINING;
 smtp_exit_function_called = FALSE;    /* For avoiding loop in not-quit exit */
@@ -2437,6 +2446,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
@@ -4169,9 +4181,12 @@ while (done <= 0)
     that the entire reply is sent in one write(). */
 
     auth_advertised = FALSE;
-    pipelining_advertised = FALSE;
+    smtp_in_pipelining_advertised = FALSE;
 #ifdef SUPPORT_TLS
     tls_advertised = FALSE;
+# ifdef EXPERIMENTAL_REQUIRETLS
+    requiretls_advertised = FALSE;
+# endif
 #endif
     dsn_advertised = FALSE;
 #ifdef SUPPORT_I18N
@@ -4292,7 +4307,7 @@ while (done <= 0)
         g = string_catn(g, smtp_code, 3);
         g = string_catn(g, US"-PIPELINING\r\n", 13);
         sync_cmd_limit = NON_SYNC_CMD_PIPELINING;
-        pipelining_advertised = TRUE;
+       smtp_in_pipelining_advertised = TRUE;
         }
 
 
@@ -4371,6 +4386,17 @@ while (done <= 0)
         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
@@ -4453,14 +4479,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");
@@ -4557,7 +4583,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;
              }
@@ -4570,7 +4596,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;
              }
@@ -4580,9 +4606,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;
              }
@@ -4669,18 +4695,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.
@@ -4698,6 +4756,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. */
 
@@ -4774,8 +4843,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;
@@ -4796,7 +4864,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
@@ -4805,7 +4872,7 @@ while (done <= 0)
     if (acl_smtp_mail)
       {
       rc = acl_check(ACL_WHERE_MAIL, NULL, acl_smtp_mail, &user_msg, &log_msg);
-      if (rc == OK && !pipelining_advertised && !check_sync())
+      if (rc == OK && !smtp_in_pipelining_advertised && !check_sync())
         goto SYNC_FAILURE;
       }
     else
@@ -4860,7 +4927,7 @@ while (done <= 0)
 
     if (sender_address == NULL)
       {
-      if (pipelining_advertised && last_was_rej_mail)
+      if (smtp_in_pipelining_advertised && last_was_rej_mail)
         {
         smtp_printf("503 sender not yet given\r\n", FALSE);
         was_rej_mail = TRUE;
@@ -4901,7 +4968,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;
           }
@@ -4914,7 +4981,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;
           }
@@ -4946,7 +5013,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;
               }
@@ -5053,7 +5120,7 @@ while (done <= 0)
     else
       if (  (rc = acl_check(ACL_WHERE_RCPT, recipient, acl_smtp_rcpt, &user_msg,
                    &log_msg)) == OK
-        && !pipelining_advertised && !check_sync())
+        && !smtp_in_pipelining_advertised && !check_sync())
         goto SYNC_FAILURE;
 
     /* The ACL was happy */
@@ -5183,7 +5250,7 @@ while (done <= 0)
           rcpt_smtp_response[len-2] = 0;
         smtp_respond(code, 3, FALSE, rcpt_smtp_response);
         }
-      if (pipelining_advertised && last_was_rcpt)
+      if (smtp_in_pipelining_advertised && last_was_rcpt)
         smtp_printf("503 Valid RCPT command must precede %s\r\n", FALSE,
          smtp_names[smtp_connection_had[smtp_ch_index-1]]);
       else
@@ -5393,7 +5460,7 @@ while (done <= 0)
     if ((rc = tls_server_start(tls_require_ciphers, &s)) == OK)
       {
       if (!tls_remember_esmtp)
-        helo_seen = esmtp = auth_advertised = pipelining_advertised = FALSE;
+        helo_seen = esmtp = auth_advertised = smtp_in_pipelining_advertised = FALSE;
       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;
@@ -5741,7 +5808,7 @@ while (done <= 0)
     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",
+      smtp_in_pipelining_advertised ? "" : " not",
       smtp_cmd_buffer, host_and_ident(TRUE),
       string_printing(smtp_inptr));
     smtp_notquit_exit(US"synchronization-error", US"554",