--- /dev/null
+LINUX SYSLOGD PERFORMANCE
+=========================
+
+LINUX syslogd uses synchronous writes by default, which is very
+expensive. For services such a mail it is recommended that you
+disable synchronous logfile writes by prepending a - to the logfile
+name:
+
+ mail.* -/var/log/mail.log
+
+SPAM BLOCKING SOURCE ROUTED ADDRESSES
+=====================================
+
+If you are responsible for a backup MX host for some domain (either
+your own domain or the domain of some other organization) it is
+prudent to block addresses with multiple address operators at the
+SMTP port, such as:
+
+ user@elsewhere@some.domain
+ user%elsewhere@some.domain
+ elsewhere!user@some.domain
+
+The problem is that the primary MX host for some.domain may forward
+the above to user@elsewhere. This can happen because the primary
+MX host somehow "trusts" your backup MX host, or because the primary
+MX host is badly configured.
+
+The bad news is that your backup MX machine can end up on a black
+list because it accepted the mail, even though the problem involves
+a primary MX host that perhaps is not under your control.
+
+The simplest solution is to install a regular expression filter:
+
+ /etc/postfix/main.cf:
+ smtpd_recipient_restrictions =
+ regexp:/etc/postfix/regexp_access
+ ...other restrictions...
+
+ /etc/postfix/regexp_access:
+ /[%!@].*[%!@]/ 550 Sender specified routing is not supported here.
+
+For the local domain, Postfix will do the right thing with:
+
+ user@elsewhere@my.own.domain
+ user%elsewhere@my.own.domain
+ elsewhere!user@my.own.domain
+
+That is, it bounces the first form because "user@elsewhere" is not
+a valid local user, and it accepts the second and third forms only
+when user@elsewhere is a valid relay destination.
to change Postfix into an open relay. File: smtpd/smtpd_check.c.
Retraction: null-length inet_interfaces is too confusing.
+
+19991224
+
+ Bugfix: the relative symlink code in INSTALL.sh computed
+ the ../ segments from the wrong pathname.
compare_or_symlink() {
cmp $1 $2 >/dev/null 2>&1 || {
rm -f $tempdir/junk || exit 1
- target=`echo $1 | sed '
+ dest=`echo $1 | sed '
+ s;^'$install_root';;
+ s;/\./;/;g
+ s;//*;/;g
+ s;^/;;
+ '`
+ link=`echo $2 | sed '
s;^'$install_root';;
s;/\./;/;g
s;//*;/;g
s;^/;;
- H
s;/[^/]*$;/;
s;[^/]*/;../;g
- G
- s/\n//
+ s;$;'$dest';
'`
- ln -s $target $tempdir/junk || exit 1
+ ln -s $link $tempdir/junk || exit 1
mv -f $tempdir/junk $2 || {
echo Error: your mv command is unable to rename symlinks. 1>&2
echo If you run Linux, upgrade to GNU fileutils-4.0 or better, 1>&2
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-19991223"
+#define DEF_MAIL_VERSION "Snapshot-19991225"
extern char *var_mail_version;
/* LICENSE
return (NO);
if (ch == '(' || ch == ')'
|| ch == '<' || ch == '>'
- || ch == '@' || ch == ','
+ /* || ch == '@' */ || ch == ','
|| ch == ';' || ch == ':'
|| ch == '\\' || ch == '"'
|| ch == '[' || ch == ']')
/* VSTRING *transport;
/* VSTRING *nexthop
/* VSTRING *recipient;
+/* int flags;
/* .in -4
/* } RESOLVE_REPLY;
/*
/* transport name, next_hop host name, and internal-form recipient
/* address. In case of communication failure the program keeps trying
/* until the mail system goes down.
+/*
+/* In the resolver reply, the flags member is the bit-wise OR of
+/* zero or more of the following:
+/* .IP RESOLVE_FLAG_FINAL
+/* The recipient address resolves to a mail transport that performs
+/* final delivery. The destination is local or corresponds to a hosted
+/* domain that is handled by the local machine. This flag is currently
+/* not used.
+/* .IP RESOLVE_FLAG_ROUTED
+/* The recipient address contains routing information, so the
+/* destination domain is not necessarily the final destination.
/* DIAGNOSTICS
/* Warnings: communication failure. Fatal error: mail system is down.
/* SEE ALSO
reply->transport = vstring_alloc(100);
reply->nexthop = vstring_alloc(100);
reply->recipient = vstring_alloc(100);
+ reply->flags = 0;
}
/* resolve_clnt_query - resolve address to (transport, next hop, recipient) */
vstring_strcpy(reply->transport, STR(last_reply.transport));
vstring_strcpy(reply->nexthop, STR(last_reply.nexthop));
vstring_strcpy(reply->recipient, STR(last_reply.recipient));
+ reply->flags = last_reply.flags;
if (msg_verbose)
msg_info("%s: cached: `%s' -> t=`%s' h=`%s' r=`%s'",
myname, addr, STR(reply->transport),
|| vstream_fflush(stream)) {
if (msg_verbose || (errno != EPIPE && errno != ENOENT))
msg_warn("%s: bad write: %m", myname);
- } else if (mail_scan(stream, "%s %s %s", reply->transport,
- reply->nexthop, reply->recipient) != 3) {
+ } else if (mail_scan(stream, "%s %s %s %d",
+ reply->transport, reply->nexthop,
+ reply->recipient, &reply->flags) != 4) {
if (msg_verbose || (errno != EPIPE && errno != ENOENT))
msg_warn("%s: bad read: %m", myname);
} else {
vstring_strcpy(last_reply.transport, STR(reply->transport));
vstring_strcpy(last_reply.nexthop, STR(reply->nexthop));
vstring_strcpy(last_reply.recipient, STR(reply->recipient));
+ last_reply.flags = reply->flags;
}
/* resolve_clnt_free - destroy reply */
*/
#define RESOLVE_ADDR "resolve"
+#define RESOLVE_FLAG_FINAL (1<<0) /* final delivery */
+#define RESOLVE_FLAG_ROUTED (1<<1) /* routed destination */
+
typedef struct RESOLVE_REPLY {
VSTRING *transport;
VSTRING *nexthop;
VSTRING *recipient;
+ int flags;
} RESOLVE_REPLY;
extern void resolve_clnt_init(RESOLVE_REPLY *);
<p>
-However, there will be a problem when you are backup MX host for
-a domain that runs Sendmail, because many Sendmail configurations
-will forward the above mail to <i>test@some.other.site</i>. The
-bad news is that your machine will be put on the black list because
-it accepted the mail, even though the problem is with the configuration
-of a Sendmail machine that is not under your control.
+However, problems lurk when you are responsible for a backup MX
+host, either for your own domain or for the domain of some other
+organization. Especially troublesome address forms are:
<p>
-So, if you're MX relay for other sites it is prudent to block
-addresses with multiple address operators at the SMTP port, even
-though you would also block legitimate addresses.
+<pre>
+ user@else.where@some.domain
+ user%else.where@some.domain
+ elsewhere!user@some.domain
+</pre>
+
+<p>
+
+The problem is that the primary MX host may forward such mail to
+user@else.where, either because the primary MX host somehow "trusts"
+yor backup MX host, or because the primary MX host is badly
+configured.
+
+<p>
+
+The bad news is that your backup MX machine can end up on a black
+list because it accepted the mail, even though the problem could
+be with the configuration of a primary MX machine that is not even
+under your control.
+
+<p>
+
+So, if you're backup MX relay it is prudent to block addresses with
+multiple address operators at the SMTP port, even though you would
+also block legitimate addresses.
<p>
<p>
<pre>
- <b>/etc/postfix/main.cf</b>
+ <b>/etc/postfix/main.cf</b>:
local_recipient_maps = $alias_maps, unix:passwd.byname
</pre>
Delivering mail to a command is a security-sensitive operation,
because the command must be executed with the right privileges.
-Only <b>root</b>-privileged software such as the Postfix delivery
-agent can set the privileges for a command.
+Only <b>root</b>-privileged software such as the Postfix local
+delivery agent can set the privileges for a command.
<p>
virtual.domain whatever
</pre>
-<p>
-
-<li>Verify that the <a
-href="uce.html#smtpd_recipient_restrictions">smtpd_recipient_restrictions</a>
-parameter contains a restriction that depends on <a
-href="uce.html#relay_domains">relay_domains</a>. This is the default setting.
-
</ul>
<p>
What is Postfix? It is <a href="http://www.porcupine.org/wietse/">Wietse
Venema's</a> attempt to provide an alternative to the widely-used
<a href="http://www.sendmail.org/">Sendmail</a> program. Sendmail
-is responsible for an estimated 70% of all e-mail delivered on the
-Internet. With an estimated 100 million users, that's billions of
-messages daily. A stunning number.
+is responsible for most of the e-mail delivered on the Internet.
+With an estimated 100 million users, that's billions of messages
+daily. A stunning number.
<p>
<p>
<dd><i>Note: you must specify at least one of the following
-restrictions: </i><b>reject</b>, <b>check_relay_domains</b> or
-<b>reject_unauth_destination</b>. <i>Postfix will refuse to
-receive mail otherwise. </i>
+restrictions: </i><b>reject</b>, <b>check_relay_domains</b> <i>or</i>
+<b>reject_unauth_destination</b>. <i>Postfix will refuse to receive
+mail otherwise. </i>
<p>
<p>
-<dt> <b>permit_recipient_map</b> <i>maptype</i>:<i>mapname</i> <dd>
-Permit the request when the recipient address matches the named
-table. Table lookup is done as with the <a
-href="virtual.5.html">virtual</a> and <a
-href="canonical.5.html">canonical</a> tables. This also produces
-useful results with the <a href="aliases.5.html">aliases</a> and
-<b>unix:passwd.byname</b> maps.
-
-<p>
-
<dt> <b><a href="#reject_unknown_sender_domain">reject_unknown_sender_domain</a></b>
<dt> <b><a href="#reject_non_fqdn_sender">reject_non_fqdn_sender</a></b>
|| ch == '[' || ch == ']'
|| ch == '\\' || ch == ','
|| ch == ';' || ch == ':'
- || ch == '@' || ch == '"')
+ /* || ch == '@' */ || ch == '"')
return (NO);
}
if (cp[-1] == '.')
{
char *myname = "check_relay_domains";
char *domain;
+ static int permit_auth_destination(char *recipient);
if (msg_verbose)
msg_info("%s: %s", myname, recipient);
return (SMTPD_CHECK_OK);
/*
- * Resolve the address.
- */
- canon_addr_internal(query, recipient);
- resolve_clnt_query(STR(query), &reply);
-
- /*
- * Permit if destination is local. That is, the destination matches
- * mydestination or virtual_maps, or it resolves to any transport that
- * delivers locally.
- */
- if ((domain = strrchr(STR(reply.recipient), '@')) == 0)
- return (SMTPD_CHECK_OK);
- domain += 1;
- if (resolve_local(domain)
- || (*var_virtual_maps && maps_find(virtual_maps, domain, 0)))
- return (SMTPD_CHECK_OK);
-
- /*
- * Permit if the destination matches the relay_domains list.
+ * Permit authorized destinations.
*/
- if (domain_list_match(relay_domains, domain))
+ if (permit_auth_destination(recipient) == SMTPD_CHECK_OK)
return (SMTPD_CHECK_OK);
/*
resolve_clnt_query(STR(query), &reply);
/*
- * Permit if destination is local. That is, the destination matches
- * mydestination or virtual_maps, or it resolves to any transport that
- * delivers locally.
+ * Handle special case that is not supposed to happen.
*/
- if ((domain = strrchr(STR(reply.recipient), '@')) == 0)
+ if ((domain = split_at_right(STR(reply.recipient), '@')) == 0)
return (SMTPD_CHECK_OK);
- domain += 1;
+
+ /*
+ * Permit final delivery: the destination matches mydestination or
+ * virtual_maps.
+ */
if (resolve_local(domain)
|| (*var_virtual_maps && maps_find(virtual_maps, domain, 0)))
return (SMTPD_CHECK_OK);
/*
- * Permit if the destination matches the relay_domains list.
+ * Permit non-routed mail to a destination on the relay_domains list.
*/
- if (domain_list_match(relay_domains, domain))
+ if ((reply.flags & RESOLVE_FLAG_ROUTED) == 0
+ && domain_list_match(relay_domains, domain))
return (SMTPD_CHECK_OK);
/*
- * Skip when not matched
+ * Something else.
*/
return (SMTPD_CHECK_DUNNO);
}
msg_info("%s: %s", myname, recipient);
/*
- * Resolve the address.
- */
- canon_addr_internal(query, recipient);
- resolve_clnt_query(STR(query), &reply);
-
- /*
- * Permit if destination is local. That is, the destination matches
- * mydestination or virtual_maps, or it resolves to any transport that
- * delivers locally.
- */
- if ((domain = strrchr(STR(reply.recipient), '@')) == 0)
- return (SMTPD_CHECK_DUNNO);
- domain += 1;
- if (resolve_local(domain)
- || (*var_virtual_maps && maps_find(virtual_maps, domain, 0)))
- return (SMTPD_CHECK_DUNNO);
-
- /*
- * Pass if the destination matches the relay_domains list.
+ * Permit authorized destination.
*/
- if (domain_list_match(relay_domains, domain))
+ if (permit_auth_destination(recipient) == SMTPD_CHECK_OK)
return (SMTPD_CHECK_DUNNO);
/*
- * Reject relaying to sites that are not listed in relay_domains.
+ * Reject unauthorized destination.
*/
return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
"%d <%s>: Relay access denied",
/* resolve_addr - resolve address according to rule set */
void resolve_addr(char *addr, VSTRING *channel, VSTRING *nexthop,
- VSTRING *nextrcpt)
+ VSTRING *nextrcpt, int *flags)
{
VSTRING *addr_buf = vstring_alloc(100);
TOK822 *tree;
TOK822 *saved_domain = 0;
TOK822 *domain = 0;
+ *flags = 0;
+
/*
* The address is in internalized (unquoted) form, so we must externalize
* it first before we can parse it.
}
/*
- * Replace foo%bar by foo@bar, site!user by user@site, rewrite to
- * canonical form, and retry.
+ * After stripping the local domain, replace foo%bar by foo@bar,
+ * site!user by user@site, rewrite to canonical form, and retry.
+ * Otherwise we're done.
*/
- if (var_swap_bangpath && tok822_rfind_type(tree->tail, '!') != 0) {
- rewrite_tree(REWRITE_CANON, tree);
- } else if (var_percent_hack
- && (domain = tok822_rfind_type(tree->tail, '%')) != 0) {
- domain->type = '@';
- rewrite_tree(REWRITE_CANON, tree);
- } else {
- domain = 0;
- break;
+ if ((domain = tok822_rfind_type(tree->tail, '@')) == 0) {
+ if (var_swap_bangpath && tok822_rfind_type(tree->tail, '!') != 0) {
+ rewrite_tree(REWRITE_CANON, tree);
+ } else if (var_percent_hack
+ && (domain = tok822_rfind_type(tree->tail, '%')) != 0) {
+ domain->type = '@';
+ rewrite_tree(REWRITE_CANON, tree);
+ } else {
+ break;
+ }
}
}
+ /*
+ * If the destination is non-local, recognize routing operators in the
+ * address localpart. This is needed to protect backup hosts against
+ * relaying by primary hosts, because the backup host would end up on
+ * black lists.
+ */
+ if (domain && domain->prev)
+ if (tok822_rfind_type(domain->prev, '@') != 0
+ || tok822_rfind_type(domain->prev, '!') != 0
+ || tok822_rfind_type(domain->prev, '%') != 0)
+ *flags |= RESOLVE_FLAG_ROUTED;
+
/*
* Make sure the resolved envelope recipient has the user@domain form. If
* no domain was specified in the address, assume the local machine. See
int resolve_proto(VSTREAM *stream)
{
+ int flags;
+
if (mail_scan(stream, "%s", query) != 1)
return (-1);
- resolve_addr(STR(query), channel, nexthop, nextrcpt);
+ resolve_addr(STR(query), channel, nexthop, nextrcpt, &flags);
if (msg_verbose)
- msg_info("%s -> (`%s' `%s' `%s')", STR(query), STR(channel),
- STR(nexthop), STR(nextrcpt));
+ msg_info("%s -> (`%s' `%s' `%s' `%d')", STR(query), STR(channel),
+ STR(nexthop), STR(nextrcpt), flags);
- mail_print(stream, "%s %s %s", STR(channel), STR(nexthop), STR(nextrcpt));
+ mail_print(stream, "%s %s %s %d",
+ STR(channel), STR(nexthop), STR(nextrcpt), flags);
if (vstream_fflush(stream) != 0) {
msg_warn("write resolver reply: %m");
tok822_free_tree(tok822_sub_keep_after(tree, colon));
/*
- * Swap domain!user to user@domain.
+ * Optionally, transform address forms without @.
*/
- if (var_swap_bangpath != 0
- && (domain = tok822_rfind_type(tree->tail, '@')) == 0
- && (bang = tok822_find_type(tree->head, '!')) != 0) {
- tok822_sub_keep_before(tree, bang);
- local = tok822_cut_after(bang);
- tok822_free(bang);
- tok822_sub_prepend(tree, tok822_alloc('@', (char *) 0));
- if (local)
- tok822_sub_prepend(tree, local);
- }
-
- /*
- * Append missing @origin
- */
- if (var_append_at_myorigin != 0
- && (domain = tok822_rfind_type(tree->tail, '@')) == 0) {
- domain = tok822_sub_append(tree, tok822_alloc('@', (char *) 0));
- tok822_sub_append(tree, tok822_scan(var_myorigin, (TOK822 **) 0));
+ if ((domain = tok822_rfind_type(tree->tail, '@')) == 0) {
+
+ /*
+ * Swap domain!user to user@domain.
+ */
+ if (var_swap_bangpath != 0
+ && (bang = tok822_find_type(tree->head, '!')) != 0) {
+ tok822_sub_keep_before(tree, bang);
+ local = tok822_cut_after(bang);
+ tok822_free(bang);
+ tok822_sub_prepend(tree, tok822_alloc('@', (char *) 0));
+ if (local)
+ tok822_sub_prepend(tree, local);
+ }
+
+ /*
+ * Promote user%domain to user@domain.
+ */
+ else if (var_percent_hack != 0
+ && (domain = tok822_rfind_type(tree->tail, '%')) != 0) {
+ domain->type = '@';
+ }
+
+ /*
+ * Append missing @origin.
+ */
+ else if (var_append_at_myorigin != 0) {
+ domain = tok822_sub_append(tree, tok822_alloc('@', (char *) 0));
+ tok822_sub_append(tree, tok822_scan(var_myorigin, (TOK822 **) 0));
+ }
}
/*
*/
extern void resolve_init(void);
extern int resolve_proto(VSTREAM *);
-extern void resolve_addr(char *, VSTRING *, VSTRING *, VSTRING *);
+extern void resolve_addr(char *, VSTRING *, VSTRING *, VSTRING *, int *);
/* LICENSE
/* .ad