changeset 412:e63c6b4835ef stable-6-0-56

refactor spf code; allow wildcard *.example.com in dkim signing restrictions
author Carl Byington <carl@five-ten-sg.com>
date Wed, 19 Apr 2017 09:26:14 -0700
parents 701ae46d9742
children 54809ee70bb8
files ChangeLog NEWS configure.in dnsbl.spec.in src/context.cpp
diffstat 5 files changed, 66 insertions(+), 63 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Apr 16 09:16:37 2017 -0700
+++ b/ChangeLog	Wed Apr 19 09:26:14 2017 -0700
@@ -1,3 +1,7 @@
+6.56 2017-04-19
+    refactor spf code; allow wildcard *.example.com in dkim
+    signing restrictions
+
 6.55 2017-04-16
     require 3 dots in bare ip addresses.
 
--- a/NEWS	Sun Apr 16 09:16:37 2017 -0700
+++ b/NEWS	Wed Apr 19 09:26:14 2017 -0700
@@ -1,3 +1,4 @@
+6.56 2017-04-19 refactor spf code; allow wildcard *.example.com in dkim signing restrictions
 6.55 2017-04-16 require 3 dots in bare ip addresses.
 6.54 2017-03-30 document dmarc vs dnsbl dkim/spf; switch to . rather than " " for dkim impossible signer
 6.53 2017-03-17 suppress duplicate calls to acceptable_content(); redirect= in spf
--- a/configure.in	Sun Apr 16 09:16:37 2017 -0700
+++ b/configure.in	Wed Apr 19 09:26:14 2017 -0700
@@ -1,6 +1,6 @@
 
 AC_PREREQ(2.59)
-AC_INIT(dnsbl,6.55,carl@five-ten-sg.com)
+AC_INIT(dnsbl,6.56,carl@five-ten-sg.com)
 AC_CONFIG_SRCDIR([config.h.in])
 AC_CONFIG_HEADER([config.h])
 AC_CONFIG_MACRO_DIR([m4])
--- a/dnsbl.spec.in	Sun Apr 16 09:16:37 2017 -0700
+++ b/dnsbl.spec.in	Wed Apr 19 09:26:14 2017 -0700
@@ -155,6 +155,10 @@
 
 
 %changelog
+* Wed Apr 19 2017 Carl Byington <carl@five-ten-sg.com> - 6.56-1
+- refactor spf code
+- allow wildcard *.example.com in dkim signing restrictions
+
 * Sun Apr 16 2017 Carl Byington <carl@five-ten-sg.com> - 6.55-1
 - require 3 dots in bare ip addresses.
 
--- a/src/context.cpp	Sun Apr 16 09:16:37 2017 -0700
+++ b/src/context.cpp	Wed Apr 19 09:26:14 2017 -0700
@@ -1112,13 +1112,20 @@
 
 
 bool CONTEXT::in_signing_set(const char *s, const char *signers) {
+    // s is an actual signer
+    // signers is the set of acceptable signers, separated by commas
     size_t n = strlen(s);
     const char *p = signers;
     do {
-        if ((strncasecmp(p, s, n) == 0) && ((p[n] == '\0') || (p[n] == ','))) return true;
-        p = strchr(p, ',');
-        if (!p) return false;
-        p++;
+        const char *c = strchr(p, ',');
+        size_t m = (c) ? c-p : strlen(p);   // length of this element in the signing set
+        if ((m == n) && (strncasecmp(p, s, n) == 0)) return true;   // exact match
+        if ((*p == '*') && (n >= m)) {
+            // try for wildcard match
+            if (strncasecmp(p+1, s+n-(m-1), m-1) == 0) return true;
+        }
+        if (!c) return false;
+        p = c + 1;
     } while (true);
 }
 
@@ -1131,12 +1138,18 @@
     dns_interface(*priv, from, ns_t_txt, false, NULL, buf, maxlen);
     if (*buf) {
         log(priv->queueid, "found txt record %s", buf);
-        char *p = buf;
+        char *p = strchr(buf, ' '); // must start with 'v=spf1 '
+        if (!p) return false;       // broken spf
         char *e = p + strlen(p);    // point to trailing null
-        while ((p = strstr(p, " ip4:"))) {
-            p += 5;
+        while (true) {
+            while (*p == ' ') p++;
+            if (p >= e) break;
             char *b = strchr(p, ' ');
             if (b) *b = '\0';
+            if ((*p != '-') && (*p != '~')) {
+                if (*p == '+') p++;
+                if (strncmp(p, "ip4:", 4) == 0) {
+                    p += 4;
             char *s = strchr(p, '/');
             if (s) *s = '\0';
             in_addr ipx;
@@ -1153,51 +1166,32 @@
                     }
                 }
             }
-            if (b) *b = ' ';
-            if (s) *s = '/';
-            p = (b) ? b : e;
         }
-        p = buf;
-        while ((p = strstr(p, " a:"))) {
-            p += 3;
-            char *b = strchr(p, ' ');
-            if (b) *b = '\0';
+                else if (strncmp(p, "a:", 2) == 0) {
+                    p += 2;
             uint32_t ipy = ntohl(dns_interface(*priv, p, ns_t_a));
             if (ipy == ip) {
                 log(priv->queueid, "match %s", p);
                 return true;
             }
-            if (b) *b = ' ';
-            p = (b) ? b : e;
         }
-        p = buf;
-        while ((p = strstr(p, " a"))) {
-            p += 2;
-            if ((*p == ' ') || (*p == '\0')) {
+                else if (strcmp(p, "a") == 0) {
                 uint32_t ipy = ntohl(dns_interface(*priv, from, ns_t_a));
                 if (ipy == ip) {
                     log(priv->queueid, "match %s", from);
                     return true;
                 }
             }
-        }
-        p = buf;
-        while ((level < 5) && ((p = strstr(p, " redirect=")))) {
-            p += 10;
-            char *b = strchr(p, ' ');
-            if (b) *b = '\0';
+                else if ((level < 5) && (strncmp(p, "redirect=", 9) == 0)) {
+                    p += 9;
             if (resolve_spf(p, ip, priv, level+1)) return true;
-            if (b) *b = ' ';
-            p = (b) ? b : e;
         }
-        p = buf;
-        while ((level < 5) && ((p = strstr(p, " include:")))) {
-            p += 9;
-            char *b = strchr(p, ' ');
-            if (b) *b = '\0';
+                else if ((level < 5) && (strncmp(p, "include:", 8) == 0)) {
+                    p += 8;
             if (resolve_spf(p, ip, priv, level+1)) return true;
-            if (b) *b = ' ';
-            p = (b) ? b : e;
+                }
+            }
+            p = (b) ? b+1 : e;
         }
     }
     return false;