# HG changeset patch # User Carl Byington # Date 1482011248 28800 # Node ID e172dc10fe246719ebd2f073dc26c8c5e67f1a01 # Parent e27c24c1974a21fc219b23f8ef77c6e447e1c909 add dkim white/black listing diff -r e27c24c1974a -r e172dc10fe24 ChangeLog --- a/ChangeLog Sat Dec 17 09:46:40 2016 -0800 +++ b/ChangeLog Sat Dec 17 13:47:28 2016 -0800 @@ -1,3 +1,6 @@ +6.48 2016-12-17 + Add dkim white/black listing + 6.47 2016-09-21 Better smtp verify logging diff -r e27c24c1974a -r e172dc10fe24 NEWS --- a/NEWS Sat Dec 17 09:46:40 2016 -0800 +++ b/NEWS Sat Dec 17 13:47:28 2016 -0800 @@ -1,3 +1,4 @@ +6.48 2016-12-17 Add dkim white/black listing 6.47 2016-09-21 Better smtp verify logging 6.46 2016-09-19 Enable smtp verify logging 6.45 2015-04-09 Add bitcoin donation address diff -r e27c24c1974a -r e172dc10fe24 configure.in --- a/configure.in Sat Dec 17 09:46:40 2016 -0800 +++ b/configure.in Sat Dec 17 13:47:28 2016 -0800 @@ -1,6 +1,6 @@ AC_PREREQ(2.59) -AC_INIT(dnsbl,6.47,carl@five-ten-sg.com) +AC_INIT(dnsbl,6.48,carl@five-ten-sg.com) AC_CONFIG_SRCDIR([config.h.in]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([m4]) diff -r e27c24c1974a -r e172dc10fe24 dnsbl.conf --- a/dnsbl.conf Sat Dec 17 09:46:40 2016 -0800 +++ b/dnsbl.conf Sat Dec 17 13:47:28 2016 -0800 @@ -80,6 +80,17 @@ include "/etc/mail/local-host-names"; }; + dkim_signer { + sendgrid.me black; + weather.com white; + }; + + dkim_from { + yahoo.com require_signed yahoo.com; + gmail.com signed_white gmail.com; + girlscoutsla.org signed_white girlscoutsla.ccsend.com; + }; + context whitelist { content off {}; env_to { diff -r e27c24c1974a -r e172dc10fe24 dnsbl.spec.in --- a/dnsbl.spec.in Sat Dec 17 09:46:40 2016 -0800 +++ b/dnsbl.spec.in Sat Dec 17 13:47:28 2016 -0800 @@ -103,6 +103,9 @@ %changelog +* Sat Dec 17 2016 Carl Byington - 6.48-1 +- allow dkim whitelisting. + * Wed Sep 21 2016 Carl Byington - 6.47-1 - Better smtp verify logging diff -r e27c24c1974a -r e172dc10fe24 src/context.cpp --- a/src/context.cpp Sat Dec 17 09:46:40 2016 -0800 +++ b/src/context.cpp Sat Dec 17 13:47:28 2016 -0800 @@ -69,6 +69,11 @@ const char *token_white; const char *token_white_regex; const char *token_yes; +const char *token_dkim_signer; +const char *token_dkim_from; +const char *token_signed_white; +const char *token_signed_black; +const char *token_require_signed; const char *token_myhostname; #ifndef HOST_NAME_MAX @@ -610,6 +615,12 @@ } +DKIM::DKIM(const char *action_, const char *signer_) { + action = action_; + signer = signer_; +} + + DNSBL::DNSBL(const char *n, const char *s, const char *m) { name = n; suffix = s; @@ -763,6 +774,11 @@ CONTEXT::~CONTEXT() { + for (dkimp_map::iterator i=dkim_from_names.begin(); i!=dkim_from_names.end(); i++) { + DKIMP d = (*i).second; + // delete the underlying DKIM objects. + delete d; + } for (dnsblp_map::iterator i=dnsbl_names.begin(); i!=dnsbl_names.end(); i++) { DNSBLP d = (*i).second; // delete the underlying DNSBL objects. @@ -985,6 +1001,22 @@ } +const char *CONTEXT::find_dkim_signer(const char *name) { + string_map::iterator i = dkim_signer_names.find(name); + if (i != dkim_signer_names.end()) return (*i).second; + if (parent) return parent->find_dkim_signer(name); + return NULL; +} + + +DKIMP CONTEXT::find_dkim_from(const char *name) { + dkimp_map::iterator i = dkim_from_names.find(name); + if (i != dkim_from_names.end()) return (*i).second; + if (parent) return parent->find_dkim_from(name); + return NULL; +} + + DNSBLP CONTEXT::find_dnsbl(const char *name) { dnsblp_map::iterator i = dnsbl_names.find(name); if (i != dnsbl_names.end()) return (*i).second; @@ -1199,6 +1231,23 @@ printf("%s content off {}; \n", indent); } + printf("%s dkim_signer { \n", indent); + for (string_map::iterator i=dkim_signer_names.begin(); i!=dkim_signer_names.end(); i++) { + const char *n = (*i).first; + const char *a = (*i).second; + printf("%s %s %s; \n", indent, n, a); + } + printf("%s } \n", indent); + + printf("%s dkim_from { \n", indent); + for (dkimp_map::iterator i=dkim_from_names.begin(); i!=dkim_from_names.end(); i++) { + const char *n = (*i).first; + DKIM &d = *(*i).second; + printf("%s %s %s %s; \n", indent, n, d.action, d.signer); + } + + printf("%s } \n", indent); + printf("%s env_to { \t// %s\n", indent, fullname); for (string_set::iterator i=env_to.begin(); i!=env_to.end(); i++) { printf("%s %s; \n", indent, *i); @@ -1810,6 +1859,62 @@ //////////////////////////////////////////////// // +bool parse_dkim_signer(TOKEN &tok, CONFIG &dc, CONTEXT &me); +bool parse_dkim_signer(TOKEN &tok, CONFIG &dc, CONTEXT &me) { + if (!tsa(tok, token_lbrace)) return false; + while (true) { + const char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) break; + if (have == token_semi) { + // optional separators + } + else { + const char *signer = have; + const char *action = tok.next(); + if ((action == token_white) || (action == token_black)) { + me.add_dkim_signer(signer, action); + } + else { + tok.token_error("white/black", action); + } + } + } + return tsa(tok, token_semi); +} + + +//////////////////////////////////////////////// +// +bool parse_dkim_from(TOKEN &tok, CONFIG &dc, CONTEXT &me); +bool parse_dkim_from(TOKEN &tok, CONFIG &dc, CONTEXT &me) { + if (!tsa(tok, token_lbrace)) return false; + while (true) { + const char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) break; + if (have == token_semi) { + // optional separators + } + else { + const char *from = have; + const char *action = tok.next(); + if ((action == token_signed_white) || (action == token_signed_black) || (action == token_require_signed)) { + const char *signer = tok.next(); + if (!signer) break; + else me.add_dkim_from(from, action, signer); + } + else { + tok.token_error("signed_white/signed_black/require_signed", action); + } + } + } + return tsa(tok, token_semi); +} + + +//////////////////////////////////////////////// +// bool parse_context(TOKEN &tok, CONFIG &dc, CONTEXTP parent); bool parse_context(TOKEN &tok, CONFIG &dc, CONTEXTP parent) { const char *name = tok.next(); @@ -1832,9 +1937,6 @@ else if (have == token_dnswll) { if (!parse_dnswll(tok, dc, *con)) return false; } - else if (have == token_requirerdns) { - if (!parse_requirerdns(tok, dc, *con)) return false; - } else if (have == token_content) { if (!parse_content(tok, dc, *con)) return false; } @@ -1856,10 +1958,19 @@ else if (have == token_envfrom) { if (!parse_envfrom(tok, dc, *con)) return false; } + else if (have == token_dkim_signer) { + if (!parse_dkim_signer(tok, dc, *con)) return false; + } + else if (have == token_dkim_from) { + if (!parse_dkim_from(tok, dc, *con)) return false; + } else if (have == token_rate) { if (parent || dc.default_context) tok.token_error("rate limit ignored in non default context"); if (!parse_rate(tok, dc, *con)) return false; } + else if (have == token_requirerdns) { + if (!parse_requirerdns(tok, dc, *con)) return false; + } else if (have == token_context) { if (!parse_context(tok, dc, con)) return false; } @@ -1958,6 +2069,11 @@ token_white = register_string("white"); token_white_regex = register_string("white_regex"); token_yes = register_string("yes"); + token_dkim_signer = register_string("dkim_signer"); + token_dkim_from = register_string("dkim_from"); + token_signed_white = register_string("signed_white"); + token_signed_black = register_string("signed_black"); + token_require_signed = register_string("require_signed"); if (gethostname(myhostname, HOST_NAME_MAX+1) != 0) { strncpy(myhostname, "localhost", HOST_NAME_MAX+1); diff -r e27c24c1974a -r e172dc10fe24 src/context.h --- a/src/context.h Sat Dec 17 09:46:40 2016 -0800 +++ b/src/context.h Sat Dec 17 13:47:28 2016 -0800 @@ -14,6 +14,7 @@ black, // blacklisted reject}; // rejected by a dns list +class DKIM; class DNSBL; class DNSWL; class CONTEXT; @@ -28,11 +29,13 @@ typedef set int32_t_set; typedef int32_t_set * int32_t_set_p; typedef list smtp_list; +typedef DKIM * DKIMP; typedef DNSBL * DNSBLP; typedef DNSWL * DNSWLP; typedef VERIFY * VERIFYP; typedef WHITELISTER * WHITELISTERP; typedef DELAYWHITE * DELAYWHITEP; +typedef map dkimp_map; typedef list dnsblp_list; typedef map dnsblp_map; typedef list dnswlp_list; @@ -126,6 +129,13 @@ CONTEXTP get_con() {return con;}; }; +struct DKIM { + const char *action; + const char *signer; +public: + DKIM(const char *action_, const char *signer_); +}; + struct DNSBL { const char *name; // nickname for this dns based list const char *suffix; // blacklist suffix like blackholes.five-ten-sg.com @@ -179,6 +189,8 @@ bool require_rdns; // require proper rdns on client ip bool dcc_greylist; // should we do dcc greylisting? int dcc_bulk_threshold; // off = 0, many = 1000 + dkimp_map dkim_from_names; // map header from domains to dkim constraints + string_map dkim_signer_names; // map dkim signers to actions dnsblp_map dnsbl_names; // name to dnsbl mapping for lists that are available in this context and children dnsblp_list dnsbl_list; // list of dnsbls to be used in this context bool dnsbl_list_parsed; // true iff we have actually parsed a dnsbl_list @@ -252,6 +264,13 @@ void set_tag_message(const char *message) {tag_limit_message = message;}; void add_tag(const char *tag) {html_tags.insert(tag); }; + const char *find_dkim_signer(const char *name); + void add_dkim_signer(const char *signer, const char *action) + {dkim_signer_names[signer] = action;}; + DKIMP find_dkim_from(const char *name); + void add_dkim_from(const char *from, const char *action, const char *signer) + {dkim_from_names[from] = new DKIM(action,signer);}; + void add_dnsbl(const char *name, DNSBLP dns) {dnsbl_names[name] = dns; }; void add_dnsbl(DNSBLP dns) {dnsbl_list.push_back(dns);}; DNSBLP find_dnsbl(const char *name); @@ -370,6 +389,10 @@ extern const char *token_white; extern const char *token_white_regex; extern const char *token_yes; +extern const char *token_dkim; +extern const char *token_signed_white; +extern const char *token_signed_black; +extern const char *token_require_signed; extern pthread_mutex_t verifier_mutex; // protect the verifier map extern pthread_mutex_t whitelister_mutex; // protect the diff -r e27c24c1974a -r e172dc10fe24 src/dnsbl.cpp --- a/src/dnsbl.cpp Sat Dec 17 09:46:40 2016 -0800 +++ b/src/dnsbl.cpp Sat Dec 17 13:47:28 2016 -0800 @@ -520,6 +520,8 @@ ip = 0; helo = NULL; mailaddr = NULL; + fromaddr = NULL; + header_count = 0; queueid = NULL; authenticated = NULL; client_name = NULL; @@ -567,6 +569,7 @@ delayer.pop_front(); } if (mailaddr) free((void*)mailaddr); + if (fromaddr) free((void*)fromaddr); if (queueid) free((void*)queueid); if (authenticated) free((void*)authenticated); if (client_name) free((void*)client_name); @@ -582,6 +585,8 @@ ctx = NULL; eom = false; mailaddr = NULL; + fromaddr = NULL; + header_count = 0; queueid = NULL; authenticated = NULL; client_name = NULL; @@ -1444,6 +1449,16 @@ sfsistat mlfi_header(SMFICTX* ctx, char* headerf, char* headerv) { mlfiPriv &priv = *MLFIPRIV; + priv.header_count++; + if ((priv.header_count < 4) || (strcasecmp(headerf, "from") == 0)) { + char msg[maxlen]; + snprintf(msg, sizeof(msg), "header %s: %s", headerf, headerv); + for (int i=0; i checked_white; // map of dnswlp to result of (ip listed on that dnswl) // message specific data const char *mailaddr; // envelope from value + const char *fromaddr; // header from value, set by mlfi_header() + int header_count; // count of headers already seen const char *queueid; // sendmail queue id const char *authenticated; // client authenticated? if so, suppress all dnsbl checks, but check rate limits const char *client_name; // fully qualified host name of the smtp client xxx [ip.ad.dr.es] (may be forged) diff -r e27c24c1974a -r e172dc10fe24 xml/dnsbl.in --- a/xml/dnsbl.in Sat Dec 17 09:46:40 2016 -0800 +++ b/xml/dnsbl.in Sat Dec 17 13:47:28 2016 -0800 @@ -25,7 +25,7 @@ - 2013-05-22 + 2016-12-17 Carl Byington @@ -666,7 +666,7 @@ - 2013-05-22 + 2016-12-17 Carl Byington @@ -706,7 +706,7 @@ CONTEXT = "context" NAME "{" {STATEMENT}+ "}" STATEMENT = (DNSBL | DNSBLLIST | DNSWL | DNSWLLIST | CONTENT | ENV-TO | VERIFY | GENERIC | W_REGEX | AUTOWHITE | CONTEXT | ENV-FROM - | RATE-LIMIT | REQUIRERDNS) ";" + | DKIM_SIGNER | DKIM_FROM | RATE-LIMIT | REQUIRERDNS) ";" DNSBL = "dnsbl" NAME DNSPREFIX ERROR-MSG1 DNSBLLIST = "dnsbl_list" {NAME}* @@ -742,6 +742,10 @@ DCCGREY = "dcc_greylist" ("yes" | "no") DCCBULK = "dcc_bulk_threshold" (INTEGER | "many" | "off") +DKIMSIGNER = "dkim_signer" "{" {SIGNING_DOMAIN DEF [";"]}+ "}" +DKIMFROM = "dkim_from" "{" {HEADER_FROM_DOMAIN DKIMVALUE SIGNING_DOMAIN [";"]}+ "}" +DKIMVALUE = "signed_white" | "signed_black" | "require_signed" + ENV-TO = "env_to" "{" {(TO-ADDR | DCC-TO)}+ "}" TO-ADDR = ADDRESS [";"] DCC-TO = "dcc_to" ("ok" | "many") "{" DCCINCLUDEFILE "}" ";" @@ -766,7 +770,8 @@ DEFAULT_IP_LIMIT = INTEGER DAILY_MULTIPLE_IP = INTEGER -DEFAULT = ("white" | "black" | "unknown" | "inherit" | "") +DEF = ("white" | "black") +DEFAULT = (DEF | "unknown" | "inherit" | "") ADDRESS = (USER@ | DOMAIN | USER@DOMAIN) VALUE = ("white" | "black" | "unknown" | "inherit" | CHILD-CONTEXT-NAME)]]>