From: Wietse Venema Date: Sun, 22 Sep 2002 05:00:00 +0000 (-0500) Subject: postfix-1.1.11-20020922 X-Git-Tag: v2.0.0~34 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1b286749f386846e2d81a96801840476db7f8e3e;p=thirdparty%2Fpostfix.git postfix-1.1.11-20020922 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 444f12d66..d1aa037c4 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -6980,12 +6980,31 @@ Apologies for any names omitted. 20020919 - Feature: reject_rbl by LaMont Jones. + Feature: reject_rbl for client address blacklisting + by LaMont Jones, including $name expansion for per-domain + customized response messages. The obsolete reject_maps_rbl + is now a wrapper that uses the new code. 20020921 - Internal: generic caching and reject reporting that can be - used for both RBL and RHSBL. + Internal: added caching and reject reporting that can be + used for both reject_rbl and for the upcoming reject_rhsbl. + +20020922 + + Feature: reject_rhsbl for sender domain blacklisting. + Provides the same per-domain customized response message + mechanisms with $name expansion as reject_rbl. + + Safety: the smtpd_expansion_filter parameter controls what + characters are allowed in the expansion of $name macros in + template RBL responses. + + Cleanup. In order to make sensible warnings possible when + expanding a non-existent $name in RBL reply templates, + mac_expand() had to be changed so that an empty string + result (i.e. the name does exist) will no longer cause + ${name?text} to succeed. File: util/mac_expand.c. Open problems: diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index b8542435d..3e6d36232 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -12,6 +12,34 @@ snapshot release). Patches change the patchlevel and the release date. Snapshots change only the release date, unless they include the same bugfixes as a patch release. +Incompatible changes with Postfix snapshot 1.1.11-20020922 +========================================================== + +Subtle change in ${name:result} macro expansions: the expansion +no longer happens when $name is an empty string. + +Major changes with Postfix snapshot 1.1.11-20020922 +=================================================== + +Complete rewrite of RBL internals to avoid unnecessary code +duplication and to implement caching of results. + +Feature: "reject_rbl rbl.domain.tld" for client IP address +blacklisting. The old "reject_maps_rbl" is now implemented as a +wrapper around the reject_rbl code. Based on code by LaMont Jones. + +Feature: "reject_rhsbl rbl.domain.tld" for sender domain based +blacklisting. + +"rbl_reply_maps" configuration parameter for lookup tables with +template responses per RBL server. The template responses support +$name expansion of client, helo, sender, recipient and RBL server +attributes. See sample-smtpd.cf for details. Based on code by LaMont +Jones. + +"smtpd_expansion_filter" configuration parameter to control what +characters are allowed in the expansion of $name macros. + Incompatible changes with Postfix snapshot 1.1.11-20020917 ========================================================== diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html index 14e79e9d8..d5b4000f6 100644 --- a/postfix/html/smtpd.8.html +++ b/postfix/html/smtpd.8.html @@ -168,6 +168,10 @@ SMTPD(8) SMTPD(8) Text that follows the 220 status code in the SMTP greeting banner. + smtpd_expansion_filter + Controls what characters are allowed in $name + expansion of rbl template responses and other text. + smtpd_recipient_limit Restrict the number of recipients that the SMTP server accepts per message delivery. @@ -276,36 +280,43 @@ SMTPD(8) SMTPD(8) instead of the null sender address. A null sender address cannot be looked up. - maps_rbl_domains + maps_rbl_domains (deprecated) List of DNS domains that publish the addresses of - blacklisted hosts. + blacklisted hosts. This is used with the deprecated + reject_maps_rbl restriction. permit_mx_backup_networks - Only domains whose primary MX hosts match the - listed networks are eligible for the per- + Only domains whose primary MX hosts match the + listed networks are eligible for the per- mit_mx_backup feature. relay_domains - Restrict what domains or networks this mail system + Restrict what domains or networks this mail system will relay mail from or to. UCE control responses access_map_reject_code - Server response when a client violates an access + Server response when a client violates an access database restriction. defer_code - Server response when a client request is rejected + Server response when a client request is rejected by the defer restriction. invalid_hostname_reject_code - Server response when a client violates the + Server response when a client violates the reject_invalid_hostname restriction. maps_rbl_reject_code - Server response when a client violates the + Server response when a client violates the maps_rbl_domains restriction. + rbl_reply_maps + Table with template responses, indexed by RBL + domain name. These templates are used by the + reject_rbl and reject_rhsbl restrictions. See also: + smtpd_expansion_filter. + reject_code Response code when the client matches a reject restriction. diff --git a/postfix/man/man8/smtpd.8 b/postfix/man/man8/smtpd.8 index 3fbdab426..1076efce6 100644 --- a/postfix/man/man8/smtpd.8 +++ b/postfix/man/man8/smtpd.8 @@ -149,6 +149,9 @@ postmaster. .RE .IP \fBsmtpd_banner\fR Text that follows the \fB220\fR status code in the SMTP greeting banner. +.IP \fBsmtpd_expansion_filter\fR +Controls what characters are allowed in $name expansion of +rbl template responses and other text. .IP \fBsmtpd_recipient_limit\fR Restrict the number of recipients that the SMTP server accepts per message delivery. @@ -226,9 +229,10 @@ then be used instead of the restriction lists that they represent. .IP \fBsmtpd_null_access_lookup_key\fR The lookup key to be used in SMTPD access tables instead of the null sender address. A null sender address cannot be looked up. -.IP \fBmaps_rbl_domains\fR +.IP "\fBmaps_rbl_domains\fR (deprecated)" List of DNS domains that publish the addresses of blacklisted -hosts. +hosts. This is used with the deprecated \fBreject_maps_rbl\fR +restriction. .IP \fBpermit_mx_backup_networks\fR Only domains whose primary MX hosts match the listed networks are eligible for the \fBpermit_mx_backup\fR feature. @@ -249,6 +253,10 @@ restriction. .IP \fBmaps_rbl_reject_code\fR Server response when a client violates the \fBmaps_rbl_domains\fR restriction. +.IP \fBrbl_reply_maps\fR +Table with template responses, indexed by RBL domain name. These +templates are used by the \fBreject_rbl\fR and \fBreject_rhsbl\fR +restrictions. See also: \fBsmtpd_expansion_filter\fR. .IP \fBreject_code\fR Response code when the client matches a \fBreject\fR restriction. .IP \fBrelay_domains_reject_code\fR diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 6b625a595..13fc88744 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -1203,6 +1203,7 @@ extern int var_access_map_code; #define WARN_IF_REJECT "warn_if_reject" #define REJECT_RBL "reject_rbl" +#define REJECT_RHSBL "reject_rhsbl" #define VAR_RBL_REPLY_MAPS "rbl_reply_maps" #define DEF_RBL_REPLY_MAPS "" extern char *var_rbl_reply_maps; @@ -1226,6 +1227,12 @@ extern int var_smtpd_delay_reject; #define DEF_SMTPD_NULL_KEY "<>" extern char *var_smtpd_null_key; +#define VAR_SMTPD_EXP_FILTER "smtpd_expansion_filter" +#define DEF_SMTPD_EXP_FILTER "\\t\\40!\"#$%&'()*+,-./0123456789:;<=>?@\ +ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`\ +abcdefghijklmnopqrstuvwxyz{|}~" +extern char *var_smtpd_exp_filter; + /* * Heuristic to reject most unknown recipients at the SMTP port. */ diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 772bd9211..e7f91836b 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change the patchlevel and the release date. Snapshots change the * release date only, unless they include the same bugfix as a patch release. */ -#define MAIL_RELEASE_DATE "20020921" +#define MAIL_RELEASE_DATE "20020922" #define VAR_MAIL_VERSION "mail_version" #define DEF_MAIL_VERSION "1.1.11-" MAIL_RELEASE_DATE diff --git a/postfix/src/smtpd/Makefile.in b/postfix/src/smtpd/Makefile.in index ed185bac9..0ea8d79ab 100644 --- a/postfix/src/smtpd/Makefile.in +++ b/postfix/src/smtpd/Makefile.in @@ -36,7 +36,7 @@ SMTPD_CHECK_OBJ = smtpd_state.o smtpd_peer.o smtpd_token: smtpd_token.c $(LIBS) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIBS) $(SYSLIBS) -smtpd_check: smtpd_check.c $(SMTPD_CHECK_OBJ) $(LIBS) +smtpd_check: smtpd_check.o smtpd_check.c $(SMTPD_CHECK_OBJ) $(LIBS) mv $@.o junk $(CC) $(CFLAGS) -DTEST -o $@ smtpd_check.c $(SMTPD_CHECK_OBJ) \ $(LIBS) $(SYSLIBS) diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 2a4d16be7..ac9c1c441 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -135,6 +135,9 @@ /* .RE /* .IP \fBsmtpd_banner\fR /* Text that follows the \fB220\fR status code in the SMTP greeting banner. +/* .IP \fBsmtpd_expansion_filter\fR +/* Controls what characters are allowed in $name expansion of +/* rbl template responses and other text. /* .IP \fBsmtpd_recipient_limit\fR /* Restrict the number of recipients that the SMTP server accepts /* per message delivery. @@ -212,9 +215,10 @@ /* .IP \fBsmtpd_null_access_lookup_key\fR /* The lookup key to be used in SMTPD access tables instead of the /* null sender address. A null sender address cannot be looked up. -/* .IP \fBmaps_rbl_domains\fR +/* .IP "\fBmaps_rbl_domains\fR (deprecated)" /* List of DNS domains that publish the addresses of blacklisted -/* hosts. +/* hosts. This is used with the deprecated \fBreject_maps_rbl\fR +/* restriction. /* .IP \fBpermit_mx_backup_networks\fR /* Only domains whose primary MX hosts match the listed networks /* are eligible for the \fBpermit_mx_backup\fR feature. @@ -235,6 +239,10 @@ /* .IP \fBmaps_rbl_reject_code\fR /* Server response when a client violates the \fBmaps_rbl_domains\fR /* restriction. +/* .IP \fBrbl_reply_maps\fR +/* Table with template responses, indexed by RBL domain name. These +/* templates are used by the \fBreject_rbl\fR and \fBreject_rhsbl\fR +/* restrictions. See also: \fBsmtpd_expansion_filter\fR. /* .IP \fBreject_code\fR /* Response code when the client matches a \fBreject\fR restriction. /* .IP \fBrelay_domains_reject_code\fR @@ -393,6 +401,7 @@ char *var_smtpd_snd_auth_maps; char *var_smtpd_noop_cmds; char *var_smtpd_null_key; int var_smtpd_hist_thrsh; +char *var_smtpd_exp_filter; /* * Silly little macros. @@ -1637,6 +1646,10 @@ int main(int argc, char **argv) VAR_SMTPD_NULL_KEY, DEF_SMTPD_NULL_KEY, &var_smtpd_null_key, 0, 0, 0, }; + static CONFIG_RAW_TABLE raw_table[] = { + VAR_SMTPD_EXP_FILTER, DEF_SMTPD_EXP_FILTER, &var_smtpd_exp_filter, 1, 0, + 0, + }; /* * Pass control to the single-threaded service skeleton. @@ -1644,6 +1657,7 @@ int main(int argc, char **argv) single_server_main(argc, argv, smtpd_service, MAIL_SERVER_INT_TABLE, int_table, MAIL_SERVER_STR_TABLE, str_table, + MAIL_SERVER_RAW_TABLE, raw_table, MAIL_SERVER_BOOL_TABLE, bool_table, MAIL_SERVER_TIME_TABLE, time_table, MAIL_SERVER_PRE_INIT, pre_jail_init, diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index 965820cfa..bfe75394f 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -89,7 +89,7 @@ /* .IP "check_recipient_access maptype:mapname" /* Look up the resolved recipient address in the named access table, /* any parent domains of the recipient domain, and the localpart@. -/* .IP reject_rbl rbl.domain +/* .IP reject_rbl rbl.domain.tld /* Look up the reversed client network address in the specified /* real-time blackhole DNS zone. The \fIrbl_reply_maps\fR configuration /* parameter is used to generate the template for the reject message. @@ -97,6 +97,14 @@ /* default template is used. The \fImaps_rbl_reject_code\fR /* configuration parameter specifies the reject status code used in /* the default template (default: 554). +/* .IP reject_rhsbl rbl.domain.tld +/* Look up the sender domain name in the specified real-time +/* blackhole DNS zone. The \fIrbl_reply_maps\fR configuration +/* parameter is used to generate the template for the reject message. +/* If it is not specified, or the rbl domain cannot be found, then a +/* default template is used. The \fImaps_rbl_reject_code\fR +/* configuration parameter specifies the reject status code used in +/* the default template (default: 554). /* .IP reject_maps_rbl /* Look up the reversed client network address in the real-time blackhole /* DNS zones below the domains listed in the "maps_rbl_domains" @@ -382,6 +390,11 @@ static ARGV *data_restrctions; static HTABLE *smtpd_rest_classes; + /* + * Pre-parsed expansion filter. + */ +static VSTRING *expand_filter; + /* * The routine that recursively applies restrictions. */ @@ -435,6 +448,9 @@ typedef struct { char *txt; /* TXT record or null */ } SMTPD_RBL_STATE; +static void *rbl_pagein(const char *, void *); +static void rbl_pageout(void *, void *); + /* * Context for RBL $name expansion. */ @@ -486,53 +502,6 @@ static void resolve_pageout(void *data, void *unused_context) myfree((void *) reply); } -/* rbl_pagein - page in an RBL lookup result */ - -static void *rbl_pagein(const char *query, void *unused_context) -{ - DNS_RR *txt_list; - VSTRING *why; - int dns_status; - SMTPD_RBL_STATE *rbl; - - /* - * Do the query. - */ - why = vstring_alloc(10); - dns_status = dns_lookup(query, T_A, 0, (DNS_RR **) 0, - (VSTRING *) 0, why); - if (dns_status != DNS_OK && dns_status != DNS_NOTFOUND) - msg_warn("%s: RBL lookup error: %s", query, STR(why)); - vstring_free(why); - if (dns_status != DNS_OK) - return (0); - - /* - * Save the result. - */ - rbl = (SMTPD_RBL_STATE *) mymalloc(sizeof(*rbl)); - if (dns_lookup(query, T_TXT, 0, &txt_list, - (VSTRING *) 0, (VSTRING *) 0) == DNS_OK) { - rbl->txt = mystrdup(txt_list->data); - dns_rr_free(txt_list); - } else - rbl->txt = 0; - return ((void *) rbl); -} - -/* rbl_pageout - page out an RBL lookup result */ - -static void rbl_pageout(void *data, void *unused_context) -{ - SMTPD_RBL_STATE *rbl = (SMTPD_RBL_STATE *) data; - - if (rbl != 0) { - if (rbl->txt) - myfree(rbl->txt); - myfree((char *) rbl); - } -} - /* smtpd_check_parse - pre-parse restrictions */ static ARGV *smtpd_check_parse(const char *checks) @@ -678,13 +647,15 @@ void smtpd_check_init(void) error_text = vstring_alloc(10); /* - * Initialize the resolved address cache. + * Initialize the resolved address cache. Note: the cache persists across + * SMTP sessions so we cannot make it dependent on session state. */ - smtpd_resolve_cache = ctable_create(100, resolve_pagein, resolve_pageout, - (void *) 0); + smtpd_resolve_cache = ctable_create(100, resolve_pagein, + resolve_pageout, (void *) 0); /* - * Initialize the RBL lookup cache. + * Initialize the RBL lookup cache. Note: the cache persists across SMTP + * sessions so we cannot make it dependent on session state. */ smtpd_rbl_cache = ctable_create(100, rbl_pagein, rbl_pageout, (void *) 0); @@ -731,6 +702,12 @@ void smtpd_check_init(void) if (!has_required(rcpt_restrctions, rcpt_required)) fail_required(VAR_RCPT_CHECKS, rcpt_required); #endif + + /* + * Expand the expansion filter :-) + */ + expand_filter = vstring_alloc(10); + unescape(expand_filter, var_smtpd_exp_filter); } /* log_whatsup - log as much context as we have */ @@ -2123,22 +2100,33 @@ static int check_mail_access(SMTPD_STATE *state, const char *table, CHECK_MAIL_ACCESS_RETURN(SMTPD_CHECK_DUNNO); } -/* edit_addr - return address or substring thereof */ +/* smtpd_expand_unknown - report unknown macro name */ -static const char *edit_addr(VSTRING *buf, const char *addr, const char *name) +static void smtpd_expand_unknown(const char *name) +{ + msg_warn("unknown macro name \"%s\" in expansion request", name); +} + +/* smtpd_expand_addr - return address or substring thereof */ + +static const char *smtpd_expand_addr(VSTRING *buf, const char *addr, + const char *name, int prefix_len) { const char *p; + const char *suffix; /* - * Return "undefined" when the address is unavailable. + * Return NULL only for unknown names in expansion requests. */ if (addr == 0) - return (0); + return (""); + + suffix = name + prefix_len; /* * "sender" or "recipient". */ - if (*name == 0) { + if (*suffix == 0) { if (*addr) return (addr); else @@ -2150,7 +2138,7 @@ static const char *edit_addr(VSTRING *buf, const char *addr, const char *name) */ #define STREQ(x,y) (*(x) == *(y) && strcmp((x), (y)) == 0) - else if (STREQ(name, "_name")) { + else if (STREQ(suffix, "_name")) { if (*addr) { if ((p = strrchr(addr, '@')) != 0) { vstring_strncpy(buf, addr, p - addr); @@ -2165,22 +2153,24 @@ static const char *edit_addr(VSTRING *buf, const char *addr, const char *name) /* * "sender_domain" or "recipient_domain". */ - else if (STREQ(name, "_domain")) { + else if (STREQ(suffix, "_domain")) { if (*addr) { if ((p = strrchr(addr, '@')) != 0) { return (p + 1); } else { - return (0); + return (""); } } else - return (0); + return (""); } /* - * Unknown. + * Unknown. Return NULL to indicate an "unknown name" error. */ - else + else { + smtpd_expand_unknown(name); return (0); + } } /* smtpd_expand_lookup - generic SMTP attribute $name expansion */ @@ -2202,6 +2192,8 @@ static const char *smtpd_expand_lookup(const char *name, int unused_mode, /* * Don't query main.cf parameters, as the result of expansion could * reveal system-internal information in server replies. + * + * Return NULL only for non-existent names. */ if (STREQ(name, "client")) { return (state->namaddr); @@ -2210,18 +2202,69 @@ static const char *smtpd_expand_lookup(const char *name, int unused_mode, } else if (STREQ(name, "client_name")) { return (state->name); } else if (STREQ(name, "helo_name")) { - return (state->helo_name ? state->helo_name : 0); + return (state->helo_name ? state->helo_name : ""); } else if (STREQN(name, "sender", CONST_LEN("sender"))) { - return (edit_addr(state->expand_buf, state->sender, - name + CONST_LEN("sender"))); + return (smtpd_expand_addr(state->expand_buf, state->sender, + name, CONST_LEN("sender"))); } else if (STREQN(name, "recipient", CONST_LEN("recipient"))) { - return (edit_addr(state->expand_buf, state->recipient, - name + CONST_LEN("recipient"))); + return (smtpd_expand_addr(state->expand_buf, state->recipient, + name, CONST_LEN("recipient"))); } else { + smtpd_expand_unknown(name); return (0); } } +/* rbl_pagein - page in an RBL lookup result */ + +static void *rbl_pagein(const char *query, void *unused_context) +{ + DNS_RR *txt_list; + VSTRING *why; + int dns_status; + SMTPD_RBL_STATE *rbl; + + /* + * Do the query. If the DNS lookup produces no definitive reply, give the + * requestor the benefit of the doubt. We can't block all email simply + * because an RBL server is unavailable. + */ + why = vstring_alloc(10); + dns_status = dns_lookup(query, T_A, 0, (DNS_RR **) 0, + (VSTRING *) 0, why); + if (dns_status != DNS_OK && dns_status != DNS_NOTFOUND) + msg_warn("%s: RBL lookup error: %s", query, STR(why)); + vstring_free(why); + if (dns_status != DNS_OK) + return (0); + + /* + * Save the result. Yes, we cache negative results as well as positive + * results. + */ + rbl = (SMTPD_RBL_STATE *) mymalloc(sizeof(*rbl)); + if (dns_lookup(query, T_TXT, 0, &txt_list, + (VSTRING *) 0, (VSTRING *) 0) == DNS_OK) { + rbl->txt = mystrndup(txt_list->data, 512); + dns_rr_free(txt_list); + } else + rbl->txt = mystrdup(""); + return ((void *) rbl); +} + +/* rbl_pageout - page out an RBL lookup result */ + +static void rbl_pageout(void *data, void *unused_context) +{ + SMTPD_RBL_STATE *rbl = (SMTPD_RBL_STATE *) data; + + if (rbl != 0) { + if (rbl->txt) + myfree(rbl->txt); + myfree((char *) rbl); + } +} + /* rbl_expand_lookup - RBL specific $name expansion */ static const char *rbl_expand_lookup(const char *name, int mode, @@ -2237,6 +2280,9 @@ static const char *rbl_expand_lookup(const char *name, int mode, if (msg_verbose > 1) msg_info("rbl_expand_lookup: ${%s}", name); + /* + * Be sure to return NULL only for non-existent names. + */ if (STREQ(name, "rbl_code")) { vstring_sprintf(state->expand_buf, "%d", var_maps_rbl_code); return (STR(state->expand_buf)); @@ -2268,24 +2314,25 @@ static int rbl_reject_reply(SMTPD_STATE *state, SMTPD_RBL_STATE *rbl, } if (template) { why = vstring_alloc(10); - rbl_exp.state = state; /* XXX */ + rbl_exp.state = state; rbl_exp.rbl_state = rbl; rbl_exp.domain = rbl_domain; -#define NO_SMTPD_EXP_FILTER ((char *) 0) /* XXX */ if (mac_expand(why, template, MAC_EXP_FLAG_NONE, - NO_SMTPD_EXP_FILTER, rbl_expand_lookup, - (char *) &rbl_exp) & MAC_PARSE_ERROR) { - msg_warn("%s: bad rbl template: %s", myname, template); + STR(expand_filter), rbl_expand_lookup, + (char *) &rbl_exp) != 0) { + msg_warn("%s: bad rbl reply template: %s", myname, template); template = 0; /* pretend not found */ } } if (template) { result = smtpd_check_reject(state, MAIL_ERROR_POLICY, STR(why)); } else { + /* Hard-coded to avoid trouble with future ?: ternary operator. */ result = smtpd_check_reject(state, MAIL_ERROR_POLICY, "%d Service unavailable; [%s] blocked using %s%s%s", var_maps_rbl_code, state->addr, - rbl_domain, rbl->txt ? ", reason: " : "", rbl->txt); + rbl_domain, rbl->txt[0] ? + ", reason: " : "", rbl->txt); } /* @@ -2305,7 +2352,6 @@ static int reject_rbl(SMTPD_STATE *state, const char *rbl_domain) ARGV *octets; VSTRING *query; int i; - int result; SMTPD_RBL_STATE *rbl; if (msg_verbose) @@ -2319,18 +2365,11 @@ static int reject_rbl(SMTPD_STATE *state, const char *rbl_domain) return SMTPD_CHECK_DUNNO; #endif - /* - * Initialize. - */ - query = vstring_alloc(100); - /* * Reverse the client IPV4 address, tack on the RBL domain name and query - * the DNS for an A record. If the record exists, the client address is - * blacklisted. If the DNS lookup produces no definitive reply, give the - * client the benefit of the doubt. We can't block all email simply - * because an RBL server is unavailable. + * the DNS for an A record. */ + query = vstring_alloc(100); octets = argv_split(state->addr, "."); for (i = octets->argc - 1; i >= 0; i--) { vstring_strcat(query, octets->argv[i]); @@ -2339,19 +2378,53 @@ static int reject_rbl(SMTPD_STATE *state, const char *rbl_domain) argv_free(octets); vstring_strcat(query, rbl_domain); rbl = (SMTPD_RBL_STATE *) ctable_locate(smtpd_rbl_cache, STR(query)); + vstring_free(query); + /* + * If the record exists, the client address is blacklisted. + */ if (rbl == 0) { - result = SMTPD_CHECK_DUNNO; + return (SMTPD_CHECK_DUNNO); } else { - result = rbl_reject_reply(state, rbl, rbl_domain); + return (rbl_reject_reply(state, rbl, rbl_domain)); } +} + +/* reject_rhsbl - reject if sender domain in real-time blackhole list */ + +static int reject_rhsbl(SMTPD_STATE *state, const char *rbl_domain) +{ + char *myname = "reject_rhsbl"; + VSTRING *query; + SMTPD_RBL_STATE *rbl; + const char *domain; + + if (msg_verbose) + msg_info("%s: %s", myname, state->sender); /* - * Clean up. + * Extract the sender domain, tack on the RBL domain name and query the + * DNS for an A record. */ + if ((domain = strrchr(state->sender, '@')) == 0) + return (SMTPD_CHECK_DUNNO); + domain += 1; + if (domain[0] == 0 || domain[0] == '#' || domain[0] == '[') + return (SMTPD_CHECK_DUNNO); + + query = vstring_alloc(100); + vstring_sprintf(query, "%s.%s", domain, rbl_domain); + rbl = (SMTPD_RBL_STATE *) ctable_locate(smtpd_rbl_cache, STR(query)); vstring_free(query); - return (result); + /* + * If the record exists, the sender domain is blacklisted. + */ + if (rbl == 0) { + return (SMTPD_CHECK_DUNNO); + } else { + return (rbl_reject_reply(state, rbl, rbl_domain)); + } } /* reject_maps_rbl - reject if client address in real-time blackhole list */ @@ -2529,11 +2602,11 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, } else if (strcasecmp(name, REJECT_MAPS_RBL) == 0) { status = reject_maps_rbl(state); } else if (strcasecmp(name, REJECT_RBL) == 0) { - if (*(cpp += 1) == 0) + if (*(cpp[1]) == 0) msg_warn("restriction %s requires domain name argument", REJECT_RBL); else - status = reject_rbl(state, *cpp); + status = reject_rbl(state, *(cpp += 1)); } /* @@ -2609,6 +2682,12 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, } else if (strcasecmp(name, REJECT_SENDER_LOGIN_MISMATCH) == 0) { if (state->sender && *state->sender) status = reject_sender_login_mismatch(state, state->sender); + } else if (strcasecmp(name, REJECT_RHSBL) == 0) { + if (cpp[1] == 0) + msg_warn("restriction %s requires domain name argument", + REJECT_RHSBL); + else if (state->sender && *state->sender) + status = reject_rhsbl(state, *(cpp += 1)); } /* @@ -3161,6 +3240,7 @@ char *var_smtpd_null_key; char *var_smtpd_snd_auth_maps; char *var_double_bounce_sender; char *var_rbl_reply_maps; +char *var_smtpd_exp_filter; typedef struct { char *name; @@ -3188,6 +3268,7 @@ static STRING_TABLE string_table[] = { VAR_SMTPD_NULL_KEY, DEF_SMTPD_NULL_KEY, &var_smtpd_null_key, VAR_DOUBLE_BOUNCE, DEF_DOUBLE_BOUNCE, &var_double_bounce_sender, VAR_RBL_REPLY_MAPS, DEF_RBL_REPLY_MAPS, &var_rbl_reply_maps, + VAR_SMTPD_EXP_FILTER, DEF_SMTPD_EXP_FILTER, &var_smtpd_exp_filter, 0, }; diff --git a/postfix/src/smtpd/smtpd_check_access b/postfix/src/smtpd/smtpd_check_access index 3f031d243..2681a6c09 100644 --- a/postfix/src/smtpd/smtpd_check_access +++ b/postfix/src/smtpd/smtpd_check_access @@ -34,6 +34,13 @@ ok.domain OK blackholes.mail-abuse.org $rbl_code client=$client client_address=$client_address client_name=$client_name helo_name=$helo_name - sender=$sender sender_name=$sender_name - recipient=$recipient recipient_name=$recipient_name + sender=$sender sender_name=$sender_name sender_domain=$sender_domain + recipient=$recipient recipient_name=$recipient_name recipient_domain=$recipient_domain + rbl_code=$rbl_code rbl_domain=$rbl_domain rbl_txt=$rbl_txt + +dsn.rfc-ignorant.org $rbl_code client=$client + client_address=$client_address + client_name=$client_name helo_name=$helo_name + sender=$sender sender_name=$sender_name sender_domain=$sender_domain + recipient=$recipient recipient_name=$recipient_name recipient_domain=$recipient_domain rbl_code=$rbl_code rbl_domain=$rbl_domain rbl_txt=$rbl_txt diff --git a/postfix/src/smtpd/smtpd_exp.in b/postfix/src/smtpd/smtpd_exp.in index 072f56873..6e7585797 100644 --- a/postfix/src/smtpd/smtpd_exp.in +++ b/postfix/src/smtpd/smtpd_exp.in @@ -8,9 +8,28 @@ mynetworks 127.0.0.0/8,168.100.189.0/28 relay_domains porcupine.org maps_rbl_domains blackholes.mail-abuse.org rbl_reply_maps hash:smtpd_check_access +helo foobar # # RBL # -client_restrictions reject_maps_rbl +mail sname@sdomain +recipient_restrictions reject_maps_rbl client spike.porcupine.org 168.100.189.2 +rcpt rname@rdomain client foo 127.0.0.2 +rcpt rname@rdomain +# +recipient_restrictions reject_rbl,blackholes.mail-abuse.org +client spike.porcupine.org 168.100.189.2 +rcpt rname@rdomain +client foo 127.0.0.2 +rcpt rname@rdomain +# +# RHSBL +# +recipient_restrictions reject_rhsbl,dsn.rfc-ignorant.org +client spike.porcupine.org 168.100.189.2 +mail sname@example.tld +rcpt rname@rdomain +mail sname@sdomain +rcpt rname@rdomain diff --git a/postfix/src/smtpd/smtpd_exp.ref b/postfix/src/smtpd/smtpd_exp.ref index 5f8013ba6..03c58826b 100644 --- a/postfix/src/smtpd/smtpd_exp.ref +++ b/postfix/src/smtpd/smtpd_exp.ref @@ -13,13 +13,49 @@ OK OK >>> rbl_reply_maps hash:smtpd_check_access OK +>>> helo foobar +OK >>> # >>> # RBL >>> # ->>> client_restrictions reject_maps_rbl +>>> mail sname@sdomain +OK +>>> recipient_restrictions reject_maps_rbl +OK +>>> client spike.porcupine.org 168.100.189.2 +OK +>>> rcpt rname@rdomain +OK +>>> client foo 127.0.0.2 +OK +>>> rcpt rname@rdomain +./smtpd_check: reject: RCPT from foo[127.0.0.2]: 554 client=foo[127.0.0.2] client_address=127.0.0.2 client_name=foo helo_name=foobar sender=sname@sdomain sender_name=sname sender_domain=sdomain recipient=rname@rdomain recipient_name=rname recipient_domain=rdomain rbl_code=554 rbl_domain=blackholes.mail-abuse.org rbl_txt=Blackholed - see ; from= to= +554 client=foo[127.0.0.2] client_address=127.0.0.2 client_name=foo helo_name=foobar sender=sname@sdomain sender_name=sname sender_domain=sdomain recipient=rname@rdomain recipient_name=rname recipient_domain=rdomain rbl_code=554 rbl_domain=blackholes.mail-abuse.org rbl_txt=Blackholed - see +>>> # +>>> recipient_restrictions reject_rbl,blackholes.mail-abuse.org OK >>> client spike.porcupine.org 168.100.189.2 OK +>>> rcpt rname@rdomain +OK >>> client foo 127.0.0.2 -./smtpd_check: reject: CONNECT from foo[127.0.0.2]: 554 client=foo[127.0.0.2] client_address=127.0.0.2 client_name=foo helo_name= sender= sender_name= recipient= recipient_name= rbl_code=554 rbl_domain=blackholes.mail-abuse.org rbl_txt=Blackholed - see -554 client=foo[127.0.0.2] client_address=127.0.0.2 client_name=foo helo_name= sender= sender_name= recipient= recipient_name= rbl_code=554 rbl_domain=blackholes.mail-abuse.org rbl_txt=Blackholed - see +OK +>>> rcpt rname@rdomain +./smtpd_check: reject: RCPT from foo[127.0.0.2]: 554 client=foo[127.0.0.2] client_address=127.0.0.2 client_name=foo helo_name=foobar sender=sname@sdomain sender_name=sname sender_domain=sdomain recipient=rname@rdomain recipient_name=rname recipient_domain=rdomain rbl_code=554 rbl_domain=blackholes.mail-abuse.org rbl_txt=Blackholed - see ; from= to= +554 client=foo[127.0.0.2] client_address=127.0.0.2 client_name=foo helo_name=foobar sender=sname@sdomain sender_name=sname sender_domain=sdomain recipient=rname@rdomain recipient_name=rname recipient_domain=rdomain rbl_code=554 rbl_domain=blackholes.mail-abuse.org rbl_txt=Blackholed - see +>>> # +>>> # RHSBL +>>> # +>>> recipient_restrictions reject_rhsbl,dsn.rfc-ignorant.org +OK +>>> client spike.porcupine.org 168.100.189.2 +OK +>>> mail sname@example.tld +OK +>>> rcpt rname@rdomain +./smtpd_check: reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 client=spike.porcupine.org[168.100.189.2] client_address=168.100.189.2 client_name=spike.porcupine.org helo_name=foobar sender=sname@example.tld sender_name=sname sender_domain=example.tld recipient=rname@rdomain recipient_name=rname recipient_domain=rdomain rbl_code=554 rbl_domain=dsn.rfc-ignorant.org rbl_txt=Not supporting null originator (DSN); from= to= +554 client=spike.porcupine.org[168.100.189.2] client_address=168.100.189.2 client_name=spike.porcupine.org helo_name=foobar sender=sname@example.tld sender_name=sname sender_domain=example.tld recipient=rname@rdomain recipient_name=rname recipient_domain=rdomain rbl_code=554 rbl_domain=dsn.rfc-ignorant.org rbl_txt=Not supporting null originator (DSN) +>>> mail sname@sdomain +OK +>>> rcpt rname@rdomain +OK diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index 6f5be131e..44c895691 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -332,7 +332,7 @@ stream_test: stream_test.c $(LIB) tests: valid_hostname_test mac_expand_test dict_test unescape_test \ hex_quote_test ctable_test inet_addr_list_test base64_code_test \ - attr_scan64_test attr_scan0_test pcre_test + attr_scan64_test attr_scan0_test dict_pcre_test valid_hostname_test: valid_hostname valid_hostname.in valid_hostname.ref ./valid_hostname valid_hostname.tmp diff --git a/postfix/src/util/mac_expand.c b/postfix/src/util/mac_expand.c index 7ce3684f1..02f64980f 100644 --- a/postfix/src/util/mac_expand.c +++ b/postfix/src/util/mac_expand.c @@ -23,15 +23,15 @@ /* /* The following expansions are implemented: /* .IP "$name, ${name}, $(name)" -/* Unconditional expansion. If the named attribute is defined, the +/* Unconditional expansion. If the named attribute is non-empty, the /* expansion is the value of the named attribute, optionally subjected /* to further $name expansions. Otherwise, the expansion is empty. /* .IP "${name?text}, $(name?text)" -/* Conditional expansion. If the named attribute is defined, the +/* Conditional expansion. If the named attribute is non-empty, the /* expansion is the given text, subjected to another iteration of /* $name expansion. Otherwise, the expansion is empty. /* .IP "${name:text}, $(name:text)" -/* Conditional expansion. If the named attribute is undefined, the +/* Conditional expansion. If the named attribute is empty or undefined, /* the expansion is the given text, subjected to another iteration /* of $name expansion. Otherwise, the expansion is empty. /* .PP @@ -71,7 +71,7 @@ /* A syntax error was found in \fBpattern\fR, or some macro had /* an unreasonable nesting depth. /* .IP MAC_PARSE_UNDEF -/* A macro was expanded but not defined. +/* A macro was expanded but its value not defined. /* SEE ALSO /* mac_parse(3) locate macro references in string. /* LICENSE @@ -170,11 +170,11 @@ static int mac_expand_callback(int type, VSTRING *buf, char *ptr) */ switch (ch) { case '?': - if (text != 0) + if (text != 0 && *text != 0) mac_parse(cp, mac_expand_callback, (char *) mc); break; case ':': - if (text == 0) + if (text == 0 || *text == 0) mac_parse(cp, mac_expand_callback, (char *) mc); break; default: