+- CLIENT_ADDRESS
+
+- Remote client network address. Available in Postfix 2.2 and
+later.
+
+- CLIENT_HELO
+
+- Remote client EHLO command parameter. Available in Postfix 2.2
+and later.
+
+- CLIENT_HOSTNAME
+
+- Remote client hostname. Available in Postfix 2.2 and later.
+
+
+- CLIENT_PROTOCOL
+
+- Remote client protocol. Available in Postfix 2.2 and later.
+
+
- DOMAIN
- The domain part of the recipient address.
@@ -2116,6 +2136,21 @@ The following environment variables are exported to the command:
- The full recipient address.
+- SASL_METHOD
+
+- SASL authentication method specified in the remote client AUTH
+command. Available in Postfix 2.2 and later.
+
+- SASL_SENDER
+
+- SASL sender address specified in the remote client MAIL FROM
+command. Available in Postfix 2.2 and later.
+
+- SASL_USER
+
+- SASL username specified in the remote client AUTH command.
+Available in Postfix 2.2 and later.
+
- SENDER
- The full sender address.
@@ -7224,6 +7259,34 @@ remote domains. Available before Postfix version 2.0. With Postfix 2.1
and later, this is replaced by separate controls: virtual_alias_domains
and virtual_alias_maps.
+%PARAM smtp_disable_ehlo_keywords
+
+ A case insensitive list of EHLO keywords (pipelining, starttls,
+auth, etc.) that the SMTP client will ignore in the EHLO response
+from a remote SMTP server. Use the smtp_disable_ehlo_keyword_address_maps
+feature to disable EHLO keywords selectively.
+
+%PARAM smtpd_disable_ehlo_keywords
+
+ A case insensitive list of EHLO keywords (pipelining, starttls,
+auth, etc.) that the SMTP server will not send in the EHLO response
+to a remote SMTP client. Use the smtpd_disable_ehlo_keyword_address_maps
+feature to disable EHLO keywords selectively.
+
+%PARAM smtp_disable_ehlo_keyword_address_maps
+
+ Lookup tables, indexed by the remote SMTP server address, with
+case insensitive lists of EHLO keywords (pipelining, starttls,
+auth, etc.) that the SMTP client will ignore in the EHLO response
+from a remote SMTP server.
+
+%PARAM smtpd_disable_ehlo_keyword_address_maps
+
+ Lookup tables, indexed by the remote SMTP client address, with
+case insensitive lists of EHLO keywords (pipelining, starttls,
+auth, etc.) that the SMTP server will not send in the EHLO response
+to a remote SMTP client.
+
%PARAM session_cache_service scache
The name of the scache(8) connection cache service. This service
diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in
index 0f23fb940..6c8b786cc 100644
--- a/postfix/src/global/Makefile.in
+++ b/postfix/src/global/Makefile.in
@@ -23,7 +23,8 @@ SRCS = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \
sys_exits.c timed_ipc.c tok822_find.c tok822_node.c tok822_parse.c \
tok822_resolve.c tok822_rewrite.c tok822_tree.c trace.c verify.c \
verify_clnt.c verp_sender.c virtual8_maps.c xtext.c scache_single.c \
- scache_clnt.c scache_multi.c user_acl.c mkmap_cdb.c mkmap_sdbm.c
+ scache_clnt.c scache_multi.c user_acl.c mkmap_cdb.c mkmap_sdbm.c \
+ ehlo_mask.c
OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \
clnt_stream.o debug_peer.o debug_process.o defer.o \
@@ -48,7 +49,8 @@ OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
sys_exits.o timed_ipc.o tok822_find.o tok822_node.o tok822_parse.o \
tok822_resolve.o tok822_rewrite.o tok822_tree.o trace.o verify.o \
verify_clnt.o verp_sender.o virtual8_maps.o xtext.o scache_single.o \
- scache_clnt.o scache_multi.o user_acl.o mkmap_cdb.o mkmap_sdbm.o
+ scache_clnt.o scache_multi.o user_acl.o mkmap_cdb.o mkmap_sdbm.o \
+ ehlo_mask.o
HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \
debug_peer.h debug_process.h defer.h deliver_completed.h \
@@ -69,7 +71,7 @@ HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
resolve_local.h rewrite_clnt.h sent.h smtp_stream.h split_addr.h \
string_list.h strip_addr.h sys_exits.h timed_ipc.h tok822.h \
trace.h verify.h verify_clnt.h verp_sender.h virtual8_maps.h \
- xtext.h scache.h user_acl.h
+ xtext.h scache.h user_acl.h ehlo_mask.h
TESTSRC = rec2stream.c stream2rec.c recdump.c
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
@@ -80,7 +82,7 @@ TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \
off_cvt quote_822_local rec2stream recdump resolve_clnt \
resolve_local rewrite_clnt stream2rec string_list tok822_parse \
quote_821_local mail_conf_time mime_state strip_addr \
- virtual8_maps verify_clnt xtext anvil_clnt scache
+ virtual8_maps verify_clnt xtext anvil_clnt scache ehlo_mask
LIBS = ../../lib/libutil.a
LIB_DIR = ../../lib
@@ -251,9 +253,12 @@ anvil_clnt: $(LIB) $(LIBS)
scache: scache.c $(LIB) $(LIBS)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
+ehlo_mask: ehlo_mask.c $(LIB) $(LIBS)
+ $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
+
tests: tok822_test mime_test mime_nest mime_8bit mime_dom mime_trunc \
mime_cvt mime_cvt2 mime_cvt3 strip_addr_test tok822_limit_test \
- virtual8_test xtext_test scache_multi_test
+ virtual8_test xtext_test scache_multi_test ehlo_mask_test
tok822_test: tok822_parse tok822_parse.in tok822_parse.ref
./tok822_parse tok822_parse.tmp 2>&1
@@ -352,6 +357,11 @@ scache_multi_test: scache scache_multi.in scache_multi.ref
diff scache_multi.ref scache_multi.tmp
rm -f scache_multi.tmp
+ehlo_mask_test: ehlo_mask ehlo_mask.in ehlo_mask.ref
+ ./ehlo_mask ehlo_mask.tmp
+ diff ehlo_mask.ref ehlo_mask.tmp
+ rm -f ehlo_mask.tmp
+
printfck: $(OBJS) $(PROG)
rm -rf printfck
mkdir printfck
@@ -619,6 +629,10 @@ dot_lockfile_as.o: dot_lockfile.h
dot_lockfile_as.o: ../../include/vstring.h
dot_lockfile_as.o: ../../include/vbuf.h
dot_lockfile_as.o: dot_lockfile_as.h
+ehlo_mask.o: ehlo_mask.c
+ehlo_mask.o: ../../include/sys_defs.h
+ehlo_mask.o: ../../include/name_mask.h
+ehlo_mask.o: ehlo_mask.h
ext_prop.o: ext_prop.c
ext_prop.o: ../../include/sys_defs.h
ext_prop.o: ../../include/name_mask.h
diff --git a/postfix/src/global/ehlo_mask.c b/postfix/src/global/ehlo_mask.c
new file mode 100644
index 000000000..ea42bf7c0
--- /dev/null
+++ b/postfix/src/global/ehlo_mask.c
@@ -0,0 +1,129 @@
+/*++
+/* NAME
+/* ehlo_mask 3
+/* SUMMARY
+/* map EHLO keywords to bit mask
+/* SYNOPSIS
+/* #include
+/*
+/* #define EHLO_MASK_8BITMIME (1<<0)
+/* #define EHLO_MASK_PIPELINING (1<<1)
+/* #define EHLO_MASK_SIZE (1<<2)
+/* #define EHLO_MASK_VRFY (1<<3)
+/* #define EHLO_MASK_ETRN (1<<4)
+/* #define EHLO_MASK_AUTH (1<<5)
+/* #define EHLO_MASK_VERP (1<<6)
+/* #define EHLO_MASK_STARTTLS (1<<7)
+/* #define EHLO_MASK_XCLIENT (1<<8)
+/* #define EHLO_MASK_XFORWARD (1<<9)
+/*
+/* int ehlo_mask(keyword_list)
+/* const char *keyword_list;
+/*
+/* const char *str_ehlo_mask(bitmask)
+/* int bitmask;
+/* DESCRIPTION
+/* ehlo_mask() computes the bit-wise OR of the masks that correspond
+/* to the names listed in the \fIkeyword_list\fR argument, separated by
+/* comma and/or whitespace characters. Undefined names are silently
+/* ignored.
+/*
+/* str_ehlo_mask() translates a mask into its equivalent names.
+/* The result is written to a static buffer that is overwritten
+/* upon each call. Undefined bits cause a fatal run-time error.
+/* DIAGNOSTICS
+/* Fatal: str_ehlo_mask() found an undefined bit.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library.*/
+
+#include
+
+/* Utility library. */
+
+#include
+
+/* Global library. */
+
+#include
+
+ /*
+ * The lookup table.
+ */
+static NAME_MASK ehlo_mask_table[] = {
+ "8BITMIME", EHLO_MASK_8BITMIME,
+ "AUTH", EHLO_MASK_AUTH,
+ "ETRN", EHLO_MASK_ETRN,
+ "PIPELINING", EHLO_MASK_PIPELINING,
+ "SIZE", EHLO_MASK_SIZE,
+ "VERP", EHLO_MASK_VERP,
+ "VRFY", EHLO_MASK_VRFY,
+ "XCLIENT", EHLO_MASK_XCLIENT,
+ "XFORWARD", EHLO_MASK_XFORWARD,
+ "STARTTLS", EHLO_MASK_STARTTLS,
+ 0,
+};
+
+/* ehlo_mask - string to bit mask */
+
+int ehlo_mask(const char *mask_str)
+{
+
+ /*
+ * We allow "STARTTLS" besides "starttls, because EHLO keywords are often
+ * spelled in uppercase. We ignore non-existent EHLO keywords so people
+ * can switch between Postfix versions without trouble.
+ */
+ return (name_mask_opt("ehlo string mask", ehlo_mask_table,
+ mask_str, NAME_MASK_ANY_CASE));
+}
+
+/* str_ehlo_mask - mask to string */
+
+const char *str_ehlo_mask(int mask_bits)
+{
+
+ /*
+ * We don't allow non-existent bits. Doing so makes no sense at this
+ * time.
+ */
+ return (str_name_mask_opt("ehlo bitmask", ehlo_mask_table,
+ mask_bits, NAME_MASK_NONE));
+}
+
+#ifdef TEST
+
+ /*
+ * Stand-alone test program.
+ */
+#include
+#include
+#include
+
+int main(int unused_argc, char **unused_argv)
+{
+ int mask_bits;
+ VSTRING *buf = vstring_alloc(1);
+ const char *mask_string;
+
+ while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) {
+ mask_bits = ehlo_mask(vstring_str(buf));
+ mask_string = str_ehlo_mask(mask_bits);
+ vstream_printf("%s -> 0x%x -> %s\n", vstring_str(buf), mask_bits,
+ mask_string);
+ vstream_fflush(VSTREAM_OUT);
+ }
+ vstring_free(buf);
+ exit(0);
+}
+
+#endif
diff --git a/postfix/src/global/ehlo_mask.h b/postfix/src/global/ehlo_mask.h
new file mode 100644
index 000000000..1ade500ad
--- /dev/null
+++ b/postfix/src/global/ehlo_mask.h
@@ -0,0 +1,43 @@
+#ifndef _EHLO_MASK_H_INCLUDED_
+#define _EHLO_MASK_H_INCLUDED_
+
+/*++
+/* NAME
+/* name_mask 3h
+/* SUMMARY
+/* map names to bit mask
+/* SYNOPSIS
+/* #include
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * External interface.
+ */
+#define EHLO_MASK_8BITMIME (1<<0) /* start of first byte */
+#define EHLO_MASK_PIPELINING (1<<1)
+#define EHLO_MASK_SIZE (1<<2)
+#define EHLO_MASK_VRFY (1<<3)
+#define EHLO_MASK_ETRN (1<<4)
+#define EHLO_MASK_AUTH (1<<5)
+#define EHLO_MASK_VERP (1<<6)
+#define EHLO_MASK_STARTTLS (1<<7)
+
+#define EHLO_MASK_XCLIENT (1<<8) /* start of second byte */
+#define EHLO_MASK_XFORWARD (1<<9)
+
+extern int ehlo_mask(const char *);
+extern const char *str_ehlo_mask(int);
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff --git a/postfix/src/global/ehlo_mask.in b/postfix/src/global/ehlo_mask.in
new file mode 100644
index 000000000..50fc24882
--- /dev/null
+++ b/postfix/src/global/ehlo_mask.in
@@ -0,0 +1,3 @@
+starttls, 8bitmime, verp, etrn, etrn
+foobar, auth, pipelining, size, vrfy
+xclient, xforward
diff --git a/postfix/src/global/ehlo_mask.ref b/postfix/src/global/ehlo_mask.ref
new file mode 100644
index 000000000..4c6dab355
--- /dev/null
+++ b/postfix/src/global/ehlo_mask.ref
@@ -0,0 +1,3 @@
+starttls, 8bitmime, verp, etrn, etrn -> 0x51 -> 8BITMIME ETRN VERP
+foobar, auth, pipelining, size, vrfy -> 0x2e -> AUTH PIPELINING SIZE VRFY
+xclient, xforward -> 0x180 -> XCLIENT XFORWARD
diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h
index 051560976..76aadc51b 100644
--- a/postfix/src/global/mail_params.h
+++ b/postfix/src/global/mail_params.h
@@ -2114,6 +2114,25 @@ extern char *var_remote_rwr_domain;
#endif
extern char *var_local_rwr_clients;
+ /*
+ * EHLO keyword filter.
+ */
+#define VAR_SMTPD_EHLO_DIS_WORDS "smtpd_disable_ehlo_keywords"
+#define DEF_SMTPD_EHLO_DIS_WORDS ""
+extern char *var_smtpd_ehlo_dis_words;
+
+#define VAR_SMTPD_EHLO_DIS_MAPS "smtpd_disable_ehlo_keyword_address_maps"
+#define DEF_SMTPD_EHLO_DIS_MAPS ""
+extern char *var_smtpd_ehlo_dis_maps;
+
+#define VAR_SMTP_EHLO_DIS_WORDS "smtp_disable_ehlo_keywords"
+#define DEF_SMTP_EHLO_DIS_WORDS ""
+extern char *var_smtp_ehlo_dis_words;
+
+#define VAR_SMTP_EHLO_DIS_MAPS "smtp_disable_ehlo_keyword_address_maps"
+#define DEF_SMTP_EHLO_DIS_MAPS ""
+extern char *var_smtp_ehlo_dis_maps;
+
/* LICENSE
/* .ad
/* .fi
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index bf355f8ed..395e08a9f 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.
*/
-#define MAIL_RELEASE_DATE "20041215"
+#define MAIL_RELEASE_DATE "20041218"
#define MAIL_VERSION_NUMBER "2.2"
#define VAR_MAIL_VERSION "mail_version"
diff --git a/postfix/src/local/command.c b/postfix/src/local/command.c
index 6bb68db8b..8241ff0ac 100644
--- a/postfix/src/local/command.c
+++ b/postfix/src/local/command.c
@@ -162,6 +162,18 @@ int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *comma
argv_add(env, "DOMAIN", state.msg_attr.domain, ARGV_END);
if (state.msg_attr.extension)
argv_add(env, "EXTENSION", state.msg_attr.extension, ARGV_END);
+
+#define EXPORT_REQUEST(name, value) \
+ if ((value)[0]) argv_add(env, (name), (value), ARGV_END);
+
+ EXPORT_REQUEST("CLIENT_HOSTNAME", state.msg_attr.request->client_name);
+ EXPORT_REQUEST("CLIENT_ADDRESS", state.msg_attr.request->client_addr);
+ EXPORT_REQUEST("CLIENT_HELO", state.msg_attr.request->client_helo);
+ EXPORT_REQUEST("CLIENT_PROTOCOL", state.msg_attr.request->client_proto);
+ EXPORT_REQUEST("SASL_METHOD", state.msg_attr.request->sasl_method);
+ EXPORT_REQUEST("SASL_SENDER", state.msg_attr.request->sasl_sender);
+ EXPORT_REQUEST("SASL_USERNAME", state.msg_attr.request->sasl_username);
+
argv_terminate(env);
/*
diff --git a/postfix/src/local/local.c b/postfix/src/local/local.c
index ab3ea4558..19465075d 100644
--- a/postfix/src/local/local.c
+++ b/postfix/src/local/local.c
@@ -197,6 +197,26 @@
/* .IP \fBSENDER\fR
/* The entire sender address.
/* .PP
+/* Additional remote client information is made available via
+/* the following pseudo variables:
+/* .IP \fBCLIENT_ADDRESS\fR
+/* Remote client network address. Available as of Postfix 2.2.
+/* .IP \fBCLIENT_HELO\fR
+/* Remote client EHLO command parameter. Available as of Postfix 2.2.
+/* .IP \fBCLIENT_HOSTNAME\fR
+/* Remote client hostname. Available as of Postfix 2.2.
+/* .IP \fBCLIENT_PROTOCOL\fR
+/* Remote client protocol. Available as of Postfix 2.2.
+/* .IP \fBSASL_METHOD\fR
+/* SASL authentication method specified in the
+/* remote client AUTH command. Available as of Postfix 2.2.
+/* .IP \fBSASL_SENDER\fR
+/* SASL sender address specified in the remote client MAIL
+/* FROM command. Available as of Postfix 2.2.
+/* .IP \fBSASL_USERNAME\fR
+/* SASL username specified in the remote client AUTH command.
+/* Available as of Postfix 2.2.
+/* .PP
/* The \fBPATH\fR environment variable is always reset to a
/* system-dependent default path, and environment variables
/* whose names are blessed by the \fBexport_environment\fR
diff --git a/postfix/src/local/local_expand.c b/postfix/src/local/local_expand.c
index 61806c96b..93bde453e 100644
--- a/postfix/src/local/local_expand.c
+++ b/postfix/src/local/local_expand.c
@@ -22,6 +22,14 @@
/* See mac_parse(3).
/* .PP
/* Attributes:
+/* .IP client_address
+/* The client network address.
+/* .IP client_helo
+/* The client HELO command parameter.
+/* .IP client_hostname
+/* The client hostname.
+/* .IP client_protocol
+/* The client protocol.
/* .IP domain
/* The recipient address domain.
/* .IP extension
@@ -36,6 +44,12 @@
/* The recipient delimiter.
/* .IP shell
/* The recipient shell program.
+/* .IP sasl_method
+/* The SASL authentication method.
+/* .IP sasl_sender
+/* The SASL MAIL FROM address.
+/* .IP sasl_username
+/* The SASL login name.
/* .IP user
/* The recipient user name.
/* .PP
@@ -122,6 +136,22 @@ static const char *local_expand_lookup(const char *name, int mode, char *ptr)
return (local->state->msg_attr.extension);
} else if (STREQ(name, "recipient_delimiter")) {
return (*var_rcpt_delim ? var_rcpt_delim : 0);
+#if 0
+ } else if (STREQ(name, "client_hostname")) {
+ return (local->state->msg_attr.request->client_name);
+ } else if (STREQ(name, "client_address")) {
+ return (local->state->msg_attr.request->client_addr);
+ } else if (STREQ(name, "client_protocol")) {
+ return (local->state->msg_attr.request->client_proto);
+ } else if (STREQ(name, "client_helo")) {
+ return (local->state->msg_attr.request->client_helo);
+ } else if (STREQ(name, "sasl_method")) {
+ return (local->state->msg_attr.request->sasl_method);
+ } else if (STREQ(name, "sasl_sender")) {
+ return (local->state->msg_attr.request->sasl_sender);
+ } else if (STREQ(name, "sasl_username")) {
+ return (local->state->msg_attr.request->sasl_username);
+#endif
} else {
return (0);
}
diff --git a/postfix/src/pipe/pipe.c b/postfix/src/pipe/pipe.c
index 0b6e3ca07..c518f66e1 100644
--- a/postfix/src/pipe/pipe.c
+++ b/postfix/src/pipe/pipe.c
@@ -131,6 +131,22 @@
/* $(\fIname\fR) are also recognized. Specify \fB$$\fR where a single
/* \fB$\fR is wanted.
/* .RS
+/* .IP \fB${\fBclient_address\fR}\fR
+/* This macro expands to the remote client network address.
+/* .sp
+/* This is available in Postfix 2.2 and later.
+/* .IP \fB${\fBclient_helo\fR}\fR
+/* This macro expands to the remote client HELO command parameter.
+/* .sp
+/* This is available in Postfix 2.2 and later.
+/* .IP \fB${\fBclient_hostname\fR}\fR
+/* This macro expands to the remote client hostname.
+/* .sp
+/* This is available in Postfix 2.2 and later.
+/* .IP \fB${\fBclient_protocol\fR}\fR
+/* This macro expands to the remote client protocol.
+/* .sp
+/* This is available in Postfix 2.2 and later.
/* .IP \fB${\fBextension\fR}\fR
/* This macro expands to the extension part of a recipient address.
/* For example, with an address \fIuser+foo@domain\fR the extension is
@@ -359,6 +375,10 @@
#define PIPE_DICT_EXTENSION "extension" /* key */
#define PIPE_DICT_MAILBOX "mailbox" /* key */
#define PIPE_DICT_SIZE "size" /* key */
+#define PIPE_DICT_CLIENT_ADDR "client_address" /* key */
+#define PIPE_DICT_CLIENT_NAME "client_hostname" /* key */
+#define PIPE_DICT_CLIENT_PROTO "client_protocol" /* key */
+#define PIPE_DICT_CLIENT_HELO "client_helo" /* key */
#define PIPE_DICT_SASL_METHOD "sasl_method" /* key */
#define PIPE_DICT_SASL_USERNAME "sasl_username" /* key */
#define PIPE_DICT_SASL_SENDER "sasl_sender" /* key */
@@ -443,6 +463,10 @@ static int parse_callback(int type, VSTRING *buf, char *context)
PIPE_DICT_EXTENSION, PIPE_FLAG_EXTENSION,
PIPE_DICT_MAILBOX, PIPE_FLAG_MAILBOX,
PIPE_DICT_SIZE, 0,
+ PIPE_DICT_CLIENT_ADDR, 0,
+ PIPE_DICT_CLIENT_NAME, 0,
+ PIPE_DICT_CLIENT_PROTO, 0,
+ PIPE_DICT_CLIENT_HELO, 0,
PIPE_DICT_SASL_METHOD, 0,
PIPE_DICT_SASL_USERNAME, 0,
PIPE_DICT_SASL_SENDER, 0,
@@ -994,9 +1018,17 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
dict_update(PIPE_DICT_TABLE, PIPE_DICT_NEXTHOP, request->nexthop);
vstring_sprintf(buf, "%ld", (long) request->data_size);
dict_update(PIPE_DICT_TABLE, PIPE_DICT_SIZE, STR(buf));
+ dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_ADDR,
+ request->client_addr);
+ dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_HELO,
+ request->client_helo);
+ dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_NAME,
+ request->client_name);
+ dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_PROTO,
+ request->client_proto);
dict_update(PIPE_DICT_TABLE, PIPE_DICT_SASL_METHOD,
request->sasl_method);
- dict_update(PIPE_DICT_TABLE, PIPE_DICT_SASL_USERNAME,
+ dict_update(PIPE_DICT_TABLE, PIPE_DICT_SASL_USERNAME,
request->sasl_username);
dict_update(PIPE_DICT_TABLE, PIPE_DICT_SASL_SENDER,
request->sasl_sender);
diff --git a/postfix/src/postcat/postcat.c b/postfix/src/postcat/postcat.c
index 935dc9c76..c9b6961ff 100644
--- a/postfix/src/postcat/postcat.c
+++ b/postfix/src/postcat/postcat.c
@@ -4,7 +4,7 @@
/* SUMMARY
/* show Postfix queue file contents
/* SYNOPSIS
-/* \fBpostcat\fR [\fB-vq\fR] [\fB-c \fIconfig_dir\fR] [\fIfiles\fR...]
+/* \fBpostcat\fR [\fB-oqv\fR] [\fB-c \fIconfig_dir\fR] [\fIfiles\fR...]
/* DESCRIPTION
/* The \fBpostcat\fR command prints the contents of the named
/* \fIfiles\fR in human-readable form. The files are expected
@@ -16,6 +16,8 @@
/* .IP "\fB-c \fIconfig_dir\fR"
/* The \fBmain.cf\fR configuration file is in the named directory
/* instead of the default configuration directory.
+/* .IP \fB-o\fR
+/* Print the queue file offset of each record.
/* .IP \fB-q\fR
/* Search the Postfix queue for the named \fIfiles\fR instead
/* of taking the names literally.
@@ -88,19 +90,21 @@
/* Application-specific. */
#define PC_FLAG_QUEUE (1<<0) /* search queue */
+#define PC_FLAG_OFFSET (1<<1) /* print record offsets */
#define STR vstring_str
#define LEN VSTRING_LEN
/* postcat - visualize Postfix queue file contents */
-static void postcat(VSTREAM *fp, VSTRING *buffer)
+static void postcat(VSTREAM *fp, VSTRING *buffer, int flags)
{
int prev_type = 0;
int rec_type;
time_t time;
int first = 1;
int ch;
+ off_t offset;
#define TEXT_RECORD(rec_type) \
(rec_type == REC_TYPE_CONT || rec_type == REC_TYPE_NORM)
@@ -120,6 +124,8 @@ static void postcat(VSTREAM *fp, VSTRING *buffer)
* Now look at the rest.
*/
for (;;) {
+ if (flags & PC_FLAG_OFFSET)
+ offset = vstream_ftell(fp);
rec_type = rec_get(fp, buffer, 0);
if (rec_type == REC_TYPE_ERROR)
msg_fatal("record read error");
@@ -129,8 +135,11 @@ static void postcat(VSTREAM *fp, VSTRING *buffer)
vstream_printf("*** ENVELOPE RECORDS %s ***\n", VSTREAM_PATH(fp));
first = 0;
}
- if (prev_type == REC_TYPE_CONT && !TEXT_RECORD(rec_type))
+ if ((prev_type == REC_TYPE_CONT && !TEXT_RECORD(rec_type))
+ || !(flags & PC_FLAG_OFFSET))
VSTREAM_PUTCHAR('\n');
+ if (flags & PC_FLAG_OFFSET)
+ vstream_printf("%9lu ", (unsigned long) offset);
switch (rec_type) {
case REC_TYPE_TIME:
case REC_TYPE_WARN:
@@ -218,12 +227,15 @@ int main(int argc, char **argv)
/*
* Parse JCL.
*/
- while ((ch = GETOPT(argc, argv, "c:qv")) > 0) {
+ while ((ch = GETOPT(argc, argv, "c:oqv")) > 0) {
switch (ch) {
case 'c':
if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
msg_fatal("out of memory");
break;
+ case 'o':
+ flags |= PC_FLAG_OFFSET;
+ break;
case 'q':
flags |= PC_FLAG_QUEUE;
break;
@@ -252,7 +264,7 @@ int main(int argc, char **argv)
vstream_control(VSTREAM_IN,
VSTREAM_CTL_PATH, "stdin",
VSTREAM_CTL_END);
- postcat(VSTREAM_IN, buffer);
+ postcat(VSTREAM_IN, buffer, flags);
}
/*
@@ -269,7 +281,7 @@ int main(int argc, char **argv)
fp = mail_queue_open(*cpp, argv[optind], O_RDONLY, 0);
if (fp == 0)
msg_fatal("open queue file %s: %m", argv[optind]);
- postcat(fp, buffer);
+ postcat(fp, buffer, flags);
if (vstream_fclose(fp))
msg_warn("close %s: %m", argv[optind]);
optind++;
@@ -283,7 +295,7 @@ int main(int argc, char **argv)
while (optind < argc) {
if ((fp = vstream_fopen(argv[optind], O_RDONLY, 0)) == 0)
msg_fatal("open %s: %m", argv[optind]);
- postcat(fp, buffer);
+ postcat(fp, buffer, flags);
if (vstream_fclose(fp))
msg_warn("close %s: %m", argv[optind]);
optind++;
diff --git a/postfix/src/smtp/Makefile.in b/postfix/src/smtp/Makefile.in
index b5ad1555e..ad9795994 100644
--- a/postfix/src/smtp/Makefile.in
+++ b/postfix/src/smtp/Makefile.in
@@ -80,6 +80,7 @@ smtp.o: ../../include/scache.h
smtp.o: ../../include/string_list.h
smtp.o: ../../include/match_list.h
smtp.o: ../../include/match_ops.h
+smtp.o: ../../include/maps.h
smtp.o: ../../include/mail_server.h
smtp.o: smtp.h
smtp.o: ../../include/htable.h
@@ -106,6 +107,8 @@ smtp_addr.o: ../../include/scache.h
smtp_addr.o: ../../include/string_list.h
smtp_addr.o: ../../include/match_list.h
smtp_addr.o: ../../include/match_ops.h
+smtp_addr.o: ../../include/maps.h
+smtp_addr.o: ../../include/dict.h
smtp_addr.o: smtp_addr.h
smtp_chat.o: smtp_chat.c
smtp_chat.o: ../../include/sys_defs.h
@@ -132,6 +135,8 @@ smtp_chat.o: ../../include/scache.h
smtp_chat.o: ../../include/string_list.h
smtp_chat.o: ../../include/match_list.h
smtp_chat.o: ../../include/match_ops.h
+smtp_chat.o: ../../include/maps.h
+smtp_chat.o: ../../include/dict.h
smtp_connect.o: smtp_connect.c
smtp_connect.o: ../../include/sys_defs.h
smtp_connect.o: ../../include/msg.h
@@ -163,6 +168,8 @@ smtp_connect.o: ../../include/scache.h
smtp_connect.o: ../../include/string_list.h
smtp_connect.o: ../../include/match_list.h
smtp_connect.o: ../../include/match_ops.h
+smtp_connect.o: ../../include/maps.h
+smtp_connect.o: ../../include/dict.h
smtp_connect.o: smtp_addr.h
smtp_connect.o: smtp_reuse.h
smtp_proto.o: smtp_proto.c
@@ -194,8 +201,11 @@ smtp_proto.o: ../../include/mail_proto.h
smtp_proto.o: ../../include/attr.h
smtp_proto.o: ../../include/mime_state.h
smtp_proto.o: ../../include/header_opts.h
-smtp_proto.o: smtp.h
+smtp_proto.o: ../../include/ehlo_mask.h
+smtp_proto.o: ../../include/maps.h
+smtp_proto.o: ../../include/dict.h
smtp_proto.o: ../../include/argv.h
+smtp_proto.o: smtp.h
smtp_proto.o: ../../include/htable.h
smtp_proto.o: ../../include/scache.h
smtp_proto.o: ../../include/string_list.h
@@ -219,6 +229,8 @@ smtp_rcpt.o: ../../include/scache.h
smtp_rcpt.o: ../../include/string_list.h
smtp_rcpt.o: ../../include/match_list.h
smtp_rcpt.o: ../../include/match_ops.h
+smtp_rcpt.o: ../../include/maps.h
+smtp_rcpt.o: ../../include/dict.h
smtp_reuse.o: smtp_reuse.c
smtp_reuse.o: ../../include/sys_defs.h
smtp_reuse.o: ../../include/msg.h
@@ -237,6 +249,8 @@ smtp_reuse.o: ../../include/recipient_list.h
smtp_reuse.o: ../../include/string_list.h
smtp_reuse.o: ../../include/match_list.h
smtp_reuse.o: ../../include/match_ops.h
+smtp_reuse.o: ../../include/maps.h
+smtp_reuse.o: ../../include/dict.h
smtp_reuse.o: smtp_reuse.h
smtp_reuse.o: ../../include/dns.h
smtp_sasl_glue.o: smtp_sasl_glue.c
@@ -280,6 +294,8 @@ smtp_sasl_proto.o: ../../include/scache.h
smtp_sasl_proto.o: ../../include/string_list.h
smtp_sasl_proto.o: ../../include/match_list.h
smtp_sasl_proto.o: ../../include/match_ops.h
+smtp_sasl_proto.o: ../../include/maps.h
+smtp_sasl_proto.o: ../../include/dict.h
smtp_sasl_proto.o: smtp_sasl.h
smtp_session.o: smtp_session.c
smtp_session.o: ../../include/sys_defs.h
@@ -302,6 +318,8 @@ smtp_session.o: ../../include/scache.h
smtp_session.o: ../../include/string_list.h
smtp_session.o: ../../include/match_list.h
smtp_session.o: ../../include/match_ops.h
+smtp_session.o: ../../include/maps.h
+smtp_session.o: ../../include/dict.h
smtp_state.o: smtp_state.c
smtp_state.o: ../../include/sys_defs.h
smtp_state.o: ../../include/mymalloc.h
@@ -318,6 +336,8 @@ smtp_state.o: ../../include/scache.h
smtp_state.o: ../../include/string_list.h
smtp_state.o: ../../include/match_list.h
smtp_state.o: ../../include/match_ops.h
+smtp_state.o: ../../include/maps.h
+smtp_state.o: ../../include/dict.h
smtp_state.o: smtp_sasl.h
smtp_trouble.o: smtp_trouble.c
smtp_trouble.o: ../../include/sys_defs.h
@@ -342,6 +362,8 @@ smtp_trouble.o: ../../include/scache.h
smtp_trouble.o: ../../include/string_list.h
smtp_trouble.o: ../../include/match_list.h
smtp_trouble.o: ../../include/match_ops.h
+smtp_trouble.o: ../../include/maps.h
+smtp_trouble.o: ../../include/dict.h
smtp_unalias.o: smtp_unalias.c
smtp_unalias.o: ../../include/sys_defs.h
smtp_unalias.o: ../../include/htable.h
@@ -358,3 +380,5 @@ smtp_unalias.o: ../../include/scache.h
smtp_unalias.o: ../../include/string_list.h
smtp_unalias.o: ../../include/match_list.h
smtp_unalias.o: ../../include/match_ops.h
+smtp_unalias.o: ../../include/maps.h
+smtp_unalias.o: ../../include/dict.h
diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c
index 9ffad1b5c..11d6d5031 100644
--- a/postfix/src/smtp/smtp.c
+++ b/postfix/src/smtp/smtp.c
@@ -110,6 +110,17 @@
/* .IP "\fBsmtp_skip_4xx_greeting (yes)\fR"
/* Skip SMTP servers that greet with a 4XX status code (go away, try
/* again later).
+/* .PP
+/* Available in Postfix version 2.2 and later:
+/* .IP "\fBsmtp_disable_ehlo_keyword_address_maps (empty)\fR"
+/* Lookup tables, indexed by the remote SMTP server address, with
+/* case insensitive lists of EHLO keywords (pipelining, starttls,
+/* auth, etc.) that the SMTP client will ignore in the EHLO response
+/* from a remote SMTP server.
+/* .IP "\fBsmtp_disable_ehlo_keywords (empty)\fR"
+/* A case insensitive list of EHLO keywords (pipelining, starttls,
+/* auth, etc.) that the SMTP client will ignore in the EHLO response
+/* from a remote SMTP server.
/* MIME PROCESSING CONTROLS
/* .ad
/* .fi
@@ -334,6 +345,7 @@
#include
#include
#include
+#include
/* Single server skeleton. */
@@ -389,6 +401,8 @@ int var_smtp_reuse_limit;
char *var_smtp_cache_dest;
char *var_scache_service;
bool var_smtp_cache_demand;
+char *var_smtp_ehlo_dis_words;
+char *var_smtp_ehlo_dis_maps;
/*
* Global variables. smtp_errno is set by the address lookup routines and by
@@ -398,6 +412,7 @@ int smtp_errno;
int smtp_host_lookup_mask;
STRING_LIST *smtp_cache_dest;
SCACHE *smtp_scache;
+MAPS *smtp_ehlo_disable_maps;
/* deliver_message - deliver message with extreme prejudice */
@@ -540,6 +555,14 @@ static void pre_init(char *unused_name, char **unused_argv)
*/
if (*var_smtp_cache_dest)
smtp_cache_dest = string_list_init(MATCH_FLAG_NONE, var_smtp_cache_dest);
+
+ /*
+ * EHLO keyword filter.
+ */
+ if (*var_smtp_ehlo_dis_maps)
+ smtp_ehlo_disable_maps = maps_create(VAR_SMTPD_EHLO_DIS_MAPS,
+ var_smtp_ehlo_dis_maps,
+ DICT_FLAG_LOCK);
}
/* pre_accept - see if tables have changed */
@@ -581,6 +604,8 @@ int main(int argc, char **argv)
VAR_SMTP_HOST_LOOKUP, DEF_SMTP_HOST_LOOKUP, &var_smtp_host_lookup, 1, 0,
VAR_SMTP_CACHE_DEST, DEF_SMTP_CACHE_DEST, &var_smtp_cache_dest, 0, 0,
VAR_SCACHE_SERVICE, DEF_SCACHE_SERVICE, &var_scache_service, 1, 0,
+ VAR_SMTP_EHLO_DIS_WORDS, DEF_SMTP_EHLO_DIS_WORDS, &var_smtp_ehlo_dis_words, 0, 0,
+ VAR_SMTP_EHLO_DIS_MAPS, DEF_SMTP_EHLO_DIS_MAPS, &var_smtp_ehlo_dis_maps, 0, 0,
0,
};
static CONFIG_TIME_TABLE time_table[] = {
diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h
index 6600c9658..d368b8c8d 100644
--- a/postfix/src/smtp/smtp.h
+++ b/postfix/src/smtp/smtp.h
@@ -30,6 +30,7 @@
#include
#include
#include
+#include
/*
* State information associated with each SMTP delivery request.
@@ -143,6 +144,8 @@ extern int smtp_host_lookup_mask; /* host lookup methods to use */
extern SCACHE *smtp_scache; /* connection cache instance */
extern STRING_LIST *smtp_cache_dest; /* cached destinations */
+extern MAPS *smtp_ehlo_disable_maps; /* ehlo keyword filter */
+
/*
* smtp_session.c
*/
diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c
index e90ac7095..e3fc7ca52 100644
--- a/postfix/src/smtp/smtp_proto.c
+++ b/postfix/src/smtp/smtp_proto.c
@@ -121,6 +121,8 @@
#include
#include
#include
+#include
+#include
/* Application-specific. */
@@ -227,6 +229,8 @@ int smtp_helo(SMTP_STATE *state, int misc_flags)
0, 0,
};
SOCKOPT_SIZE optlen;
+ const char *ehlo_words;
+ int disable_mask;
/*
* Prepare for disaster.
@@ -302,6 +306,17 @@ int smtp_helo(SMTP_STATE *state, int misc_flags)
return (0);
}
+ /*
+ * Determine what server EHLO keywords to ignore, typically to avoid
+ * inter-operability problems.
+ */
+ if (smtp_ehlo_disable_maps == 0
+ || (ehlo_words = maps_find(smtp_ehlo_disable_maps, state->session->addr, 0)) == 0)
+ ehlo_words = var_smtp_ehlo_dis_words;
+ disable_mask = ehlo_mask(ehlo_words);
+ if (disable_mask)
+ msg_info("disabled EHLO keywords: %s", str_ehlo_mask(disable_mask));
+
/*
* Pick up some useful features offered by the SMTP server. XXX Until we
* have a portable routine to convert from string to off_t with proper
@@ -315,29 +330,34 @@ int smtp_helo(SMTP_STATE *state, int misc_flags)
lines = resp->str;
while ((words = mystrtok(&lines, "\n")) != 0) {
if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t=")) != 0) {
- if (strcasecmp(word, "8BITMIME") == 0)
- session->features |= SMTP_FEATURE_8BITMIME;
- else if (strcasecmp(word, "PIPELINING") == 0)
- session->features |= SMTP_FEATURE_PIPELINING;
- else if (strcasecmp(word, "XFORWARD") == 0)
- while ((word = mystrtok(&words, " \t")) != 0)
- session->features |= name_code(xforward_features,
+ if (strcasecmp(word, "8BITMIME") == 0) {
+ if ((disable_mask & EHLO_MASK_8BITMIME) == 0)
+ session->features |= SMTP_FEATURE_8BITMIME;
+ } else if (strcasecmp(word, "PIPELINING") == 0) {
+ if ((disable_mask & EHLO_MASK_PIPELINING) == 0)
+ session->features |= SMTP_FEATURE_PIPELINING;
+ } else if (strcasecmp(word, "XFORWARD") == 0) {
+ if ((disable_mask & EHLO_MASK_XFORWARD) == 0)
+ while ((word = mystrtok(&words, " \t")) != 0)
+ session->features |= name_code(xforward_features,
NAME_CODE_FLAG_NONE, word);
- else if (strcasecmp(word, "SIZE") == 0) {
- session->features |= SMTP_FEATURE_SIZE;
- if ((word = mystrtok(&words, " \t")) != 0) {
- if (!alldig(word))
- msg_warn("bad size limit \"%s\" in EHLO reply from %s",
- word, session->namaddr);
- else
- session->size_limit = off_cvt_string(word);
+ } else if (strcasecmp(word, "SIZE") == 0) {
+ if ((disable_mask & EHLO_MASK_SIZE) == 0) {
+ session->features |= SMTP_FEATURE_SIZE;
+ if ((word = mystrtok(&words, " \t")) != 0) {
+ if (!alldig(word))
+ msg_warn("bad EHLO SIZE limit \"%s\" from %s",
+ word, session->namaddr);
+ else
+ session->size_limit = off_cvt_string(word);
+ }
}
- }
#ifdef USE_SASL_AUTH
- else if (var_smtp_sasl_enable && strcasecmp(word, "AUTH") == 0)
- smtp_sasl_helo_auth(session, words);
+ } else if (var_smtp_sasl_enable && strcasecmp(word, "AUTH") == 0) {
+ if ((disable_mask & EHLO_MASK_AUTH) == 0)
+ smtp_sasl_helo_auth(session, words);
#endif
- else if (strcasecmp(word, var_myhostname) == 0) {
+ } else if (strcasecmp(word, var_myhostname) == 0) {
if (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) {
msg_warn("host %s replied to HELO/EHLO with my own hostname %s",
session->namaddr, var_myhostname);
diff --git a/postfix/src/smtpd/Makefile.in b/postfix/src/smtpd/Makefile.in
index 62a97aa53..4229c19e2 100644
--- a/postfix/src/smtpd/Makefile.in
+++ b/postfix/src/smtpd/Makefile.in
@@ -149,8 +149,11 @@ smtpd.o: ../../include/quote_flags.h
smtpd.o: ../../include/lex_822.h
smtpd.o: ../../include/namadr_list.h
smtpd.o: ../../include/input_transp.h
+smtpd.o: ../../include/is_header.h
smtpd.o: ../../include/anvil_clnt.h
smtpd.o: ../../include/attr_clnt.h
+smtpd.o: ../../include/ehlo_mask.h
+smtpd.o: ../../include/maps.h
smtpd.o: ../../include/mail_server.h
smtpd.o: smtpd_token.h
smtpd.o: smtpd.h
diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c
index 91b449683..d5d54c4e2 100644
--- a/postfix/src/smtpd/smtpd.c
+++ b/postfix/src/smtpd/smtpd.c
@@ -86,6 +86,17 @@
/* access restriction is specified.
/* .IP "\fBsmtpd_sasl_exceptions_networks (empty)\fR"
/* What SMTP clients Postfix will not offer AUTH support to.
+/* .PP
+/* Available in Postfix version 2.2 and later:
+/* .IP "\fBsmtpd_disable_ehlo_keyword_address_maps (empty)\fR"
+/* Lookup tables, indexed by the remote SMTP client address, with
+/* case insensitive lists of EHLO keywords (pipelining, starttls,
+/* auth, etc.) that the SMTP server will not send in the EHLO response
+/* to a remote SMTP client.
+/* .IP "\fBsmtpd_disable_ehlo_keywords (empty)\fR"
+/* A case insensitive list of EHLO keywords (pipelining, starttls,
+/* auth, etc.) that the SMTP server will not send in the EHLO response
+/* to a remote SMTP client.
/* ADDRESS REWRITING CONTROLS
/* .ad
/* .fi
@@ -691,6 +702,8 @@
#include
#endif
#include
+#include /* ehlo filter */
+#include /* ehlo filter */
/* Single-threaded server skeleton. */
@@ -806,6 +819,8 @@ char *var_smtpd_hoggers;
#endif
char *var_local_rwr_clients;
+char *var_smtpd_ehlo_dis_words;
+char *var_smtpd_ehlo_dis_maps;
/*
* Silly little macros.
@@ -813,6 +828,11 @@ char *var_local_rwr_clients;
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
+ /*
+ * EHLO keyword filter
+ */
+static MAPS *ehlo_disable_maps;
+
/*
* VERP command name.
*/
@@ -955,6 +975,9 @@ static int helo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
char *err;
+ int todo_mask;
+ const char *ehlo_words;
+ VSTRING *ehlo_buf;
/*
* XXX 2821 new feature: Section 4.1.4 specifies that a server must clear
@@ -981,40 +1004,102 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
rcpt_reset(state);
state->helo_name = mystrdup(printable(argv[1].strval, '?'));
neuter(state->helo_name, NEUTER_CHARACTERS, '?');
+
+ /*
+ * XXX reject_unauth_pipelining depends on the following. If the user
+ * sends EHLO then we announce PIPELINING and we can't accuse them of
+ * using pipelining in places where it is allowed.
+ *
+ * XXX The reject_unauth_pipelining test needs to change and also account
+ * for mechanisms that disable PIPELINING selectively.
+ */
if (strcasecmp(state->protocol, MAIL_PROTO_ESMTP) != 0) {
myfree(state->protocol);
state->protocol = mystrdup(MAIL_PROTO_ESMTP);
}
- smtpd_chat_reply(state, "250-%s", var_myhostname);
- smtpd_chat_reply(state, "250-PIPELINING");
- if (var_message_limit)
- smtpd_chat_reply(state, "250-SIZE %lu",
- (unsigned long) var_message_limit); /* XXX */
- else
- smtpd_chat_reply(state, "250-SIZE");
- if (var_disable_vrfy_cmd == 0)
- smtpd_chat_reply(state, "250-VRFY");
- smtpd_chat_reply(state, "250-ETRN");
+
+ /*
+ * Determine what server EHLO keywords to suppress, typically to avoid
+ * inter-operability problems.
+ */
+ if (ehlo_disable_maps == 0
+ || (ehlo_words = maps_find(ehlo_disable_maps, state->addr, 0)) == 0)
+ ehlo_words = var_smtpd_ehlo_dis_words;
+ todo_mask = ~ehlo_mask(ehlo_words);
+ if (~todo_mask)
+ msg_info("disabled EHLO keywords: %s", str_ehlo_mask(~todo_mask));
+
+ /*
+ * Build the EHLO response, suppressing features as requested. We store
+ * each output line in one-element output queue, where it sits until we
+ * know if we need to prepend "250-" or "250 " to it. Each time we
+ * enqueue a reply line we flush the one that sits in the queue. We use a
+ * couple ugly macros to avoid making mistakes in code that repeats a
+ * lot.
+ */
+#define ENQUEUE_FIX_REPLY(state, ehlo_buf, cmd) \
+ do { \
+ smtpd_chat_reply((state), "250-%s", STR(ehlo_buf)); \
+ vstring_strcpy((ehlo_buf), (cmd)); \
+ } while (0)
+
+#define ENQUEUE_FMT_REPLY(state, ehlo_buf, fmt, arg) \
+ do { \
+ smtpd_chat_reply((state), "250-%s", STR(ehlo_buf)); \
+ vstring_sprintf((ehlo_buf), (fmt), (arg)); \
+ } while (0)
+
+ ehlo_buf = vstring_alloc(10);
+ vstring_strcpy(ehlo_buf, var_myhostname);
+ if (todo_mask & EHLO_MASK_PIPELINING)
+ ENQUEUE_FIX_REPLY(state, ehlo_buf, "PIPELINING");
+ if (todo_mask & EHLO_MASK_SIZE) {
+ if (var_message_limit)
+ ENQUEUE_FMT_REPLY(state, ehlo_buf, "SIZE %lu",
+ (unsigned long) var_message_limit); /* XXX */
+ else
+ ENQUEUE_FIX_REPLY(state, ehlo_buf, "SIZE");
+ }
+ if (todo_mask & EHLO_MASK_VRFY)
+ if (var_disable_vrfy_cmd == 0)
+ ENQUEUE_FIX_REPLY(state, ehlo_buf, "VRFY");
+ if (todo_mask & EHLO_MASK_ETRN)
+ ENQUEUE_FIX_REPLY(state, ehlo_buf, "ETRN");
#ifdef USE_SASL_AUTH
- if (var_smtpd_sasl_enable && !sasl_client_exception(state)) {
- smtpd_chat_reply(state, "250-AUTH %s", state->sasl_mechanism_list);
- if (var_broken_auth_clients)
- smtpd_chat_reply(state, "250-AUTH=%s", state->sasl_mechanism_list);
+ if (todo_mask & EHLO_MASK_AUTH) {
+ if (var_smtpd_sasl_enable && !sasl_client_exception(state)) {
+ ENQUEUE_FMT_REPLY(state, ehlo_buf, "AUTH %s",
+ state->sasl_mechanism_list);
+ if (var_broken_auth_clients)
+ ENQUEUE_FMT_REPLY(state, ehlo_buf, "AUTH=%s",
+ state->sasl_mechanism_list);
+ }
}
#endif
- if (namadr_list_match(verp_clients, state->name, state->addr))
- smtpd_chat_reply(state, "250-%s", VERP_CMD);
+ if (todo_mask & EHLO_MASK_VERP)
+ if (namadr_list_match(verp_clients, state->name, state->addr))
+ ENQUEUE_FIX_REPLY(state, ehlo_buf, VERP_CMD);
/* XCLIENT must not override its own access control. */
- if (xclient_allowed)
- smtpd_chat_reply(state, "250-" XCLIENT_CMD
- " " XCLIENT_NAME " " XCLIENT_ADDR
- " " XCLIENT_PROTO " " XCLIENT_HELO);
- if (xforward_allowed)
- smtpd_chat_reply(state, "250-" XFORWARD_CMD
- " " XFORWARD_NAME " " XFORWARD_ADDR
- " " XFORWARD_PROTO " " XFORWARD_HELO
- " " XFORWARD_DOMAIN);
- smtpd_chat_reply(state, "250 8BITMIME");
+ if (todo_mask & EHLO_MASK_XCLIENT)
+ if (xclient_allowed)
+ ENQUEUE_FIX_REPLY(state, ehlo_buf, XCLIENT_CMD
+ " " XCLIENT_NAME " " XCLIENT_ADDR
+ " " XCLIENT_PROTO " " XCLIENT_HELO);
+ if (todo_mask & EHLO_MASK_XFORWARD)
+ if (xforward_allowed)
+ ENQUEUE_FIX_REPLY(state, ehlo_buf, XFORWARD_CMD
+ " " XFORWARD_NAME " " XFORWARD_ADDR
+ " " XFORWARD_PROTO " " XFORWARD_HELO
+ " " XFORWARD_DOMAIN);
+ if (todo_mask & EHLO_MASK_8BITMIME)
+ ENQUEUE_FIX_REPLY(state, ehlo_buf, "8BITMIME");
+ smtpd_chat_reply(state, "250 %s", STR(ehlo_buf));
+
+ /*
+ * Clean up.
+ */
+ vstring_free(ehlo_buf);
+
return (0);
}
@@ -2801,6 +2886,14 @@ static void pre_jail_init(char *unused_name, char **unused_argv)
* flush client.
*/
flush_init();
+
+ /*
+ * EHLO keyword filter.
+ */
+ if (*var_smtpd_ehlo_dis_maps)
+ ehlo_disable_maps = maps_create(VAR_SMTPD_EHLO_DIS_MAPS,
+ var_smtpd_ehlo_dis_maps,
+ DICT_FLAG_LOCK);
}
/* post_jail_init - post-jail initialization */
@@ -2940,6 +3033,8 @@ int main(int argc, char **argv)
VAR_SMTPD_HOGGERS, DEF_SMTPD_HOGGERS, &var_smtpd_hoggers, 0, 0,
#endif
VAR_LOC_RWR_CLIENTS, DEF_LOC_RWR_CLIENTS, &var_local_rwr_clients, 0, 0,
+ VAR_SMTPD_EHLO_DIS_WORDS, DEF_SMTPD_EHLO_DIS_WORDS, &var_smtpd_ehlo_dis_words, 0, 0,
+ VAR_SMTPD_EHLO_DIS_MAPS, DEF_SMTPD_EHLO_DIS_MAPS, &var_smtpd_ehlo_dis_maps, 0, 0,
0,
};
static CONFIG_RAW_TABLE raw_table[] = {
diff --git a/postfix/src/util/name_mask.c b/postfix/src/util/name_mask.c
index 14e206a57..5695c41c6 100644
--- a/postfix/src/util/name_mask.c
+++ b/postfix/src/util/name_mask.c
@@ -15,6 +15,18 @@
/* const char *context;
/* NAME_MASK *table;
/* int mask;
+/*
+/* int name_mask_opt(context, table, names, flags)
+/* const char *context;
+/* NAME_MASK *table;
+/* const char *names;
+/* int flags;
+/*
+/* const char *str_name_mask_opt(context, table, mask, flags)
+/* const char *context;
+/* NAME_MASK *table;
+/* int mask;
+/* int flags;
/* DESCRIPTION
/* name_mask() takes a null-terminated \fItable\fR with (name, mask)
/* values and computes the bit-wise OR of the masks that correspond
@@ -25,12 +37,39 @@
/* The result is written to a static buffer that is overwritten
/* upon each call.
/*
-/* The \fIcontext\fR argument specifies what kind of names and
+/* name_mask_opt() and str_name_mask_opt() are extended versions
+/* with additional fine control.
+/*
+/* Arguments:
+/* .IP context
+/* What kind of names and
/* masks are being manipulated, in order to make error messages
/* more understandable. Typically, this would be the name of a
/* user-configurable parameter.
+/* .IP table
+/* Table with (name, bit mask) pairs.
+/* .IP names
+/* A list of names that is to be converted into a bit mask.
+/* .IP mask
+/* A bit mask.
+/* .IP flags
+/* Bit-wise OR of zero or more of the following:
+/* .RS
+/* .IP NAME_MASK_MATCH_REQ
+/* Require that all names listed in \fIname\fR exist in \fItable\fR,
+/* and that all bits listed in \fImask\fR exist in \fItable\fR.
+/* This feature is enabled by default when calling name_mask()
+/* or str_name_mask().
+/* .IP NAME_MASK_ANY_CASE
+/* Enable case-insensitive matching.
+/* This feature is not enabled by default when calling name_mask();
+/* it has no effect with str_name_mask().
+/* .RE
+/* The value NAME_MASK_NONE explicitly requests no features,
+/* and NAME_MASK_DEFAULT enables the default options.
/* DIAGNOSTICS
/* Fatal: the \fInames\fR argument specifies a name not found in
+/* \fItable\fR, or the \fImask\fR specifies a bit not found in
/* \fItable\fR.
/* LICENSE
/* .ad
@@ -48,6 +87,10 @@
#include
#include
+#ifdef STRCASECMP_IN_STRING_H
+#include
+#endif
+
/* Utility library. */
#include
@@ -58,9 +101,10 @@
#define STR(x) vstring_str(x)
-/* name_mask - compute mask corresponding to list of names */
+/* name_mask_opt - compute mask corresponding to list of names */
-int name_mask(const char *context, NAME_MASK *table, const char *names)
+int name_mask_opt(const char *context, NAME_MASK *table, const char *names,
+ int flags)
{
char *myname = "name_mask";
char *saved_names = mystrdup(names);
@@ -75,10 +119,14 @@ int name_mask(const char *context, NAME_MASK *table, const char *names)
*/
while ((name = mystrtok(&bp, ", \t\r\n")) != 0) {
for (np = table; /* void */ ; np++) {
- if (np->name == 0)
- msg_fatal("unknown %s value \"%s\" in \"%s\"",
- context, name, names);
- if (strcmp(name, np->name) == 0) {
+ if (np->name == 0) {
+ if (flags & NAME_MASK_MATCH_REQ)
+ msg_fatal("unknown %s value \"%s\" in \"%s\"",
+ context, name, names);
+ break;
+ }
+ if (((flags & NAME_MASK_ANY_CASE) ? strcasecmp : strcmp)
+ (name, np->name) == 0) {
if (msg_verbose)
msg_info("%s: %s", myname, name);
result |= np->mask;
@@ -90,9 +138,10 @@ int name_mask(const char *context, NAME_MASK *table, const char *names)
return (result);
}
-/* str_name_mask - mask to string */
+/* str_name_mask_opt - mask to string */
-const char *str_name_mask(const char *context, NAME_MASK *table, int mask)
+const char *str_name_mask_opt(const char *context, NAME_MASK *table,
+ int mask, int flags)
{
char *myname = "name_mask";
NAME_MASK *np;
@@ -105,8 +154,12 @@ const char *str_name_mask(const char *context, NAME_MASK *table, int mask)
VSTRING_RESET(buf);
for (np = table; mask != 0; np++) {
- if (np->name == 0)
- msg_panic("%s: invalid %s bitmask: 0x%x", myname, context, mask);
+ if (np->name == 0) {
+ if (flags & NAME_MASK_MATCH_REQ)
+ msg_fatal("%s: invalid %s bit in mask: 0x%x",
+ myname, context, mask);
+ break;
+ }
if (mask & np->mask) {
mask &= ~np->mask;
vstring_sprintf_append(buf, "%s ", np->name);
@@ -118,6 +171,22 @@ const char *str_name_mask(const char *context, NAME_MASK *table, int mask)
return (STR(buf));
}
+ /*
+ * ABI backwards compatibility.
+ */
+#undef name_mask
+#undef str_name_mask
+
+int name_mask(const char *context, NAME_MASK *table, const char *names)
+{
+ return(name_mask_opt(context, table,names, NAME_MASK_DEFAULT));
+}
+
+const char *str_name_mask(const char *context, NAME_MASK *table, int mask)
+{
+ return(str_name_mask_opt(context, table, mask, NAME_MASK_DEFAULT));
+}
+
#ifdef TEST
/*
diff --git a/postfix/src/util/name_mask.h b/postfix/src/util/name_mask.h
index fafd7cd88..d29f49b72 100644
--- a/postfix/src/util/name_mask.h
+++ b/postfix/src/util/name_mask.h
@@ -19,8 +19,19 @@ typedef struct {
int mask;
} NAME_MASK;
-extern int name_mask(const char *, NAME_MASK *, const char *);
-extern const char *str_name_mask(const char *, NAME_MASK *, int);
+#define NAME_MASK_MATCH_REQ (1<<0)
+#define NAME_MASK_ANY_CASE (1<<1)
+
+#define NAME_MASK_NONE 0
+#define NAME_MASK_DEFAULT (NAME_MASK_MATCH_REQ)
+
+#define name_mask(tag, table, str) \
+ name_mask_opt((tag), (table), (str), NAME_MASK_DEFAULT)
+#define str_name_mask(tag, table, mask) \
+ str_name_mask_opt((tag), (table), (mask), NAME_MASK_DEFAULT)
+
+extern int name_mask_opt(const char *, NAME_MASK *, const char *, int);
+extern const char *str_name_mask_opt(const char *, NAME_MASK *, int, int);
/* LICENSE
/* .ad