From 26e5f6287cfe765163473b4de77a377c235182ef Mon Sep 17 00:00:00 2001
From: Wietse Venema
Date: Sun, 18 Dec 2011 00:00:00 -0500
Subject: [PATCH] postfix-2.9-20111218
---
postfix/HISTORY | 19 +++
postfix/README_FILES/XCLIENT_README | 9 +-
postfix/RELEASE_NOTES | 15 +++
postfix/WISHLIST | 7 +-
postfix/html/XCLIENT_README.html | 10 +-
postfix/html/memcache_table.5.html | 116 +++++++++-------
postfix/man/man5/memcache_table.5 | 92 ++++++++-----
postfix/proto/XCLIENT_README.html | 10 +-
postfix/proto/memcache_table | 90 ++++++++-----
postfix/src/global/dict_memcache.c | 191 +++++++++++++++++++--------
postfix/src/global/mail_proto.h | 1 +
postfix/src/global/mail_version.h | 2 +-
postfix/src/smtpd/smtpd.c | 62 +++++----
postfix/src/smtpd/smtpd.h | 1 +
postfix/src/smtpd/smtpd_check.c | 20 +--
postfix/src/smtpd/smtpd_sasl_glue.c | 45 ++++++-
postfix/src/smtpd/smtpd_sasl_glue.h | 2 +
postfix/src/smtpd/smtpd_sasl_proto.c | 24 ++--
postfix/src/smtpd/smtpd_sasl_proto.h | 3 +
postfix/src/smtpd/smtpd_state.c | 6 +
20 files changed, 495 insertions(+), 230 deletions(-)
diff --git a/postfix/HISTORY b/postfix/HISTORY
index 7ee65cf81..6afc09ae0 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -17353,3 +17353,22 @@ Apologies for any names omitted.
Human factors: the postscreen/verify cache manager now logs
the full database name including the proxy: prefix, to avoid
WTF surprises. File: util/dict_cache.c.
+
+20111218
+
+ Cleanup: more configurable memcache client error handling.
+ Files: global/dict_memcache.c, proto/memcache_table.
+
+ Feature: the Postfix SMTP server XCLIENT command now supports
+ the LOGIN attribute (e.g., login information from nginx).
+ Based on the nginx:xclient-login-patch from citrin.ru (Anton
+ Yuzhis). The patch was further enhanced to support SASL
+ login information everywhere in the Postfix SMTP server
+ without having to specify "smtpd_sasl_auth_enable = yes"
+ in main.cf. Files: smtpd.[hc], smtpd_sasl_glue.[hc],
+ smtpd_check.c, smtpd_sasl_proto.[hc], smtpd_state.c,
+ proto/XCLIENT_README.html.
+
+ Incompatibility: the Postfix SMTP server now always checks
+ the smtpd_sender_login_maps table, even without having
+ "smtpd_sasl_auth_enable = yes" in main.cf.
diff --git a/postfix/README_FILES/XCLIENT_README b/postfix/README_FILES/XCLIENT_README
index e524e82bf..54258d6ca 100644
--- a/postfix/README_FILES/XCLIENT_README
+++ b/postfix/README_FILES/XCLIENT_README
@@ -50,7 +50,7 @@ are in fact case insensitive.
xclient-command = XCLIENT 1*( SP attribute-name"="attribute-value )
- attribute-name = ( NAME | ADDR | PORT | PROTO | HELO )
+ attribute-name = ( NAME | ADDR | PORT | PROTO | HELO | LOGIN )
attribute-value = xtext
@@ -73,6 +73,9 @@ are in fact case insensitive.
* The HELO attribute specifies an SMTP HELO parameter value, or the value
[UNAVAILABLE] when the information is unavailable.
+ * The LOGIN attribute specifies a SASL login name, or the value [UNAVAILABLE]
+ when the information is unavailable.
+
Note 1: syntactically valid NAME and HELO attribute-value elements can be up to
255 characters long. The client must not send XCLIENT commands that exceed the
512 character limit for SMTP commands. To avoid exceeding the limit the client
@@ -86,8 +89,8 @@ Note 3: Postfix implementations prior to version 2.3 do not xtext encode
attribute values. Servers that wish to interoperate with these older
implementations should be prepared to receive unencoded information.
-Note 4: Postfix implementations prior to version 2.5 do not implement the PORT
-attribute.
+Note 4: Some Postfix implementations do not implement the PORT or LOGIN
+attributes.
XXCCLLIIEENNTT SSeerrvveerr rreessppoonnssee
diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES
index 5bc1f69da..ab67e1f75 100644
--- a/postfix/RELEASE_NOTES
+++ b/postfix/RELEASE_NOTES
@@ -14,6 +14,21 @@ specifies the release date of a stable release or snapshot release.
If you upgrade from Postfix 2.7 or earlier, read RELEASE_NOTES-2.8
before proceeding.
+Incompatible changes with snapshot 201112XX
+===========================================
+
+To support external SASL authentication, the Postfix SMTP server
+now always checks the smtpd_sender_login_maps table, even without
+having "smtpd_sasl_auth_enable = yes" in main.cf.
+
+Major changes with snapshot 201112XX
+====================================
+
+Support for external SASL authentication via the XCLIENT command.
+This is used to accept SASL authentication from an SMTP proxy such
+as nginx. This support works even without having to specify
+"smtpd_sasl_auth_enable = yes" in main.cf.
+
Major changes with snapshot 20111213
====================================
diff --git a/postfix/WISHLIST b/postfix/WISHLIST
index 0daf63399..c38f721db 100644
--- a/postfix/WISHLIST
+++ b/postfix/WISHLIST
@@ -2,6 +2,11 @@ Wish list:
Things to do before the stable release:
+ Remove this file from the stable release.
+
+ limits on attribute string length in IPC protocols. 10-20KB
+ seems OK. We could start with limits enabled only in proxymap.
+
Either make all void dict_* operations return an error code,
or require that they reset dict_errno on entry, either exit
with a fatal error or set dict_errno on error.
@@ -25,8 +30,6 @@ Wish list:
by msg_warn and longjmp? The callers will have to specify
if they want the code to return instead of terminate.
- Remove this file from the stable release.
-
Things to do after the stable release:
What is the feasibility of adding an mta_name (personality)
diff --git a/postfix/html/XCLIENT_README.html b/postfix/html/XCLIENT_README.html
index 7b1f278ea..db2df6250 100644
--- a/postfix/html/XCLIENT_README.html
+++ b/postfix/html/XCLIENT_README.html
@@ -80,7 +80,7 @@ names are shown in upper case, they are in fact case insensitive.
xclient-command = XCLIENT 1*( SP attribute-name"="attribute-value )
- attribute-name = ( NAME | ADDR | PORT | PROTO | HELO )
+ attribute-name = ( NAME | ADDR | PORT | PROTO | HELO | LOGIN )
attribute-value = xtext
@@ -113,6 +113,10 @@ names are shown in upper case, they are in fact case insensitive.
value, or the value [UNAVAILABLE] when the information is
unavailable.
+ The LOGIN attribute specifies a SASL login name, or
+ the value [UNAVAILABLE] when the information is unavailable.
+
+
Note 1: syntactically valid NAME and HELO attribute-value
@@ -130,8 +134,8 @@ xtext encode attribute values. Servers that wish to interoperate
with these older implementations should be prepared to receive
unencoded information.
- Note 4: Postfix implementations prior to version 2.5 do not
-implement the PORT attribute.
+ Note 4: Some Postfix implementations do not implement the PORT
+or LOGIN attributes.
XCLIENT Server response
diff --git a/postfix/html/memcache_table.5.html b/postfix/html/memcache_table.5.html
index a2042e7c2..1dd8ba15f 100644
--- a/postfix/html/memcache_table.5.html
+++ b/postfix/html/memcache_table.5.html
@@ -34,11 +34,29 @@ MEMCACHE_TABLE(5) MEMCACHE_TABLE(5)
operation requires a backup database that supports the
operation.
-MEMCACHE PARAMETERS
- backup An optional Postfix database that provides persis-
- tent backup for the memcache database. The Postfix
- memcache client will update the memcache database
- whenever it looks up or changes information in the
+MEMCACHE MAIN PARAMETERS
+ memcache (default: inet:localhost:11211)
+ The memcache server (note: singular) that Postfix
+ will try to connect to. For a TCP server specify
+ "inet:" followed by a hostname or address, ":", and
+ a port name or number. Specify an IPv6 address
+ inside "[]". For a UNIX-domain server specify
+ "unix:" followed by the socket pathname. Examples:
+
+ memcache = inet:memcache.example.com:11211
+ memcache = inet:127.0.0.1:11211
+ memcache = inet:[fc00:8d00:189::3]:11211
+ memcache = unix:/path/to/socket
+
+ NOTE: to access a UNIX-domain socket with the prox-
+ ymap(8) server, the socket must be accessible by
+ the unprivileged postfix user.
+
+ backup (default: undefined)
+ An optional Postfix database that provides persis-
+ tent backup for the memcache database. The Postfix
+ memcache client will update the memcache database
+ whenever it looks up or changes information in the
persistent database. Specify a Postfix "type:table"
database. Examples:
@@ -51,40 +69,49 @@ MEMCACHE_TABLE(5) MEMCACHE_TABLE(5)
Access to remote proxymap servers is under develop-
ment.
- NOTE 1: When using memcache with persistent backup
- as postscreen(8) or verify(8) cache, disable auto-
- matic cache cleanup (*_cache_cleanup_interval = 0)
- in all Postfix instances except for one instance
+ NOTE 1: When using memcache with persistent backup
+ as postscreen(8) or verify(8) cache, disable auto-
+ matic cache cleanup (*_cache_cleanup_interval = 0)
+ in all Postfix instances except for one instance
that will be responsible for cache cleanup.
NOTE 2: In the case of a proxied database, the full
- database name (including the "proxy:" prefix) must
- be specified in the proxymap server's
- proxy_read_maps or proxy_write_maps setting
- (depending on whether the access is read-only or
+ database name (including the "proxy:" prefix) must
+ be specified in the proxymap server's
+ proxy_read_maps or proxy_write_maps setting
+ (depending on whether the access is read-only or
read-write).
- memcache (default: inet:localhost:11211)
- The memcache server (note: singular) that Postfix
- will try to connect to. For a TCP server specify
- "inet:" followed by a hostname or address, ":", and
- a port name or number. For a UNIX-domain server
- specify "unix:" followed by the socket pathname.
- Examples:
+ flags (default: 0)
+ Optional flags that should be stored along with a
+ memcache update.
- memcache = inet:memcache.example.com
- memcache = unix:/path/to/socket
+ ttl (default: 3600)
+ The expiration time in seconds of memcache updates.
+
+ NOTE 1: When using a memcache table as
+ postscreen(8) or verify(8) cache without persistent
+ backup, specify a zero *_cache_cleanup_interval
+ value with all Postfix instances that use the mem-
+ cache, and specify the largest postscreen(8) *_ttl
+ value or verify(8) *_expire_time value as the mem-
+ cache table's ttl value.
- NOTE: In the case of a UNIX-domain socket, it must
- be accessible by the unprivileged postfix user and
- by the memcached process.
+ NOTE 2: According to memcache protocol documenta-
+ tion, a value greater than 30 days (2592000 sec-
+ onds) specifies absolute UNIX time. Smaller values
+ are relative to the time of the update.
+MEMCACHE KEY PARAMETERS
key_format (default: %s)
- Format of the lookup and update keys in memcache
- queries. By default, these are the same as the
- lookup and update keys that are given to the Post-
+ Format of the lookup and update keys in memcache
+ requests. By default, these are the same as the
+ lookup and update keys that are given to the Post-
fix memcache client.
+ NOTE: The key_format feature is not used for backup
+ database requests.
+
When the same memcache database is used to cache
information from multiple tables, you can use the
key_format feature to avoid name collisions by
@@ -144,25 +171,24 @@ MEMCACHE_TABLE(5) MEMCACHE_TABLE(5)
domain = example.com, hash:/etc/postfix/searchdomains
- flags (default: 0)
- Optional flags that should be stored along with a
- memcache update.
+MEMCACHE ERROR CONTROLS
+ data_size_limit (default: 10240)
+ The maximal memcache reply data length in bytes.
- ttl (default: 3600)
- The expiration time in seconds of memcache updates.
+ line_size_limit (default: 1024)
+ The maximal memcache reply line length in bytes.
- NOTE 1: When using a memcache table as
- postscreen(8) or verify(8) cache without persistent
- backup, specify a zero *_cache_cleanup_interval
- value with all Postfix instances that use the mem-
- cache, and specify the largest postscreen(8) *_ttl
- value or verify(8) *_expire_time value as the mem-
- cache table's ttl value.
+ max_try (default: 2)
+ The number of times to try a memcache command
+ before giving up.
- NOTE 2: According to memcache protocol documenta-
- tion, a value greater than 30 days (2592000 sec-
- onds) specifies absolute UNIX time. Smaller values
- are relative to the time of the update.
+ retry_pause (default: 1)
+ The time in seconds to wait after a memcache com-
+ mand fails.
+
+ timeout (default: 2)
+ The time limit for sending a memcache command and
+ for receiving a memcache reply.
BUGS
The Postfix memcache client cannot be used for security-
@@ -178,7 +204,7 @@ MEMCACHE_TABLE(5) MEMCACHE_TABLE(5)
The Postfix memcache client requires additional configura-
tion when used as postscreen(8) or verify(8) cache. For
details see the backup and ttl parameter discussions in
- the MEMCACHE PARAMETERS section above.
+ the MEMCACHE MAIN PARAMETERS section above.
SEE ALSO
postmap(1), Postfix lookup table manager
diff --git a/postfix/man/man5/memcache_table.5 b/postfix/man/man5/memcache_table.5
index 87de7eccd..5ceb73d1d 100644
--- a/postfix/man/man5/memcache_table.5
+++ b/postfix/man/man5/memcache_table.5
@@ -34,12 +34,30 @@ The Postfix memcache client supports the lookup, update,
delete and sequence (first/next) operations. The sequence
operation requires a backup database that supports the
operation.
-.SH "MEMCACHE PARAMETERS"
+.SH "MEMCACHE MAIN PARAMETERS"
.na
.nf
.ad
.fi
-.IP \fBbackup\fR
+.IP "\fBmemcache (default: inet:localhost:11211)\fR"
+The memcache server (note: singular) that Postfix will try
+to connect to. For a TCP server specify "inet:" followed by
+a hostname or address, ":", and a port name or number.
+Specify an IPv6 address inside "[]".
+For a UNIX-domain server specify "unix:" followed by the
+socket pathname. Examples:
+
+.nf
+ memcache = inet:memcache.example.com:11211
+ memcache = inet:127.0.0.1:11211
+ memcache = inet:[fc00:8d00:189::3]:11211
+ memcache = unix:/path/to/socket
+.fi
+
+NOTE: to access a UNIX-domain socket with the proxymap(8)
+server, the socket must be accessible by the unprivileged
+postfix user.
+.IP "\fBbackup (default: undefined)\fR"
An optional Postfix database that provides persistent backup
for the memcache database. The Postfix memcache client will
update the memcache database whenever it looks up or changes
@@ -67,25 +85,36 @@ name (including the "proxy:" prefix) must be specified in
the proxymap server's proxy_read_maps or proxy_write_maps
setting (depending on whether the access is read-only or
read-write).
-.IP "\fBmemcache (default: inet:localhost:11211)\fR"
-The memcache server (note: singular) that Postfix will try
-to connect to. For a TCP server specify "inet:" followed by
-a hostname or address, ":", and a port name or number.
-For a UNIX-domain server specify "unix:" followed by the
-socket pathname. Examples:
+.IP "\fBflags (default: 0)\fR"
+Optional flags that should be stored along with a memcache
+update.
+.IP "\fBttl (default: 3600)\fR"
+The expiration time in seconds of memcache updates.
+NOTE 1: When using a memcache table as \fBpostscreen\fR(8)
+or \fBverify\fR(8) cache without persistent backup, specify
+a zero *_cache_cleanup_interval value with all Postfix
+instances that use the memcache, and specify the largest
+\fBpostscreen\fR(8) *_ttl value or \fBverify\fR(8) *_expire_time
+value as the memcache table's \fBttl\fR value.
+
+NOTE 2: According to memcache protocol documentation, a
+value greater than 30 days (2592000 seconds) specifies
+absolute UNIX
+time. Smaller values are relative to the time of the update.
+.SH "MEMCACHE KEY PARAMETERS"
+.na
.nf
- memcache = inet:memcache.example.com
- memcache = unix:/path/to/socket
+.ad
.fi
-
-NOTE: In the case of a UNIX-domain socket, it must be accessible
-by the unprivileged postfix user and by the memcached process.
.IP "\fBkey_format (default: %s)\fB"
-Format of the lookup and update keys in memcache queries.
+Format of the lookup and update keys in memcache requests.
By default, these are the same as the lookup and update
keys that are given to the Postfix memcache client.
+NOTE: The \fBkey_format\fR feature is not used for \fBbackup\fR
+database requests.
+
When the same memcache database is used to cache information
from multiple tables, you can use the \fBkey_format\fR
feature to avoid name collisions by prepending a fixed
@@ -141,23 +170,22 @@ are skipped with a warning). Example:
.nf
domain = example.com, hash:/etc/postfix/searchdomains
.fi
-.IP "\fBflags (default: 0)\fR"
-Optional flags that should be stored along with a memcache
-update.
-.IP "\fBttl (default: 3600)\fR"
-The expiration time in seconds of memcache updates.
-
-NOTE 1: When using a memcache table as \fBpostscreen\fR(8)
-or \fBverify\fR(8) cache without persistent backup, specify
-a zero *_cache_cleanup_interval value with all Postfix
-instances that use the memcache, and specify the largest
-\fBpostscreen\fR(8) *_ttl value or \fBverify\fR(8) *_expire_time
-value as the memcache table's \fBttl\fR value.
-
-NOTE 2: According to memcache protocol documentation, a
-value greater than 30 days (2592000 seconds) specifies
-absolute UNIX
-time. Smaller values are relative to the time of the update.
+.SH "MEMCACHE ERROR CONTROLS"
+.na
+.nf
+.ad
+.fi
+.IP "\fBdata_size_limit (default: 10240)\fR"
+The maximal memcache reply data length in bytes.
+.IP "\fBline_size_limit (default: 1024)\fR"
+The maximal memcache reply line length in bytes.
+.IP "\fBmax_try (default: 2)\fR"
+The number of times to try a memcache command before giving up.
+.IP "\fBretry_pause (default: 1)\fR"
+The time in seconds to wait after a memcache command fails.
+.IP "\fBtimeout (default: 2)\fR"
+The time limit for sending a memcache command and for
+receiving a memcache reply.
.SH BUGS
.ad
.fi
@@ -175,7 +203,7 @@ unprivileged Postfix user.
The Postfix memcache client requires additional configuration
when used as \fBpostscreen\fR(8) or \fBverify\fR(8) cache.
For details see the \fBbackup\fR and \fBttl\fR parameter
-discussions in the MEMCACHE PARAMETERS section above.
+discussions in the MEMCACHE MAIN PARAMETERS section above.
.SH "SEE ALSO"
.na
.nf
diff --git a/postfix/proto/XCLIENT_README.html b/postfix/proto/XCLIENT_README.html
index 97692c0db..daf0109b9 100644
--- a/postfix/proto/XCLIENT_README.html
+++ b/postfix/proto/XCLIENT_README.html
@@ -80,7 +80,7 @@ names are shown in upper case, they are in fact case insensitive.
xclient-command = XCLIENT 1*( SP attribute-name"="attribute-value )
- attribute-name = ( NAME | ADDR | PORT | PROTO | HELO )
+ attribute-name = ( NAME | ADDR | PORT | PROTO | HELO | LOGIN )
attribute-value = xtext
@@ -113,6 +113,10 @@ names are shown in upper case, they are in fact case insensitive.
value, or the value [UNAVAILABLE] when the information is
unavailable.
+ The LOGIN attribute specifies a SASL login name, or
+ the value [UNAVAILABLE] when the information is unavailable.
+
+
Note 1: syntactically valid NAME and HELO attribute-value
@@ -130,8 +134,8 @@ xtext encode attribute values. Servers that wish to interoperate
with these older implementations should be prepared to receive
unencoded information.
- Note 4: Postfix implementations prior to version 2.5 do not
-implement the PORT attribute.
+ Note 4: Some Postfix implementations do not implement the PORT
+or LOGIN attributes.
XCLIENT Server response
diff --git a/postfix/proto/memcache_table b/postfix/proto/memcache_table
index 129e7d438..45dbe2942 100644
--- a/postfix/proto/memcache_table
+++ b/postfix/proto/memcache_table
@@ -28,10 +28,28 @@
# delete and sequence (first/next) operations. The sequence
# operation requires a backup database that supports the
# operation.
-# MEMCACHE PARAMETERS
+# MEMCACHE MAIN PARAMETERS
# .ad
# .fi
-# .IP \fBbackup\fR
+# .IP "\fBmemcache (default: inet:localhost:11211)\fR"
+# The memcache server (note: singular) that Postfix will try
+# to connect to. For a TCP server specify "inet:" followed by
+# a hostname or address, ":", and a port name or number.
+# Specify an IPv6 address inside "[]".
+# For a UNIX-domain server specify "unix:" followed by the
+# socket pathname. Examples:
+#
+# .nf
+# memcache = inet:memcache.example.com:11211
+# memcache = inet:127.0.0.1:11211
+# memcache = inet:[fc00:8d00:189::3]:11211
+# memcache = unix:/path/to/socket
+# .fi
+#
+# NOTE: to access a UNIX-domain socket with the proxymap(8)
+# server, the socket must be accessible by the unprivileged
+# postfix user.
+# .IP "\fBbackup (default: undefined)\fR"
# An optional Postfix database that provides persistent backup
# for the memcache database. The Postfix memcache client will
# update the memcache database whenever it looks up or changes
@@ -59,25 +77,34 @@
# the proxymap server's proxy_read_maps or proxy_write_maps
# setting (depending on whether the access is read-only or
# read-write).
-# .IP "\fBmemcache (default: inet:localhost:11211)\fR"
-# The memcache server (note: singular) that Postfix will try
-# to connect to. For a TCP server specify "inet:" followed by
-# a hostname or address, ":", and a port name or number.
-# For a UNIX-domain server specify "unix:" followed by the
-# socket pathname. Examples:
+# .IP "\fBflags (default: 0)\fR"
+# Optional flags that should be stored along with a memcache
+# update.
+# .IP "\fBttl (default: 3600)\fR"
+# The expiration time in seconds of memcache updates.
#
-# .nf
-# memcache = inet:memcache.example.com
-# memcache = unix:/path/to/socket
-# .fi
+# NOTE 1: When using a memcache table as \fBpostscreen\fR(8)
+# or \fBverify\fR(8) cache without persistent backup, specify
+# a zero *_cache_cleanup_interval value with all Postfix
+# instances that use the memcache, and specify the largest
+# \fBpostscreen\fR(8) *_ttl value or \fBverify\fR(8) *_expire_time
+# value as the memcache table's \fBttl\fR value.
#
-# NOTE: In the case of a UNIX-domain socket, it must be accessible
-# by the unprivileged postfix user and by the memcached process.
+# NOTE 2: According to memcache protocol documentation, a
+# value greater than 30 days (2592000 seconds) specifies
+# absolute UNIX
+# time. Smaller values are relative to the time of the update.
+# MEMCACHE KEY PARAMETERS
+# .ad
+# .fi
# .IP "\fBkey_format (default: %s)\fB"
-# Format of the lookup and update keys in memcache queries.
+# Format of the lookup and update keys in memcache requests.
# By default, these are the same as the lookup and update
# keys that are given to the Postfix memcache client.
#
+# NOTE: The \fBkey_format\fR feature is not used for \fBbackup\fR
+# database requests.
+#
# When the same memcache database is used to cache information
# from multiple tables, you can use the \fBkey_format\fR
# feature to avoid name collisions by prepending a fixed
@@ -133,23 +160,20 @@
# .nf
# domain = example.com, hash:/etc/postfix/searchdomains
# .fi
-# .IP "\fBflags (default: 0)\fR"
-# Optional flags that should be stored along with a memcache
-# update.
-# .IP "\fBttl (default: 3600)\fR"
-# The expiration time in seconds of memcache updates.
-#
-# NOTE 1: When using a memcache table as \fBpostscreen\fR(8)
-# or \fBverify\fR(8) cache without persistent backup, specify
-# a zero *_cache_cleanup_interval value with all Postfix
-# instances that use the memcache, and specify the largest
-# \fBpostscreen\fR(8) *_ttl value or \fBverify\fR(8) *_expire_time
-# value as the memcache table's \fBttl\fR value.
-#
-# NOTE 2: According to memcache protocol documentation, a
-# value greater than 30 days (2592000 seconds) specifies
-# absolute UNIX
-# time. Smaller values are relative to the time of the update.
+# MEMCACHE ERROR CONTROLS
+# .ad
+# .fi
+# .IP "\fBdata_size_limit (default: 10240)\fR"
+# The maximal memcache reply data length in bytes.
+# .IP "\fBline_size_limit (default: 1024)\fR"
+# The maximal memcache reply line length in bytes.
+# .IP "\fBmax_try (default: 2)\fR"
+# The number of times to try a memcache command before giving up.
+# .IP "\fBretry_pause (default: 1)\fR"
+# The time in seconds to wait after a memcache command fails.
+# .IP "\fBtimeout (default: 2)\fR"
+# The time limit for sending a memcache command and for
+# receiving a memcache reply.
# BUGS
# The Postfix memcache client cannot be used for security-sensitive
# tables such as \fBalias_maps\fR (these may contain
@@ -165,7 +189,7 @@
# The Postfix memcache client requires additional configuration
# when used as \fBpostscreen\fR(8) or \fBverify\fR(8) cache.
# For details see the \fBbackup\fR and \fBttl\fR parameter
-# discussions in the MEMCACHE PARAMETERS section above.
+# discussions in the MEMCACHE MAIN PARAMETERS section above.
# SEE ALSO
# postmap(1), Postfix lookup table manager
# postconf(5), configuration parameters
diff --git a/postfix/src/global/dict_memcache.c b/postfix/src/global/dict_memcache.c
index f140ee7df..3fc6101c0 100644
--- a/postfix/src/global/dict_memcache.c
+++ b/postfix/src/global/dict_memcache.c
@@ -44,6 +44,7 @@
/* System library. */
#include
+#include
#include
#include
#include /* XXX sscanf() */
@@ -77,10 +78,12 @@ typedef struct {
void *dbc_ctxt; /* db_common context */
char *key_format; /* query key translation */
int timeout; /* client timeout */
- int mc_ttl; /* memcache expiration */
- int mc_flags; /* memcache flags */
- int mc_pause; /* sleep between errors */
- int mc_maxtry; /* number of tries */
+ int mc_ttl; /* memcache update expiration */
+ int mc_flags; /* memcache update flags */
+ int err_pause; /* delay between errors */
+ int max_tries; /* number of tries */
+ int max_line; /* reply line limit */
+ int max_data; /* reply data limit */
char *memcache; /* memcache server spec */
AUTO_CLNT *clnt; /* memcache client stream */
VSTRING *clnt_buf; /* memcache client buffer */
@@ -91,7 +94,7 @@ typedef struct {
} DICT_MC;
/*
- * Default memcache options.
+ * Memcache option defaults and names.
*/
#define DICT_MC_DEF_HOST "localhost"
#define DICT_MC_DEF_PORT "11211"
@@ -100,8 +103,21 @@ typedef struct {
#define DICT_MC_DEF_MC_TTL 3600
#define DICT_MC_DEF_MC_TIMEOUT 2
#define DICT_MC_DEF_MC_FLAGS 0
-#define DICT_MC_DEF_MC_MAXTRY 2
-#define DICT_MC_DEF_MC_PAUSE 1
+#define DICT_MC_DEF_MAX_TRY 2
+#define DICT_MC_DEF_MAX_LINE 1024
+#define DICT_MC_DEF_MAX_DATA 10240
+#define DICT_MC_DEF_ERR_PAUSE 1
+
+#define DICT_MC_NAME_MEMCACHE "memcache"
+#define DICT_MC_NAME_BACKUP "backup"
+#define DICT_MC_NAME_KEY_FMT "key_format"
+#define DICT_MC_NAME_MC_TTL "ttl"
+#define DICT_MC_NAME_MC_TIMEOUT "timeout"
+#define DICT_MC_NAME_MC_FLAGS "flags"
+#define DICT_MC_NAME_MAX_TRY "max_try"
+#define DICT_MC_NAME_MAX_LINE "line_size_limit"
+#define DICT_MC_NAME_MAX_DATA "data_size_limit"
+#define DICT_MC_NAME_ERR_PAUSE "retry_pause"
/*
* SLMs.
@@ -117,35 +133,48 @@ static void dict_memcache_set(DICT_MC *dict_mc, const char *value, int ttl)
{
VSTREAM *fp;
int count;
+ int data_len = strlen(value);
-#define MC_LINE_LIMIT 1024
-
- dict_mc->mc_errno = DICT_ERR_RETRY;
- for (count = 0; count < dict_mc->mc_maxtry; count++) {
+ /*
+ * If we can't retrieve it, then we must not store it.
+ */
+ dict_mc->mc_errno = DICT_ERR_RETRY; /* XXX */
+ if (data_len > dict_mc->max_data) {
+ msg_warn("database %s:%s: data for key %s is too long (%s=%d) "
+ "-- not stored", DICT_TYPE_MEMCACHE, dict_mc->dict.name,
+ STR(dict_mc->key_buf), DICT_MC_NAME_MAX_DATA,
+ dict_mc->max_data);
+ return;
+ }
+ for (count = 0; count < dict_mc->max_tries; count++) {
if (count > 0)
- sleep(1);
- if ((fp = auto_clnt_access(dict_mc->clnt)) != 0) {
+ sleep(dict_mc->err_pause);
+ if ((fp = auto_clnt_access(dict_mc->clnt)) == 0) {
+ if (errno == ECONNREFUSED)
+ break;
+ } else {
if (memcache_printf(fp, "set %s %d %d %ld",
STR(dict_mc->key_buf), dict_mc->mc_flags,
- ttl, strlen(value)) < 0
+ ttl, data_len) < 0
|| memcache_fwrite(fp, value, strlen(value)) < 0
- || memcache_get(fp, dict_mc->clnt_buf, MC_LINE_LIMIT) < 0) {
+ || memcache_get(fp, dict_mc->clnt_buf,
+ dict_mc->max_line) < 0) {
if (count > 0)
- msg_warn("database %s:%s: I/O error: %m",
+ msg_warn(errno ? "database %s:%s: I/O error: %m" :
+ "database %s:%s: I/O error",
DICT_TYPE_MEMCACHE, dict_mc->dict.name);
- auto_clnt_recover(dict_mc->clnt);
} else if (strcmp(STR(dict_mc->clnt_buf), "STORED") != 0) {
if (count > 0)
msg_warn("database %s:%s: update failed: %.30s",
DICT_TYPE_MEMCACHE, dict_mc->dict.name,
STR(dict_mc->clnt_buf));
- auto_clnt_recover(dict_mc->clnt);
} else {
/* Victory! */
dict_mc->mc_errno = 0;
break;
}
}
+ auto_clnt_recover(dict_mc->clnt);
}
}
@@ -160,41 +189,89 @@ static const char *dict_memcache_get(DICT_MC *dict_mc)
dict_mc->mc_errno = DICT_ERR_RETRY;
retval = 0;
- for (count = 0; count < dict_mc->mc_maxtry; count++) {
+ for (count = 0; count < dict_mc->max_tries; count++) {
if (count > 0)
- sleep(1);
- if ((fp = auto_clnt_access(dict_mc->clnt)) != 0) {
+ sleep(dict_mc->err_pause);
+ if ((fp = auto_clnt_access(dict_mc->clnt)) == 0) {
+ if (errno == ECONNREFUSED)
+ break;
+ } else {
if (memcache_printf(fp, "get %s", STR(dict_mc->key_buf)) < 0
- || memcache_get(fp, dict_mc->clnt_buf, MC_LINE_LIMIT) < 0) {
+ || memcache_get(fp, dict_mc->clnt_buf, dict_mc->max_line) < 0) {
if (count > 0)
- msg_warn("database %s:%s: I/O error: %m",
+ msg_warn(errno ? "database %s:%s: I/O error: %m" :
+ "database %s:%s: I/O error",
DICT_TYPE_MEMCACHE, dict_mc->dict.name);
- auto_clnt_recover(dict_mc->clnt);
} else if (strcmp(STR(dict_mc->clnt_buf), "END") == 0) {
/* Not found. */
dict_mc->mc_errno = 0;
break;
} else if (sscanf(STR(dict_mc->clnt_buf),
- "VALUE %*s %*s %ld", &todo) != 1 || todo < 0) {
+ "VALUE %*s %*s %ld", &todo) != 1
+ || todo < 0 || todo > dict_mc->max_data) {
if (count > 0)
msg_warn("%s: unexpected memcache server reply: %.30s",
dict_mc->dict.name, STR(dict_mc->clnt_buf));
- auto_clnt_recover(dict_mc->clnt);
} else if (memcache_fread(fp, dict_mc->res_buf, todo) < 0) {
if (count > 0)
msg_warn("%s: EOF receiving memcache server reply",
dict_mc->dict.name);
- auto_clnt_recover(dict_mc->clnt);
} else {
/* Victory! */
retval = STR(dict_mc->res_buf);
dict_mc->mc_errno = 0;
- if (memcache_get(fp, dict_mc->clnt_buf, MC_LINE_LIMIT) < 0
+ if (memcache_get(fp, dict_mc->clnt_buf, dict_mc->max_line) < 0
|| strcmp(STR(dict_mc->clnt_buf), "END") != 0)
auto_clnt_recover(dict_mc->clnt);
break;
}
}
+ auto_clnt_recover(dict_mc->clnt);
+ }
+ return (retval);
+}
+
+/* dict_memcache_del - delete memcache key/value */
+
+static int dict_memcache_del(DICT_MC *dict_mc)
+{
+ VSTREAM *fp;
+ int count;
+ int retval = -1;
+
+ dict_mc->mc_errno = DICT_ERR_RETRY;
+ for (count = 0; count < dict_mc->max_tries; count++) {
+ if (count > 0)
+ sleep(dict_mc->err_pause);
+ if ((fp = auto_clnt_access(dict_mc->clnt)) == 0) {
+ if (errno == ECONNREFUSED)
+ break;
+ } else {
+ if (memcache_printf(fp, "delete %s", STR(dict_mc->key_buf)) < 0
+ || memcache_get(fp, dict_mc->clnt_buf,
+ dict_mc->max_line) < 0) {
+ if (count > 0)
+ msg_warn(errno ? "database %s:%s: I/O error: %m" :
+ "database %s:%s: I/O error",
+ DICT_TYPE_MEMCACHE, dict_mc->dict.name);
+ } else if (strcmp(STR(dict_mc->clnt_buf), "DELETED") == 0) {
+ /* Victory! */
+ dict_mc->mc_errno = 0;
+ retval = 0;
+ break;
+ } else if (strcmp(STR(dict_mc->clnt_buf), "NOT_FOUND") == 0) {
+ /* Not found! */
+ dict_mc->mc_errno = 0;
+ retval = 1;
+ break;
+ } else {
+ if (count > 0)
+ msg_warn("database %s:%s: delete failed: %.30s",
+ DICT_TYPE_MEMCACHE, dict_mc->dict.name,
+ STR(dict_mc->clnt_buf));
+ }
+ }
+ auto_clnt_recover(dict_mc->clnt);
}
return (retval);
}
@@ -298,12 +375,12 @@ static void dict_memcache_update(DICT *dict, const char *name,
dict_memcache_set(dict_mc, value, dict_mc->mc_ttl);
if (msg_verbose)
- msg_info("%s: %s: update key \"%s\" => \"%s\" %s",
- myname, dict_mc->dict.name, STR(dict_mc->key_buf), value,
- dict_mc->mc_errno ? "(memcache error)" :
+ msg_info("%s: %s: update key \"%s\"(%s) => \"%s\" %s",
+ myname, dict_mc->dict.name, name, STR(dict_mc->key_buf),
+ value, dict_mc->mc_errno ? "(memcache error)" :
backup_errno ? "(backup error)" : "(no error)");
- dict_errno = (backup_errno ? backup_errno : dict_mc->mc_errno);
+ dict_errno = (dict_mc->backup ? backup_errno : dict_mc->mc_errno);
}
/* dict_memcache_lookup - lookup memcache */
@@ -340,12 +417,13 @@ static const char *dict_memcache_lookup(DICT *dict, const char *name)
dict_memcache_set(dict_mc, retval, dict_mc->mc_ttl);
}
if (msg_verbose)
- msg_info("%s: %s: key %s => %s",
- myname, dict_mc->dict.name, STR(dict_mc->key_buf),
- retval ? retval :
- dict_mc->mc_errno ? "(memcache error)" :
+ msg_info("%s: %s: key \"%s\"(%s) => %s",
+ myname, dict_mc->dict.name, name, STR(dict_mc->key_buf),
+ retval ? retval : dict_mc->mc_errno ? "(memcache error)" :
backup_errno ? "(backup error)" : "(not found)");
+ dict_errno = (dict_mc->backup ? backup_errno : dict_mc->mc_errno);
+
return (retval);
}
@@ -355,9 +433,9 @@ static int dict_memcache_delete(DICT *dict, const char *name)
{
const char *myname = "dict_memcache_delete";
DICT_MC *dict_mc = (DICT_MC *) dict;
- const char *retval;
int backup_errno = 0;
int del_res = 0;
+ int mem_res;
/*
* Skip lookups with an inapplicable key, silently. This is just deleting
@@ -377,21 +455,19 @@ static int dict_memcache_delete(DICT *dict, const char *name)
}
/*
- * Update the memcache last. There is no memcache delete operation.
- * Instead, we set a short expiration time if the data exists.
+ * Update the memcache last.
*/
- if ((retval = dict_memcache_get(dict_mc)) != 0)
- dict_memcache_set(dict_mc, retval, 1);
+ mem_res = dict_memcache_del(dict_mc);
if (msg_verbose)
- msg_info("%s: %s: delete key %s => %s",
- myname, dict_mc->dict.name, STR(dict_mc->key_buf),
+ msg_info("%s: %s: delete key \"%s\"(%s) => %s",
+ myname, dict_mc->dict.name, name, STR(dict_mc->key_buf),
dict_mc->mc_errno ? "(memcache error)" :
backup_errno ? "(backup error)" : "(no error)");
- dict_errno = (backup_errno ? backup_errno : dict_mc->mc_errno);
+ dict_errno = (dict_mc->backup ? backup_errno : dict_mc->mc_errno);
- return (del_res);
+ return (dict_mc->backup ? del_res : mem_res);
}
/* dict_memcache_sequence - first/next lookup */
@@ -467,19 +543,23 @@ DICT *dict_memcache_open(const char *name, int open_flags, int dict_flags)
* Parse the configuration file.
*/
dict_mc->parser = cfg_parser_alloc(name);
- dict_mc->key_format = cfg_get_str(dict_mc->parser, "key_format",
+ dict_mc->key_format = cfg_get_str(dict_mc->parser, DICT_MC_NAME_KEY_FMT,
DICT_MC_DEF_KEY_FMT, 0, 0);
- dict_mc->timeout = cfg_get_int(dict_mc->parser, "timeout",
+ dict_mc->timeout = cfg_get_int(dict_mc->parser, DICT_MC_NAME_MC_TIMEOUT,
DICT_MC_DEF_MC_TIMEOUT, 0, 0);
- dict_mc->mc_ttl = cfg_get_int(dict_mc->parser, "ttl",
+ dict_mc->mc_ttl = cfg_get_int(dict_mc->parser, DICT_MC_NAME_MC_TTL,
DICT_MC_DEF_MC_TTL, 0, 0);
- dict_mc->mc_flags = cfg_get_int(dict_mc->parser, "flags",
+ dict_mc->mc_flags = cfg_get_int(dict_mc->parser, DICT_MC_NAME_MC_FLAGS,
DICT_MC_DEF_MC_FLAGS, 0, 0);
- dict_mc->mc_pause = cfg_get_int(dict_mc->parser, "error_pause",
- DICT_MC_DEF_MC_PAUSE, 1, 0);
- dict_mc->mc_maxtry = cfg_get_int(dict_mc->parser, "maxtry",
- DICT_MC_DEF_MC_MAXTRY, 1, 0);
- dict_mc->memcache = cfg_get_str(dict_mc->parser, "memcache",
+ dict_mc->err_pause = cfg_get_int(dict_mc->parser, DICT_MC_NAME_ERR_PAUSE,
+ DICT_MC_DEF_ERR_PAUSE, 1, 0);
+ dict_mc->max_tries = cfg_get_int(dict_mc->parser, DICT_MC_NAME_MAX_TRY,
+ DICT_MC_DEF_MAX_TRY, 1, 0);
+ dict_mc->max_line = cfg_get_int(dict_mc->parser, DICT_MC_NAME_MAX_LINE,
+ DICT_MC_DEF_MAX_LINE, 1, 0);
+ dict_mc->max_data = cfg_get_int(dict_mc->parser, DICT_MC_NAME_MAX_DATA,
+ DICT_MC_DEF_MAX_DATA, 1, 0);
+ dict_mc->memcache = cfg_get_str(dict_mc->parser, DICT_MC_NAME_MEMCACHE,
DICT_MC_DEF_MEMCACHE, 0, 0);
/*
@@ -491,7 +571,8 @@ DICT *dict_memcache_open(const char *name, int open_flags, int dict_flags)
/*
* Open the optional backup database.
*/
- backup = cfg_get_str(dict_mc->parser, "backup", (char *) 0, 0, 0);
+ backup = cfg_get_str(dict_mc->parser, DICT_MC_NAME_BACKUP,
+ (char *) 0, 0, 0);
if (backup) {
dict_mc->backup = dict_open(backup, open_flags, dict_flags);
myfree(backup);
diff --git a/postfix/src/global/mail_proto.h b/postfix/src/global/mail_proto.h
index 66f6ff521..538b01e57 100644
--- a/postfix/src/global/mail_proto.h
+++ b/postfix/src/global/mail_proto.h
@@ -210,6 +210,7 @@ extern char *mail_pathname(const char *, const char *);
#define XCLIENT_PORT "PORT" /* client port */
#define XCLIENT_PROTO "PROTO" /* client protocol */
#define XCLIENT_HELO "HELO" /* client helo */
+#define XCLIENT_LOGIN "LOGIN" /* SASL login name */
#define XCLIENT_UNAVAILABLE "[UNAVAILABLE]" /* permanently unavailable */
#define XCLIENT_TEMPORARY "[TEMPUNAVAIL]" /* temporarily unavailable */
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index 032b403aa..a4e1808e1 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 "20111217"
+#define MAIL_RELEASE_DATE "20111218"
#define MAIL_VERSION_NUMBER "2.9"
#ifdef SNAPSHOT
diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c
index 6dee4fbcb..d42fefbf5 100644
--- a/postfix/src/smtpd/smtpd.c
+++ b/postfix/src/smtpd/smtpd.c
@@ -1690,6 +1690,9 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
state->sasl_mechanism_list);
}
}
+#define XCLIENT_LOGIN_KLUDGE " " XCLIENT_LOGIN
+#else
+#define XCLIENT_LOGIN_KLUDGE ""
#endif
if ((discard_mask & EHLO_MASK_VERP) == 0)
if (namadr_list_match(verp_clients, state->name, state->addr))
@@ -1700,7 +1703,8 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
ENQUEUE_FIX_REPLY(state, reply_buf, XCLIENT_CMD
" " XCLIENT_NAME " " XCLIENT_ADDR
" " XCLIENT_PROTO " " XCLIENT_HELO
- " " XCLIENT_REVERSE_NAME " " XCLIENT_PORT);
+ " " XCLIENT_REVERSE_NAME " " XCLIENT_PORT
+ XCLIENT_LOGIN_KLUDGE);
if ((discard_mask & EHLO_MASK_XFORWARD) == 0)
if (xforward_allowed)
ENQUEUE_FIX_REPLY(state, reply_buf, XFORWARD_CMD
@@ -1837,17 +1841,16 @@ static int mail_open_stream(SMTPD_STATE *state)
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_RWR_CONTEXT, FORWARD_DOMAIN(state));
#ifdef USE_SASL_AUTH
- if (smtpd_sasl_is_active(state)) {
- if (state->sasl_method)
- rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
- MAIL_ATTR_SASL_METHOD, state->sasl_method);
- if (state->sasl_username)
- rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
- MAIL_ATTR_SASL_USERNAME, state->sasl_username);
- if (state->sasl_sender)
- rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
- MAIL_ATTR_SASL_SENDER, state->sasl_sender);
- }
+ /* Make external authentication painless (e.g., XCLIENT). */
+ if (state->sasl_method)
+ rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_SASL_METHOD, state->sasl_method);
+ if (state->sasl_username)
+ rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_SASL_USERNAME, state->sasl_username);
+ if (state->sasl_sender)
+ rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_SASL_SENDER, state->sasl_sender);
#endif
/*
@@ -1947,7 +1950,7 @@ static int mail_open_stream(SMTPD_STATE *state)
* Log the queue ID with the message origin.
*/
#ifdef USE_SASL_AUTH
- if (smtpd_sasl_is_active(state))
+ if (state->sasl_username)
smtpd_sasl_mail_log(state);
else
#endif
@@ -2203,8 +2206,7 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
return (-1);
}
#ifdef USE_SASL_AUTH
- } else if (smtpd_sasl_is_active(state)
- && strncasecmp(arg, "AUTH=", 5) == 0) {
+ } else if (strncasecmp(arg, "AUTH=", 5) == 0) {
if ((err = smtpd_sasl_mail_opt(state, arg + 5)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
@@ -2390,7 +2392,7 @@ static void mail_reset(SMTPD_STATE *state)
state->saved_delay = 0;
#endif
#ifdef USE_SASL_AUTH
- if (smtpd_sasl_is_active(state))
+ if (state->sasl_sender)
smtpd_sasl_mail_reset(state);
#endif
state->discard = 0;
@@ -2928,8 +2930,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
#endif
rfc3848_sess = "";
#ifdef USE_SASL_AUTH
- if (smtpd_sasl_is_active(state) && var_smtpd_sasl_auth_hdr
- && state->sasl_username) {
+ if (var_smtpd_sasl_auth_hdr && state->sasl_username) {
username = VSTRING_STRDUP(state->sasl_username);
comment_sanitize(username);
out_fprintf(out_stream, REC_TYPE_NORM,
@@ -2937,7 +2938,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
vstring_free(username);
}
/* RFC 3848 is defined for ESMTP only. */
- if (smtpd_sasl_is_active(state) && state->sasl_username
+ if (state->sasl_username
&& strcmp(state->protocol, MAIL_PROTO_ESMTP) == 0)
rfc3848_auth = "A";
else
@@ -3460,6 +3461,7 @@ static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
};
int got_helo = 0;
int got_proto = 0;
+ int got_login = 0;
/*
* Sanity checks.
@@ -3652,6 +3654,20 @@ static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
got_proto = 1;
}
+ /*
+ * LOGIN=sasl_username. Sets the authentication method as XCLIENT.
+ * This can be used even if SASL authentication is turned off in
+ * main.cf. We can't make it easier than that.
+ */
+#ifdef USE_SASL_AUTH
+ else if (STREQ(attr_name, XCLIENT_LOGIN)) {
+ if (STREQ(attr_value, XCLIENT_UNAVAILABLE) == 0) {
+ smtpd_sasl_auth_extern(state, attr_value, XCLIENT_CMD);
+ got_login = 1;
+ }
+ }
+#endif
+
/*
* Unknown attribute name. Complain.
*/
@@ -3700,7 +3716,7 @@ static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
state->protocol = mystrdup(MAIL_PROTO_SMTP);
}
#ifdef USE_SASL_AUTH
- if (smtpd_sasl_is_active(state))
+ if (got_login == 0)
smtpd_sasl_auth_reset(state);
#endif
chat_reset(state, 0);
@@ -4767,8 +4783,8 @@ static void smtpd_proto(SMTPD_STATE *state)
#endif
helo_reset(state);
#ifdef USE_SASL_AUTH
+ smtpd_sasl_auth_reset(state);
if (smtpd_sasl_is_active(state)) {
- smtpd_sasl_auth_reset(state);
smtpd_sasl_deactivate(state);
}
#endif
@@ -4815,13 +4831,13 @@ static void smtpd_service(VSTREAM *stream, char *service, char **argv)
/*
* XCLIENT must not override its own access control.
*/
- xclient_allowed =
+ xclient_allowed = SMTPD_STAND_ALONE((&state)) == 0 &&
namadr_list_match(xclient_hosts, state.name, state.addr);
/*
* Overriding XFORWARD access control makes no sense, either.
*/
- xforward_allowed =
+ xforward_allowed = SMTPD_STAND_ALONE((&state)) == 0 &&
namadr_list_match(xforward_hosts, state.name, state.addr);
/*
diff --git a/postfix/src/smtpd/smtpd.h b/postfix/src/smtpd/smtpd.h
index 73f15ffbe..2d6c46c92 100644
--- a/postfix/src/smtpd/smtpd.h
+++ b/postfix/src/smtpd/smtpd.h
@@ -267,6 +267,7 @@ extern void smtpd_state_reset(SMTPD_STATE *);
#define CLIENT_PROTO_UNKNOWN CLIENT_ATTR_UNKNOWN
#define CLIENT_IDENT_UNKNOWN 0
#define CLIENT_DOMAIN_UNKNOWN 0
+#define CLIENT_LOGIN_UNKNOWN 0
#define IS_AVAIL_CLIENT_ATTR(v) ((v) && strcmp((v), CLIENT_ATTR_UNKNOWN))
diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c
index 45c4a53e6..f53741f82 100644
--- a/postfix/src/smtpd/smtpd_check.c
+++ b/postfix/src/smtpd/smtpd_check.c
@@ -2668,6 +2668,7 @@ static int check_ccert_access(SMTPD_STATE *state, const char *table,
const char *def_acl)
{
int result = SMTPD_CHECK_DUNNO;
+
#ifdef USE_TLS
const char *myname = "check_ccert_access";
int found;
@@ -3354,16 +3355,10 @@ static int reject_auth_sender_login_mismatch(SMTPD_STATE *state, const char *sen
char *name;
int found = 0;
- /*
- * Replace obscure code by self-evident code.
- */
-#define SMTPD_SASL_AUTHENTICATED(state) \
- (smtpd_sasl_is_active(state) && state->sasl_username != 0)
-
/*
* Reject if the client is logged in and does not own the sender address.
*/
- if (var_smtpd_sasl_enable && SMTPD_SASL_AUTHENTICATED(state)) {
+ if (smtpd_sender_login_maps && state->sasl_username) {
reply = smtpd_resolve_addr(sender);
if (reply->flags & RESOLVE_FLAG_FAIL)
reject_dict_retry(state, sender);
@@ -3396,7 +3391,7 @@ static int reject_unauth_sender_login_mismatch(SMTPD_STATE *state, const char *s
* Reject if the client is not logged in and the sender address has an
* owner.
*/
- if (var_smtpd_sasl_enable && !SMTPD_SASL_AUTHENTICATED(state)) {
+ if (smtpd_sender_login_maps && !state->sasl_username) {
reply = smtpd_resolve_addr(sender);
if (reply->flags & RESOLVE_FLAG_FAIL)
reject_dict_retry(state, sender);
@@ -3489,14 +3484,11 @@ static int check_policy_service(SMTPD_STATE *state, const char *server,
ATTR_TYPE_STR, MAIL_ATTR_STRESS, var_stress,
#ifdef USE_SASL_AUTH
ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD,
- smtpd_sasl_is_active(state) && state->sasl_method ?
- state->sasl_method : "",
+ state->sasl_method ? state->sasl_method : "",
ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME,
- smtpd_sasl_is_active(state) && state->sasl_username ?
- state->sasl_username : "",
+ state->sasl_username ? state->sasl_username : "",
ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER,
- smtpd_sasl_is_active(state) && state->sasl_sender ?
- state->sasl_sender : "",
+ state->sasl_sender ? state->sasl_sender : "",
#endif
#ifdef USE_TLS
#define IF_ENCRYPTED(x, y) ((state->tls_context && ((x) != 0)) ? (x) : (y))
diff --git a/postfix/src/smtpd/smtpd_sasl_glue.c b/postfix/src/smtpd/smtpd_sasl_glue.c
index da37c4f9a..5062ee9ac 100644
--- a/postfix/src/smtpd/smtpd_sasl_glue.c
+++ b/postfix/src/smtpd/smtpd_sasl_glue.c
@@ -6,6 +6,9 @@
/* SYNOPSIS
/* #include "smtpd_sasl_glue.h"
/*
+/* void smtpd_sasl_state_init(state)
+/* SMTPD_STATE *state;
+/*
/* void smtpd_sasl_initialize()
/*
/* void smtpd_sasl_activate(state, sasl_opts_name, sasl_opts_val)
@@ -21,6 +24,11 @@
/* void smtpd_sasl_logout(state)
/* SMTPD_STATE *state;
/*
+/* void smtpd_sasl_login(state, sasl_username, sasl_method)
+/* SMTPD_STATE *state;
+/* const char *sasl_username;
+/* const char *sasl_method;
+/*
/* void smtpd_sasl_deactivate(state)
/* SMTPD_STATE *state;
/*
@@ -33,6 +41,11 @@
/* This module encapsulates most of the detail specific to SASL
/* authentication.
/*
+/* smtpd_sasl_state_init() performs minimal server state
+/* initialization to support external authentication (e.g.,
+/* XCLIENT) without having to enable SASL in main.cf. This
+/* should always be called at process startup.
+/*
/* smtpd_sasl_initialize() initializes the SASL library. This
/* routine should be called once at process start-up. It may
/* need access to the file system for run-time loading of
@@ -57,6 +70,10 @@
/* This member is a null pointer in the absence of successful
/* authentication.
/* .PP
+/* smtpd_sasl_login() records the result of successful external
+/* authentication, i.e. without invoking smtpd_sasl_authenticate(),
+/* but produces an otherwise equivalent result.
+/*
/* smtpd_sasl_logout() cleans up after smtpd_sasl_authenticate().
/* This routine exists for the sake of symmetry.
/*
@@ -181,9 +198,6 @@ void smtpd_sasl_activate(SMTPD_STATE *state, const char *sasl_opts_name,
*/
state->sasl_reply = vstring_alloc(20);
state->sasl_mechanism_list = 0;
- state->sasl_username = 0;
- state->sasl_method = 0;
- state->sasl_sender = 0;
/*
* Set up a new server context for this connection.
@@ -204,7 +218,7 @@ void smtpd_sasl_activate(SMTPD_STATE *state, const char *sasl_opts_name,
client_addr = ADDR_OR_EMPTY(state->addr,
CLIENT_ADDR_UNKNOWN),
service = SMTPD_SASL_SERVICE,
- user_realm = REALM_OR_NULL(var_smtpd_sasl_realm),
+ user_realm = REALM_OR_NULL(var_smtpd_sasl_realm),
security_options = sasl_opts_val,
tls_flag = tls_flag)) == 0)
msg_fatal("SASL per-connection initialization failed");
@@ -218,6 +232,16 @@ void smtpd_sasl_activate(SMTPD_STATE *state, const char *sasl_opts_name,
state->sasl_mechanism_list = mystrdup(mechanism_list);
}
+/* smtpd_sasl_state_init - initialize state to allow extern authentication. */
+
+void smtpd_sasl_state_init(SMTPD_STATE *state)
+{
+ /* Initialization to support external authentication (e.g., XCLIENT). */
+ state->sasl_username = 0;
+ state->sasl_method = 0;
+ state->sasl_sender = 0;
+}
+
/* smtpd_sasl_deactivate - per-connection cleanup */
void smtpd_sasl_deactivate(SMTPD_STATE *state)
@@ -322,4 +346,17 @@ void smtpd_sasl_logout(SMTPD_STATE *state)
}
}
+/* smtpd_sasl_login - set login information */
+
+void smtpd_sasl_login(SMTPD_STATE *state, const char *sasl_username,
+ const char *sasl_method)
+{
+ if (state->sasl_username)
+ myfree(state->sasl_username);
+ state->sasl_username = mystrdup(sasl_username);
+ if (state->sasl_method)
+ myfree(state->sasl_method);
+ state->sasl_method = mystrdup(sasl_method);
+}
+
#endif
diff --git a/postfix/src/smtpd/smtpd_sasl_glue.h b/postfix/src/smtpd/smtpd_sasl_glue.h
index c6e76e3f5..d81eec1bb 100644
--- a/postfix/src/smtpd/smtpd_sasl_glue.h
+++ b/postfix/src/smtpd/smtpd_sasl_glue.h
@@ -11,10 +11,12 @@
/*
* SASL protocol interface
*/
+extern void smtpd_sasl_state_init(SMTPD_STATE *);
extern void smtpd_sasl_initialize(void);
extern void smtpd_sasl_activate(SMTPD_STATE *, const char *, const char *);
extern void smtpd_sasl_deactivate(SMTPD_STATE *);
extern int smtpd_sasl_authenticate(SMTPD_STATE *, const char *, const char *);
+extern void smtpd_sasl_login(SMTPD_STATE *, const char *, const char *);
extern void smtpd_sasl_logout(SMTPD_STATE *);
extern int permit_sasl_auth(SMTPD_STATE *, int, int);
diff --git a/postfix/src/smtpd/smtpd_sasl_proto.c b/postfix/src/smtpd/smtpd_sasl_proto.c
index 823e98886..297a445a6 100644
--- a/postfix/src/smtpd/smtpd_sasl_proto.c
+++ b/postfix/src/smtpd/smtpd_sasl_proto.c
@@ -12,6 +12,11 @@
/* int argc;
/* SMTPD_TOKEN *argv;
/*
+/* void smtpd_sasl_auth_extern(state, username, method)
+/* SMTPD_STATE *state;
+/* const char *username;
+/* const char *method;
+/*
/* void smtpd_sasl_auth_reset(state)
/* SMTPD_STATE *state;
/*
@@ -47,6 +52,13 @@
/* .PP
/* smtpd_sasl_auth_reset() cleans up after the AUTH command.
/* This is required before smtpd_sasl_auth_cmd() can be used again.
+/* This may be called even if SASL authentication is turned off
+/* in main.cf.
+/*
+/* smtpd_sasl_auth_extern() records authentication information
+/* that is received from an external source.
+/* This may be called even if SASL authentication is turned off
+/* in main.cf.
/*
/* smtpd_sasl_mail_opt() implements the SASL-specific AUTH=sender
/* option to the MAIL FROM command. The result is an error response
@@ -183,7 +195,6 @@ int smtpd_sasl_auth_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
smtpd_chat_reply(state, "501 5.5.4 Syntax: AUTH mechanism");
return (-1);
}
-
/* Don't reuse the SASL handle after authentication failure. */
#ifndef XSASL_TYPE_CYRUS
#define XSASL_TYPE_CYRUS "cyrus"
@@ -212,13 +223,6 @@ int smtpd_sasl_auth_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
return (smtpd_sasl_authenticate(state, auth_mechanism, initial_response));
}
-/* smtpd_sasl_auth_reset - clean up after AUTH command */
-
-void smtpd_sasl_auth_reset(SMTPD_STATE *state)
-{
- smtpd_sasl_logout(state);
-}
-
/* smtpd_sasl_mail_opt - SASL-specific MAIL FROM option */
char *smtpd_sasl_mail_opt(SMTPD_STATE *state, const char *addr)
@@ -227,10 +231,6 @@ char *smtpd_sasl_mail_opt(SMTPD_STATE *state, const char *addr)
/*
* Do not store raw RFC2554 protocol data.
*/
- if (!smtpd_sasl_is_active(state)) {
- state->error_mask |= MAIL_ERROR_PROTOCOL;
- return ("503 5.5.4 Error: authentication disabled");
- }
#if 0
if (state->sasl_username == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
diff --git a/postfix/src/smtpd/smtpd_sasl_proto.h b/postfix/src/smtpd/smtpd_sasl_proto.h
index bd326be58..7f8ac3d4b 100644
--- a/postfix/src/smtpd/smtpd_sasl_proto.h
+++ b/postfix/src/smtpd/smtpd_sasl_proto.h
@@ -17,6 +17,9 @@ extern char *smtpd_sasl_mail_opt(SMTPD_STATE *, const char *);
extern void smtpd_sasl_mail_log(SMTPD_STATE *);
extern void smtpd_sasl_mail_reset(SMTPD_STATE *);
+#define smtpd_sasl_auth_extern smtpd_sasl_login
+#define smtpd_sasl_auth_reset smtpd_sasl_logout
+
/* LICENSE
/* .ad
/* .fi
diff --git a/postfix/src/smtpd/smtpd_state.c b/postfix/src/smtpd/smtpd_state.c
index 12bca3025..43baa74bc 100644
--- a/postfix/src/smtpd/smtpd_state.c
+++ b/postfix/src/smtpd/smtpd_state.c
@@ -145,10 +145,16 @@ void smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream,
state->tls_context = 0;
#endif
+
+ /*
+ * Minimal initialization to support external authentication (e.g.,
+ * XCLIENT) without having to enable SASL in main.cf.
+ */
#ifdef USE_SASL_AUTH
if (SMTPD_STAND_ALONE(state))
var_smtpd_sasl_enable = 0;
smtpd_sasl_set_inactive(state);
+ smtpd_sasl_state_init(state);
#endif
state->milter_argv = 0;
--
2.47.3