Debug: option access for expansion
[exim.git] / src / src / spf.c
index fd9831c43470ed84ceda9ec271c38fbff81c0ffb..cc36463e44bb1d513f90972d1e8838a22a26c9fd 100644 (file)
@@ -3,9 +3,10 @@
 *************************************************/
 
 /* SPF support.
+   Copyright (c) The Exim Maintainers 2015 - 2023
    Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004 - 2014
    License: GPL
-   Copyright (c) The Exim Maintainers 2015 - 2020
+   SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 /* Code for calling spf checks via libspf-alt. Called from acl.c. */
@@ -34,15 +35,17 @@ SPF_response_t  *spf_response_2mx = NULL;
 SPF_dns_rr_t  * spf_nxdomain = NULL;
 
 
-void
-spf_lib_version_report(FILE * fp)
+gstring *
+spf_lib_version_report(gstring * g)
 {
 int maj, min, patch;
+
 SPF_get_lib_version(&maj, &min, &patch);
-fprintf(fp, "Library version: spf2: Compile: %d.%d.%d\n",
+g = string_fmt_append(g, "Library version: spf2: Compile: %d.%d.%d\n",
        SPF_LIB_VERSION_MAJOR, SPF_LIB_VERSION_MINOR, SPF_LIB_VERSION_PATCH);
-fprintf(fp, "                       Runtime: %d.%d.%d\n",
+g = string_fmt_append(g,    "                       Runtime: %d.%d.%d\n",
         maj, min, patch);
+return g;
 }
 
 
@@ -68,39 +71,41 @@ SPF_dns_rr_t srr = {
   .hook = NULL,                                /* misc information */
   .source = spf_dns_server
 };
-int dns_rc;
 
 DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", domain);
 
-/* Shortcircuit SPF RR lookups by returning HOST_NOT_FOUND (shortest code path
-in libspf2).  They were obsoleted by RFC 6686/7208 years ago. see bug #1294
-*/
+/* Shortcircuit SPF RR lookups by returning NO_DATA.  They were obsoleted by
+RFC 6686/7208 years ago. see bug #1294 */
 
 if (rr_type == T_SPF)
   {
-  HDEBUG(D_host_lookup) debug_printf("faking HOST_NOT_FOUND for SPF RR(99) lookup\n");
-  srr.herrno = HOST_NOT_FOUND;
+  HDEBUG(D_host_lookup) debug_printf("faking NO_DATA for SPF RR(99) lookup\n");
+  srr.herrno = NO_DATA;
   SPF_dns_rr_dup(&spfrr, &srr);
+  store_free_dns_answer(dnsa);
   return spfrr;
   }
 
-switch (dns_rc = dns_lookup(dnsa, US domain, rr_type, NULL))
+switch (dns_lookup(dnsa, US domain, rr_type, NULL))
   {
-  case DNS_SUCCEED:    srr.herrno = NETDB_SUCCESS;     break;
   case DNS_AGAIN:      srr.herrno = TRY_AGAIN;         break;
   case DNS_NOMATCH:    srr.herrno = HOST_NOT_FOUND;    break;
   case DNS_NODATA:     srr.herrno = NO_DATA;           break;
   case DNS_FAIL:
   default:             srr.herrno = NO_RECOVERY;       break;
-  } 
-
-for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
-     rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
-  if (rr->type == rr_type) found++;
+  case DNS_SUCCEED:
+    srr.herrno = NETDB_SUCCESS;
+    for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
+        rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
+      /* Need to alloc space for all records, so no early-out */
+      if (rr->type == rr_type) found++;
+    break;
+  }
 
 if (found == 0)
   {
   SPF_dns_rr_dup(&spfrr, &srr);
+  store_free_dns_answer(dnsa);
   return spfrr;
   }
 
@@ -117,6 +122,7 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
     switch(rr_type)
       {
       case T_MX:
+       if (rr->size < 2) continue;
        s += 2; /* skip the MX precedence field */
       case T_PTR:
        {
@@ -132,6 +138,7 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
        gstring * g = NULL;
        uschar chunk_len;
 
+       if (rr->size < 1+6) continue;           /* min for version str */
        if (strncmpic(rr->data+1, US SPF_VER_STR, 6) != 0)
          {
          HDEBUG(D_host_lookup) debug_printf("not an spf record: %.*s\n",
@@ -139,9 +146,12 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
          continue;
          }
 
-       for (int off = 0; off < rr->size; off += chunk_len)
+       /* require 1 byte for the chunk_len */
+       for (int off = 0; off < rr->size - 1; off += chunk_len)
          {
-         if (!(chunk_len = s[off++])) break;
+         if (  !(chunk_len = s[off++])
+            || rr->size < off + chunk_len      /* ignore bogus size chunks */
+            ) break;
          g = string_catn(g, s+off, chunk_len);
          }
        if (!g)
@@ -172,6 +182,7 @@ if (!(srr.num_rr = found))
 
 /* spfrr->rr must have been malloc()d for this */
 SPF_dns_rr_dup(&spfrr, &srr);
+store_free_dns_answer(dnsa);
 return spfrr;
 }
 
@@ -200,7 +211,7 @@ spf_nxdomain = SPF_dns_rr_new_init(spf_dns_server,
   "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND);
 if (!spf_nxdomain)
   {
-  free(spf_dns_server);
+  store_free(spf_dns_server);
   return NULL;
   }
 
@@ -219,6 +230,7 @@ spf_init(void)
 {
 SPF_dns_server_t * dc;
 int debug = 0;
+const uschar *s;
 
 DEBUG(D_receive) debug = 1;
 
@@ -241,11 +253,19 @@ if (!(spf_server = SPF_server_new_dns(dc, debug)))
   DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n");
   return FALSE;
   }
-  /* Quick hack to override the outdated explanation URL.
-  See https://www.mail-archive.com/mailop@mailop.org/msg08019.html */
-  SPF_server_set_explanation(spf_server, "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}", &spf_response);
-  if (SPF_response_errcode(spf_response) != SPF_E_SUCCESS)
-    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", SPF_strerror(SPF_response_errcode(spf_response)));
+
+/* Override the outdated explanation URL.
+See https://www.mail-archive.com/mailop@mailop.org/msg08019.html
+Used to work as "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}",
+but is broken now (May 18th, 2020) */
+
+GET_OPTION("spf_smtp_comment_template");
+if (!(s = expand_string(spf_smtp_comment_template)))
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE, "expansion of spf_smtp_comment_template failed");
+
+SPF_server_set_explanation(spf_server, CCS s, &spf_response);
+if (SPF_response_errcode(spf_response) != SPF_E_SUCCESS)
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", SPF_strerror(SPF_response_errcode(spf_response)));
 
 return TRUE;
 }
@@ -324,7 +344,8 @@ else for (int i = 0; i < SPF_response_messages(spf_response); i++)
 Return: OK/FAIL  */
 
 int
-spf_process(const uschar **listptr, uschar *spf_envelope_sender, int action)
+spf_process(const uschar ** listptr, const uschar * spf_envelope_sender,
+  int action)
 {
 int sep = 0;
 const uschar *list = *listptr;
@@ -390,16 +411,31 @@ gstring *
 authres_spf(gstring * g)
 {
 uschar * s;
-if (!spf_result) return g;
+if (spf_result)
+  {
+  int start = 0;               /* Compiler quietening */
+  DEBUG(D_acl) start = gstring_length(g);
 
-g = string_append(g, 2, US";\n\tspf=", spf_result);
-if (spf_result_guessed)
-  g = string_cat(g, US" (best guess record for domain)");
+  g = string_append(g, 2, US";\n\tspf=", spf_result);
+  if (spf_result_guessed)
+    g = string_cat(g, US" (best guess record for domain)");
 
-s = expand_string(US"$sender_address_domain");
-return s && *s
-  ? string_append(g, 2, US" smtp.mailfrom=", s)
-  : string_cat(g, US" smtp.mailfrom=<>");
+  s = expand_string(US"$sender_address_domain");
+  if (s && *s)
+    g = string_append(g, 2, US" smtp.mailfrom=", s);
+  else
+    {
+    s = sender_helo_name;
+    g = s && *s
+      ? string_append(g, 2, US" smtp.helo=", s)
+      : string_cat(g, US" smtp.mailfrom=<>");
+    }
+  DEBUG(D_acl) debug_printf("SPF:\tauthres '%.*s'\n",
+                 gstring_length(g) - start - 3, g->s + start + 3);
+  }
+else
+  DEBUG(D_acl) debug_printf("SPF:\tno authres\n");
+return g;
 }