DMARC: Fix forensic-report envelopes to permit non-null. Bug 1896
[exim.git] / src / src / dmarc.c
index eb4d636206e88467ef4b987bdb0bffd5e990c61b..efb2ef0a26673ee50647cc8d1805b2725611d6e2 100644 (file)
@@ -83,7 +83,6 @@ dmarc_init()
 {
 int *netmask   = NULL;   /* Ignored */
 int is_ipv6    = 0;
-char *tld_file = dmarc_tld_file ? CS dmarc_tld_file : DMARC_TLD_FILE;
 
 /* Set some sane defaults.  Also clears previous results when
  * multiple messages in one connection. */
@@ -92,14 +91,13 @@ dmarc_status       = US"none";
 dmarc_abort        = FALSE;
 dmarc_pass_fail    = US"skipped";
 dmarc_used_domain  = US"";
-dmarc_ar_header    = NULL;
-dmarc_has_been_checked = FALSE;
+f.dmarc_has_been_checked = FALSE;
 header_from_sender = NULL;
 spf_sender_domain  = NULL;
 spf_human_readable = NULL;
 
 /* ACLs have "control=dmarc_disable_verify" */
-if (dmarc_disable_verify == TRUE)
+if (f.dmarc_disable_verify == TRUE)
   return OK;
 
 (void) memset(&dmarc_ctx, '\0', sizeof dmarc_ctx);
@@ -111,22 +109,27 @@ if (libdm_status != DMARC_PARSE_OKAY)
                       opendmarc_policy_status_to_str(libdm_status));
   dmarc_abort = TRUE;
   }
-if (dmarc_tld_file == NULL)
+if (!dmarc_tld_file)
+  {
+  DEBUG(D_receive) debug_printf("DMARC: no dmarc_tld_file\n");
   dmarc_abort = TRUE;
-else if (opendmarc_tld_read_file(tld_file, NULL, NULL, NULL))
+  }
+else if (opendmarc_tld_read_file(dmarc_tld_file, NULL, NULL, NULL))
   {
   log_write(0, LOG_MAIN|LOG_PANIC, "DMARC failure to load tld list %s: %d",
-                      tld_file, errno);
+                      dmarc_tld_file, errno);
   dmarc_abort = TRUE;
   }
-if (sender_host_address == NULL)
+if (!sender_host_address)
+  {
+  DEBUG(D_receive) debug_printf("DMARC: no sender_host_address\n");
   dmarc_abort = TRUE;
+  }
 /* This catches locally originated email and startup errors above. */
 if (!dmarc_abort)
   {
   is_ipv6 = string_is_ip_address(sender_host_address, netmask) == 6;
-  dmarc_pctx = opendmarc_policy_connect_init(sender_host_address, is_ipv6);
-  if (dmarc_pctx == NULL)
+  if (!(dmarc_pctx = opendmarc_policy_connect_init(sender_host_address, is_ipv6)))
     {
     log_write(0, LOG_MAIN|LOG_PANIC,
       "DMARC failure creating policy context: ip=%s", sender_host_address);
@@ -139,13 +142,15 @@ return OK;
 
 
 /* dmarc_store_data stores the header data so that subsequent
- * dmarc_process can access the data */
+dmarc_process can access the data */
 
-int dmarc_store_data(header_line *hdr) {
-  /* No debug output because would change every test debug output */
-  if (dmarc_disable_verify != TRUE)
-    from_header = hdr;
-  return OK;
+int
+dmarc_store_data(header_line *hdr)
+{
+/* No debug output because would change every test debug output */
+if (!f.dmarc_disable_verify)
+  from_header = hdr;
+return OK;
 }
 
 
@@ -159,7 +164,7 @@ error_block *eblock = NULL;
 FILE *message_file = NULL;
 
 /* Earlier ACL does not have *required* control=dmarc_enable_forensic */
-if (!dmarc_enable_forensic)
+if (!f.dmarc_enable_forensic)
   return;
 
 if (  dmarc_policy == DMARC_POLICY_REJECT     && action == DMARC_RESULT_REJECT
@@ -173,14 +178,11 @@ if (  dmarc_policy == DMARC_POLICY_REJECT     && action == DMARC_RESULT_REJECT
     eblock = add_to_eblock(eblock, US"Sender IP Address", sender_host_address);
     eblock = add_to_eblock(eblock, US"Received Date", tod_stamp(tod_full));
     eblock = add_to_eblock(eblock, US"SPF Alignment",
-                          (sa==DMARC_POLICY_SPF_ALIGNMENT_PASS) ?US"yes":US"no");
+                    sa == DMARC_POLICY_SPF_ALIGNMENT_PASS ? US"yes" : US"no");
     eblock = add_to_eblock(eblock, US"DKIM Alignment",
-                          (da==DMARC_POLICY_DKIM_ALIGNMENT_PASS)?US"yes":US"no");
+                    da == DMARC_POLICY_DKIM_ALIGNMENT_PASS ? US"yes" : US"no");
     eblock = add_to_eblock(eblock, US"DMARC Results", dmarc_status_text);
-    /* Set a sane default envelope sender */
-    dsn_from = dmarc_forensic_sender ? dmarc_forensic_sender :
-              dsn_from ? dsn_from :
-              string_sprintf("do-not-reply@%s",primary_hostname);
+
     for (c = 0; ruf[c]; c++)
       {
       recipient = string_copylc(ruf[c]);
@@ -190,16 +192,12 @@ if (  dmarc_policy == DMARC_POLICY_REJECT     && action == DMARC_RESULT_REJECT
       recipient += 7;
       DEBUG(D_receive)
        debug_printf("DMARC forensic report to %s%s\n", recipient,
-            (host_checking || running_in_test_harness) ? " (not really)" : "");
-      if (host_checking || running_in_test_harness)
+            (host_checking || f.running_in_test_harness) ? " (not really)" : "");
+      if (host_checking || f.running_in_test_harness)
        continue;
 
-      save_sender = sender_address;
-      sender_address = recipient;
-      send_status = moan_to_sender(ERRMESS_DMARC_FORENSIC, eblock,
-                                  header_list, message_file, FALSE);
-      sender_address = save_sender;
-      if (!send_status)
+      if (!moan_send_message(recipient, ERRMESS_DMARC_FORENSIC, eblock,
+                           header_list, message_file, NULL))
        log_write(0, LOG_MAIN|LOG_PANIC,
          "failure to send DMARC forensic report to %s", recipient);
       }
@@ -207,8 +205,8 @@ if (  dmarc_policy == DMARC_POLICY_REJECT     && action == DMARC_RESULT_REJECT
 }
 
 /* dmarc_process adds the envelope sender address to the existing
-   context (if any), retrieves the result, sets up expansion
-   strings and evaluates the condition outcome. */
+context (if any), retrieves the result, sets up expansion
+strings and evaluates the condition outcome. */
 
 int
 dmarc_process()
@@ -221,11 +219,8 @@ BOOL has_dmarc_record = TRUE;
 u_char **ruf; /* forensic report addressees, if called for */
 
 /* ACLs have "control=dmarc_disable_verify" */
-if (dmarc_disable_verify)
-  {
-  dmarc_ar_header = dmarc_auth_results_header(from_header, NULL);
+if (f.dmarc_disable_verify)
   return OK;
-  }
 
 /* Store the header From: sender domain for this part of DMARC.
  * If there is no from_header struct, then it's likely this message
@@ -233,16 +228,19 @@ if (dmarc_disable_verify)
  * the entire DMARC system if we can't find a From: header....or if
  * there was a previous error.
  */
-if (!from_header || dmarc_abort)
+if (!from_header)
+  {
+  DEBUG(D_receive) debug_printf("DMARC: no From: header\n");
   dmarc_abort = TRUE;
-else
+  }
+else if (!dmarc_abort)
   {
   uschar * errormsg;
   int dummy, domain;
   uschar * p;
   uschar saveend;
 
-  parse_allow_group = TRUE;
+  f.parse_allow_group = TRUE;
   p = parse_find_address_end(from_header->text, FALSE);
   saveend = *p; *p = '\0';
   if ((header_from_sender = parse_extract_address(from_header->text, &errormsg,
@@ -469,8 +467,8 @@ if (!dmarc_abort && !sender_host_authenticated)
     log_write(0, LOG_MAIN, "DMARC results: spf_domain=%s dmarc_domain=%s "
                           "spf_align=%s dkim_align=%s enforcement='%s'",
                           spf_sender_domain, dmarc_used_domain,
-                          (sa==DMARC_POLICY_SPF_ALIGNMENT_PASS) ?"yes":"no",
-                          (da==DMARC_POLICY_DKIM_ALIGNMENT_PASS)?"yes":"no",
+                          sa==DMARC_POLICY_SPF_ALIGNMENT_PASS  ?"yes":"no",
+                          da==DMARC_POLICY_DKIM_ALIGNMENT_PASS ?"yes":"no",
                           dmarc_status_text);
     history_file_status = dmarc_write_history_file();
     /* Now get the forensic reporting addresses, if any */
@@ -479,19 +477,16 @@ if (!dmarc_abort && !sender_host_authenticated)
     }
   }
 
-/* set some global variables here */
-dmarc_ar_header = dmarc_auth_results_header(from_header, NULL);
-
 /* shut down libopendmarc */
 if (dmarc_pctx)
   (void) opendmarc_policy_connect_shutdown(dmarc_pctx);
-if (!dmarc_disable_verify)
+if (!f.dmarc_disable_verify)
   (void) opendmarc_policy_library_shutdown(&dmarc_ctx);
 
 return OK;
 }
 
-int
+static int
 dmarc_write_history_file()
 {
 int history_file_fd;
@@ -501,7 +496,10 @@ u_char **rua; /* aggregate report addressees */
 uschar *history_buffer = NULL;
 
 if (!dmarc_history_file)
+  {
+  DEBUG(D_receive) debug_printf("DMARC history file not set\n");
   return DMARC_HIST_DISABLED;
+  }
 history_file_fd = log_create(dmarc_history_file);
 
 if (history_file_fd < 0)
@@ -553,8 +551,8 @@ history_buffer = string_sprintf(
 /* Write the contents to the history file */
 DEBUG(D_receive)
   debug_printf("DMARC logging history data for opendmarc reporting%s\n",
-            (host_checking || running_in_test_harness) ? " (not really)" : "");
-if (host_checking || running_in_test_harness)
+            (host_checking || f.running_in_test_harness) ? " (not really)" : "");
+if (host_checking || f.running_in_test_harness)
   {
   DEBUG(D_receive)
     debug_printf("DMARC history data for debugging:\n%s", history_buffer);
@@ -579,7 +577,7 @@ return DMARC_HIST_OK;
 uschar *
 dmarc_exim_expand_query(int what)
 {
-if (dmarc_disable_verify || !dmarc_pctx)
+if (f.dmarc_disable_verify || !dmarc_pctx)
   return dmarc_exim_expand_defaults(what);
 
 if (what == DMARC_VERIFY_STATUS)
@@ -591,45 +589,21 @@ uschar *
 dmarc_exim_expand_defaults(int what)
 {
 if (what == DMARC_VERIFY_STATUS)
-  return dmarc_disable_verify ?  US"off" : US"none";
+  return f.dmarc_disable_verify ?  US"off" : US"none";
 return US"";
 }
 
-uschar *
-dmarc_auth_results_header(header_line *from_header, uschar *hostname)
-{
-uschar *hdr_tmp    = US"";
-
-/* Allow a server hostname to be passed to this function, but is
- * currently unused */
-if (!hostname)
-  hostname = primary_hostname;
-hdr_tmp = string_sprintf("%s %s;", DMARC_AR_HEADER, hostname);
-
-#if 0
-/* I don't think this belongs here, but left it here commented out
- * because it was a lot of work to get working right. */
-if (spf_response != NULL) {
-  uschar *dmarc_ar_spf = US"";
-  int sr               = 0;
-  sr = spf_response->result;
-  dmarc_ar_spf = (sr == SPF_RESULT_NEUTRAL)  ? US"neutral" :
-                (sr == SPF_RESULT_PASS)     ? US"pass" :
-                (sr == SPF_RESULT_FAIL)     ? US"fail" :
-                (sr == SPF_RESULT_SOFTFAIL) ? US"softfail" :
-                US"none";
-  hdr_tmp = string_sprintf("%s spf=%s (%s) smtp.mail=%s;",
-                          hdr_tmp, dmarc_ar_spf_result,
-                          spf_response->header_comment,
-                          expand_string(US"$sender_address") );
-}
-#endif
 
-hdr_tmp = string_sprintf("%s dmarc=%s", hdr_tmp, dmarc_pass_fail);
-if (header_from_sender)
-  hdr_tmp = string_sprintf("%s header.from=%s",
-                          hdr_tmp, header_from_sender);
-return hdr_tmp;
+gstring *
+authres_dmarc(gstring * g)
+{
+if (f.dmarc_has_been_checked)
+  {
+  g = string_append(g, 2, US";\n\tdmarc=", dmarc_pass_fail);
+  if (header_from_sender)
+    g = string_append(g, 2, US" header.from=", header_from_sender);
+  }
+return g;
 }
 
 # endif /* SUPPORT_SPF */