MX Policy test
+
@@ -206,7 +208,7 @@ whitelist for SMTP client IP addresses that have passed all
the tests described below. The postscreen_cache_map parameter
specifies the location of the temporary whitelist. The
temporary whitelist is not used for SMTP client addresses
-that appear on the permanent blacklist or whitelist.
+that appear on the permanent access list.
When the SMTP client address appears on the temporary
whitelist, postscreen(8) logs this with the client address and port
@@ -222,6 +224,62 @@ excluded from further tests until its temporary whitelist
entry expires, as controlled with the postscreen_*_ttl
parameters. Expired entries are silently renewed if possible.
+
+
+ When the remote SMTP client is not on the static access list
+or temporary whitelist, postscreen(8) can implement a number of
+whitelist tests before it grants the client a temporary whitelist
+status to talk to a Postfix SMTP server process.
+
+ By listening on both primary and backup MX addresses, postscreen(8)
+can deny the temporary whitelist status to clients that connect
+only to backup MX hosts (an old trick to take advantage of backup
+MX hosts with weaker anti-spam policies).
+
+ Note 1: The status of this feature is still experimental, and
+implementation details are likely to change.
+
+ Note 2: MX policy enforcement is currently supported only for
+domains with one Postfix MTA. Support for domains with multiple
+Postfix MTAs will have to wait until Postfix has a database client
+that can update a shared postscreen(8) database.
+
+
+
+-
First, configure the host to listen on both primary and
+backup MX addresses. Use the appropriate ifconfig command
+for the local operating system, or update the appropriate configuration
+files and "refresh" the network protocol stack.
+
+ -
Then, configure postscreen(8) to deny the temporary whitelist
+status on the backup MX address(es). An example for Wietse's
+server is:
+
+
+/etc/postfix/main.cf:
+ postscreen_whitelist_interfaces = !168.100.189.8 static:all
+
+
+ Translation: allow clients to obtain the temporary whitelist
+status on all server IP addresses except 168.100.189.8, which is a
+backup MX address.
+
+
+
+ When a non-whitelisted client connects the backup MX address,
+postscreen(8) logs this with the client address and port number as:
+
+
+
+ CONNECT from [address]:port to [168.100.189.8]:25
+ WHITELIST VETO [address]:port
+
+
+ Translation: the client at [address]:port connected to
+the backup MX address 168.100.189.8 while it was not whitelisted.
+The client will not be granted the temporary whitelist status, even
+if passes all the whitelist tests described below.
+
The postscreen_greet_wait parameter specifies a short time
@@ -754,7 +812,7 @@ For example:
/etc/postfix/main.cf:
- postscreen_dnsbl_reply_map = texthash:/etc/postfix/dnsbl_reply
+ postscreen_dnsbl_reply_map = texthash:/etc/postfix/dnsbl_reply
@@ -763,7 +821,7 @@ For example:
secret.zen.spamhaus.org zen.spamhaus.org
- The texthash: format is similar to hash: except that there is
+
The texthash: format is similar to hash: except that there is
no need to run postmap(1) before the file can be used, and that it
does not detect changes after the file is read. It is new with
Postfix version 2.8.
diff --git a/postfix/html/pgsql_table.5.html b/postfix/html/pgsql_table.5.html
index 8ec3d2d02..14c337a0d 100644
--- a/postfix/html/pgsql_table.5.html
+++ b/postfix/html/pgsql_table.5.html
@@ -88,7 +88,7 @@ PGSQL_TABLE(5) PGSQL_TABLE(5)
hosts The hosts that Postfix will try to connect to and
query from. Specify unix: for UNIX-domain sockets,
inet: for TCP connections (default). Example:
- hosts = host1.some.domain host2.some.domain
+ hosts = host1.some.domain host2.some.domain:port
hosts = unix:/file/name
The hosts are tried in random order, with all con-
diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html
index 16469b44d..891ea211d 100644
--- a/postfix/html/postconf.5.html
+++ b/postfix/html/postconf.5.html
@@ -666,7 +666,7 @@ $local_header_rewrite_cli
To get the behavior before Postfix version 2.2, specify
-"local_header_rewrite_clients = static:all".
+"local_header_rewrite_clients = static:all".
Example:
@@ -830,7 +830,7 @@ $local_header_rewrite_cli
To get the behavior before Postfix version 2.2, specify
-"local_header_rewrite_clients = static:all".
+"local_header_rewrite_clients = static:all".
@@ -867,7 +867,7 @@ $local_header_rewrite_cli
To get the behavior before Postfix version 2.2, specify
-"local_header_rewrite_clients = static:all".
+"local_header_rewrite_clients = static:all".
@@ -893,7 +893,7 @@ This feature is available in Postfix 2.1 and later.
authorized_flush_users
-(default: static:anyone)
+(default: static:anyone)
List of users who are authorized to flush the queue.
@@ -927,7 +927,7 @@ This feature is available in Postfix 2.2 and later.
authorized_mailq_users
-(default: static:anyone)
+(default: static:anyone)
List of users who are authorized to view the queue.
@@ -961,7 +961,7 @@ This feature is available in Postfix 2.2 and later.
authorized_submit_users
-(default: static:anyone)
+(default: static:anyone)
List of users who are authorized to submit mail with the sendmail(1)
@@ -992,7 +992,7 @@ Example:
-authorized_submit_users = !www, static:all
+authorized_submit_users = !www, static:all
@@ -1331,7 +1331,7 @@ $local_header_rewrite_cli
To get the behavior before Postfix version 2.2, specify
-"local_header_rewrite_clients = static:all".
+"local_header_rewrite_clients = static:all".
Examples:
@@ -4042,7 +4042,7 @@ configuration parameter. See there for details.
The LMTP-specific version of the smtp_per_record_deadline
configuration parameter. See there for details.
- This feature is available in Postfix 2.8 and later.
+ This feature is available in Postfix 2.9 and later.
@@ -4885,7 +4885,7 @@ header addresses.
-local_header_rewrite_clients = static:all
+local_header_rewrite_clients = static:all
@@ -5530,7 +5530,7 @@ $local_header_rewrite_cli
To get the behavior before Postfix version 2.2, specify
-"local_header_rewrite_clients = static:all".
+"local_header_rewrite_clients = static:all".
Example:
@@ -6988,16 +6988,16 @@ when it rejects mail. When no mapping is found, the actual DNSBL
domain will be used.
For maximal stability it is best to use a file that is read
-into memory such as pcre:, regexp: or texthash: (texthash: is similar
+into memory such as pcre:, regexp: or texthash: (texthash: is similar
to hash:, except a) there is no need to run postmap(1) before the
-file can be used, and b) texthash: does not detect changes after
+file can be used, and b) texthash: does not detect changes after
the file is read).
Example:
/etc/postfix/main.cf:
- postscreen_dnsbl_reply_map = texthash:/etc/postfix/dnsbl_reply
+ postscreen_dnsbl_reply_map = texthash:/etc/postfix/dnsbl_reply
@@ -7451,6 +7451,35 @@ one-letter suffix that specifies the time unit). Time units: s
This feature is available in Postfix 2.8.
+
+
+postscreen_whitelist_interfaces
+(default: static:all)
+
+ A list of local postscreen(8) server IP addresses where a
+non-whitelisted SMTP client can obtain postscreen(8)'s temporary
+whitelist status to talk to a Postfix SMTP server process. By
+default, a client can pass postscreen(8)'s whitelist tests on any
+local postscreen(8) server IP address.
+
+ When postscreen(8) listens on both primary and backup MX
+addresses, the postscreen_whitelist_interfaces parameter can be
+used to disable whitelisting on backup MX addresses. With this
+configuration, postscreen(8) denies whitelisting status to clients
+that connect only to backup MX addresses, and prevents them from
+talking to a Postfix SMTP server process.
+
+ Example:
+
+
+/etc/postfix/main.cf:
+ # Don't whitelist connections to the backup IP address.
+ postscreen_whitelist_interfaces = !168.100.189.8, static:all
+
+
+ This feature is available in Postfix 2.9 and later.
+
+
prepend_delivered_header
@@ -9924,7 +9953,7 @@ Examples:
smtp_sasl_mechanism_filter = plain, login
smtp_sasl_mechanism_filter = /etc/postfix/smtp_mechs
-smtp_sasl_mechanism_filter = !gssapi, !login, static:rest
+smtp_sasl_mechanism_filter = !gssapi, !login, static:rest
@@ -14914,7 +14943,7 @@ $local_header_rewrite_cli
To get the behavior before Postfix version 2.2, specify
-"local_header_rewrite_clients = static:all".
+"local_header_rewrite_clients = static:all".
Example:
diff --git a/postfix/html/postscreen.8.html b/postfix/html/postscreen.8.html
index 7a656ac26..3d5716590 100644
--- a/postfix/html/postscreen.8.html
+++ b/postfix/html/postscreen.8.html
@@ -156,6 +156,22 @@ POSTSCREEN(8) POSTSCREEN(8)
client is permanently blacklisted with the
postscreen_access_list parameter.
+MAIL EXCHANGER POLICY TESTS
+ When a remote SMTP client is not on the permanent access
+ list, postscreen(8) can implement a number of whitelist
+ tests before it grants the client a temporary whitelist
+ status to talk to a Postfix SMTP server process.
+
+ By listening on both primary and backup MX addresses,
+ postscreen(8) can deny the temporary whitelist status to
+ clients that connect only to backup MX hosts.
+
+ postscreen_whitelist_interfaces (static:all)
+ A list of local postscreen(8) server IP addresses
+ where a non-whitelisted SMTP client can obtain
+ postscreen(8)'s temporary whitelist status to talk
+ to a Postfix SMTP server process.
+
BEFORE-GREETING TESTS
These tests are executed before the remote SMTP client
receives the "220 servername" greeting. If no tests remain
diff --git a/postfix/man/man5/pgsql_table.5 b/postfix/man/man5/pgsql_table.5
index 3c99dc7e6..ce072f2fc 100644
--- a/postfix/man/man5/pgsql_table.5
+++ b/postfix/man/man5/pgsql_table.5
@@ -103,7 +103,7 @@ The hosts that Postfix will try to connect to and query from.
Specify \fIunix:\fR for UNIX-domain sockets, \fIinet:\fR for TCP
connections (default). Example:
.nf
- hosts = host1.some.domain host2.some.domain
+ hosts = host1.some.domain host2.some.domain:port
hosts = unix:/file/name
.fi
diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5
index 600f530f1..b07bbd1df 100644
--- a/postfix/man/man5/postconf.5
+++ b/postfix/man/man5/postconf.5
@@ -2240,7 +2240,7 @@ This feature is available in Postfix 2.5 and later.
The LMTP-specific version of the smtp_per_record_deadline
configuration parameter. See there for details.
.PP
-This feature is available in Postfix 2.8 and later.
+This feature is available in Postfix 2.9 and later.
.SH lmtp_pix_workaround_delay_time (default: 10s)
The LMTP-specific version of the smtp_pix_workaround_delay_time
configuration parameter. See there for details.
@@ -4259,6 +4259,33 @@ one-letter suffix that specifies the time unit). Time units: s
(seconds), m (minutes), h (hours), d (days), w (weeks).
.PP
This feature is available in Postfix 2.8.
+.SH postscreen_whitelist_interfaces (default: static:all)
+A list of local \fBpostscreen\fR(8) server IP addresses where a
+non-whitelisted SMTP client can obtain \fBpostscreen\fR(8)'s temporary
+whitelist status to talk to a Postfix SMTP server process. By
+default, a client can pass \fBpostscreen\fR(8)'s whitelist tests on any
+local \fBpostscreen\fR(8) server IP address.
+.PP
+When \fBpostscreen\fR(8) listens on both primary and backup MX
+addresses, the postscreen_whitelist_interfaces parameter can be
+used to disable whitelisting on backup MX addresses. With this
+configuration, \fBpostscreen\fR(8) denies whitelisting status to clients
+that connect only to backup MX addresses, and prevents them from
+talking to a Postfix SMTP server process.
+.PP
+Example:
+.PP
+.nf
+.na
+.ft C
+/etc/postfix/main.cf:
+ # Don't whitelist connections to the backup IP address.
+ postscreen_whitelist_interfaces = !168.100.189.8, static:all
+.fi
+.ad
+.ft R
+.PP
+This feature is available in Postfix 2.9 and later.
.SH prepend_delivered_header (default: command, file, forward)
The message delivery contexts where the Postfix \fBlocal\fR(8) delivery
agent prepends a Delivered-To: message header with the address
diff --git a/postfix/man/man8/postscreen.8 b/postfix/man/man8/postscreen.8
index 97ee76847..e2f73bcae 100644
--- a/postfix/man/man8/postscreen.8
+++ b/postfix/man/man8/postscreen.8
@@ -161,6 +161,23 @@ Permanent white/blacklist for remote SMTP client IP addresses.
.IP "\fBpostscreen_blacklist_action (ignore)\fR"
The action that \fBpostscreen\fR(8) takes when an SMTP client is
permanently blacklisted with the postscreen_access_list parameter.
+.SH "MAIL EXCHANGER POLICY TESTS"
+.na
+.nf
+.ad
+.fi
+When a remote SMTP client is not on the permanent access
+list, \fBpostscreen\fR(8) can implement a number of whitelist
+tests before it grants the client a temporary whitelist
+status to talk to a Postfix SMTP server process.
+
+By listening on both primary and backup MX addresses,
+\fBpostscreen\fR(8) can deny the temporary whitelist status
+to clients that connect only to backup MX hosts.
+.IP "\fBpostscreen_whitelist_interfaces (static:all)\fR"
+A list of local \fBpostscreen\fR(8) server IP addresses where a
+non-whitelisted SMTP client can obtain \fBpostscreen\fR(8)'s temporary
+whitelist status to talk to a Postfix SMTP server process.
.SH "BEFORE-GREETING TESTS"
.na
.nf
diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink
index 416dd95d7..387d95255 100755
--- a/postfix/mantools/postlink
+++ b/postfix/mantools/postlink
@@ -962,6 +962,7 @@ while (<>) {
s;\bpostscreen_expansion_filter\b;$&;g;
s;\bpostscreen_reject_footer\b;$&;g;
s;\bpostscreen_command_filter\b;$&;g;
+ s;\bpostscreen_whitelist_interfaces\b;$&;g;
s;\btlsproxy_watchdog_timeout\b;$&;g;
s;\btlsproxy_enforce_tls\b;$&;g;
@@ -1051,7 +1052,9 @@ while (<>) {
s/\b(ldap):/$1<\/a>:/g;
s/\b(regexp):/$1<\/a>:/g;
s/\b(sqlite):/$1<\/a>:/g;
+ s/\b(static):/$1<\/a>:/g;
s/\b(tcp):/$1<\/a>:/g;
+ s/\b(texthash):/$1<\/a>:/g;
# Do nice links for smtp:host:port etc.
diff --git a/postfix/proto/POSTSCREEN_README.html b/postfix/proto/POSTSCREEN_README.html
index 287da6439..0d31c7d46 100644
--- a/postfix/proto/POSTSCREEN_README.html
+++ b/postfix/proto/POSTSCREEN_README.html
@@ -150,6 +150,8 @@ handling of known clients.
Temporary whitelist test
+ MX Policy test
+
@@ -206,7 +208,7 @@ whitelist for SMTP client IP addresses that have passed all
the tests described below. The postscreen_cache_map parameter
specifies the location of the temporary whitelist. The
temporary whitelist is not used for SMTP client addresses
-that appear on the permanent blacklist or whitelist.
+that appear on the permanent access list.
When the SMTP client address appears on the temporary
whitelist, postscreen(8) logs this with the client address and port
@@ -222,6 +224,62 @@ excluded from further tests until its temporary whitelist
entry expires, as controlled with the postscreen_*_ttl
parameters. Expired entries are silently renewed if possible.
+
+
+ When the remote SMTP client is not on the static access list
+or temporary whitelist, postscreen(8) can implement a number of
+whitelist tests before it grants the client a temporary whitelist
+status to talk to a Postfix SMTP server process.
+
+ By listening on both primary and backup MX addresses, postscreen(8)
+can deny the temporary whitelist status to clients that connect
+only to backup MX hosts (an old trick to take advantage of backup
+MX hosts with weaker anti-spam policies).
+
+ Note 1: The status of this feature is still experimental, and
+implementation details are likely to change.
+
+ Note 2: MX policy enforcement is currently supported only for
+domains with one Postfix MTA. Support for domains with multiple
+Postfix MTAs will have to wait until Postfix has a database client
+that can update a shared postscreen(8) database.
+
+
+
+-
First, configure the host to listen on both primary and
+backup MX addresses. Use the appropriate ifconfig command
+for the local operating system, or update the appropriate configuration
+files and "refresh" the network protocol stack.
+
+ -
Then, configure postscreen(8) to deny the temporary whitelist
+status on the backup MX address(es). An example for Wietse's
+server is:
+
+
+/etc/postfix/main.cf:
+ postscreen_whitelist_interfaces = !168.100.189.8 static:all
+
+
+ Translation: allow clients to obtain the temporary whitelist
+status on all server IP addresses except 168.100.189.8, which is a
+backup MX address.
+
+
+
+ When a non-whitelisted client connects the backup MX address,
+postscreen(8) logs this with the client address and port number as:
+
+
+
+ CONNECT from [address]:port to [168.100.189.8]:25
+ WHITELIST VETO [address]:port
+
+
+ Translation: the client at [address]:port connected to
+the backup MX address 168.100.189.8 while it was not whitelisted.
+The client will not be granted the temporary whitelist status, even
+if passes all the whitelist tests described below.
+
The postscreen_greet_wait parameter specifies a short time
diff --git a/postfix/proto/pgsql_table b/postfix/proto/pgsql_table
index c62190e65..fcb86ab15 100644
--- a/postfix/proto/pgsql_table
+++ b/postfix/proto/pgsql_table
@@ -91,7 +91,7 @@
# Specify \fIunix:\fR for UNIX-domain sockets, \fIinet:\fR for TCP
# connections (default). Example:
# .nf
-# hosts = host1.some.domain host2.some.domain
+# hosts = host1.some.domain host2.some.domain:port
# hosts = unix:/file/name
# .fi
#
diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto
index 210f787fb..023051d99 100644
--- a/postfix/proto/postconf.proto
+++ b/postfix/proto/postconf.proto
@@ -14054,4 +14054,29 @@ Postfix releases, the behavior is as if this parameter is set to
The LMTP-specific version of the smtp_per_record_deadline
configuration parameter. See there for details.
- This feature is available in Postfix 2.8 and later.
+ This feature is available in Postfix 2.9 and later.
+
+%PARAM postscreen_whitelist_interfaces static:all
+
+ A list of local postscreen(8) server IP addresses where a
+non-whitelisted SMTP client can obtain postscreen(8)'s temporary
+whitelist status to talk to a Postfix SMTP server process. By
+default, a client can pass postscreen(8)'s whitelist tests on any
+local postscreen(8) server IP address.
+
+ When postscreen(8) listens on both primary and backup MX
+addresses, the postscreen_whitelist_interfaces parameter can be
+used to disable whitelisting on backup MX addresses. With this
+configuration, postscreen(8) denies whitelisting status to clients
+that connect only to backup MX addresses, and prevents them from
+talking to a Postfix SMTP server process.
+
+ Example:
+
+
+/etc/postfix/main.cf:
+ # Don't whitelist connections to the backup IP address.
+ postscreen_whitelist_interfaces = !168.100.189.8, static:all
+
+
+ This feature is available in Postfix 2.9 and later.
diff --git a/postfix/src/global/cfg_parser.c b/postfix/src/global/cfg_parser.c
index 5495c2994..73c81d025 100644
--- a/postfix/src/global/cfg_parser.c
+++ b/postfix/src/global/cfg_parser.c
@@ -30,6 +30,9 @@
/* const CFG_PARSER *parser;
/* const char *name;
/* int defval;
+/*
+/* DICT_OWNER cfg_get_owner(parser)
+/* const CFG_PARSER *parser;
/* DESCRIPTION
/* This module implements utilities for parsing parameters defined
/* either as "\fIname\fR = \fBvalue\fR" in a file pointed to by
@@ -55,6 +58,8 @@
/* Conveniently, \fIcfg_get_str\fR returns \fBNULL\fR if
/* \fIdefval\fR is \fBNULL\fR and no value was found. The returned
/* string has to be freed by the caller if not \fBNULL\fR.
+/*
+/* cfg_get_owner() looks up the configuration file owner.
/* DIAGNOSTICS
/* Fatal errors: bad string length, malformed numerical value, malformed
/* boolean value.
@@ -222,6 +227,7 @@ CFG_PARSER *cfg_parser_alloc(const char *pname)
{
const char *myname = "cfg_parser_alloc";
CFG_PARSER *parser;
+ DICT *dict;
if (pname == 0 || *pname == 0)
msg_fatal("%s: null parser name", myname);
@@ -232,11 +238,16 @@ CFG_PARSER *cfg_parser_alloc(const char *pname)
parser->get_str = get_dict_str;
parser->get_int = get_dict_int;
parser->get_bool = get_dict_bool;
+ dict = dict_handle(parser->name);
} else {
parser->get_str = get_main_str;
parser->get_int = get_main_int;
parser->get_bool = get_main_bool;
+ dict = dict_handle(CONFIG_DICT); /* XXX Use proper API */
}
+ if (dict == 0)
+ msg_panic("%s: dict_handle failed", myname);
+ parser->owner = dict->owner;
return (parser);
}
diff --git a/postfix/src/global/cfg_parser.h b/postfix/src/global/cfg_parser.h
index 40544a24a..c3f8d9b3c 100644
--- a/postfix/src/global/cfg_parser.h
+++ b/postfix/src/global/cfg_parser.h
@@ -12,15 +12,20 @@
.nf
/*
- * External interface.
+ * Utility library.
*/
+#include
+ /*
+ * External interface.
+ */
typedef struct CFG_PARSER {
char *name;
char *(*get_str) (const struct CFG_PARSER *, const char *, const char *,
int, int);
int (*get_int) (const struct CFG_PARSER *, const char *, int, int, int);
int (*get_bool) (const struct CFG_PARSER *, const char *, int);
+ DICT_OWNER owner;
} CFG_PARSER;
extern CFG_PARSER *cfg_parser_alloc(const char *);
@@ -30,6 +35,8 @@ extern int cfg_get_int(const CFG_PARSER *, const char *, int, int, int);
extern int cfg_get_bool(const CFG_PARSER *, const char *, int);
extern CFG_PARSER *cfg_parser_free(CFG_PARSER *);
+#define cfg_get_owner(cfg) ((cfg)->owner)
+
/* LICENSE
/* .ad
/* .fi
diff --git a/postfix/src/global/dict_ldap.c b/postfix/src/global/dict_ldap.c
index f9df3c597..57be7dfec 100644
--- a/postfix/src/global/dict_ldap.c
+++ b/postfix/src/global/dict_ldap.c
@@ -1957,6 +1957,7 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
/*
* Return the new dict_ldap structure.
*/
+ dict_ldap->dict.owner = cfg_get_owner(dict_ldap->parser);
return (DICT_DEBUG (&dict_ldap->dict));
}
diff --git a/postfix/src/global/dict_mysql.c b/postfix/src/global/dict_mysql.c
index 27663ad1e..8425cd72a 100644
--- a/postfix/src/global/dict_mysql.c
+++ b/postfix/src/global/dict_mysql.c
@@ -667,6 +667,7 @@ DICT *dict_mysql_open(const char *name, int open_flags, int dict_flags)
dict_mysql->pldb = plmysql_init(dict_mysql->hosts);
if (dict_mysql->pldb == NULL)
msg_fatal("couldn't intialize pldb!\n");
+ dict_mysql->dict.owner = cfg_get_owner(dict_mysql->parser);
return (DICT_DEBUG (&dict_mysql->dict));
}
diff --git a/postfix/src/global/dict_pgsql.c b/postfix/src/global/dict_pgsql.c
index 9899cf4fc..c4c0e5fb8 100644
--- a/postfix/src/global/dict_pgsql.c
+++ b/postfix/src/global/dict_pgsql.c
@@ -764,7 +764,8 @@ DICT *dict_pgsql_open(const char *name, int open_flags, int dict_flags)
dict_pgsql->pldb = plpgsql_init(dict_pgsql->hosts);
if (dict_pgsql->pldb == NULL)
msg_fatal("couldn't intialize pldb!\n");
- return &dict_pgsql->dict;
+ dict_pgsql->dict.owner = cfg_get_owner(dict_pgsql->parser);
+ return (DICT_DEBUG(&dict_pgsql->dict));
}
/* plpgsql_init - initalize a PGSQL database */
diff --git a/postfix/src/global/dict_sqlite.c b/postfix/src/global/dict_sqlite.c
index c349930e3..697442592 100644
--- a/postfix/src/global/dict_sqlite.c
+++ b/postfix/src/global/dict_sqlite.c
@@ -317,6 +317,8 @@ DICT *dict_sqlite_open(const char *name, int open_flags, int dict_flags)
msg_fatal("%s:%s: Can't open database: %s\n",
DICT_TYPE_SQLITE, name, sqlite3_errmsg(dict_sqlite->db));
+ dict_sqlite->dict.owner = cfg_get_owner(dict_sqlite->parser);
+
return (DICT_DEBUG (&dict_sqlite->dict));
}
diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h
index 2403c437b..169a9100a 100644
--- a/postfix/src/global/mail_params.h
+++ b/postfix/src/global/mail_params.h
@@ -3441,6 +3441,10 @@ extern char *var_psc_cmd_filter;
#define DEF_PSC_ACL PSC_ACL_NAME_WL_MYNETWORKS
extern char *var_psc_acl;
+#define VAR_PSC_WLIST_IF "postscreen_whitelist_interfaces"
+#define DEF_PSC_WLIST_IF "static:all"
+extern char *var_psc_wlist_if;
+
#define VAR_DNSBLOG_SERVICE "dnsblog_service_name"
#define DEF_DNSBLOG_SERVICE MAIL_SERVICE_DNSBLOG
extern char *var_dnsblog_service;
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index 467671c23..782fff818 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20110219"
+#define MAIL_RELEASE_DATE "20110228"
#define MAIL_VERSION_NUMBER "2.9"
#ifdef SNAPSHOT
diff --git a/postfix/src/local/alias.c b/postfix/src/local/alias.c
index 0fc0b9fbf..966a5dcb0 100644
--- a/postfix/src/local/alias.c
+++ b/postfix/src/local/alias.c
@@ -100,27 +100,6 @@
#define NO 0
#define YES 1
-/* dict_owner - find out alias database owner */
-
-static uid_t dict_owner(char *table)
-{
- const char *myname = "dict_owner";
- DICT *dict;
- struct stat st;
-
- /*
- * This code sits here for now, but we may want to move it to the library
- * some time.
- */
- if ((dict = dict_handle(table)) == 0)
- msg_panic("%s: can't find dictionary: %s", myname, table);
- if (dict->stat_fd < 0)
- return (0);
- if (fstat(dict->stat_fd, &st) < 0)
- msg_fatal("%s: fstat dictionary %s: %m", myname, table);
- return (st.st_uid);
-}
-
/* deliver_alias - expand alias file entry */
int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
@@ -131,7 +110,6 @@ int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
char *saved_alias_result;
char *owner;
char **cpp;
- uid_t alias_uid;
struct mypasswd *alias_pwd;
VSTRING *canon_owner;
DICT *dict;
@@ -227,11 +205,20 @@ int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
* database is owned by root, otherwise it will use the rights of
* the alias database owner.
*/
- if ((alias_uid = dict_owner(*cpp)) == 0) {
+ if (dict->owner.status == DICT_OWNER_TRUSTED) {
alias_pwd = 0;
RESET_USER_ATTR(usr_attr, state.level);
} else {
- if ((alias_pwd = mypwuid(alias_uid)) == 0) {
+ if (dict->owner.status == DICT_OWNER_UNKNOWN) {
+ msg_warn("%s: no owner UID for alias database %s",
+ myname, *cpp);
+ dsb_simple(state.msg_attr.why, "4.3.0",
+ "mail system configuration error");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ return (YES);
+ }
+ if ((alias_pwd = mypwuid(dict->owner.uid)) == 0) {
msg_warn("cannot find alias database owner for %s", *cpp);
dsb_simple(state.msg_attr.why, "4.3.0",
"cannot find alias database owner");
diff --git a/postfix/src/postscreen/postscreen.c b/postfix/src/postscreen/postscreen.c
index 4a41f7ff3..4776a274d 100644
--- a/postfix/src/postscreen/postscreen.c
+++ b/postfix/src/postscreen/postscreen.c
@@ -139,6 +139,21 @@
/* .IP "\fBpostscreen_blacklist_action (ignore)\fR"
/* The action that \fBpostscreen\fR(8) takes when an SMTP client is
/* permanently blacklisted with the postscreen_access_list parameter.
+/* MAIL EXCHANGER POLICY TESTS
+/* .ad
+/* .fi
+/* When a remote SMTP client is not on the permanent access
+/* list, \fBpostscreen\fR(8) can implement a number of whitelist
+/* tests before it grants the client a temporary whitelist
+/* status to talk to a Postfix SMTP server process.
+/*
+/* By listening on both primary and backup MX addresses,
+/* \fBpostscreen\fR(8) can deny the temporary whitelist status
+/* to clients that connect only to backup MX hosts.
+/* .IP "\fBpostscreen_whitelist_interfaces (static:all)\fR"
+/* A list of local \fBpostscreen\fR(8) server IP addresses where a
+/* non-whitelisted SMTP client can obtain \fBpostscreen\fR(8)'s temporary
+/* whitelist status to talk to a Postfix SMTP server process.
/* BEFORE-GREETING TESTS
/* .ad
/* .fi
@@ -460,6 +475,8 @@ int var_psc_cconn_limit;
char *var_smtpd_exp_filter;
char *var_psc_exp_filter;
+char *var_psc_wlist_if;
+
/*
* Global variables.
*/
@@ -491,6 +508,7 @@ HTABLE *psc_client_concurrency; /* per-client concurrency */
*/
static ARGV *psc_acl; /* permanent white/backlist */
static int psc_blist_action; /* PSC_ACT_DROP/ENFORCE/etc */
+static ADDR_MATCH_LIST *psc_wlist_if; /* whitelist interfaces */
/* psc_dump - dump some statistics before exit */
@@ -561,6 +579,8 @@ static void psc_service(VSTREAM *smtp_client_stream,
SOCKADDR_SIZE addr_storage_len = sizeof(addr_storage);
MAI_HOSTADDR_STR smtp_client_addr;
MAI_SERVPORT_STR smtp_client_port;
+ MAI_HOSTADDR_STR smtp_server_addr;
+ MAI_SERVPORT_STR smtp_server_port;
int aierr;
const char *stamp_str;
int saved_flags;
@@ -579,7 +599,12 @@ static void psc_service(VSTREAM *smtp_client_stream,
* connections so we have to invoke getpeername() to find out the remote
* address and port.
*/
+
+ /* Best effort - if this non-blocking write(2) fails, so be it. */
#define PSC_SERVICE_DISCONNECT_AND_RETURN(stream) do { \
+ (void) write(vstream_fileno(stream), \
+ "421 4.3.2 No system resources\r\n", \
+ sizeof("421 4.3.2 No system resources\r\n") - 1); \
event_server_disconnect(stream); \
return; \
} while (0);
@@ -590,10 +615,6 @@ static void psc_service(VSTREAM *smtp_client_stream,
if (getpeername(vstream_fileno(smtp_client_stream), (struct sockaddr *)
& addr_storage, &addr_storage_len) < 0) {
msg_warn("getpeername: %m -- dropping this connection");
- /* Best effort - if this non-blocking write(2) fails, so be it. */
- (void) write(vstream_fileno(smtp_client_stream),
- "421 4.3.2 No system resources\r\n",
- sizeof("421 4.3.2 No system resources\r\n") - 1);
PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream);
}
@@ -607,10 +628,6 @@ static void psc_service(VSTREAM *smtp_client_stream,
msg_warn("cannot convert client address/port to string: %s"
" -- dropping this connection",
MAI_STRERROR(aierr));
- /* Best effort - if this non-blocking write(2) fails, so be it. */
- (void) write(vstream_fileno(smtp_client_stream),
- "421 4.3.2 No system resources\r\n",
- sizeof("421 4.3.2 No system resources\r\n") - 1);
PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream);
}
if (strncasecmp("::ffff:", smtp_client_addr.buf, 7) == 0)
@@ -621,7 +638,34 @@ static void psc_service(VSTREAM *smtp_client_stream,
myname, psc_post_queue_length, psc_check_queue_length,
smtp_client_addr.buf, smtp_client_port.buf);
- msg_info("CONNECT from [%s]:%s", smtp_client_addr.buf, smtp_client_port.buf);
+ /*
+ * Look up the local SMTP server address and port.
+ */
+ if (getsockname(vstream_fileno(smtp_client_stream), (struct sockaddr *)
+ & addr_storage, &addr_storage_len) < 0) {
+ msg_warn("getsockname: %m -- dropping this connection");
+ PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream);
+ }
+
+ /*
+ * Convert the local SMTP server address and port to printable form for
+ * logging and access control.
+ */
+ if ((aierr = sockaddr_to_hostaddr((struct sockaddr *) & addr_storage,
+ addr_storage_len, &smtp_server_addr,
+ &smtp_server_port, 0)) != 0) {
+ msg_warn("cannot convert server address/port to string: %s"
+ " -- dropping this connection",
+ MAI_STRERROR(aierr));
+ PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream);
+ }
+ if (strncasecmp("::ffff:", smtp_server_addr.buf, 7) == 0)
+ memmove(smtp_server_addr.buf, smtp_server_addr.buf + 7,
+ sizeof(smtp_server_addr.buf) - 7);
+
+ msg_info("CONNECT from [%s]:%s to [%s]:%s",
+ smtp_client_addr.buf, smtp_client_port.buf,
+ smtp_server_addr.buf, smtp_server_port.buf);
/*
* Bundle up all the loose session pieces. This zeroes all flags and time
@@ -734,6 +778,14 @@ static void psc_service(VSTREAM *smtp_client_stream,
myname, psc_print_state_flags(state->flags, myname));
}
+ /*
+ * Don't whitelist clients that connect to backup MX addresses.
+ */
+ if (addr_match_list_match(psc_wlist_if, smtp_server_addr.buf) == 0) {
+ state->flags |= (PSC_STATE_FLAG_WLIST_FAIL | PSC_STATE_FLAG_NOFORWARD);
+ msg_info("WHITELIST VETO [%s]:%s", PSC_CLIENT_ADDR_PORT(state));
+ }
+
/*
* Reply with 421 when we can't analyze more connections. That also means
* no deep protocol tests when the noforward flag is raised.
@@ -938,6 +990,7 @@ static void post_jail_init(char *unused_name, char **unused_argv)
var_psc_barlf_action)) < 0)
msg_fatal("bad %s value: %s", VAR_PSC_BARLF_ACTION,
var_psc_barlf_action);
+ psc_wlist_if = addr_match_list_init(MATCH_FLAG_NONE, var_psc_wlist_if);
/*
* Start the cache maintenance pseudo thread last. Early cleanup makes
@@ -1039,6 +1092,7 @@ int main(int argc, char **argv)
VAR_PSC_CMD_FILTER, DEF_PSC_CMD_FILTER, &var_psc_cmd_filter, 0, 0,
VAR_DNSBLOG_SERVICE, DEF_DNSBLOG_SERVICE, &var_dnsblog_service, 1, 0,
VAR_TLSPROXY_SERVICE, DEF_TLSPROXY_SERVICE, &var_tlsproxy_service, 1, 0,
+ VAR_PSC_WLIST_IF, DEF_PSC_WLIST_IF, &var_psc_wlist_if, 0, 0,
0,
};
static const CONFIG_INT_TABLE int_table[] = {
diff --git a/postfix/src/postscreen/postscreen.h b/postfix/src/postscreen/postscreen.h
index afea976ff..7a82b3697 100644
--- a/postfix/src/postscreen/postscreen.h
+++ b/postfix/src/postscreen/postscreen.h
@@ -82,6 +82,7 @@ typedef struct {
#define PSC_STATE_FLAG_BLIST_FAIL (1<<4) /* blacklisted */
#define PSC_STATE_FLAG_HANGUP (1<<5) /* NOT a test failure */
#define PSC_STATE_FLAG_CACHE_EXPIRED (1<<6) /* cache retention expired */
+#define PSC_STATE_FLAG_WLIST_FAIL (1<<7) /* do not whitelist */
/*
* Important: every MUMBLE_TODO flag must have a MUMBLE_PASS flag, such that
@@ -190,7 +191,8 @@ typedef struct {
*/
#define PSC_STATE_MASK_ANY_FAIL \
(PSC_STATE_FLAG_BLIST_FAIL | PSC_STATE_FLAG_PENAL_FAIL | \
- PSC_STATE_MASK_EARLY_FAIL | PSC_STATE_MASK_SMTPD_FAIL)
+ PSC_STATE_MASK_EARLY_FAIL | PSC_STATE_MASK_SMTPD_FAIL | \
+ PSC_STATE_FLAG_WLIST_FAIL)
#define PSC_STATE_MASK_ANY_PASS \
(PSC_STATE_MASK_EARLY_PASS | PSC_STATE_MASK_SMTPD_PASS)
diff --git a/postfix/src/postscreen/postscreen_state.c b/postfix/src/postscreen/postscreen_state.c
index fe189ffa2..51c3bf0c9 100644
--- a/postfix/src/postscreen/postscreen_state.c
+++ b/postfix/src/postscreen/postscreen_state.c
@@ -256,6 +256,7 @@ const char *psc_print_state_flags(int flags, const char *context)
"BLIST_FAIL", PSC_STATE_FLAG_BLIST_FAIL,
"HANGUP", PSC_STATE_FLAG_HANGUP,
"CACHE_EXPIRED", PSC_STATE_FLAG_CACHE_EXPIRED,
+ "WLIST_FAIL", PSC_STATE_FLAG_WLIST_FAIL,
"PENAL_UPDATE", PSC_STATE_FLAG_PENAL_UPDATE,
"PENAL_FAIL", PSC_STATE_FLAG_PENAL_FAIL,
diff --git a/postfix/src/smtpstone/smtp-sink.c b/postfix/src/smtpstone/smtp-sink.c
index af3e06abb..60750cc2d 100644
--- a/postfix/src/smtpstone/smtp-sink.c
+++ b/postfix/src/smtpstone/smtp-sink.c
@@ -386,7 +386,7 @@ static void do_stats(void)
static void hard_err_resp(SINK_STATE *state)
{
- smtp_printf(state->stream, hard_error_resp);
+ smtp_printf(state->stream, "%s", hard_error_resp);
smtp_flush(state->stream);
}
@@ -394,7 +394,7 @@ static void hard_err_resp(SINK_STATE *state)
static void soft_err_resp(SINK_STATE *state)
{
- smtp_printf(state->stream, soft_error_resp);
+ smtp_printf(state->stream, "%s", soft_error_resp);
smtp_flush(state->stream);
}
@@ -745,9 +745,9 @@ static void dot_resp_hard(SINK_STATE *state)
{
if (enable_lmtp) {
while (state->rcpts-- > 0) /* XXX this could block */
- smtp_printf(state->stream, hard_error_resp);
+ smtp_printf(state->stream, "%s", hard_error_resp);
} else {
- smtp_printf(state->stream, hard_error_resp);
+ smtp_printf(state->stream, "%s", hard_error_resp);
}
smtp_flush(state->stream);
}
@@ -758,9 +758,9 @@ static void dot_resp_soft(SINK_STATE *state)
{
if (enable_lmtp) {
while (state->rcpts-- > 0) /* XXX this could block */
- smtp_printf(state->stream, soft_error_resp);
+ smtp_printf(state->stream, "%s", soft_error_resp);
} else {
- smtp_printf(state->stream, soft_error_resp);
+ smtp_printf(state->stream, "%s", soft_error_resp);
}
smtp_flush(state->stream);
}
diff --git a/postfix/src/util/dict.c b/postfix/src/util/dict.c
index 10b2a3f6d..af41c7663 100644
--- a/postfix/src/util/dict.c
+++ b/postfix/src/util/dict.c
@@ -406,10 +406,13 @@ void dict_load_fp(const char *dict_name, VSTREAM *fp)
char *val;
int lineno;
const char *err;
+ struct stat st;
buf = vstring_alloc(100);
lineno = 0;
+ if (fstat(vstream_fileno(fp), &st) < 0)
+ msg_fatal("fstat %s: %m", VSTREAM_PATH(fp));
while (readlline(buf, fp, &lineno)) {
if ((err = split_nameval(STR(buf), &member, &val)) != 0)
msg_fatal("%s, line %d: %s: \"%s\"",
@@ -417,6 +420,8 @@ void dict_load_fp(const char *dict_name, VSTREAM *fp)
dict_update(dict_name, member, val);
}
vstring_free(buf);
+ dict_handle(dict_name)->owner.uid = st.st_uid;
+ dict_handle(dict_name)->owner.status = (st.st_uid != 0);
}
/* dict_eval_lookup - macro parser call-back routine */
diff --git a/postfix/src/util/dict.h b/postfix/src/util/dict.h
index 9829d28ef..fb9efb5fc 100644
--- a/postfix/src/util/dict.h
+++ b/postfix/src/util/dict.h
@@ -23,6 +23,18 @@
#include
#include
+ /*
+ * Provenance information.
+ */
+typedef struct DICT_OWNER {
+ int status; /* see below */
+ uid_t uid; /* use only if status == UNTRUSTED */
+} DICT_OWNER;
+
+#define DICT_OWNER_UNKNOWN (-1) /* ex: unauthenticated tcp, proxy */
+#define DICT_OWNER_TRUSTED (!1) /* ex: root-owned config file */
+#define DICT_OWNER_UNTRUSTED (!0) /* ex: non-root config file */
+
/*
* Generic dictionary interface - in reality, a dictionary extends this
* structure with private members to maintain internal state.
@@ -40,6 +52,7 @@ typedef struct DICT {
int stat_fd; /* change detection */
time_t mtime; /* mod time at open */
VSTRING *fold_buf; /* key folding buffer */
+ DICT_OWNER owner; /* provenance */
} DICT;
extern DICT *dict_alloc(const char *, const char *, ssize_t);
diff --git a/postfix/src/util/dict_alloc.c b/postfix/src/util/dict_alloc.c
index 9c08a9071..0f97d1b6e 100644
--- a/postfix/src/util/dict_alloc.c
+++ b/postfix/src/util/dict_alloc.c
@@ -121,6 +121,8 @@ DICT *dict_alloc(const char *dict_type, const char *dict_name, ssize_t size)
dict->stat_fd = -1;
dict->mtime = 0;
dict->fold_buf = 0;
+ dict->owner.status = DICT_OWNER_UNKNOWN;
+ dict->owner.uid = ~0;
return dict;
}
diff --git a/postfix/src/util/dict_cdb.c b/postfix/src/util/dict_cdb.c
index 05e6743f9..36437abc3 100644
--- a/postfix/src/util/dict_cdb.c
+++ b/postfix/src/util/dict_cdb.c
@@ -200,6 +200,8 @@ static DICT *dict_cdbq_open(const char *path, int dict_flags)
if (fstat(fd, &st) < 0)
msg_fatal("dict_dbq_open: fstat: %m");
dict_cdbq->dict.mtime = st.st_mtime;
+ dict_cdbq->dict.owner.uid = st.st_uid;
+ dict_cdbq->dict.owner.status = (st.st_uid != 0);
close_on_exec(fd, CLOSE_ON_EXEC);
/*
@@ -373,6 +375,8 @@ static DICT *dict_cdbm_open(const char *path, int dict_flags)
dict_cdbm->dict.update = dict_cdbm_update;
dict_cdbm->cdb_path = cdb_path;
dict_cdbm->tmp_path = tmp_path;
+ dict_cdbm->dict.owner.uid = st1.st_uid;
+ dict_cdbm->dict.owner.status = (st1.st_uid != 0);
close_on_exec(fd, CLOSE_ON_EXEC);
/*
diff --git a/postfix/src/util/dict_cidr.c b/postfix/src/util/dict_cidr.c
index 6f12f9f87..82480119d 100644
--- a/postfix/src/util/dict_cidr.c
+++ b/postfix/src/util/dict_cidr.c
@@ -32,6 +32,7 @@
/* System library. */
#include
+#include
#include
#include
#include
@@ -164,6 +165,7 @@ DICT *dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
{
DICT_CIDR *dict_cidr;
VSTREAM *map_fp;
+ struct stat st;
VSTRING *line_buffer = vstring_alloc(100);
VSTRING *why = vstring_alloc(100);
DICT_CIDR_ENTRY *rule;
@@ -190,6 +192,10 @@ DICT *dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
msg_fatal("open %s: %m", mapname);
+ if (fstat(vstream_fileno(map_fp), &st) < 0)
+ msg_fatal("fstat %s: %m", mapname);
+ dict_cidr->dict.owner.uid = st.st_uid;
+ dict_cidr->dict.owner.status = (st.st_uid != 0);
while (readlline(line_buffer, map_fp, &lineno)) {
rule = dict_cidr_parse_rule(vstring_str(line_buffer), why);
diff --git a/postfix/src/util/dict_db.c b/postfix/src/util/dict_db.c
index c827b8d20..7fb00985a 100644
--- a/postfix/src/util/dict_db.c
+++ b/postfix/src/util/dict_db.c
@@ -705,6 +705,8 @@ static DICT *dict_db_open(const char *class, const char *path, int open_flags,
if (fstat(dict_db->dict.stat_fd, &st) < 0)
msg_fatal("dict_db_open: fstat: %m");
dict_db->dict.mtime = st.st_mtime;
+ dict_db->dict.owner.uid = st.st_uid;
+ dict_db->dict.owner.status = (st.st_uid != 0);
/*
* Warn if the source file is newer than the indexed file, except when
diff --git a/postfix/src/util/dict_dbm.c b/postfix/src/util/dict_dbm.c
index 3603e44de..0a0a256a1 100644
--- a/postfix/src/util/dict_dbm.c
+++ b/postfix/src/util/dict_dbm.c
@@ -169,7 +169,6 @@ static void dict_dbm_update(DICT *dict, const char *name, const char *value)
vstring_strcpy(dict->fold_buf, name);
name = lowercase(vstring_str(dict->fold_buf));
}
-
dbm_key.dptr = (void *) name;
dbm_value.dptr = (void *) value;
dbm_key.dsize = strlen(name);
@@ -449,6 +448,8 @@ DICT *dict_dbm_open(const char *path, int open_flags, int dict_flags)
if (fstat(dict_dbm->dict.stat_fd, &st) < 0)
msg_fatal("dict_dbm_open: fstat: %m");
dict_dbm->dict.mtime = st.st_mtime;
+ dict_dbm->dict.owner.uid = st.st_uid;
+ dict_dbm->dict.owner.status = (st.st_uid != 0);
/*
* Warn if the source file is newer than the indexed file, except when
diff --git a/postfix/src/util/dict_env.c b/postfix/src/util/dict_env.c
index b1a2a01df..bc15d6c92 100644
--- a/postfix/src/util/dict_env.c
+++ b/postfix/src/util/dict_env.c
@@ -104,5 +104,6 @@ DICT *dict_env_open(const char *name, int unused_flags, int dict_flags)
dict->flags = dict_flags | DICT_FLAG_FIXED;
if (dict_flags & DICT_FLAG_FOLD_FIX)
dict->fold_buf = vstring_alloc(10);
+ dict->owner.status = DICT_OWNER_TRUSTED;
return (DICT_DEBUG (dict));
}
diff --git a/postfix/src/util/dict_ht.c b/postfix/src/util/dict_ht.c
index 657f1fc38..0eff0810a 100644
--- a/postfix/src/util/dict_ht.c
+++ b/postfix/src/util/dict_ht.c
@@ -143,5 +143,6 @@ DICT *dict_ht_open(const char *name, int unused_open_flags, int dict_flags)
if (dict_flags & DICT_FLAG_FOLD_FIX)
dict_ht->dict.fold_buf = vstring_alloc(10);
dict_ht->table = htable_create(0);
+ dict_ht->dict.owner.status = DICT_OWNER_TRUSTED;
return (&dict_ht->dict);
}
diff --git a/postfix/src/util/dict_ni.c b/postfix/src/util/dict_ni.c
index e9bbd0f6e..569188c0c 100644
--- a/postfix/src/util/dict_ni.c
+++ b/postfix/src/util/dict_ni.c
@@ -185,6 +185,7 @@ DICT *dict_ni_open(const char *path, int unused_flags, int dict_flags)
d->dict.flags = dict_flags | DICT_FLAG_FIXED;
if (dict_flags & DICT_FLAG_FOLD_FIX)
d->dict.fold_buf = vstring_alloc(10);
+ d->dict.owner.status = DICT_OWNER_TRUSTED;
return (DICT_DEBUG (&d->dict));
}
diff --git a/postfix/src/util/dict_nis.c b/postfix/src/util/dict_nis.c
index 552f550b1..27a20a54e 100644
--- a/postfix/src/util/dict_nis.c
+++ b/postfix/src/util/dict_nis.c
@@ -238,6 +238,7 @@ DICT *dict_nis_open(const char *map, int open_flags, int dict_flags)
dict_nis->dict.fold_buf = vstring_alloc(10);
if (dict_nis_domain == 0)
dict_nis_init();
+ dict_nis->dict.owner.status = DICT_OWNER_TRUSTED;
return (DICT_DEBUG (&dict_nis->dict));
}
diff --git a/postfix/src/util/dict_nisplus.c b/postfix/src/util/dict_nisplus.c
index 52ca213f4..c10fa45b9 100644
--- a/postfix/src/util/dict_nisplus.c
+++ b/postfix/src/util/dict_nisplus.c
@@ -270,6 +270,7 @@ DICT *dict_nisplus_open(const char *map, int open_flags, int dict_flags)
dict_nisplus->dict.flags = dict_flags | DICT_FLAG_FIXED;
if (dict_flags & DICT_FLAG_FOLD_FIX)
dict_nisplus->dict.fold_buf = vstring_alloc(10);
+ dict_nisplus->dict.owner.status = DICT_OWNER_TRUSTED;
/*
* Convert the query template into an indexed name and column number. The
diff --git a/postfix/src/util/dict_pcre.c b/postfix/src/util/dict_pcre.c
index 2f1f3906d..f7a692153 100644
--- a/postfix/src/util/dict_pcre.c
+++ b/postfix/src/util/dict_pcre.c
@@ -35,6 +35,7 @@
/* System library. */
+#include
#include /* sprintf() prototype */
#include
#include
@@ -797,6 +798,7 @@ DICT *dict_pcre_open(const char *mapname, int unused_flags, int dict_flags)
{
DICT_PCRE *dict_pcre;
VSTREAM *map_fp;
+ struct stat st;
VSTRING *line_buffer;
DICT_PCRE_RULE *last_rule = 0;
DICT_PCRE_RULE *rule;
@@ -827,6 +829,10 @@ DICT *dict_pcre_open(const char *mapname, int unused_flags, int dict_flags)
*/
if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
msg_fatal("open %s: %m", mapname);
+ if (fstat(vstream_fileno(map_fp), &st) < 0)
+ msg_fatal("fstat %s: %m", mapname);
+ dict_pcre->dict.owner.uid = st.st_uid;
+ dict_pcre->dict.owner.status = (st.st_uid != 0);
while (readlline(line_buffer, map_fp, &lineno)) {
p = vstring_str(line_buffer);
diff --git a/postfix/src/util/dict_regexp.c b/postfix/src/util/dict_regexp.c
index f3cb1f9ba..c88d2bd55 100644
--- a/postfix/src/util/dict_regexp.c
+++ b/postfix/src/util/dict_regexp.c
@@ -39,6 +39,7 @@
#ifdef HAS_POSIX_REGEXP
+#include
#include
#include
#include
@@ -737,6 +738,7 @@ DICT *dict_regexp_open(const char *mapname, int unused_flags, int dict_flags)
{
DICT_REGEXP *dict_regexp;
VSTREAM *map_fp;
+ struct stat st;
VSTRING *line_buffer;
DICT_REGEXP_RULE *rule;
DICT_REGEXP_RULE *last_rule = 0;
@@ -763,6 +765,10 @@ DICT *dict_regexp_open(const char *mapname, int unused_flags, int dict_flags)
*/
if ((map_fp = vstream_fopen(mapname, O_RDONLY, 0)) == 0)
msg_fatal("open %s: %m", mapname);
+ if (fstat(vstream_fileno(map_fp), &st) < 0)
+ msg_fatal("fstat %s: %m", mapname);
+ dict_regexp->dict.owner.uid = st.st_uid;
+ dict_regexp->dict.owner.status = (st.st_uid != 0);
while (readlline(line_buffer, map_fp, &lineno)) {
p = vstring_str(line_buffer);
diff --git a/postfix/src/util/dict_sdbm.c b/postfix/src/util/dict_sdbm.c
index 85e6092f9..c6a374914 100644
--- a/postfix/src/util/dict_sdbm.c
+++ b/postfix/src/util/dict_sdbm.c
@@ -439,6 +439,8 @@ DICT *dict_sdbm_open(const char *path, int open_flags, int dict_flags)
if (fstat(dict_sdbm->dict.stat_fd, &st) < 0)
msg_fatal("dict_sdbm_open: fstat: %m");
dict_sdbm->dict.mtime = st.st_mtime;
+ dict_sdbm->dict.owner.uid = st.st_uid;
+ dict_sdbm->dict.owner.status = (st.st_uid != 0);
/*
* Warn if the source file is newer than the indexed file, except when
diff --git a/postfix/src/util/dict_static.c b/postfix/src/util/dict_static.c
index 346e166b9..7c9cabcb6 100644
--- a/postfix/src/util/dict_static.c
+++ b/postfix/src/util/dict_static.c
@@ -68,5 +68,6 @@ DICT *dict_static_open(const char *name, int unused_flags, int dict_flags)
dict->lookup = dict_static_lookup;
dict->close = dict_static_close;
dict->flags = dict_flags | DICT_FLAG_FIXED;
+ dict->owner.status = DICT_OWNER_TRUSTED;
return (DICT_DEBUG (dict));
}
diff --git a/postfix/src/util/dict_thash.c b/postfix/src/util/dict_thash.c
index c6fb98184..6379d2a80 100644
--- a/postfix/src/util/dict_thash.c
+++ b/postfix/src/util/dict_thash.c
@@ -259,6 +259,8 @@ DICT *dict_thash_open(const char *path, int open_flags, int dict_flags)
doze(300000);
}
vstring_free(line_buffer);
+ dict_thash->dict.owner.uid = st.st_uid;
+ dict_thash->dict.owner.status = (st.st_uid != 0);
return (DICT_DEBUG (&dict_thash->dict));
}
diff --git a/postfix/src/util/dict_unix.c b/postfix/src/util/dict_unix.c
index 8ba6188f8..66baba8eb 100644
--- a/postfix/src/util/dict_unix.c
+++ b/postfix/src/util/dict_unix.c
@@ -188,6 +188,7 @@ DICT *dict_unix_open(const char *map, int unused_flags, int dict_flags)
dict_unix->dict.flags = dict_flags | DICT_FLAG_FIXED;
if (dict_flags & DICT_FLAG_FOLD_FIX)
dict_unix->dict.fold_buf = vstring_alloc(10);
+ dict_unix->dict.owner.status = DICT_OWNER_TRUSTED;
return (DICT_DEBUG (&dict_unix->dict));
}
diff --git a/postfix/src/util/host_port.c b/postfix/src/util/host_port.c
index bfcad8c09..7cc932479 100644
--- a/postfix/src/util/host_port.c
+++ b/postfix/src/util/host_port.c
@@ -95,22 +95,41 @@
#include
+ /*
+ * Point-fix workaround. The libutil library should be email agnostic, but
+ * we can't rip up the library APIs in the stable releases.
+ */
+#include
+#ifdef STRCASECMP_IN_STRINGS_H
+#include
+#endif
+#define IPV6_COL "IPv6:" /* RFC 2821 */
+#define IPV6_COL_LEN (sizeof(IPV6_COL) - 1)
+#define HAS_IPV6_COL(str) (strncasecmp((str), IPV6_COL, IPV6_COL_LEN) == 0)
+
/* host_port - parse string into host and port, destroy string */
const char *host_port(char *buf, char **host, char *def_host,
char **port, char *def_service)
{
char *cp = buf;
+ int ipv6 = 0;
/*
* [host]:port, [host]:, [host].
+ * [ipv6:ipv6addr]:port, [ipv6:ipv6addr]:, [ipv6:ipv6addr].
*/
if (*cp == '[') {
- *host = ++cp;
+ ++cp;
+ if ((ipv6 = HAS_IPV6_COL(cp)) != 0)
+ cp += IPV6_COL_LEN;
+ *host = cp;
if ((cp = split_at(cp, ']')) == 0)
return ("missing \"]\"");
if (*cp && *cp++ != ':')
return ("garbage after \"]\"");
+ if (ipv6 && !valid_ipv6_hostaddr(*host, DONT_GRIPE))
+ return ("malformed IPv6 address");
*port = *cp ? cp : def_service;
}
diff --git a/postfix/src/util/host_port.in b/postfix/src/util/host_port.in
index 324892fe0..160822218 100644
--- a/postfix/src/util/host_port.in
+++ b/postfix/src/util/host_port.in
@@ -10,3 +10,7 @@ hhh:1pp
[hh.]
hh.
999
+[::1]
+[ipv6:::1]
+[ipv6:127.0.0.1]
+[ipv6:example.com]
diff --git a/postfix/src/util/host_port.ref b/postfix/src/util/host_port.ref
index 28c518f63..1d79745fa 100644
--- a/postfix/src/util/host_port.ref
+++ b/postfix/src/util/host_port.ref
@@ -20,3 +20,11 @@ unknown: warning: valid hostname or network address required in [hh.]
unknown: warning: valid hostname or network address required in hh.
>> 999
unknown: warning: valid hostname or network address required in 999
+>> [::1]
+host ::1 port default-service
+>> [ipv6:::1]
+host ::1 port default-service
+>> [ipv6:127.0.0.1]
+unknown: warning: malformed IPv6 address in [ipv6:127.0.0.1]
+>> [ipv6:example.com]
+unknown: warning: malformed IPv6 address in [ipv6:example.com]
diff --git a/postfix/src/util/sys_defs.h b/postfix/src/util/sys_defs.h
index f3d0eaa5b..90eb075d6 100644
--- a/postfix/src/util/sys_defs.h
+++ b/postfix/src/util/sys_defs.h
@@ -111,7 +111,8 @@
#define HAS_DUPLEX_PIPE /* 4.1 breaks with kqueue(2) */
#endif
-#if __FreeBSD_version >= 800107 /* safe; don't believe the experts */
+#if (__FreeBSD_version >= 702104 && __FreeBSD_version <= 800000) \
+ || __FreeBSD_version >= 800100
#define HAS_CLOSEFROM
#endif
diff --git a/postfix/src/util/vstream.c b/postfix/src/util/vstream.c
index 2abd0516c..fdb1b2101 100644
--- a/postfix/src/util/vstream.c
+++ b/postfix/src/util/vstream.c
@@ -1528,9 +1528,9 @@ ssize_t vstream_peek(VSTREAM *vp)
const char *vstream_peek_data(VSTREAM *vp)
{
if (vp->buf.flags & VSTREAM_FLAG_READ) {
- return (vp->buf.ptr);
+ return ((const char *) vp->buf.ptr);
} else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
- return (vp->read_buf.ptr);
+ return ((const char *) vp->read_buf.ptr);
} else {
return (0);
}