Mercurial > dnsbl
comparison src/context.cpp @ 464:428de28b34b7
cleanup code for adding extra spf data in dkim_from
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Sun, 10 Mar 2019 08:49:27 -0700 |
parents | f3f1ece619ba |
children | 79e944269c0b |
comparison
equal
deleted
inserted
replaced
463:95b4666a2a0b | 464:428de28b34b7 |
---|---|
614 w = w_; | 614 w = w_; |
615 con = con_; | 615 con = con_; |
616 } | 616 } |
617 | 617 |
618 | 618 |
619 DKIM::DKIM(const char *action_, const char *signer_) { | 619 DKIM::DKIM(const char *action_, const char *signer_, const char*extraspf_) { |
620 action = action_; | 620 action = action_; |
621 signer = signer_; | 621 signer = signer_; |
622 extraspf = extraspf_; | |
622 } | 623 } |
623 | 624 |
624 | 625 |
625 DNSBL::DNSBL(const char *n, const char *s, const char *m) { | 626 DNSBL::DNSBL(const char *n, const char *s, const char *m) { |
626 name = n; | 627 name = n; |
1109 my_syslog(queueid, buf); | 1110 my_syslog(queueid, buf); |
1110 } | 1111 } |
1111 } | 1112 } |
1112 | 1113 |
1113 | 1114 |
1114 const char *CONTEXT::extra_spf_data(const char *signers) { | |
1115 const char *e = strchr(signers, ';'); | |
1116 if (e) e++; | |
1117 return e; | |
1118 } | |
1119 | |
1120 | |
1121 bool CONTEXT::in_signing_set(const char *s, const char *signers) { | 1115 bool CONTEXT::in_signing_set(const char *s, const char *signers) { |
1122 // s is an actual signer | 1116 // s is an actual signer |
1123 // signers is the set of acceptable signers, separated by commas | 1117 // signers is the set of acceptable signers, separated by commas |
1124 size_t n = strlen(s); | 1118 size_t n = strlen(s); |
1125 const char *p = signers; | 1119 const char *p = signers; |
1126 char *e = (char *)strchr(p, ';'); // only search up to ; which separates signers from extra spf data | |
1127 if (e) *e = '\0'; | |
1128 bool rc = true; | |
1129 do { | 1120 do { |
1130 const char *c = strchr(p, ','); | 1121 const char *c = strchr(p, ','); |
1131 size_t m = (c) ? c-p : strlen(p); // length of this element in the signing set | 1122 size_t m = (c) ? c-p : strlen(p); // length of this element in the signing set |
1132 if ((m == n) && (strncasecmp(p, s, n) == 0)) break; // exact match | 1123 if ((m == n) && (strncasecmp(p, s, n) == 0)) return true; // exact match |
1133 if ((*p == '*') && (n >= m)) { | 1124 if ((*p == '*') && (n >= m)) { |
1134 if (strncasecmp(p+1, s+n-(m-1), m-1) == 0) break; // wildcard match | 1125 if (strncasecmp(p+1, s+n-(m-1), m-1) == 0) return true; // wildcard match |
1135 } | 1126 } |
1136 if (!c) { | 1127 if (!c) return false; |
1137 rc = false; | |
1138 break; | |
1139 } | |
1140 p = c + 1; | 1128 p = c + 1; |
1141 } while (true); | 1129 } while (true); |
1142 if (e) *e = ';'; | 1130 } |
1143 return rc; | 1131 |
1144 } | 1132 |
1145 | 1133 void CONTEXT::replace(char *buf, char *p, int nn, const char *what) |
1146 | |
1147 void CONTEXT::replace(char *buf, char *p, const char *what) | |
1148 { | 1134 { |
1149 // replace 4 chars in buf starting at p with what | 1135 // replace nn chars in buf starting at p with what |
1150 char repl[maxlen]; | 1136 char repl[maxlen]; |
1151 size_t bn = strlen(buf); | 1137 size_t bn = strlen(buf); |
1152 size_t wn = strlen(what); | 1138 size_t wn = strlen(what); |
1153 if ((bn - 4 + wn) < (size_t)maxlen) { | 1139 if ((bn - nn + wn) < (size_t)maxlen) { |
1154 size_t n = p - buf; // leading part length | 1140 size_t n = p - buf; // leading part length |
1155 strncpy(repl, buf, n); // leading part | 1141 strncpy(repl, buf, n); // leading part |
1156 strcpy(repl+n, what); // replacement | 1142 strcpy(repl+n, what); // replacement |
1157 strcpy(repl+n+wn, buf+n+4); // trailing part | 1143 strcpy(repl+n+wn, buf+n+nn); // trailing part |
1158 strcpy(buf, repl); | 1144 strcpy(buf, repl); |
1159 } | 1145 } |
1160 } | 1146 } |
1161 | 1147 |
1162 | 1148 |
1163 bool CONTEXT::resolve_spf(const char *from, uint32_t ip, mlfiPriv *priv, const char *signers) | 1149 bool CONTEXT::resolve_spf(const char *from, uint32_t ip, mlfiPriv *priv, const char *extraspf) |
1164 { | 1150 { |
1165 const char *extraspf = extra_spf_data(signers); | |
1166 // ip is in host order | 1151 // ip is in host order |
1167 if (priv->mailaddr) { | 1152 if (priv->mailaddr) { |
1168 const char *f = strchr(priv->mailaddr, '@'); | 1153 const char *f = strchr(priv->mailaddr, '@'); |
1169 if (f) { | 1154 if (f) { |
1170 f++; | 1155 f++; |
1187 bool CONTEXT::resolve_one_spf(const char *from, uint32_t ip, mlfiPriv *priv, const char *extraspf, int level) | 1172 bool CONTEXT::resolve_one_spf(const char *from, uint32_t ip, mlfiPriv *priv, const char *extraspf, int level) |
1188 { | 1173 { |
1189 char buf[maxdnslength]; | 1174 char buf[maxdnslength]; |
1190 log(priv->queueid, "looking for %s txt record", from); | 1175 log(priv->queueid, "looking for %s txt record", from); |
1191 dns_interface(*priv, from, ns_t_txt, false, NULL, buf, maxdnslength); | 1176 dns_interface(*priv, from, ns_t_txt, false, NULL, buf, maxdnslength); |
1192 if ((level == 0) && | 1177 if ((level == 0) && extraspf && ((strlen(buf) + strlen(extraspf) + 1) < sizeof(buf))) { |
1193 extraspf && | 1178 if (strlen(buf) >= 7) { |
1194 ((strlen(buf) + strlen(extraspf) + 1) < sizeof(buf))) { | 1179 // modify existing spf record |
1195 strcat(buf, " "); | 1180 replace(buf, buf+7, 0, extraspf); |
1196 strcat(buf, extraspf); | 1181 replace(buf, buf+7+strlen(extraspf), 0, " "); |
1182 } | |
1183 else { | |
1184 // synthesize full spf record | |
1185 strcat(buf, "v=spf1 "); | |
1186 strcat(buf, extraspf); | |
1187 } | |
1197 } | 1188 } |
1198 if (*buf) { | 1189 if (*buf) { |
1199 log(priv->queueid, "found txt record %s", buf); | 1190 log(priv->queueid, "found txt record %s", buf); |
1200 // expand some macros here - a very restricted subset of all possible spf macros | 1191 // expand some macros here - a very restricted subset of all possible spf macros |
1201 // only expand the first instance of each. | 1192 // only expand the first instance of each. |
1202 char *p = strstr(buf, "%{i}"); | 1193 char *p = strstr(buf, "%{i}"); |
1203 if (p) { | 1194 if (p) { |
1204 char adr[sizeof "255.255.255.255 "]; | 1195 char adr[sizeof "255.255.255.255 "]; |
1205 adr[0] = '\0'; | 1196 adr[0] = '\0'; |
1206 inet_ntop(AF_INET, (const u_char *)&priv->ip, adr, sizeof(adr)); | 1197 inet_ntop(AF_INET, (const u_char *)&priv->ip, adr, sizeof(adr)); |
1207 replace(buf, p, adr); | 1198 replace(buf, p, 4, adr); |
1208 log(priv->queueid, "have txt record %s", buf); | 1199 log(priv->queueid, "have txt record %s", buf); |
1209 } | 1200 } |
1210 p = strstr(buf, "%{h}"); | 1201 p = strstr(buf, "%{h}"); |
1211 if (p) { | 1202 if (p) { |
1212 replace(buf, p, priv->helo); | 1203 replace(buf, p, 4, priv->helo); |
1213 log(priv->queueid, "have txt record %s", buf); | 1204 log(priv->queueid, "have txt record %s", buf); |
1214 } | 1205 } |
1215 p = strstr(buf, "%{d}"); | 1206 p = strstr(buf, "%{d}"); |
1216 if (p) { | 1207 if (p) { |
1217 replace(buf, p, from); | 1208 replace(buf, p, 4, from); |
1218 log(priv->queueid, "have txt record %s", buf); | 1209 log(priv->queueid, "have txt record %s", buf); |
1219 } | 1210 } |
1220 // | 1211 // |
1221 p = strchr(buf, ' '); // must start with 'v=spf1 ' | 1212 p = strchr(buf, ' '); // must start with 'v=spf1 ' |
1222 if (!p) return false; // broken spf | 1213 if (!p) return false; // broken spf |
1373 } | 1364 } |
1374 } | 1365 } |
1375 if (st == token_unsigned_black) { | 1366 if (st == token_unsigned_black) { |
1376 // enforce dmarc | 1367 // enforce dmarc |
1377 if (!dmarc) { | 1368 if (!dmarc) { |
1378 dmarc = resolve_spf(from, ntohl(priv->ip), priv, dk->signer); | 1369 dmarc = resolve_spf(from, ntohl(priv->ip), priv, dk->extraspf); |
1379 } | 1370 } |
1380 if (!dmarc) { | 1371 if (!dmarc) { |
1381 // not signed and does not pass spf, reject it | 1372 // not signed and does not pass spf, reject it |
1382 char buf[maxlen]; | 1373 char buf[maxlen]; |
1383 snprintf(buf, sizeof(buf), "Mail rejected - not dkim signed by %s", dk->signer); | 1374 snprintf(buf, sizeof(buf), "Mail rejected - not dkim signed by %s", dk->signer); |
1385 return token_black; | 1376 return token_black; |
1386 } | 1377 } |
1387 } | 1378 } |
1388 if (st == token_signed_white) { | 1379 if (st == token_signed_white) { |
1389 // not signed by a white listed signer, but maybe passes strong spf check | 1380 // not signed by a white listed signer, but maybe passes strong spf check |
1390 if (resolve_spf(from, ntohl(priv->ip), priv, dk->signer)) { | 1381 if (resolve_spf(from, ntohl(priv->ip), priv, dk->extraspf)) { |
1391 log(queueid, "spf pass for %s rather than whitelisted dkim signer", from); | 1382 log(queueid, "spf pass for %s rather than whitelisted dkim signer", from); |
1392 return token_white; | 1383 return token_white; |
1393 } | 1384 } |
1394 } | 1385 } |
1395 if (st == token_require_signed) { | 1386 if (st == token_require_signed) { |
1396 // not signed by a required signer, but maybe passes strong spf check | 1387 // not signed by a required signer, but maybe passes strong spf check |
1397 // only check spf if the list of required signers is not a single dot. | 1388 // only check spf if the list of required signers is not a single dot. |
1398 if (strcmp(dk->signer, ".") && resolve_spf(from, ntohl(priv->ip), priv, dk->signer)) { | 1389 if (strcmp(dk->signer, ".") && resolve_spf(from, ntohl(priv->ip), priv, dk->extraspf)) { |
1399 log(queueid, "spf pass for %s rather than required dkim signer", from); | 1390 log(queueid, "spf pass for %s rather than required dkim signer", from); |
1400 return token_white; | 1391 return token_white; |
1401 } | 1392 } |
1402 // todo - we could also check spf for the rfc5321 envelope from domain, | 1393 // todo - we could also check spf for the rfc5321 envelope from domain, |
1403 // if it is dmarc aligned (relaxed) with the rfc5322 header from domain. | 1394 // if it is dmarc aligned (relaxed) with the rfc5322 header from domain. |
1497 printf("%s }; \n", indent); | 1488 printf("%s }; \n", indent); |
1498 printf("%s dkim_from { \n", indent); | 1489 printf("%s dkim_from { \n", indent); |
1499 for (dkimp_map::iterator i=dkim_from_names.begin(); i!=dkim_from_names.end(); i++) { | 1490 for (dkimp_map::iterator i=dkim_from_names.begin(); i!=dkim_from_names.end(); i++) { |
1500 const char *n = (*i).first; | 1491 const char *n = (*i).first; |
1501 DKIM &d = *(*i).second; | 1492 DKIM &d = *(*i).second; |
1502 printf("%s %s %s \"%s\"; \n", indent, n, d.action, d.signer); | 1493 if (d.extraspf) { |
1494 printf("%s %s %s \"%s;%s\"; \n", indent, n, d.action, d.signer, d.extraspf); | |
1495 } | |
1496 else { | |
1497 printf("%s %s %s \"%s\"; \n", indent, n, d.action, d.signer); | |
1498 } | |
1503 } | 1499 } |
1504 printf("%s }; \n", indent); | 1500 printf("%s }; \n", indent); |
1505 if (content_suffix) { | 1501 if (content_suffix) { |
1506 printf("%s filter %s \"%s\"; \n", indent, content_suffix, content_message); | 1502 printf("%s filter %s \"%s\"; \n", indent, content_suffix, content_message); |
1507 } | 1503 } |
1821 const char *from = have; | 1817 const char *from = have; |
1822 const char *action = tok.next(); | 1818 const char *action = tok.next(); |
1823 if ((action == token_signed_white) || (action == token_signed_black) || (action == token_unsigned_black) || (action == token_require_signed)) { | 1819 if ((action == token_signed_white) || (action == token_signed_black) || (action == token_unsigned_black) || (action == token_require_signed)) { |
1824 const char *signer = tok.next(); | 1820 const char *signer = tok.next(); |
1825 if (!signer) break; | 1821 if (!signer) break; |
1826 else me.add_dkim_from(from, action, signer); | 1822 else { |
1823 const char *extraspf = NULL; | |
1824 if (strchr(signer, ';')) { | |
1825 char *x = strdup(signer); | |
1826 char *e = strchr(x, ';'); | |
1827 *e = '\0'; | |
1828 signer = register_string(x); | |
1829 extraspf = register_string(e+1); | |
1830 free(x); | |
1831 } | |
1832 me.add_dkim_from(from, action, signer, extraspf); | |
1833 } | |
1827 } | 1834 } |
1828 else { | 1835 else { |
1829 tok.token_error("signed_white/signed_black/unsigned_black/require_signed", action); | 1836 tok.token_error("signed_white/signed_black/unsigned_black/require_signed", action); |
1830 } | 1837 } |
1831 } | 1838 } |