# HG changeset patch # User Carl Byington # Date 1369247384 25200 # Node ID f941563c2a95611b9f90d457b1bf3bc07bd37443 # Parent db12f6028f8b52cd34d9a10875584b19beffb3cf Add require_rdns checking diff -r db12f6028f8b -r f941563c2a95 ChangeLog --- a/ChangeLog Sat Jul 21 13:13:07 2012 -0700 +++ b/ChangeLog Wed May 22 11:29:44 2013 -0700 @@ -1,3 +1,6 @@ +6.34 2013-05-22 + Add require_rdns checking. + 6.33 2012-07-21 Fix unauthenticated rate limit bug for empty mail from. Move unauthenticated rate limit checks after spam filtering. diff -r db12f6028f8b -r f941563c2a95 NEWS --- a/NEWS Sat Jul 21 13:13:07 2012 -0700 +++ b/NEWS Wed May 22 11:29:44 2013 -0700 @@ -1,3 +1,4 @@ +6.34 2013-05-22 Add require_rdns checking. 6.33 2012-07-21 Fix unauthenticated rate limit bug for empty mail from. Move unauthenticated rate limit checks after spam filtering. 6.32 2012-07-21 Allow rate limiting for unauthentication connections by mail from address or domain. 6.31 2012-07-01 Fix uribl lookups on client dns name. diff -r db12f6028f8b -r f941563c2a95 configure.in --- a/configure.in Sat Jul 21 13:13:07 2012 -0700 +++ b/configure.in Wed May 22 11:29:44 2013 -0700 @@ -1,6 +1,6 @@ AC_PREREQ(2.59) -AC_INIT(dnsbl,6.33,carl@five-ten-sg.com) +AC_INIT(dnsbl,6.34,carl@five-ten-sg.com) AC_CONFIG_SRCDIR([config.h.in]) AC_CONFIG_HEADER([config.h]) diff -r db12f6028f8b -r f941563c2a95 dnsbl.conf --- a/dnsbl.conf Sat Jul 21 13:13:07 2012 -0700 +++ b/dnsbl.conf Wed May 22 11:29:44 2013 -0700 @@ -48,6 +48,7 @@ dnswl dnswl.org list.dnswl.org 2; dnsbl_list local sbl; dnswl_list dnswl.org; + require_rdns yes; content on { filter sbl-xbl.spamhaus.org "Mail containing %s rejected - sbl; see http://www.spamhaus.org/query/bl?ip=%s"; diff -r db12f6028f8b -r f941563c2a95 dnsbl.spec.in --- a/dnsbl.spec.in Sat Jul 21 13:13:07 2012 -0700 +++ b/dnsbl.spec.in Wed May 22 11:29:44 2013 -0700 @@ -103,6 +103,9 @@ %changelog +* Wed May 22 2013 Carl Byington - 6.34-1 +- Add require_rdns checking. + * Sat Jul 21 2012 Carl Byington - 6.33-1 - Fix unauthenticated rate limit bug for empty mail from. Move unauthenticated rate limit checks after spam filtering. diff -r db12f6028f8b -r f941563c2a95 src/context.cpp --- a/src/context.cpp Sat Jul 21 13:13:07 2012 -0700 +++ b/src/context.cpp Wed May 22 11:29:44 2013 -0700 @@ -55,6 +55,7 @@ const char *token_rate; const char *token_rbrace; const char *token_require; +const char *token_requirerdns; const char *token_semi; const char *token_soft; const char *token_spamassassin; @@ -725,6 +726,7 @@ tag_limit_message = NULL; spamassassin_limit = (parent) ? parent->spamassassin_limit : 0; require_match = (parent) ? parent->require_match : false; + require_rdns = (parent) ? parent->require_rdns : false; dcc_greylist = (parent) ? parent->dcc_greylist : false; dcc_bulk_threshold = (parent) ? parent->dcc_bulk_threshold : 0; dnsbl_list_parsed = false; @@ -1074,6 +1076,7 @@ printf(" %s", d.name); } printf("; \n"); + printf("%s require_rdns %s; \n", indent, (require_rdns) ? "yes" : "no"); } { @@ -1350,6 +1353,22 @@ //////////////////////////////////////////////// // +bool parse_requirerdns(TOKEN &tok, CONFIG &dc, CONTEXT &me); +bool parse_requirerdns(TOKEN &tok, CONFIG &dc, CONTEXT &me) { + const char *have = tok.next(); + if (have == token_yes) me.set_requirerdns(true); + else if (have == token_no) me.set_requirerdns(false); + else { + tok.token_error("yes/no", have); + return false; + } + if (!tsa(tok, token_semi)) return false; + return true; +} + + +//////////////////////////////////////////////// +// bool parse_content(TOKEN &tok, CONFIG &dc, CONTEXT &me); bool parse_content(TOKEN &tok, CONFIG &dc, CONTEXT &me) { const char *setting = tok.next(); @@ -1768,6 +1787,9 @@ 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; } @@ -1877,6 +1899,7 @@ token_rate = register_string("rate_limit"); token_rbrace = register_string("}"); token_require = register_string("require_match"); + token_requirerdns = register_string("require_rdns"); token_semi = register_string(";"); token_soft = register_string("soft"); token_spamassassin = register_string("spamassassin"); diff -r db12f6028f8b -r f941563c2a95 src/context.h --- a/src/context.h Sat Jul 21 13:13:07 2012 -0700 +++ b/src/context.h Wed May 22 11:29:44 2013 -0700 @@ -171,6 +171,7 @@ const char * tag_limit_message; // error message for excessive bad html tags int spamassassin_limit; // max score from spamassassin bool require_match; // require matching context filtering context + 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 dnsblp_map dnsbl_names; // name to dnsbl mapping for lists that are available in this context and children @@ -254,11 +255,13 @@ const char* generic_match(const char *client); void set_require(bool r) {require_match = r; }; + void set_requirerdns(bool r) {require_rdns = r; }; void set_grey(bool g) {dcc_greylist = g; }; void set_bulk(int b) {dcc_bulk_threshold = b; }; bool get_content_filtering() {return content_filtering; }; bool get_require() {return content_filtering && require_match; }; + bool get_requirerdns() {return require_rdns; }; bool get_grey() {return content_filtering && dcc_greylist; }; int get_bulk() {return (content_filtering) ? dcc_bulk_threshold : 0;}; int get_host_limit() {return (content_filtering) ? host_limit : 0;}; @@ -338,6 +341,7 @@ extern const char *token_rate; extern const char *token_rbrace; extern const char *token_require; +extern const char *token_requirerdns; extern const char *token_semi; extern const char *token_soft; extern const char *token_spamassassin; diff -r db12f6028f8b -r f941563c2a95 src/dnsbl.cpp --- a/src/dnsbl.cpp Sat Jul 21 13:13:07 2012 -0700 +++ b/src/dnsbl.cpp Wed May 22 11:29:44 2013 -0700 @@ -488,6 +488,7 @@ authenticated = NULL; client_name = NULL; client_dns_name = NULL; + client_dns_forged = false; host_uribl = NULL; helo_uribl = false; client_uribl = false; @@ -1149,6 +1150,15 @@ //snprintf(text, sizeof(text), "found simple dns client name %s", priv.client_dns_name); //my_syslog(text); } + p = strstr(priv.client_name, "] (may be forged)"); + if (p) { + priv.client_dns_forged = true; + if (priv.client_dns_name) { + char text[500]; + snprintf(text, sizeof(text), "forged dns client name %s", priv.client_dns_name); + my_syslog(text); + } + } } if (spamc != spamc_empty) { priv.assassin = new SpamAssassin(&priv, priv.ip, priv.helo, priv.mailaddr, priv.queueid); @@ -1247,6 +1257,15 @@ return SMFIS_REJECT; } if (st == oksofar) { + // check forged rdns + if (con.get_requirerdns() && (!priv.client_dns_name || priv.client_dns_forged)) { + // reject the recipient based on forged reverse dns + char buf[maxlen]; + snprintf(buf, sizeof(buf), "%s is not acceptable", priv.client_name); + smfi_setreply(ctx, (char*)"550", (char*)"5.7.1", buf); + return SMFIS_REJECT; + } + // check generic rdns const char *msg = con.generic_match(priv.client_name); if (msg) { // reject the recipient based on generic reverse dns @@ -1323,12 +1342,12 @@ snprintf(bu, sizeof(bu), "(helo %s)", priv.host_uribl); uri = true; } - // content filterint implies also checking client reverse dns name on uribl (if enabled) + // content filtering implies also checking client reverse dns name on uribl (if enabled) if (priv.client_uribl) { snprintf(bu, sizeof(bu), "(rdns %s)", priv.host_uribl); uri = true; } - // content filterint implies also checking mail from domain name on uribl (if enabled) + // content filtering implies also checking mail from domain name on uribl (if enabled) if (priv.from_uribl) { snprintf(bu, sizeof(bu), "(from %s)", priv.host_uribl); uri = true; diff -r db12f6028f8b -r f941563c2a95 src/dnsbl.h --- a/src/dnsbl.h Sat Jul 21 13:13:07 2012 -0700 +++ b/src/dnsbl.h Wed May 22 11:29:44 2013 -0700 @@ -42,6 +42,7 @@ 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) char *client_dns_name; // fully qualified host name of the smtp client xxx + bool client_dns_forged; // rdns mismatch const char *host_uribl; // pointer to helo/client/from host name if found on uribl string_set hosts_uribl; // string set to hold the helo/client/from host name if found on uribl bool helo_uribl; // helo value on uribl diff -r db12f6028f8b -r f941563c2a95 xml/dnsbl.in --- a/xml/dnsbl.in Sat Jul 21 13:13:07 2012 -0700 +++ b/xml/dnsbl.in Wed May 22 11:29:44 2013 -0700 @@ -335,8 +335,8 @@ If the client has authenticated with sendmail, the rate limits are - checked. If the authenticated user has not exceeded the hourly rate - limit, then the mail is accepted, the filtering contexts are not used, + checked. If the authenticated user has not exceeded the hourly or daily rate + limits, then the mail is accepted, the filtering contexts are not used, the dns lists are not checked, and the body content is not scanned. If the client has not authenticated with sendmail, we follow these steps for each recipient. @@ -405,6 +405,11 @@ If the mail has not been accepted or rejected yet, and the filtering + context (or any ancestor context) requires matching reverse dns client + name, the mail is rejected if the client name is empty or forged. + + + If the mail has not been accepted or rejected yet, and the filtering context (or any ancestor context) specifies a non-empty generic regular expression, then we check the fully qualified client name (obtained via the sendmail macro "_"). The mail is rejected if the client name @@ -597,10 +602,6 @@ Add the ability to use the DBL for content filtering. We need to avoid checking bare ip addresses against that list. - - Add daily recipient limits based on some fixed multiple (perhaps 3?) - of the hourly limit. - @@ -672,7 +673,7 @@ CONTEXT = "context" NAME "{" {STATEMENT}+ "}" STATEMENT = (DNSBL | DNSBLLIST | DNSWL | DNSWLLIST | CONTENT | ENV-TO | VERIFY | GENERIC | W_REGEX | AUTOWHITE | CONTEXT | ENV-FROM - | RATE-LIMIT) ";" + | RATE-LIMIT | REQUIRERDNS) ";" DNSBL = "dnsbl" NAME DNSPREFIX ERROR-MSG1 DNSBLLIST = "dnsbl_list" {NAME}* @@ -681,6 +682,8 @@ DNSWLLIST = "dnswl_list" {NAME}* LEVEL = INTEGER +REQUIRERDNS = "require_rdns" ("yes" | "no") + CONTENT = "content" ("on" | "off") "{" {CONTENT-ST}+ "}" CONTENT-ST = (FILTER | URIBL | IGNORE | TLD | CCTLD | HTML-TAGS | HTML-LIMIT | HOST-LIMIT | SPAMASS | REQUIRE | DCCGREY | @@ -787,6 +790,7 @@ dnswl dnswl.org list.dnswl.org 2; dnsbl_list local sbl; dnswl_list dnswl.org; + require_rdns yes; content on { filter sbl-xbl.spamhaus.org "Mail containing %s rejected - sbl; see http://www.spamhaus.org/query/bl?ip=%s";