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.