Mercurial > dnsbl
comparison 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 |
comparison
equal
deleted
inserted
replaced
461:c22fc705c597 | 462:f3f1ece619ba |
---|---|
1109 my_syslog(queueid, buf); | 1109 my_syslog(queueid, buf); |
1110 } | 1110 } |
1111 } | 1111 } |
1112 | 1112 |
1113 | 1113 |
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 | |
1114 bool CONTEXT::in_signing_set(const char *s, const char *signers) { | 1121 bool CONTEXT::in_signing_set(const char *s, const char *signers) { |
1115 // s is an actual signer | 1122 // s is an actual signer |
1116 // signers is the set of acceptable signers, separated by commas | 1123 // signers is the set of acceptable signers, separated by commas |
1117 size_t n = strlen(s); | 1124 size_t n = strlen(s); |
1118 const char *p = signers; | 1125 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; | |
1119 do { | 1129 do { |
1120 const char *c = strchr(p, ','); | 1130 const char *c = strchr(p, ','); |
1121 size_t m = (c) ? c-p : strlen(p); // length of this element in the signing set | 1131 size_t m = (c) ? c-p : strlen(p); // length of this element in the signing set |
1122 if ((m == n) && (strncasecmp(p, s, n) == 0)) return true; // exact match | 1132 if ((m == n) && (strncasecmp(p, s, n) == 0)) break; // exact match |
1123 if ((*p == '*') && (n >= m)) { | 1133 if ((*p == '*') && (n >= m)) { |
1124 // try for wildcard match | 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; | 1135 } |
1126 } | 1136 if (!c) { |
1127 if (!c) return false; | 1137 rc = false; |
1138 break; | |
1139 } | |
1128 p = c + 1; | 1140 p = c + 1; |
1129 } while (true); | 1141 } while (true); |
1142 if (e) *e = ';'; | |
1143 return rc; | |
1130 } | 1144 } |
1131 | 1145 |
1132 | 1146 |
1133 void CONTEXT::replace(char *buf, char *p, const char *what) | 1147 void CONTEXT::replace(char *buf, char *p, const char *what) |
1134 { | 1148 { |
1144 strcpy(buf, repl); | 1158 strcpy(buf, repl); |
1145 } | 1159 } |
1146 } | 1160 } |
1147 | 1161 |
1148 | 1162 |
1149 bool CONTEXT::resolve_spf(const char *from, uint32_t ip, mlfiPriv *priv) | 1163 bool CONTEXT::resolve_spf(const char *from, uint32_t ip, mlfiPriv *priv, const char *signers) |
1150 { | 1164 { |
1165 const char *extraspf = extra_spf_data(signers); | |
1151 // ip is in host order | 1166 // ip is in host order |
1152 if (priv->mailaddr) { | 1167 if (priv->mailaddr) { |
1153 const char *f = strchr(priv->mailaddr, '@'); | 1168 const char *f = strchr(priv->mailaddr, '@'); |
1154 if (f) { | 1169 if (f) { |
1155 f++; | 1170 f++; |
1158 if (efl > hfl) { | 1173 if (efl > hfl) { |
1159 size_t off = efl - hfl; | 1174 size_t off = efl - hfl; |
1160 if ((f[off-1] == '.') && (strcmp(f+off,from) == 0)) { | 1175 if ((f[off-1] == '.') && (strcmp(f+off,from) == 0)) { |
1161 // envelope from is a strict child of header from | 1176 // envelope from is a strict child of header from |
1162 // use envelope from rather than header from | 1177 // use envelope from rather than header from |
1163 if (resolve_one_spf(f, ip, priv)) return true; | 1178 if (resolve_one_spf(f, ip, priv, extraspf)) return true; |
1164 } | 1179 } |
1165 } | 1180 } |
1166 } | 1181 } |
1167 } | 1182 } |
1168 return resolve_one_spf(from, ip, priv); | 1183 return resolve_one_spf(from, ip, priv, extraspf); |
1169 } | 1184 } |
1170 | 1185 |
1171 | 1186 |
1172 bool CONTEXT::resolve_one_spf(const char *from, uint32_t ip, mlfiPriv *priv, int level) | 1187 bool CONTEXT::resolve_one_spf(const char *from, uint32_t ip, mlfiPriv *priv, const char *extraspf, int level) |
1173 { | 1188 { |
1174 char buf[maxdnslength]; | 1189 char buf[maxdnslength]; |
1175 log(priv->queueid, "looking for %s txt record", from); | 1190 log(priv->queueid, "looking for %s txt record", from); |
1176 dns_interface(*priv, from, ns_t_txt, false, NULL, buf, maxdnslength); | 1191 dns_interface(*priv, from, ns_t_txt, false, NULL, buf, maxdnslength); |
1192 if ((level == 0) && | |
1193 extraspf && | |
1194 ((strlen(buf) + strlen(extraspf) + 1) < sizeof(buf))) { | |
1195 strcat(buf, " "); | |
1196 strcat(buf, extraspf); | |
1197 } | |
1177 if (*buf) { | 1198 if (*buf) { |
1178 log(priv->queueid, "found txt record %s", buf); | 1199 log(priv->queueid, "found txt record %s", buf); |
1179 // expand some macros here - a very restricted subset of all possible spf macros | 1200 // expand some macros here - a very restricted subset of all possible spf macros |
1180 // only expand the first instance of each. | 1201 // only expand the first instance of each. |
1181 char *p = strstr(buf, "%{i}"); | 1202 char *p = strstr(buf, "%{i}"); |
1295 } | 1316 } |
1296 } | 1317 } |
1297 } | 1318 } |
1298 else if ((level < 5) && (strncmp(p, "redirect=", 9) == 0)) { | 1319 else if ((level < 5) && (strncmp(p, "redirect=", 9) == 0)) { |
1299 p += 9; | 1320 p += 9; |
1300 if (resolve_one_spf(p, ip, priv, level+1)) return true; | 1321 if (resolve_one_spf(p, ip, priv, NULL, level+1)) return true; |
1301 } | 1322 } |
1302 else if ((level < 5) && (strncmp(p, "include:", 8) == 0)) { | 1323 else if ((level < 5) && (strncmp(p, "include:", 8) == 0)) { |
1303 p += 8; | 1324 p += 8; |
1304 if (resolve_one_spf(p, ip, priv, level+1)) return true; | 1325 if (resolve_one_spf(p, ip, priv, NULL, level+1)) return true; |
1305 } | 1326 } |
1306 } | 1327 } |
1307 p = (b) ? b+1 : e; | 1328 p = (b) ? b+1 : e; |
1308 } | 1329 } |
1309 } | 1330 } |
1352 } | 1373 } |
1353 } | 1374 } |
1354 if (st == token_unsigned_black) { | 1375 if (st == token_unsigned_black) { |
1355 // enforce dmarc | 1376 // enforce dmarc |
1356 if (!dmarc) { | 1377 if (!dmarc) { |
1357 dmarc = resolve_spf(from, ntohl(priv->ip), priv); | 1378 dmarc = resolve_spf(from, ntohl(priv->ip), priv, dk->signer); |
1358 } | 1379 } |
1359 if (!dmarc) { | 1380 if (!dmarc) { |
1360 // not signed and does not pass spf, reject it | 1381 // not signed and does not pass spf, reject it |
1361 char buf[maxlen]; | 1382 char buf[maxlen]; |
1362 snprintf(buf, sizeof(buf), "Mail rejected - not dkim signed by %s", dk->signer); | 1383 snprintf(buf, sizeof(buf), "Mail rejected - not dkim signed by %s", dk->signer); |
1364 return token_black; | 1385 return token_black; |
1365 } | 1386 } |
1366 } | 1387 } |
1367 if (st == token_signed_white) { | 1388 if (st == token_signed_white) { |
1368 // not signed by a white listed signer, but maybe passes strong spf check | 1389 // not signed by a white listed signer, but maybe passes strong spf check |
1369 if (resolve_spf(from, ntohl(priv->ip), priv)) { | 1390 if (resolve_spf(from, ntohl(priv->ip), priv, dk->signer)) { |
1370 log(queueid, "spf pass for %s rather than whitelisted dkim signer", from); | 1391 log(queueid, "spf pass for %s rather than whitelisted dkim signer", from); |
1371 return token_white; | 1392 return token_white; |
1372 } | 1393 } |
1373 } | 1394 } |
1374 if (st == token_require_signed) { | 1395 if (st == token_require_signed) { |
1375 // not signed by a required signer, but maybe passes strong spf check | 1396 // not signed by a required signer, but maybe passes strong spf check |
1376 // only check spf if the list of required signers is not a single dot. | 1397 // only check spf if the list of required signers is not a single dot. |
1377 if (strcmp(dk->signer, ".") && resolve_spf(from, ntohl(priv->ip), priv)) { | 1398 if (strcmp(dk->signer, ".") && resolve_spf(from, ntohl(priv->ip), priv, dk->signer)) { |
1378 log(queueid, "spf pass for %s rather than required dkim signer", from); | 1399 log(queueid, "spf pass for %s rather than required dkim signer", from); |
1379 return token_white; | 1400 return token_white; |
1380 } | 1401 } |
1381 // todo - we could also check spf for the rfc5321 envelope from domain, | 1402 // todo - we could also check spf for the rfc5321 envelope from domain, |
1382 // if it is dmarc aligned (relaxed) with the rfc5322 header from domain. | 1403 // if it is dmarc aligned (relaxed) with the rfc5322 header from domain. |