"check_smtpd_policy_service" in smtpd_mumble_restrictions.
See SMTPD_POLICY_SERVICE_README for details.
+20030716
+
+ Bugfix: in the sample policy server, changed "ok" into
+ "dunno" so the server can be used in the middle of a
+ restriction list.
+
+ Cleanup: when an RBL reply has multiple TXT records,
+ concatenate them up to some reasonable limit, instead of
+ selecting one randomly. File: smtpd/smtpd_check.c.
+
+ Safety: always truncate SMTP server error replies to 512
+ bytes. File: smtpd/smtpd_check.c.
+
Open problems:
Low: smtp-source may block when sending large test messages.
The following is specific to SMTPD delegated policy requests:
- Protocol names are ESMTP or SMTP.
-- Protocol states are CONNECT, EHLO, HELO, MAIL, RCPT, or DATA;
- these are all the SMTP protocol states where the Postfix SMTP
+- Protocol states are CONNECT, EHLO, HELO, MAIL, RCPT, DATA or
+ ETRN; these are the SMTP protocol states where the Postfix SMTP
server makes an OK/REJECT/HOLD/etc. decision.
The policy server replies with any action that is allowed in a
update_database($key, $time_stamp);
}
+ # Specify DUNNO instead of OK so that the check_policy_service restriction
+ # can be used in the middle of a restriction list.
syslog $syslog_priority, "request age %d", $now - $time_stamp if $verbose;
if ($now - $time_stamp > $greylist_delay) {
- return "ok";
+ return "dunno";
} else {
return "450 Service is unavailable";
}
# in an A record under domain.tld.
# Append e.g., "=127.0.0.2" to the RBL domain name to select a specific
# address record when an RBL server provides multi-valued results.
+# check_policy_service transport:endpoint: delegate the decision to
+# an external policy server. See SMTPD_POLICY_README for details.
# reject: reject the request. Place this at the end of a restriction.
# permit: permit the request. Place this at the end of a restriction.
# warn_if_reject: next restriction logs a warning instead of rejecting.
# in an A record under domain.tld.
# Append e.g., "=127.0.0.2" to the RBL domain name to select a specific
# address record when an RBL server provides multi-valued results.
+# check_policy_service transport:endpoint: delegate the decision to
+# an external policy server. See SMTPD_POLICY_README for details.
# reject: reject the request. Place this at the end of a restriction.
# permit: permit the request. Place this at the end of a restriction.
# warn_if_reject: next restriction logs a warning instead of rejecting.
# Filter the message if the result is FILTER transport:nexthop.
# Redirect the message if the result is REDIRECT user@domain.
# Permit the HELO command if the result is OK or all numerical.
+# check_policy_service transport:endpoint: delegate the decision to
+# an external policy server. See SMTPD_POLICY_README for details.
# reject: reject the request. Place this at the end of a restriction.
# permit: permit the request. Place this at the end of a restriction.
# warn_if_reject: next restriction logs a warning instead of rejecting.
# the client login name doesn't own the MAIL FROM address according to
# $smtpd_sender_login_maps (see above).
# reject_non_fqdn_sender: reject sender address that is not in FQDN form
+# check_policy_service transport:endpoint: delegate the decision to
+# an external policy server. See SMTPD_POLICY_README for details.
# reject: reject the request. Place this at the end of a restriction.
# permit: permit the request. Place this at the end of a restriction.
# warn_if_reject: next restriction logs a warning instead of rejecting.
# Redirect the message if the result is REDIRECT user@domain.
# Permit the recipient if the result is OK or all numerical.
# reject_non_fqdn_recipient: reject recipient address that is not in FQDN form
+# check_policy_service transport:endpoint: delegate the decision to
+# an external policy server. See SMTPD_POLICY_README for details.
# reject: reject the request. Place this at the end of a restriction.
# permit: permit the request. Place this at the end of a restriction.
# warn_if_reject: next restriction logs a warning instead of rejecting.
# To use this from Postfix SMTPD, use in /etc/postfix/main.cf:
#
# smtpd_recipient_restrictions =
-# ... reject_unauth_destination
+# ... reject_unauth_destination
# check_policy_service unix:private/policy ...
#
# NOTE: specify check_policy_service AFTER reject_unauth_destination
# The policy server script will answer in the same style, with an
# attribute list followed by a empty line:
#
-# action=ok
+# action=dunno
# [empty line]
#
$greylist_delay=3600;
#
-# Syslogging options for verbose mode and for fatal errors.
+# Syslogging options for verbose mode and for fatal errors.
# NOTE: comment out the $syslog_socktype line if syslogging does not
# work on your system.
#
$time_stamp = read_database($key);
$now = time();
- # If new request, add this client/sender/recipient to the database.
+ # If this is a new request add this client/sender/recipient to the database.
if ($time_stamp == 0) {
$time_stamp = $now;
update_database($key, $time_stamp);
}
+ # Specify DUNNO instead of OK so that the check_policy_service restriction
+ # can be followed by other restrictions.
syslog $syslog_priority, "request age %d", $now - $time_stamp if $verbose;
if ($now - $time_stamp > $greylist_delay) {
- return "ok";
+ return "dunno";
} else {
return "450 Service is unavailable";
}
my($database_fd);
# Use tied database to make complex manipulations easier to express.
- $database_obj = tie(%db_hash, 'DB_File', $database_name,
+ $database_obj = tie(%db_hash, 'DB_File', $database_name,
O_CREAT|O_RDWR, 0644) ||
fatal_exit "Cannot open database %s: $!", $database_name;
$database_fd = $database_obj->fd;
if ($option eq "-v") {
$verbose = 1;
} else {
- syslog $syslog_priority, "Invalid option: %s. Usage: %s [-v]",
+ syslog $syslog_priority, "Invalid option: %s. Usage: %s [-v]",
$option, $0;
exit 1;
}
<dt> <b><a href="#reject_unauth_pipelining">reject_unauth_pipelining</a></b>
+<dt> <b><a href="#check_policy_service">check_policy_service</a></b>
+
<dd> See generic restrictions.
</dl>
<dt> <b><a href="#reject_unauth_pipelining">reject_unauth_pipelining</a></b>
+<dt> <b><a href="#check_policy_service">check_policy_service</a></b>
+
<dd> See generic restrictions.
</dl>
<dt> <b><a href="#reject_unauth_pipelining">reject_unauth_pipelining</a></b>
+<dt> <b><a href="#check_policy_service">check_policy_service</a></b>
+
<dd> See generic restrictions.
</dl>
<dt> <b><a href="#reject_unauth_pipelining">reject_unauth_pipelining</a></b>
+<dt> <b><a href="#check_policy_service">check_policy_service</a></b>
+
<dd> See generic restrictions.
</dl>
<dt> <b><a href="#reject_unauth_pipelining">reject_unauth_pipelining</a></b>
+<dt> <b><a href="#check_policy_service">check_policy_service</a></b>
+
<dd> See generic restrictions.
</dl>
from bulk mail software that improperly uses SMTP command pipelining
to speed up deliveries.
+<p>
+
+<a name="check_policy_service">
+
+<dt> <b>check_policy_service inet</b>:<i>host</i>:<i>port</i>
+
+<dt> <b>check_policy_service unix</b>:<i>pathname</i>
+
+<dd> Query the specified server with a list of attributes that
+specify (where available) the mail protocol (SMTP or ESMTP), the
+queue file name, the protocol state (CONNECT, HELO, EHLO, MAIL,
+RCPT, ETRN), the client network address, the hostname given in the
+HELO or EHLO command, the sender email address, the recipient email
+address. The server is expected to reply with an action just like
+the actions found in a Postfix <a href="access.5.html"> access</a>
+table.
+
+<p>
+
+Example:
+
+<dd><b>check_policy_service inet:localhost:9998</b>
+
+<dd><b>check_policy_service unix:private/policy</b>
+
+<dd><b>check_policy_service unix:/some/where</b>
+
</dl>
</dl>
* 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 "20030715"
+#define MAIL_RELEASE_DATE "20030716"
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "2.0.14-" MAIL_RELEASE_DATE
qmqpd.o: ../../include/match_parent_style.h
qmqpd.o: ../../include/lex_822.h
qmqpd.o: ../../include/verp_sender.h
+qmqpd.o: ../../include/input_transp.h
qmqpd.o: ../../include/mail_server.h
qmqpd.o: qmqpd.h
qmqpd_peer.o: qmqpd_peer.c
smtpd_check.o: ../../include/ctable.h
smtpd_check.o: ../../include/mac_expand.h
smtpd_check.o: ../../include/mac_parse.h
+smtpd_check.o: ../../include/attr_clnt.h
+smtpd_check.o: ../../include/attr.h
smtpd_check.o: ../../include/dns.h
smtpd_check.o: ../../include/string_list.h
smtpd_check.o: ../../include/match_list.h
smtpd_check.o: ../../include/rec_type.h
smtpd_check.o: ../../include/mail_proto.h
smtpd_check.o: ../../include/iostuff.h
-smtpd_check.o: ../../include/attr.h
smtpd_check.o: ../../include/mail_addr.h
smtpd_check.o: ../../include/verify_clnt.h
smtpd_check.o: ../../include/deliver_request.h
#include <htable.h>
#include <ctable.h>
#include <mac_expand.h>
-#include <myrand.h>
#include <attr_clnt.h>
/* DNS library. */
* Cached RBL lookup state.
*/
typedef struct {
- ARGV *txt; /* TXT records or NULL */
+ char *txt; /* TXT content or NULL */
ARGV *a; /* A records */
} SMTPD_RBL_STATE;
vstring_vsprintf(error_text, format, ap);
va_end(ap);
+ /*
+ * Ensure RFC compliance. We could do this inside smtpd_chat_reply() and
+ * switch to multi-line for long replies.
+ */
+ vstring_truncate(error_text, 510);
+ VSTRING_TERMINATE(error_text);
+
/*
* Validate the response, that is, the response must begin with a
* three-digit status code, and the first digit must be 4 or 5. If the
DNS_RR *addr_list;
struct in_addr addr;
DNS_RR *rr;
+ DNS_RR *next;
+ VSTRING *buf;
+ int space_left;
/*
* Do the query. If the DNS lookup produces no definitive reply, give the
/*
* Save the result. Yes, we cache negative results as well as positive
- * results.
+ * results. Concatenate multiple TXT records, up to some limit.
*/
-#define RBL_TXT_LIMIT 256
+#define RBL_TXT_LIMIT 500
rbl = (SMTPD_RBL_STATE *) mymalloc(sizeof(*rbl));
if (dns_lookup(query, T_TXT, 0, &txt_list,
(VSTRING *) 0, (VSTRING *) 0) == DNS_OK) {
- rbl->txt = argv_alloc(1);
- for (rr = txt_list; rr != 0; rr = rr->next)
- argv_addn(rbl->txt, rr->data, rr->data_len > RBL_TXT_LIMIT ?
- RBL_TXT_LIMIT : rr->data_len, ARGV_END);
+ buf = vstring_alloc(1);
+ space_left = RBL_TXT_LIMIT;
+ for (rr = txt_list; rr != 0 && space_left > 0; rr = next) {
+ vstring_strncat(buf, rr->data, (int) rr->data_len > space_left ?
+ space_left : rr->data_len);
+ space_left = RBL_TXT_LIMIT - VSTRING_LEN(buf);
+ next = rr->next;
+ if (next && space_left > 3) {
+ vstring_strcat(buf, " / ");
+ space_left -= 3;
+ }
+ }
+ rbl->txt = vstring_export(buf);
dns_rr_free(txt_list);
} else
rbl->txt = 0;
if (rbl != 0) {
if (rbl->txt)
- argv_free(rbl->txt);
+ myfree(rbl->txt);
if (rbl->a)
argv_free(rbl->a);
myfree((char *) rbl);
rbl_exp.domain = rbl_domain;
rbl_exp.what = what;
rbl_exp.class = reply_class;
-
- /*
- * XXX When presented with multiple txt records pick a random one! There
- * is no way to pair up the A records with associated TXT records. If a
- * client connects multiple times, it will learn the right reject reason
- * at least some of the time.
- */
- rbl_exp.txt = mystrdup(rbl->txt == 0 ? "" :
- rbl->txt->argc == 1 ? rbl->txt->argv[0] :
- rbl->txt->argv[myrand() % rbl->txt->argc]);
+ rbl_exp.txt = (rbl->txt == 0 ? "" : rbl->txt);
for (;;) {
if (template == 0)
* Clean up.
*/
vstring_free(why);
- myfree((char *) rbl_exp.txt);
return (result);
}
attr_clnt.o: vbuf.h
attr_clnt.o: connect.h
attr_clnt.o: iostuff.h
+attr_clnt.o: htable.h
attr_clnt.o: attr.h
attr_clnt.o: auto_clnt.h
attr_clnt.o: attr_clnt.h