# The smtpd_sender_restrictions parameter specifies optional restrictions
# on sender addresses that SMTP clients can send in MAIL FROM commands.
#
-# The default is to permit any sender address. The following
-# restrictions are available:
+# The default is to permit any sender address that passes the restrictions
+# marked with (+). The following restrictions are available:
#
# permit_mynetworks: permit if the client address matches $mynetworks.
# reject_unknown_sender_domain: reject sender domain without A or MX record.
+# +reject_unlisted_sender: reject a sender address in a local, virtual or
+# relay domain that is not listed as a valid address.
+# This restriction is automatically added at the end if not explicitly
+# specified.
# reject_rhsbl_sender domain.tld: reject sender domain name if it is listed
# in an A record under domain.tld.
# check_sender_access maptype:mapname
# - destinations that match $virtual_mailbox_domains.
# These destinations do not need to be listed in $relay_domains.
#
-# The following restrictions are available (* is part of default setting):
+# The following restrictions are available (* is part of default setting,
+# and + is appended by default if not explicitly specified):
#
# *permit_mynetworks: permit if the client address matches $mynetworks.
# reject_unknown_sender_domain: reject sender domain without A or MX record.
+# reject_unlisted_sender: reject a sender address in a local, virtual or
+# relay domain that is not listed as a valid address.
# reject_unverified_sender: reject undeliverable sender address.
# reject_unverified_recipient: reject undeliverable recipient address.
# reject_multi_recipient_bounce: reject mail from <> with multiple recipients.
# Use the optional permit_mx_backup_networks parameter to also
# require that the primary MX hosts match a list of network blocks.
# reject_unknown_recipient_domain: reject domains without A or MX record.
+# +reject_unlisted_recipient: reject a recipient address in a local, virtual
+# or relay domain that is not listed as a valid address.
+# This restriction is automatically added at the end if not explicitly
+# specified.
+# +check_recipient_maps: a backwards compatibility alias for the
+# reject_unlisted_recipient restriction.
# check_recipient_access maptype:mapname
# look up recipient address, parent domain, or localpart@.
# see access(5) for possible lookup results.
<p>
+<a name="reject_unlisted_sender">
+
+<dt> <b>reject_unlisted_sender</b> <dd> Reject the request when
+the sender address matches one of the domain lists below, but
+is not listed in one of the corresponding address lists:
+
+<blockquote>
+
+<table border="1">
+
+<tr><th>Domain list</th> <th>Address list</th>
+
+</tr><tr><td><a href="basic.html#mydestination"> $mydestination</a> or
+<a href="basic.html#inet_interfaces">$inet_interfaces</a></td>
+<td>$local_recipient_maps</td>
+
+</tr><tr><td>$virtual_alias_domains</td> <td>$virtual_alias_maps</td>
+
+</tr><tr><td>$virtual_mailbox_domains</td> <td>$virtual_mailbox_maps</td>
+
+</tr><tr><td>$relay_domains</td> <td>$relay_recipient_maps</td>
+
+</tr></table>
+
+</blockquote>
+
+Note 1: a null $local_recipient_maps or $relay_recipient_maps setting
+means that no address check is done for the corresponding domains.
+
+<p>
+
+Note 2: Postfix applies an implicit <b>reject_unlisted_sender</b>
+restriction at the end of all sender restrictions.
+
+<p>
+
<a name="reject_non_fqdn_sender">
<dt> <b>reject_non_fqdn_sender</b> <dd> Reject the request when
<p>
-<a name="check_recipient_maps">
+<a name="reject_unlisted_recipient">
-<dt> <b>check_recipient_maps</b> <dd> Reject the request
-when the recipient address is not listed in one of the following
-lookup tables:
+<dt> <b>reject_unlisted_recipient</b> <dd> Reject the request when
+the recipient address matches one of the domain lists below, but
+is not listed in one of the corresponding lookup tables:
<blockquote>
<table border="1">
-<tr><th>Recipient domain matches</th> <th>Recipient lookup table</th>
+<tr><th>Domain list</th> <th>Address list</th>
</tr><tr><td><a href="basic.html#mydestination"> $mydestination</a> or
<a href="basic.html#inet_interfaces">$inet_interfaces</a></td>
</blockquote>
Note 1: a null $local_recipient_maps or $relay_recipient_maps setting
-means that no recipient check is done for the corresponding domains.
+means that no address check is done for the corresponding domains.
<p>
-Note 2: Postfix applies an implicit <b>check_recipient_maps</b>
+Note 2: Postfix applies an implicit <b>reject_unlisted_recipient</b>
restriction at the end of all recipient restrictions.
<p>
+Note 3: the <b>check_recipient_maps</b> restriction is a backwards
+compatible alias for <b>reject_unlisted_recipient</b>. It will
+eventually be removed from Postfix.
+
+<p>
+
<a name="reject_multi_recipient_bounce">
<dt> <b>reject_multi_recipient_bounce</b> <dd> Reject the request
/* Apply the specified access table to the DNS server host name and IP
/* addresses for the helo hostname, sender, or recipient, respectively.
/* If no NS record is found, the parent domain is used instead.
-/* .IP "check_recipient_maps"
-/* Reject recipients not listed as valid local, virtual or relay
-/* recipients.
+/* .IP "reject_unlisted_sender"
+/* Reject senders in local, virtual or relay domains that are not
+/* listed as a valid address.
+/* .IP "reject_unlisted_recipient"
+/* Reject recipients in local, virtual or relay domains that are
+/* not listed as a valid address.
/* .IP reject_multi_recipient_bounce
/* Reject mail from <> with multiple envelope recipients.
/* .IP reject_rbl_client rbl.domain.tld
/*
* Recipient table check.
*/
-static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient);
+static int check_sender_rcpt_maps(SMTPD_STATE *, const char *);
+static int check_recipient_rcpt_maps(SMTPD_STATE *, const char *);
+static int check_rcpt_maps(SMTPD_STATE *, const char *, const char *);
/*
* Reject context.
status = reject_rbl_domain(state, *cpp, state->sender,
SMTPD_NAME_SENDER);
}
+ } else if (strcasecmp(name, REJECT_UNLISTED_SENDER) == 0) {
+ if (state->sender && *state->sender)
+ status = check_sender_rcpt_maps(state, state->sender);
}
/*
status = reject_rbl_domain(state, *cpp, state->recipient,
SMTPD_NAME_RECIPIENT);
}
- } else if (strcasecmp(name, CHECK_RCPT_MAPS) == 0) {
+ } else if (strcasecmp(name, CHECK_RCPT_MAPS) == 0
+ || strcasecmp(name, REJECT_UNLISTED_RCPT) == 0) {
if (state->recipient && *state->recipient)
- status = check_rcpt_maps(state, state->recipient);
+ status = check_recipient_rcpt_maps(state, state->recipient);
} else if (strcasecmp(name, REJECT_MUL_RCPT_BOUNCE) == 0) {
if (state->sender && *state->sender == 0 && state->rcpt_count
> (strcmp(state->where, "DATA") ? 0 : 1))
{
const RESOLVE_REPLY *resolve_reply;
char *myname = "smtpd_check_addr";
- int status;
if (msg_verbose)
msg_info("%s: addr=%s", myname, addr);
*/
state->defer_if_permit.active = state->defer_if_permit_client
| state->defer_if_permit_helo;
+ state->sender_rcptmap_checked = 0;
/*
* Apply restrictions in the order as specified.
SMTPD_NAME_SENDER, CHECK_SENDER_ACL);
state->defer_if_permit_sender = state->defer_if_permit.active;
+ /*
+ * If the "check_recipient_maps" restriction was not applied, and if mail
+ * is not being rejected or discarded, validate the recipient here.
+ */
+ if (status != SMTPD_CHECK_REJECT && state->sender_rcptmap_checked == 0
+ && state->discard == 0 && *sender)
+ status = check_sender_rcpt_maps(state, sender);
+
SMTPD_CHECK_MAIL_RETURN(status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
}
* The "check_recipient_maps" restriction is relevant only when
* responding to RCPT TO or VRFY.
*/
- state->rcptmap_checked = 0;
+ state->recipient_rcptmap_checked = 0;
/*
* Apply delayed restrictions.
* If the "check_recipient_maps" restriction was not applied, and if mail
* is not being rejected or discarded, validate the recipient here.
*/
- if (status != SMTPD_CHECK_REJECT && state->rcptmap_checked == 0
+ if (status != SMTPD_CHECK_REJECT && state->recipient_rcptmap_checked == 0
&& state->discard == 0)
- status = check_rcpt_maps(state, recipient);
+ status = check_recipient_rcpt_maps(state, recipient);
SMTPD_CHECK_RCPT_RETURN(status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
}
SMTPD_CHECK_ETRN_RETURN(status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
}
-/* check_rcpt_maps - generic_checks() interface for recipient table check */
+/* check_recipient_rcpt_maps - generic_checks() recipient table check */
-static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient)
+static int check_recipient_rcpt_maps(SMTPD_STATE *state, const char *recipient)
{
- const RESOLVE_REPLY *reply;
/*
* Duplicate suppression. There's an implicit check_recipient_maps
*/
if (smtpd_input_transp_mask & INPUT_TRANSP_UNKNOWN_RCPT)
return (0);
- if (state->rcptmap_checked == 1)
+ if (state->recipient_rcptmap_checked == 1)
+ return (0);
+ state->recipient_rcptmap_checked = 1;
+ return (check_rcpt_maps(state, recipient, SMTPD_NAME_RECIPIENT));
+}
+
+/* check_sender_rcpt_maps - generic_checks() sender table check */
+
+static int check_sender_rcpt_maps(SMTPD_STATE *state, const char *sender)
+{
+
+ /*
+ * Duplicate suppression. There's an implicit check_sender_maps
+ * restriction at the end of all sender restrictions.
+ */
+ if (smtpd_input_transp_mask & INPUT_TRANSP_UNKNOWN_RCPT)
+ return (0);
+ if (state->sender_rcptmap_checked == 1)
return (0);
- state->rcptmap_checked = 1;
+ state->sender_rcptmap_checked = 1;
+ return (check_rcpt_maps(state, sender, SMTPD_NAME_SENDER));
+}
+
+/* check_rcpt_maps - generic_checks() interface for recipient table check */
+
+static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient,
+ const char *reply_class)
+{
+ const RESOLVE_REPLY *reply;
if (msg_verbose)
msg_info(">>> CHECKING RECIPIENT MAPS <<<");
*/
if (strcmp(STR(reply->transport), MAIL_SERVICE_ERROR) == 0)
return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
- "%d <%s>: %s",
+ "%d <%s>: %s rejected: %s",
(reply->flags & RESOLVE_CLASS_ALIAS) ?
var_virt_alias_code : 550,
- recipient, STR(reply->nexthop)));
+ recipient, reply_class,
+ STR(reply->nexthop)));
/*
* Reject mail to unknown addresses in local domains (domains that match
*/
#define MATCH_LEFT(l, r, n) (strncasecmp((l), (r), (n)) == 0 && (r)[n] == '@')
- if ((reply->flags & RESOLVE_CLASS_LOCAL)
- && *var_local_rcpt_maps
- /* Generated by bounce, absorbed by qmgr. */
+ switch (reply->flags & RESOLVE_CLASS_MASK) {
+
+ case RESOLVE_CLASS_LOCAL:
+ if (*var_local_rcpt_maps
+ /* Generated by bounce, absorbed by qmgr. */
&& !MATCH_LEFT(var_double_bounce_sender, CONST_STR(reply->recipient),
strlen(var_double_bounce_sender))
- /* Absorbed by qmgr. */
- && !MATCH_LEFT(MAIL_ADDR_POSTMASTER, CONST_STR(reply->recipient),
- strlen(MAIL_ADDR_POSTMASTER))
- /* Generated by bounce. */
- && !MATCH_LEFT(MAIL_ADDR_MAIL_DAEMON, CONST_STR(reply->recipient),
- strlen(MAIL_ADDR_MAIL_DAEMON))
- && NOMATCH(local_rcpt_maps, CONST_STR(reply->recipient)))
- return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
- "%d <%s>: User unknown%s",
- var_local_rcpt_code, recipient,
- var_show_unk_rcpt_table ?
- " in local recipient table" : ""));
+ /* Absorbed by qmgr. */
+ && !MATCH_LEFT(MAIL_ADDR_POSTMASTER, CONST_STR(reply->recipient),
+ strlen(MAIL_ADDR_POSTMASTER))
+ /* Generated by bounce. */
+ && !MATCH_LEFT(MAIL_ADDR_MAIL_DAEMON, CONST_STR(reply->recipient),
+ strlen(MAIL_ADDR_MAIL_DAEMON))
+ && NOMATCH(local_rcpt_maps, CONST_STR(reply->recipient)))
+ return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
+ "%d <%s>: %s rejected: User unknown%s",
+ var_local_rcpt_code, recipient,
+ reply_class, var_show_unk_rcpt_table ?
+ " in local recipient table" : ""));
+ break;
- /*
- * Reject mail to unknown addresses in virtual mailbox domains.
- */
- if ((reply->flags & RESOLVE_CLASS_VIRTUAL)
- && *var_virt_mailbox_maps
- && NOMATCH(virt_mailbox_maps, CONST_STR(reply->recipient)))
- return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
- "%d <%s>: User unknown%s",
- var_virt_mailbox_code, recipient,
- var_show_unk_rcpt_table ?
- " in virtual mailbox table" : ""));
+ /*
+ * Reject mail to unknown addresses in virtual mailbox domains.
+ */
+ case RESOLVE_CLASS_VIRTUAL:
+ if (*var_virt_mailbox_maps
+ && NOMATCH(virt_mailbox_maps, CONST_STR(reply->recipient)))
+ return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
+ "%d <%s>: %s rejected: User unknown%s",
+ var_virt_mailbox_code, recipient,
+ reply_class, var_show_unk_rcpt_table ?
+ " in virtual mailbox table" : ""));
+ break;
- /*
- * Reject mail to unknown addresses in relay domains.
- */
- if ((reply->flags & RESOLVE_CLASS_RELAY)
- && *var_relay_rcpt_maps
- && NOMATCH(relay_rcpt_maps, CONST_STR(reply->recipient)))
- return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
- "%d <%s>: User unknown%s",
- var_relay_rcpt_code, recipient,
- var_show_unk_rcpt_table ?
- " in relay recipient table" : ""));
+ /*
+ * Reject mail to unknown addresses in relay domains.
+ */
+ case RESOLVE_CLASS_RELAY:
+ if (*var_relay_rcpt_maps
+ && NOMATCH(relay_rcpt_maps, CONST_STR(reply->recipient)))
+ return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
+ "%d <%s>: %s rejected: User unknown%s",
+ var_relay_rcpt_code, recipient,
+ reply_class, var_show_unk_rcpt_table ?
+ " in relay recipient table" : ""));
+ break;
- /*
- * Accept all other addresses - including addresses that passed the above
- * tests because of some table lookup problem.
- */
+ /*
+ * Accept all other addresses - including addresses that passed the
+ * above tests because of some table lookup problem.
+ */
+ default:
+ break;
+ }
return (0);
}