diff src/context.cpp @ 462:f3f1ece619ba stable-6-0-75

change dkim_from syntax to allow "signer1,signer2;spf data"
author Carl Byington <carl@five-ten-sg.com>
date Sat, 09 Mar 2019 18:46:25 -0800
parents ad05c61d6372
children 428de28b34b7
line wrap: on
line diff
--- a/src/context.cpp	Mon Dec 24 09:41:04 2018 -0800
+++ b/src/context.cpp	Sat Mar 09 18:46:25 2019 -0800
@@ -1111,22 +1111,36 @@
 }
 
 
+const char *CONTEXT::extra_spf_data(const char *signers) {
+    const char *e = strchr(signers, ';');
+    if (e) e++;
+    return e;
+}
+
+
 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;
+    char *e = (char *)strchr(p, ';');       // only search up to ; which separates signers from extra spf data
+    if (e) *e = '\0';
+    bool rc = true;
     do {
         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 ((m == n) && (strncasecmp(p, s, n) == 0)) break;     // exact match
         if ((*p == '*') && (n >= m)) {
-            // try for wildcard match
-            if (strncasecmp(p+1, s+n-(m-1), m-1) == 0) return true;
+            if (strncasecmp(p+1, s+n-(m-1), m-1) == 0) break;   // wildcard match
         }
-        if (!c) return false;
+        if (!c) {
+            rc = false;
+            break;
+        }
         p = c + 1;
     } while (true);
+    if (e) *e = ';';
+    return rc;
 }
 
 
@@ -1146,8 +1160,9 @@
 }
 
 
-bool CONTEXT::resolve_spf(const char *from, uint32_t ip, mlfiPriv *priv)
+bool CONTEXT::resolve_spf(const char *from, uint32_t ip, mlfiPriv *priv, const char *signers)
 {
+    const char *extraspf = extra_spf_data(signers);
     // ip is in host order
     if (priv->mailaddr) {
         const char *f = strchr(priv->mailaddr, '@');
@@ -1160,20 +1175,26 @@
                 if ((f[off-1] == '.') && (strcmp(f+off,from) == 0)) {
                     // envelope from is a strict child of header from
                     // use envelope from rather than header from
-                    if (resolve_one_spf(f, ip, priv)) return true;
+                    if (resolve_one_spf(f, ip, priv, extraspf)) return true;
                 }
             }
         }
     }
-    return resolve_one_spf(from, ip, priv);
+    return resolve_one_spf(from, ip, priv, extraspf);
 }
 
 
-bool CONTEXT::resolve_one_spf(const char *from, uint32_t ip, mlfiPriv *priv, int level)
+bool CONTEXT::resolve_one_spf(const char *from, uint32_t ip, mlfiPriv *priv, const char *extraspf, int level)
 {
     char buf[maxdnslength];
     log(priv->queueid, "looking for %s txt record", from);
     dns_interface(*priv, from, ns_t_txt, false, NULL, buf, maxdnslength);
+    if ((level == 0) &&
+        extraspf &&
+        ((strlen(buf) + strlen(extraspf) + 1) < sizeof(buf))) {
+        strcat(buf, " ");
+        strcat(buf, extraspf);
+    }
     if (*buf) {
         log(priv->queueid, "found txt record %s", buf);
         // expand some macros here - a very restricted subset of all possible spf macros
@@ -1297,11 +1318,11 @@
                 }
                 else if ((level < 5) && (strncmp(p, "redirect=", 9) == 0)) {
                     p += 9;
-                    if (resolve_one_spf(p, ip, priv, level+1)) return true;
+                    if (resolve_one_spf(p, ip, priv, NULL, level+1)) return true;
                 }
                 else if ((level < 5) && (strncmp(p, "include:", 8) == 0)) {
                     p += 8;
-                    if (resolve_one_spf(p, ip, priv, level+1)) return true;
+                    if (resolve_one_spf(p, ip, priv, NULL, level+1)) return true;
                 }
             }
             p = (b) ? b+1 : e;
@@ -1354,7 +1375,7 @@
             if (st == token_unsigned_black) {
                 // enforce dmarc
                 if (!dmarc) {
-                    dmarc = resolve_spf(from, ntohl(priv->ip), priv);
+                    dmarc = resolve_spf(from, ntohl(priv->ip), priv, dk->signer);
                 }
                 if (!dmarc) {
                     // not signed and does not pass spf, reject it
@@ -1366,7 +1387,7 @@
             }
             if (st == token_signed_white) {
                 // not signed by a white listed signer, but maybe passes strong spf check
-                if (resolve_spf(from, ntohl(priv->ip), priv)) {
+                if (resolve_spf(from, ntohl(priv->ip), priv, dk->signer)) {
                     log(queueid, "spf pass for %s rather than whitelisted dkim signer", from);
                     return token_white;
                 }
@@ -1374,7 +1395,7 @@
             if (st == token_require_signed) {
                 // not signed by a required signer, but maybe passes strong spf check
                 // only check spf if the list of required signers is not a single dot.
-                if (strcmp(dk->signer, ".") && resolve_spf(from, ntohl(priv->ip), priv)) {
+                if (strcmp(dk->signer, ".") && resolve_spf(from, ntohl(priv->ip), priv, dk->signer)) {
                     log(queueid, "spf pass for %s rather than required dkim signer", from);
                     return token_white;
                 }