smtp/smtp_smtp_proto.c, *qmgr/qmgr_message.c,
global/deliver_request.c.
+20031202
+
+ Cleanup: postfix-files now has support for files that are
+ no longer part of Postfix. When upgrading Postfix, the
+ post-install script gives the user a reminder. Files:
+ conf/postfix-files, conf/post-install.
+
Open problems:
High: when virtual aliasing is turned off after content
/etc/postfix/master.cf:
scan unix - - n - 10 smtp
+ -o myhostname=localhost.my.domain
# -o smtp_send_xclient_command=yes
+The "-o myhostname=localhost.domain.tld" overrides main.cf and
+avoids false alarms ("host <servername> greeted me with my own
+hostname") that result in mail being bounced.
+
Instead of a limit of 10 concurrent processes, use whatever process
limit is feasible for your machine. Content inspection software
can gobble up a lot of system resources, so you don't want to have
Uncomment (but keep the leading white-space) the option setting:
"smtp_send_xclient_command=yes", if you would like the scan transport
-to forward the original client name and IP address to the backend smtpd
-so that mail transaction logs show the real client name IP address.
-See sample-smtp.cf and smtp(8).
+to forward the original client name and IP address to the after-filter
+smtpd so that filtered mail is logged with the real client name IP
+address. See sample-smtp.cf and smtp(8).
The content filter can be set up with the Postfix spawn service,
which is the Postfix equivalent of inetd. For example, to instantiate
localhost:10026 inet n - n - 10 smtpd
-o content_filter=
-o receive_override_options=no_unknown_recipient_checks,no_header_body_checks
- -o myhostname=localhost.domain.tld
-o smtpd_helo_restrictions=
-o smtpd_client_restrictions=
-o smtpd_sender_restrictions=
are either implemented by the SMTP server itself, or they are passed
on to the cleanup server.
-The "-o myhostname=localhost.domain.tld" overrides main.cf and
-avoids false alarms ("host <servername> greeted me with my own
-hostname") if your content filter is based on a proxy that simply
-relays SMTP commands.
-
The "-o smtpd_xxx_restrictions" and "-o mynetworks=127.0.0.0/8"
override main.cf and turn off UCE controls that would only waste
time here.
The XCLIENT command targets problems in the following areas:
-1 - Access control tests. SMTP server access rules can be difficult
-to verify when decisions can be triggered by remote clients only.
-In order to facilitate access rule testing, an SMTP client test
-program needs the ability to override the SMTP server's idea of
-the SMTP client hostname, network address, and other information,
-for the entire duration of an SMTP session.
-
-2 - Logging after content filter. With Internet->MTA1->filter->MTA2
-style content filter applications, remote client information is
-lost when MTA1 gives the mail to the content filter. To simplify
-the interpretation of MTA2 logging, it would help if MTA1 could
-forward client information through the content filter to MTA2, for
-a single message delivery.
+1 - Access control tests. SMTP server access rules are difficult
+to verify when decisions can be triggered only by remote clients.
+In order to facilitate access rule testing, an authorized SMTP
+client test program needs the ability to override the SMTP server's
+idea of the SMTP client hostname, network address, and other
+information, for the entire duration of an SMTP session.
+
+2 - Logging after SMTP-based content filter. With the deployment
+of Internet->MTA1->filter->MTA2 style content filter applications,
+remote client information is lost when MTA1 gives the mail to the
+content filter. To simplify the interpretation of MTA2 logging,
+it would help if MTA1 could forward client information through the
+content filter to MTA2.
3 - Post-filter access control and logging. With Internet->filter->MTA
style content filter applications, the filter can be simplified if
Command overview
================
-XCLIENT is an extension to SMTP. The EHLO keyword associated with
-this extension is XCLIENT.
+The EHLO keyword associated with this extension is XCLIENT.
-The XCLIENT OVERRIDE command updates the remote client attributes
-that the MTA normally uses for access control, message headers,
-logging and so on, for the duration of an entire SMTP session.
+The XCLIENT OVERRIDE command updates remote client attributes that
+the MTA normally uses for access control, message headers, logging
+and so on, for the duration of an entire SMTP session.
-The XCLIENT FORWARD command maintains an additional set of attributes
-that concern only one message delivery attempt. In the absence of
+The XCLIENT FORWARD command updates temporary remote client attributes
+that the MTA uses for transaction logging. These attributes are
+valid for only one message delivery attempt. In the absence of
forwarded attributes the MTA must use the normal remote client
attribute values.
Code | Meaning
-----|------------
250 | success
- 501 | command syntax error
- 502 | unrecognized request name
+ 501 | bad command parameter
+ 503 | mail transaction in progress
421 | unable to proceed
The server must report success in case of an unrecognized attribute
XCLIENT OVERRIDE CLIENT_NAME=spike.porcupine.org
XCLIENT OVERRIDE CLIENT_ADDR=168.100.189.2
-The XCLIENT FORWARD request specifies remote client attributes
-concerning only one message delivery attempt. The attributes are
-discarded after the next MAIL FROM transaction finishes. In the
-absence of any XCLIENT FORWARD attributes, the MTA must use the
-normal client attributes.
+The XCLIENT FORWARD request specifies remote client attributes that
+are logged with one message delivery attempt. The attributes are
+discarded after the MAIL FROM transaction finishes. In the absence
+of any XCLIENT FORWARD attributes, the MTA must use the normal
+client attributes.
If only a subset of all possible XCLIENT FORWARD attributes is
specified, the unspecified attributes must be treated as if they
# IFS=value setting permanent. But some broken standard allows it.
create=; set_perms=; upgrade_perms=; upgrade_conf=; first_install_reminder=
+obsolete=
for arg
do
case $flags in *u*) upgrade_flag=1;; *) upgrade_flag=;; esac
case $flags in *c*) create_flag=1;; *) create_flag=;; esac
case $flags in *r*) recursive="-R";; *) recursive=;; esac
+ case $flags in *o*) obsolete_flag=1;; *) obsolete_flag=;; esac
+ # Flag obsolete objects. XXX Solaris 2..9 does not have "test -e".
+ if [ -n "$obsolete_flag" -a -r $path ]
+ then
+ obsolete="$obsolete $path"
+ continue;
+ fi
# Create missing directories with proper owner/group/mode settings.
if [ -n "$create" -a "$type" = "d" -a -n "$create_flag" -a ! -d "$path" ]
then
echo Editing $config_directory/master.cf, adding missing entry for anvil service
cat >>$config_directory/master.cf <<EOF || exit 1
anvil unix - - n - 1 anvil
+EOF
+ }
+
+ # Report (but do not remove) obsolete files/directories.
+
+ test -n "$obsolete" && {
+ cat <<EOF | ${FMT}
+
+ Note: the following files or directories still exist but are
+ no longer part of Postfix:
+
+ $obsolete
+
EOF
}
# u=update owner/group/mode (post-install upgrade-permissions).
# c=create missing directory (post-install create-missing).
# r=apply owner/group recursively (post-install set/upgrade-permissions).
+# o=obsolete, no longer part of Postfix
#
# Note: the "u" flag is for upgrading the permissions of existing files
# or directories after changes in Postfix architecture. For robustness
$config_directory/aliases:f:root:-:644:p
$config_directory/canonical:f:root:-:644:p
$config_directory/cidr_table:f:root:-:644:p
+$config_directory/install.cf:f:root:-:644:o
$config_directory/main.cf:f:root:-:644:p
$config_directory/main.cf.default:f:root:-:644
$config_directory/makedefs.out:f:root:-:644
$config_directory/transport:f:root:-:644:p
$config_directory/virtual:f:root:-:644:p
$config_directory/postfix-script:f:root:-:755
+$config_directory/postfix-script-sgid:f:root:-:755:o
+$config_directory/postfix-script-nosgid:f:root:-:755:o
$config_directory/post-install:f:root:-:755
$manpage_directory/man1/mailq.1:f:root:-:644
$manpage_directory/man1/newaliases.1:f:root:-:644
smtp_connect_timeout = 30s
# The smtp_helo_timeout parameter specifies the SMTP client timeout
-# for receiving the SMTP greeting banner.
+# for sending the SMTP HELO or EHLO command, and for receiving the
+# server response.
#
-# When the server drops the connection without sending a greeting
-# banner, or when it sends no greeting banner within the deadline,
-# the SMTP client tries the next address on the mail exchanger list.
+# In case of problems the client does NOT try the next address on
+# the mail exchanger list.
#
# Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
# The default time unit is s (seconds).
#
smtp_helo_timeout = 300s
+# The smtp_xclient_timeout parameter specifies the SMTP client timeout
+# for sending the SMTP XCLIENT command, and for receiving the server
+# response.
+# for receiving the SMTP greeting banner.
+#
+# In case of problems the client does NOT try the next address on
+# the mail exchanger list.
+#
+# Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
+# The default time unit is s (seconds).
+#
+smtp_xclient_timeout = 300s
+
# The smtp_mail_timeout parameter specifies the SMTP client timeout
# for sending the SMTP MAIL FROM command, and for receiving the server
# response.
*) continue;;
esac
+ # Skip over files that ought to be removed.
+ # Leave it up to post-install to report them to the user.
+
+ case $flags in
+ *o*) continue
+ esac
+
# Skip over files that must be preserved.
case $flags in
if (bounce_append(BOUNCE_FLAG_CLEAN, state->queue_id,
state->recip ? state->recip : "unknown",
state->recip ? state->recip : "unknown",
- (long) 0, "cleanup", state->time,
+ (long) 0, "none", state->time,
"%s", state->reason ? state->reason :
cleanup_strerror(state->errs)) == 0
&& bounce_flush(BOUNCE_FLAG_CLEAN, state->queue_name,
* Since we can't send null pointers, null strings represent unavailable
* attributes instead. They're less likely to explode in our face, too.
*/
-#define DEL_REQ_ATTR_UNAVAIL(a) (*(a))
+#define DEL_REQ_ATTR_AVAIL(a) (*(a))
/*
* How to deliver, really?
#define CLIENT_HELO_UNKNOWN 0
#define CLIENT_PROTO_UNKNOWN CLIENT_ATTR_UNKNOWN
-#define IS_UNK_CLIENT_ATTR(v) (!(v) || !strcmp((v), CLIENT_ATTR_UNKNOWN))
+#define IS_AVAIL_CLIENT_ATTR(v) ((v) && strcmp((v), CLIENT_ATTR_UNKNOWN))
-#define IS_UNK_CLIENT_NAME(v) IS_UNK_CLIENT_ATTR(v)
-#define IS_UNK_CLIENT_ADDR(v) IS_UNK_CLIENT_ATTR(v)
-#define IS_UNK_CLIENT_NAMADDR(v) IS_UNK_CLIENT_ATTR(v)
-#define IS_UNK_CLIENT_HELO(v) (!(v))
-#define IS_UNK_CLIENT_PROTO(v) IS_UNK_CLIENT_ATTR(v)
+#define IS_AVAIL_CLIENT_NAME(v) IS_AVAIL_CLIENT_ATTR(v)
+#define IS_AVAIL_CLIENT_ADDR(v) IS_AVAIL_CLIENT_ATTR(v)
+#define IS_AVAIL_CLIENT_NAMADDR(v) IS_AVAIL_CLIENT_ATTR(v)
+#define IS_AVAIL_CLIENT_HELO(v) (v)
+#define IS_AVAIL_CLIENT_PROTO(v) IS_AVAIL_CLIENT_ATTR(v)
/* LICENSE
/* .ad
* 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 "20031201"
+#define MAIL_RELEASE_DATE "20031202"
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "2.0.16-" MAIL_RELEASE_DATE
static void qmqpd_write_attributes(QMQPD_STATE *state)
{
- if (!IS_UNK_CLIENT_NAME(state->name))
+ if (IS_AVAIL_CLIENT_NAME(state->name))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_CLIENT_NAME, state->name);
- if (!IS_UNK_CLIENT_ADDR(state->addr))
+ if (IS_AVAIL_CLIENT_ADDR(state->addr))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_CLIENT_ADDR, state->addr);
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
nrcpt = 0;
if (var_smtp_send_xclient
&& (state->features & SMTP_FEATURE_XCLIENT)
- && !DEL_REQ_ATTR_UNAVAIL(request->client_name)
- && !DEL_REQ_ATTR_UNAVAIL(request->client_addr))
+ && (DEL_REQ_ATTR_AVAIL(request->client_name)
+ || DEL_REQ_ATTR_AVAIL(request->client_addr)))
recv_state = send_state = SMTP_STATE_XCLIENT_ADDR;
else
recv_state = send_state = SMTP_STATE_MAIL;
case SMTP_STATE_XCLIENT_ADDR:
vstring_strcpy(next_command,
XCLIENT_CMD " " XCLIENT_FORWARD " " XCLIENT_NAME "=");
- if (!DEL_REQ_ATTR_UNAVAIL(request->client_name))
+ if (DEL_REQ_ATTR_AVAIL(request->client_name))
xtext_quote_append(next_command, request->client_name, "");
vstring_strcat(next_command, " " XCLIENT_ADDR "=");
- if (!DEL_REQ_ATTR_UNAVAIL(request->client_addr))
+ if (DEL_REQ_ATTR_AVAIL(request->client_addr))
xtext_quote_append(next_command, request->client_addr, "");
next_state = SMTP_STATE_XCLIENT_HELO;
break;
case SMTP_STATE_XCLIENT_HELO:
vstring_strcpy(next_command,
XCLIENT_CMD " " XCLIENT_FORWARD " " XCLIENT_HELO "=");
- if (!DEL_REQ_ATTR_UNAVAIL(request->client_helo))
+ if (DEL_REQ_ATTR_AVAIL(request->client_helo))
xtext_quote_append(next_command, request->client_helo, "");
vstring_strcat(next_command, " " XCLIENT_PROTO "=");
- if (!DEL_REQ_ATTR_UNAVAIL(request->client_proto))
+ if (DEL_REQ_ATTR_AVAIL(request->client_proto))
xtext_quote_append(next_command, request->client_proto, "");
next_state = SMTP_STATE_MAIL;
break;
#include <errno.h>
#include <ctype.h>
#include <signal.h>
-#include <stddef.h>
+#include <stddef.h> /* offsetof() */
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
static NAMADR_LIST *verp_clients;
/*
- * XCLIENT command.
+ * XCLIENT command. Access control is cached, so that XCLIENT can't override
+ * its own access control.
*/
static NAMADR_LIST *xclient_hosts;
static int xclient_allowed;
state->name, state->addr);
if (msg_verbose)
- msg_info("sasl_exceptions: %s, match=%d", state->namaddr, match);
+ msg_info("sasl_exceptions: %s[%s], match=%d",
+ state->name, state->addr, match);
return (match);
}
* Store the client attributes for logging purposes.
*/
if (SMTPD_STAND_ALONE(state) == 0) {
- if (!IS_UNK_CLIENT_NAME(FORWARD_NAME(state)))
+ if (IS_AVAIL_CLIENT_NAME(FORWARD_NAME(state)))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_CLIENT_NAME, FORWARD_NAME(state));
- if (!IS_UNK_CLIENT_ADDR(FORWARD_ADDR(state)))
+ if (IS_AVAIL_CLIENT_ADDR(FORWARD_ADDR(state)))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_CLIENT_ADDR, FORWARD_ADDR(state));
- if (!IS_UNK_CLIENT_NAMADDR(FORWARD_NAMADDR(state)))
+ if (IS_AVAIL_CLIENT_NAMADDR(FORWARD_NAMADDR(state)))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_ORIGIN, FORWARD_NAMADDR(state));
- if (!IS_UNK_CLIENT_HELO(FORWARD_HELO(state)))
+ if (IS_AVAIL_CLIENT_HELO(FORWARD_HELO(state)))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_HELO_NAME, FORWARD_HELO(state));
- if (!IS_UNK_CLIENT_PROTO(FORWARD_PROTO(state)))
+ if (IS_AVAIL_CLIENT_PROTO(FORWARD_PROTO(state)))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_PROTO_NAME, FORWARD_PROTO(state));
}
*/
if (state->cleanup)
rec_fputs(state->cleanup, REC_TYPE_MESG, "");
- if (!state->proxy || state->xclient.addr == 0) {
+ if (!state->proxy || state->xclient.used == 0) {
out_fprintf(out_stream, REC_TYPE_NORM,
"Received: from %s (%s [%s])",
state->helo_name ? state->helo_name : state->name,
* The client can send multiple XCLIENT attributes in a single command,
* or multiple XCLIENT commands with fewer attributes.
*
- * Note: XCLIENT OVERRIDE overrides only the specified logging and
- * access control attributes (desirable for testing), while XCLIENT
- * FORWARD overrides all logging attributes (for audit trail
- * consistency).
+ * Note: XCLIENT OVERRIDE overrides only the specified remote client
+ * attributes (for testing), while XCLIENT FORWARD overrides all
+ * remote client attributes (for consistency).
*/
if ((raw_value = split_at(arg_val, '=')) == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "503 Error: name=value expected");
+ smtpd_chat_reply(state, "501 Error: name=value expected");
return (-1);
}
if (xtext_unquote(state->buffer, raw_value) == 0) {
}
/*
- * CLIENT_CODE=status. Reset the client hostname if the hostname
- * lookup status is not OK.
+ * CLIENT_CODE=hostname lookup status. Reset the client hostname if
+ * the hostname lookup status is not OK.
*/
else if (STREQ(arg_val, XCLIENT_CODE)) {
if (STREQ(cooked_value, "OK")) {
char *access_denied;
ARGV *history;
char *reason;
- char *sender;
- char *encoding;
- char *verp_delims;
+ char *sender;
+ char *encoding; /* owned by mail_cmd() */
+ char *verp_delims; /* owned by mail_cmd() */
char *recipient;
char *etrn_name;
char *protocol;
VSTRING *expand_buf; /* scratch space for $name expansion */
VSTREAM *proxy; /* proxy handle */
VSTRING *proxy_buffer; /* proxy query/reply buffer */
- char *proxy_mail; /* proxy MAIL FROM command */
+ char *proxy_mail; /* owned by mail_cmd() */
int proxy_features; /* proxy ESMTP features */
SMTPD_XCLIENT_ATTR xclient; /* override access control */
} SMTPD_STATE;
/*
* Choose between normal or forwarded attributes.
+ *
+ * Note 1: inside the SMTP server, forwarded attributes must have the exact
+ * same representation as normal attributes: unknown string values are
+ * "unknown", except for HELO which defaults to null. This is better than
+ * having to change every piece of code that accesses a possibly forwarded
+ * attribute.
+ *
+ * Note 2: outside the SMTP server, the representation of unknown/known
+ * attribute values is different in queue files, in queue manager delivery
+ * requests, and in over-the-network XCLIENT commands.
*/
-#define SMTPD_FEATURE_XCLIENT (1<<0) /* XCLIENT supported */
+#define SMTPD_FEATURE_XCLIENT (1<<0) /* proxy announces XCLIENT */
#define MAYBE_FORWARD(s, a) \
((s)->xclient.used ? (s)->xclient.a : (s)->a)
* forms and encode the result as xtext.
*/
if ((state->proxy_features & SMTPD_FEATURE_XCLIENT)
- && (!IS_UNK_CLIENT_NAME(FORWARD_NAME(state))
- || !IS_UNK_CLIENT_ADDR(FORWARD_ADDR(state)))) {
+ && (IS_AVAIL_CLIENT_NAME(FORWARD_NAME(state))
+ || IS_AVAIL_CLIENT_ADDR(FORWARD_ADDR(state)))) {
buf = vstring_alloc(100);
vstring_strcpy(buf, XCLIENT_CMD " " XCLIENT_FORWARD
" " XCLIENT_NAME "=");
- if (!IS_UNK_CLIENT_NAME(FORWARD_NAME(state)))
+ if (IS_AVAIL_CLIENT_NAME(FORWARD_NAME(state)))
xtext_quote_append(buf, FORWARD_NAME(state), "");
vstring_strcat(buf, " " XCLIENT_ADDR "=");
- if (!IS_UNK_CLIENT_ADDR(FORWARD_ADDR(state)))
+ if (IS_AVAIL_CLIENT_ADDR(FORWARD_ADDR(state)))
xtext_quote_append(buf, FORWARD_ADDR(state), "");
bad = smtpd_proxy_cmd(state, SMTPD_PROX_WANT_ANY, "%s", STR(buf));
if (bad == 0) {
vstring_strcpy(buf, XCLIENT_CMD " " XCLIENT_FORWARD
" " XCLIENT_HELO "=");
- if (!IS_UNK_CLIENT_HELO(FORWARD_HELO(state)))
+ if (IS_AVAIL_CLIENT_HELO(FORWARD_HELO(state)))
xtext_quote_append(buf, FORWARD_HELO(state), "");
vstring_strcat(buf, " " XCLIENT_PROTO "=");
- if (!IS_UNK_CLIENT_PROTO(FORWARD_PROTO(state)))
+ if (IS_AVAIL_CLIENT_PROTO(FORWARD_PROTO(state)))
xtext_quote_append(buf, FORWARD_PROTO(state), "");
bad = smtpd_proxy_cmd(state, SMTPD_PROX_WANT_ANY, "%s", STR(buf));
}
if (state->protocol)
myfree(state->protocol);
smtpd_peer_reset(state);
- smtpd_xclient_reset(state);
+
+ /*
+ * Buffers that are created on the fly and that may be shared among mail
+ * deliveries within the same SMTP session.
+ */
if (state->defer_if_permit.reason)
vstring_free(state->defer_if_permit.reason);
if (state->defer_if_reject.reason)
/* NAME
/* neuter 3
/* SUMMARY
-/* mask non-neuter characters
+/* neutralize characters before they can explode
/* SYNOPSIS
/* #include <stringops.h>
/*
#if __FreeBSD_version >= 200000
#define HAS_DUPLEX_PIPE
+#endif
+
+#if __FreeBSD_version >= 300000
#define HAS_ISSETUGID
#endif
#define DEF_MAILBOX_LOCK "flock, dotlock"
#endif
+#if __NetBSD_Version__ >= 105000000 /* XXX */
+#define HAS_ISSETUGID
+#endif
+
#if __NetBSD_Version__ >= 106000000 /* XXX */
#define SOCKADDR_SIZE socklen_t
#define SOCKOPT_SIZE socklen_t