2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-22 09:57:34 +00:00

postfix-2.11-20140104

This commit is contained in:
Wietse Venema 2014-01-04 00:00:00 -05:00 committed by Viktor Dukhovni
parent 6ff1c3ac62
commit 4a25a6b519
21 changed files with 491 additions and 271 deletions

View File

@ -19462,10 +19462,33 @@ Apologies for any names omitted.
Cleanup: DANE support: test script. Viktor Dukhovni. File
tls/tls_dane.sh.
LMDB will not be supported in the stable Postfix 2.11 release.
Debugging: test driver for LMDB debugging and stress testing.
Shockingly, LMDB terminates the postscreen daemon without
logfile record. File: util/dict_cache.c.
Debugging: test driver to speed up LMDB debugging and stress
testing. Shockingly, LMDB terminates the postcreen daemon
without logfile record. Fixing this will require changes
in LMDB or changes in the way Postfix can use LMDB. File:
util/dict_cache.c.
Because of the above behavior, LMDB cannot be supported in
the stable Postfix 2.11 release.
20140102
Bugfix: close the LMDB database cursor's read transaction
before writing with MDB_NOLOCK and before changing the
database memory map size. File: util/slmdb.c.
20140103
Cleanup: eliminated data duplication from the new SMTP_ITERATOR
structure to the old SMTP_SESSION structure. The SMTP_ITERATOR
structure now maintains the sole copy. Files: smtp/smtp.h,
smtp_sasl_auth_cache.c, smtp_reuse.c, smtp_sasl_glue.c,
smtp_rcpt.c, smtp_session.c, smtp_chat.c, smtp_proto.c,
smtp_connect.c.
20130104
Feature: support for optional configuration files
"$daemon-directory/postfix-files.d/*". These are processed
in sorted order after "$daemon-directory/postfix-files",
This avoids breaking "postfix set-permissions" etc. when a
Postfix distributions comes in multiple packages. File:
conf/post-install.

View File

@ -82,20 +82,21 @@ element of that group called a "generator". Presently, there are two flavors of
* PPrriimmee--ffiieelldd ggrroouuppss ((EEDDHH)):: The server needs to be configured with a
suitably-large prime and a corresponding "generator". The acronym for
forward secrecy over prime fields is EDH or Ephemeral Diffie-Hellman
(sometimes also abbreviated as DHE).
forward secrecy over prime fields is EDH for Ephemeral Diffie-Hellman (also
abbreviated as DHE for Diffie-Hellman Exchange).
* EElllliippttiicc--ccuurrvvee ggrroouuppss ((EEEECCDDHH)):: The server needs to be configured with a
"named curve". These offer better security at lower computational cost than
prime field groups, but are not as widely implemented. The acronym for the
elliptic curve version is EECDH which is short for Ephemeral Elliptic Curve
Diffie-Hellman.
Diffie-Hellman (also abbreviated as ECDHE for Elliptic Curve Diffie-Hellman
Exchange).
It is not essential to know what these are, but one does need to know that
OpenSSL only supports EECDH as of version 1.0.0. Thus the configuration
parameters related to Elliptic Curve forward secrecy are only available when
Postfix is linked with OpenSSL 1.0.0 or later (provided EC support has not been
disabled by the vendor, as in some versions of RedHat Linux).
OpenSSL supports EECDH with version 1.0.0 or later. Thus the configuration
parameters related to Elliptic-Curve forward secrecy are available when Postfix
is linked with OpenSSL >= 1.0.0 (provided EC support has not been disabled by
the vendor, as in some versions of RedHat Linux).
Elliptic curves used in cryptography are typically identified by a "name" that
stands for a set of well-known parameter values, and it is these "names" (or
@ -141,9 +142,8 @@ configured overrides.
1024 bits long (see the quick-start section for details).
It turns out that (inadvisably-patched in some Debian releases) Exim SMTP
clients enforce a minimum 2048-bit length for the non-export prime. See the
quick-start section for the recommended configuration to work around this
issue.
clients require a >= 2048-bit length for the non-export prime. See the quick-
start section for the recommended configuration to work around this issue.
EEEECCDDHH SSeerrvveerr ssuuppppoorrtt
@ -198,50 +198,60 @@ a case-by-case basis via the TLS policy table.
GGeettttiinngg ssttaarrtteedd,, qquuiicckk aanndd ddiirrttyy
* Postfix 2.6 and 2.7: Enable elliptic-curve support. This is the default
with Postfix >= 2.8.
EEEECCDDHH CClliieenntt aanndd sseerrvveerr ssuuppppoorrtt ((PPoossttffiixx >>== 22..66 wwiitthh OOppeennSSSSLL >>== 11..00..00))
/etc/postfix/main.cf:
# Postfix 2.6 or 2.7 only. This is default with Postfix 2.8 and
later.
smtpd_tls_eecdh_grade = strong
With Postfix 2.6 and 2.7, enable elliptic-curve support in the Postfix SMTP
client and server. This is the default with Postfix >= 2.8.
* Optionally generate non-default EDH parameters for improved security
against pre-computation attacks and for compatibility with Debian-patched
EXIM SMTP clients (these require a minimum 2048-bit length for the non-
export prime). The parameter files are not secret, after all these
parameters are sent to all SMTP clients in the clear. Mode 0644 is fine.
/etc/postfix/main.cf:
# Postfix 2.6 or 2.7 only. This is default with Postfix 2.8 and later.
smtpd_tls_eecdh_grade = strong
Execute as root (prime group generation can take a few seconds to a few
minutes):
EEDDHH CClliieenntt ssuuppppoorrtt ((PPoossttffiixx >>== 22..22))
# cd /etc/postfix
# openssl dhparam -out dh512.tmp 512 && mv dh512.tmp dh512.pem
# openssl dhparam -out dh1024.tmp 1024 && mv dh1024.tmp dh1024.pem
# openssl dhparam -out dh2048.tmp 2048 && mv dh2048.tmp dh2048.pem
# chmod 644 dh512.pem dh1024.pem dh2048.pem
This space intentionally left blank.
You can improve security against pre-computation attacks further by
regenerating the EDH parameters periodically (an hourly or daily cron job
running as root can automate this task).
EEDDHH SSeerrvveerr ssuuppppoorrtt ((PPoossttffiixx >>== 22..22))
Once the parameters are in place, update main.cf as follows:
Optionally generate non-default Postfix SMTP server EDH parameters for improved
security against pre-computation attacks and for compatibility with Debian-
patched Exim SMTP clients that require a >= 2048-bit length for the non-export
prime.
/etc/postfix/main.cf:
smtpd_tls_dh1024_param_file = ${config_directory}/dh2048.pem
smtpd_tls_dh512_param_file = ${config_directory}/dh512.pem
Execute as root (prime group generation can take a few seconds to a few
minutes):
If some of your MSA clients don't support 2048-bit EDH, you may need to
adjust the submission entry in master.cf accordingly:
# cd /etc/postfix
# umask 022
# openssl dhparam -out dh512.tmp 512 && mv dh512.tmp dh512.pem
# openssl dhparam -out dh1024.tmp 1024 && mv dh1024.tmp dh1024.pem
# openssl dhparam -out dh2048.tmp 2048 && mv dh2048.tmp dh2048.pem
# chmod 644 dh512.pem dh1024.pem dh2048.pem
/etc/postfix/master.cf:
submission inet n - n - - smtpd
# Some submission clients may not yet do 2048-bit EDH, if such
# clients use your MSA, configure 1024-bit EDH instead:
-o smtpd_tls_dh1024_param_file=${config_directory}/dh1024.pem
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
...
The Postfix SMTP server EDH parameter files are not secret, after all these
parameters are sent to all remote SMTP clients in the clear. Mode 0644 is fine.
You can improve security against pre-computation attacks further by
regenerating the Postfix SMTP server EDH parameters periodically (an hourly or
daily cron job running the above commands as root can automate this task).
Once the parameters are in place, update main.cf as follows:
/etc/postfix/main.cf:
smtpd_tls_dh1024_param_file = ${config_directory}/dh2048.pem
smtpd_tls_dh512_param_file = ${config_directory}/dh512.pem
If some of your MSA clients don't support 2048-bit EDH, you may need to adjust
the submission entry in master.cf accordingly:
/etc/postfix/master.cf:
submission inet n - n - - smtpd
# Some submission clients may not yet do 2048-bit EDH, if such
# clients use your MSA, configure 1024-bit EDH instead:
-o smtpd_tls_dh1024_param_file=${config_directory}/dh1024.pem
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
...
HHooww ccaann II sseeee tthhaatt aa ccoonnnneeccttiioonn hhaass ffoorrwwaarrdd sseeccrreeccyy??

View File

@ -1,5 +1,3 @@
X
PPoossttffiixx SSAASSLL HHoowwttoo
-------------------------------------------------------------------------------

View File

@ -40,25 +40,31 @@
# Arguments
# .IP create-missing
# Create missing queue directories with ownerships and permissions
# according to the contents of $daemon_directory/postfix-files, using
# the mail_owner and setgid_group parameter settings from the command
# line, process environment or from the installed main.cf file.
# according to the contents of $daemon_directory/postfix-files
# and optionally in $daemon_directory/postfix-files.d/*, using
# the mail_owner and setgid_group parameter settings from the
# command line, process environment or from the installed
# main.cf file.
#
# This is required at Postfix start-up time.
# .IP set-permissions
# Set all file/directory ownerships and permissions according to the
# contents of $daemon_directory/postfix-files, using the mail_owner
# and setgid_group parameter settings from the command line, process
# environment or from the installed main.cf file. Implies create-missing.
# contents of $daemon_directory/postfix-files and optionally
# in $daemon_directory/postfix-files.d/*, using the mail_owner
# and setgid_group parameter settings from the command line,
# process environment or from the installed main.cf file.
# Implies create-missing.
#
# This is required when installing Postfix from a pre-built package,
# or when changing the mail_owner or setgid_group installation parameter
# settings after Postfix is already installed.
# .IP upgrade-permissions
# Update ownership and permission of existing files/directories as
# specified in $daemon_directory/postfix-files, using the mail_owner
# and setgid_group parameter settings from the command line, process
# environment or from the installed main.cf file. Implies create-missing.
# specified in $daemon_directory/postfix-files and optionally
# in $daemon_directory/postfix-files.d/*, using the mail_owner
# and setgid_group parameter settings from the command line,
# process environment or from the installed main.cf file.
# Implies create-missing.
#
# This is required when upgrading an existing Postfix instance.
# .IP upgrade-configuration
@ -174,6 +180,7 @@
# FILES
# $config_directory/main.cf, Postfix installation parameters.
# $daemon_directory/postfix-files, installation control file.
# $daemon_directory/postfix-files.d/*, optional control files.
# $config_directory/install.cf, obsolete configuration file.
# LICENSE
# .ad
@ -437,91 +444,96 @@ test -n "$override" && {
# Use file/directory status information in $daemon_directory/postfix-files.
test -n "$create" && {
exec <$daemon_directory/postfix-files || exit 1
while IFS=: read path type owner group mode flags junk
postfix_files_d=$daemon_directory/postfix-files.d
for postfix_file in $daemon_directory/postfix-files \
`test -d $postfix_files_d && { find $postfix_files_d -type f | sort; }`
do
IFS="$BACKUP_IFS"
set_permission=
# Skip comments. Skip shared files, if updating a secondary instance.
case $path in
[$]*) case "$update_shared_files" in
1) $debug keep non-shared or shared $path;;
*) non_shared=
for name in $NON_SHARED
do
case $path in
"\$$name"*) non_shared=1; break;;
esac
done
case "$non_shared" in
1) $debug keep non-shared $path;;
*) $debug skip shared $path; continue;;
esac;;
esac;;
*) continue;;
esac
# Skip hard links and symbolic links.
case $type in
[hl]) continue;;
[df]) ;;
*) echo unknown type $type for $path in $daemon_directory/postfix-files1>&2; exit 1;;
esac
# Expand $name, and canonicalize null fields.
for name in path owner group flags
exec <$postfix_file || exit 1
while IFS=: read path type owner group mode flags junk
do
eval junk=\${$name}
case $junk in
[$]*) eval $name=$junk;;
-) eval $name=;;
*) ;;
IFS="$BACKUP_IFS"
set_permission=
# Skip comments. Skip shared files, if updating a secondary instance.
case $path in
[$]*) case "$update_shared_files" in
1) $debug keep non-shared or shared $path;;
*) non_shared=
for name in $NON_SHARED
do
case $path in
"\$$name"*) non_shared=1; break;;
esac
done
case "$non_shared" in
1) $debug keep non-shared $path;;
*) $debug skip shared $path; continue;;
esac;;
esac;;
*) continue;;
esac
done
# Skip uninstalled files.
case $path in
no|no/*) continue;;
esac
# Pick up the flags.
case $flags in *u*) upgrade_flag=1;; *) upgrade_flag=;; esac
case $flags in *c*) create_flag=1;; *) create_flag=;; esac
case $flags in *r*) recursive="-R";; *) recursive=;; esac
case $flags in *o*) obsolete_flag=1;; *) obsolete_flag=;; esac
case $flags in *[1i]*) test ! -r "$path" -a "$config_directory" != \
"$def_config_directory" && continue;; esac
# Flag obsolete objects. XXX Solaris 2..9 does not have "test -e".
if [ -n "$obsolete_flag" ]
then
test -r $path -a "$type" != "d" && obsolete="$obsolete $path"
continue;
else
keep_list="$keep_list $path"
fi
# Create missing directories with proper owner/group/mode settings.
if [ -n "$create" -a "$type" = "d" -a -n "$create_flag" -a ! -d "$path" ]
then
mkdir $path || exit 1
set_permission=1
# Update all owner/group/mode settings.
elif [ -n "$set_perms" ]
then
set_permission=1
# Update obsolete owner/group/mode settings.
elif [ -n "$upgrade_perms" -a -n "$upgrade_flag" ]
then
set_permission=1
fi
test -n "$set_permission" && {
chown $recursive $owner $path || exit 1
test -z "$group" || chgrp $recursive $group $path || exit 1
# Don't "chmod -R"; queue file status is encoded in mode bits.
if [ "$type" = "d" -a -n "$recursive" ]
# Skip hard links and symbolic links.
case $type in
[hl]) continue;;
[df]) ;;
*) echo unknown type $type for $path in $postfix_file 1>&2; exit 1;;
esac
# Expand $name, and canonicalize null fields.
for name in path owner group flags
do
eval junk=\${$name}
case $junk in
[$]*) eval $name=$junk;;
-) eval $name=;;
*) ;;
esac
done
# Skip uninstalled files.
case $path in
no|no/*) continue;;
esac
# Pick up the flags.
case $flags in *u*) upgrade_flag=1;; *) upgrade_flag=;; esac
case $flags in *c*) create_flag=1;; *) create_flag=;; esac
case $flags in *r*) recursive="-R";; *) recursive=;; esac
case $flags in *o*) obsolete_flag=1;; *) obsolete_flag=;; esac
case $flags in *[1i]*) test ! -r "$path" -a "$config_directory" != \
"$def_config_directory" && continue;; esac
# Flag obsolete objects. XXX Solaris 2..9 does not have "test -e".
if [ -n "$obsolete_flag" ]
then
find $path -type d -exec chmod $mode "{}" ";"
test -r $path -a "$type" != "d" && obsolete="$obsolete $path"
continue;
else
chmod $mode $path
fi || exit 1
}
keep_list="$keep_list $path"
fi
# Create missing directories with proper owner/group/mode settings.
if [ -n "$create" -a "$type" = "d" -a -n "$create_flag" -a ! -d "$path" ]
then
mkdir $path || exit 1
set_permission=1
# Update all owner/group/mode settings.
elif [ -n "$set_perms" ]
then
set_permission=1
# Update obsolete owner/group/mode settings.
elif [ -n "$upgrade_perms" -a -n "$upgrade_flag" ]
then
set_permission=1
fi
test -n "$set_permission" && {
chown $recursive $owner $path || exit 1
test -z "$group" || chgrp $recursive $group $path || exit 1
# Don't "chmod -R"; queue file status is encoded in mode bits.
if [ "$type" = "d" -a -n "$recursive" ]
then
find $path -type d -exec chmod $mode "{}" ";"
else
chmod $mode $path
fi || exit 1
}
done
IFS="$BACKUP_IFS"
done
IFS="$BACKUP_IFS"
}
# Upgrade existing Postfix configuration files if necessary.

View File

@ -125,24 +125,26 @@ Presently, there are two flavors of "groups" that work with PFS: </p>
<li> <p> <b> Prime-field groups (EDH):</b> The server needs to be
configured with a suitably-large prime and a corresponding "generator".
The acronym for forward secrecy over prime fields is EDH or Ephemeral
Diffie-Hellman (sometimes also abbreviated as DHE). </p>
The acronym for forward secrecy over prime fields is EDH for Ephemeral
Diffie-Hellman (also abbreviated as DHE for Diffie-Hellman Exchange).
</p>
<li> <p> <b> Elliptic-curve groups (EECDH): </b> The server needs
to be configured with a "named curve". These offer better security
at lower computational cost than prime field groups, but are not
as widely implemented. The acronym for the elliptic curve version
is EECDH which is short for Ephemeral Elliptic Curve Diffie-Hellman.
</p>
is EECDH which is short for Ephemeral Elliptic Curve Diffie-Hellman
(also abbreviated as ECDHE for Elliptic Curve Diffie-Hellman
Exchange). </p>
</ul>
<p> It is not essential to know what these are, but one does need
to know that OpenSSL only supports EECDH as of version 1.0.0. Thus
the configuration parameters related to Elliptic Curve forward secrecy
are only available when Postfix is linked with OpenSSL 1.0.0 or
later (provided EC support has not been disabled by the vendor, as
in some versions of RedHat Linux). </p>
to know that OpenSSL supports EECDH with version 1.0.0 or later.
Thus the configuration parameters related to Elliptic-Curve forward
secrecy are available when Postfix is linked with OpenSSL &ge; 1.0.0
(provided EC support has not been disabled by the vendor, as in
some versions of RedHat Linux). </p>
<p> Elliptic curves used in cryptography are typically identified
by a "name" that stands for a set of well-known parameter values,
@ -200,7 +202,7 @@ parameter file and the prime need not actually be 1024 bits long
</ul>
<p> It turns out that (inadvisably-patched in some Debian releases)
Exim SMTP clients enforce a minimum 2048-bit length for the non-export
Exim SMTP clients require a &ge; 2048-bit length for the non-export
prime. See the <a href="#quick-start">quick-start</a> section for
the recommended configuration to work around this issue. </p>
@ -269,10 +271,12 @@ href="TLS_README.html#client_tls_policy">TLS policy</a> table. </p>
<h2><a name="quick-start">Getting started, quick and dirty</a></h2>
<ul>
<h3> EECDH Client and server support (Postfix &ge; 2.6 with OpenSSL
&ge; 1.0.0) </h3>
<li> <p> Postfix 2.6 and 2.7: Enable elliptic-curve support. This
is the default with Postfix &ge; 2.8.
<p> With Postfix 2.6 and 2.7, enable elliptic-curve support in the
Postfix SMTP client and server. This is the default with Postfix
&ge; 2.8.
<blockquote>
<pre>
@ -282,12 +286,16 @@ is the default with Postfix &ge; 2.8.
</pre>
</blockquote>
<li> <p> Optionally generate non-default EDH parameters for improved
security against pre-computation attacks and for compatibility with
Debian-patched EXIM SMTP clients (these require a minimum 2048-bit
length for the non-export prime). The parameter files are not
secret, after all these parameters are sent to all SMTP clients in
the clear. Mode 0644 is fine. </p>
<h3> EDH Client support (Postfix &ge; 2.2) </h3>
<p> This space intentionally left blank. </p>
<h3> EDH Server support (Postfix &ge; 2.2) </h3>
<p> Optionally generate non-default Postfix SMTP server EDH parameters
for improved security against pre-computation attacks and for
compatibility with Debian-patched Exim SMTP clients that require a
&ge; 2048-bit length for the non-export prime. </p>
<p> Execute as root (prime group generation can take a
few seconds to a few minutes): </p>
@ -295,6 +303,7 @@ few seconds to a few minutes): </p>
<blockquote>
<pre>
# cd /etc/postfix
# umask 022
# openssl dhparam -out dh512.tmp 512 && mv dh512.tmp dh512.pem
# openssl dhparam -out dh1024.tmp 1024 && mv dh1024.tmp dh1024.pem
# openssl dhparam -out dh2048.tmp 2048 && mv dh2048.tmp dh2048.pem
@ -302,9 +311,14 @@ few seconds to a few minutes): </p>
</pre>
</blockquote>
<p> The Postfix SMTP server EDH parameter files are not secret,
after all these parameters are sent to all remote SMTP clients in
the clear. Mode 0644 is fine. </p>
<p> You can improve security against pre-computation attacks further
by regenerating the EDH parameters periodically (an hourly or daily
cron job running as root can automate this task). </p>
by regenerating the Postfix SMTP server EDH parameters periodically
(an hourly or daily cron job running the above commands as root can
automate this task). </p>
<p> Once the parameters are in place, update <a href="postconf.5.html">main.cf</a> as follows: </p>
@ -332,8 +346,6 @@ need to adjust the submission entry in <a href="master.5.html">master.cf</a> acc
</pre>
</blockquote>
</ul>
<h2><a name="test">How can I see that a connection has forward
secrecy? </a> </h2>
@ -392,7 +404,7 @@ OpenSSL 1.0.1e. The list is sorted in the default Postfix preference
order. It excludes null ciphers that only authenticate and don't
encrypt, together with export and low-grade ciphers whose encryption
is too weak to offer meaningful secrecy. The first column shows the
cipher name, and the second shows the key exchange method. </p>
cipher name, and the second shows the key exchange method. </p>
<blockquote>
<pre>

View File

@ -1,4 +1,4 @@
X<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<head>

View File

@ -125,24 +125,26 @@ Presently, there are two flavors of "groups" that work with PFS: </p>
<li> <p> <b> Prime-field groups (EDH):</b> The server needs to be
configured with a suitably-large prime and a corresponding "generator".
The acronym for forward secrecy over prime fields is EDH or Ephemeral
Diffie-Hellman (sometimes also abbreviated as DHE). </p>
The acronym for forward secrecy over prime fields is EDH for Ephemeral
Diffie-Hellman (also abbreviated as DHE for Diffie-Hellman Exchange).
</p>
<li> <p> <b> Elliptic-curve groups (EECDH): </b> The server needs
to be configured with a "named curve". These offer better security
at lower computational cost than prime field groups, but are not
as widely implemented. The acronym for the elliptic curve version
is EECDH which is short for Ephemeral Elliptic Curve Diffie-Hellman.
</p>
is EECDH which is short for Ephemeral Elliptic Curve Diffie-Hellman
(also abbreviated as ECDHE for Elliptic Curve Diffie-Hellman
Exchange). </p>
</ul>
<p> It is not essential to know what these are, but one does need
to know that OpenSSL only supports EECDH as of version 1.0.0. Thus
the configuration parameters related to Elliptic Curve forward secrecy
are only available when Postfix is linked with OpenSSL 1.0.0 or
later (provided EC support has not been disabled by the vendor, as
in some versions of RedHat Linux). </p>
to know that OpenSSL supports EECDH with version 1.0.0 or later.
Thus the configuration parameters related to Elliptic-Curve forward
secrecy are available when Postfix is linked with OpenSSL &ge; 1.0.0
(provided EC support has not been disabled by the vendor, as in
some versions of RedHat Linux). </p>
<p> Elliptic curves used in cryptography are typically identified
by a "name" that stands for a set of well-known parameter values,
@ -200,7 +202,7 @@ parameter file and the prime need not actually be 1024 bits long
</ul>
<p> It turns out that (inadvisably-patched in some Debian releases)
Exim SMTP clients enforce a minimum 2048-bit length for the non-export
Exim SMTP clients require a &ge; 2048-bit length for the non-export
prime. See the <a href="#quick-start">quick-start</a> section for
the recommended configuration to work around this issue. </p>
@ -269,10 +271,12 @@ href="TLS_README.html#client_tls_policy">TLS policy</a> table. </p>
<h2><a name="quick-start">Getting started, quick and dirty</a></h2>
<ul>
<h3> EECDH Client and server support (Postfix &ge; 2.6 with OpenSSL
&ge; 1.0.0) </h3>
<li> <p> Postfix 2.6 and 2.7: Enable elliptic-curve support. This
is the default with Postfix &ge; 2.8.
<p> With Postfix 2.6 and 2.7, enable elliptic-curve support in the
Postfix SMTP client and server. This is the default with Postfix
&ge; 2.8.
<blockquote>
<pre>
@ -282,12 +286,16 @@ is the default with Postfix &ge; 2.8.
</pre>
</blockquote>
<li> <p> Optionally generate non-default EDH parameters for improved
security against pre-computation attacks and for compatibility with
Debian-patched EXIM SMTP clients (these require a minimum 2048-bit
length for the non-export prime). The parameter files are not
secret, after all these parameters are sent to all SMTP clients in
the clear. Mode 0644 is fine. </p>
<h3> EDH Client support (Postfix &ge; 2.2) </h3>
<p> This space intentionally left blank. </p>
<h3> EDH Server support (Postfix &ge; 2.2) </h3>
<p> Optionally generate non-default Postfix SMTP server EDH parameters
for improved security against pre-computation attacks and for
compatibility with Debian-patched Exim SMTP clients that require a
&ge; 2048-bit length for the non-export prime. </p>
<p> Execute as root (prime group generation can take a
few seconds to a few minutes): </p>
@ -295,6 +303,7 @@ few seconds to a few minutes): </p>
<blockquote>
<pre>
# cd /etc/postfix
# umask 022
# openssl dhparam -out dh512.tmp 512 && mv dh512.tmp dh512.pem
# openssl dhparam -out dh1024.tmp 1024 && mv dh1024.tmp dh1024.pem
# openssl dhparam -out dh2048.tmp 2048 && mv dh2048.tmp dh2048.pem
@ -302,9 +311,14 @@ few seconds to a few minutes): </p>
</pre>
</blockquote>
<p> The Postfix SMTP server EDH parameter files are not secret,
after all these parameters are sent to all remote SMTP clients in
the clear. Mode 0644 is fine. </p>
<p> You can improve security against pre-computation attacks further
by regenerating the EDH parameters periodically (an hourly or daily
cron job running as root can automate this task). </p>
by regenerating the Postfix SMTP server EDH parameters periodically
(an hourly or daily cron job running the above commands as root can
automate this task). </p>
<p> Once the parameters are in place, update main.cf as follows: </p>
@ -332,8 +346,6 @@ need to adjust the submission entry in master.cf accordingly: </p>
</pre>
</blockquote>
</ul>
<h2><a name="test">How can I see that a connection has forward
secrecy? </a> </h2>
@ -392,7 +404,7 @@ OpenSSL 1.0.1e. The list is sorted in the default Postfix preference
order. It excludes null ciphers that only authenticate and don't
encrypt, together with export and low-grade ciphers whose encryption
is too weak to offer meaningful secrecy. The first column shows the
cipher name, and the second shows the key exchange method. </p>
cipher name, and the second shows the key exchange method. </p>
<blockquote>
<pre>

View File

@ -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 "20131228"
#define MAIL_RELEASE_DATE "20140104"
#define MAIL_VERSION_NUMBER "2.11"
#ifdef SNAPSHOT

View File

@ -301,9 +301,7 @@ extern HBC_CHECKS *smtp_body_checks; /* limited body checks */
typedef struct SMTP_SESSION {
VSTREAM *stream; /* network connection */
char *dest; /* nexthop or fallback */
char *host; /* mail exchanger */
char *addr; /* mail exchanger */
SMTP_ITERATOR *iterator; /* dest, host, addr, port */
char *namaddr; /* mail exchanger */
char *helo; /* helo response */
unsigned port; /* network byte order */

View File

@ -363,7 +363,8 @@ SMTP_RESP *smtp_chat_resp(SMTP_SESSION *session)
session->namaddrport, STR(session->buffer));
if (var_helpful_warnings)
msg_warn("to prevent loss of mail, turn off command pipelining "
"for %s with the %s parameter", session->addr,
"for %s with the %s parameter",
STR(session->iterator->addr),
SMTP_X(EHLO_DIS_MAPS));
}
}

View File

@ -159,7 +159,7 @@ static SMTP_SESSION *smtp_connect_unix(SMTP_ITERATOR *iter, DSN_BUF *why,
if (msg_verbose)
msg_info("%s: trying: %s...", myname, addr);
return (smtp_connect_sock(sock, (struct sockaddr *) & sock_un,
return (smtp_connect_sock(sock, (struct sockaddr *) &sock_un,
sizeof(sock_un), iter, why, sess_flags));
}
@ -170,7 +170,7 @@ static SMTP_SESSION *smtp_connect_addr(SMTP_ITERATOR *iter, DSN_BUF *why,
{
const char *myname = "smtp_connect_addr";
struct sockaddr_storage ss; /* remote */
struct sockaddr *sa = (struct sockaddr *) & ss;
struct sockaddr *sa = (struct sockaddr *) &ss;
SOCKADDR_SIZE salen = sizeof(ss);
MAI_HOSTADDR_STR hostaddr;
DNS_RR *addr = iter->rr;
@ -274,7 +274,7 @@ static SMTP_SESSION *smtp_connect_addr(SMTP_ITERATOR *iter, DSN_BUF *why,
/* smtp_connect_sock - connect a socket over some transport */
static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr * sa,
static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr *sa,
int salen,
SMTP_ITERATOR *iter,
DSN_BUF *why,
@ -668,7 +668,7 @@ static int smtp_reuse_session(SMTP_STATE *state, DNS_RR **addr_list,
if (*addr_list && SMTP_RCPT_LEFT(state) > 0
&& (session = smtp_reuse_nexthop(state, SMTP_KEY_MASK_SCACHE_DEST_LABEL)) != 0) {
session_count = 1;
smtp_update_addr_list(addr_list, session->addr, session_count);
smtp_update_addr_list(addr_list, STR(iter->addr), session_count);
if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP)
&& *addr_list == 0)
state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER;
@ -726,7 +726,7 @@ static int smtp_reuse_session(SMTP_STATE *state, DNS_RR **addr_list,
SMTP_KEY_MASK_SCACHE_ENDP_LABEL)) != 0) {
session->features |= SMTP_FEATURE_BEST_MX;
session_count += 1;
smtp_update_addr_list(addr_list, session->addr, session_count);
smtp_update_addr_list(addr_list, STR(iter->addr), session_count);
if (*addr_list == 0)
next = 0;
if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP)

View File

@ -261,6 +261,7 @@ int smtp_helo(SMTP_STATE *state)
const char *myname = "smtp_helo";
SMTP_SESSION *session = state->session;
DELIVER_REQUEST *request = state->request;
SMTP_ITERATOR *iter = state->iterator;
SMTP_RESP *resp;
SMTP_RESP fake;
int except;
@ -322,7 +323,7 @@ int smtp_helo(SMTP_STATE *state)
STR(resp->dsn_buf)[0] = '4';
/* FALLTHROUGH */
default:
return (smtp_site_fail(state, session->host, resp,
return (smtp_site_fail(state, STR(iter->host), resp,
"host %s refused to talk to me: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
@ -350,7 +351,7 @@ int smtp_helo(SMTP_STATE *state)
if (smtp_pix_bug_maps != 0
&& (pix_bug_words =
maps_find(smtp_pix_bug_maps,
state->session->addr, 0)) != 0) {
STR(iter->addr), 0)) != 0) {
pix_bug_source = SMTP_X(PIX_BUG_MAPS);
} else {
pix_bug_words = var_smtp_pix_bug_words;
@ -415,7 +416,7 @@ int smtp_helo(SMTP_STATE *state)
smtp_chat_cmd(session, "EHLO %s", var_smtp_helo_name);
if ((resp = smtp_chat_resp(session))->code / 100 != 2) {
if (resp->code == 421)
return (smtp_site_fail(state, session->host, resp,
return (smtp_site_fail(state, STR(iter->host), resp,
"host %s refused to talk to me: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
@ -427,7 +428,7 @@ int smtp_helo(SMTP_STATE *state)
where = "performing the HELO handshake";
smtp_chat_cmd(session, "HELO %s", var_smtp_helo_name);
if ((resp = smtp_chat_resp(session))->code / 100 != 2)
return (smtp_site_fail(state, session->host, resp,
return (smtp_site_fail(state, STR(iter->host), resp,
"host %s refused to talk to me: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
@ -436,7 +437,7 @@ int smtp_helo(SMTP_STATE *state)
where = "performing the LHLO handshake";
smtp_chat_cmd(session, "LHLO %s", var_smtp_helo_name);
if ((resp = smtp_chat_resp(session))->code / 100 != 2)
return (smtp_site_fail(state, session->host, resp,
return (smtp_site_fail(state, STR(iter->host), resp,
"host %s refused to talk to me: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
@ -454,12 +455,12 @@ int smtp_helo(SMTP_STATE *state)
*/
if (smtp_ehlo_dis_maps == 0
|| (ehlo_words = maps_find(smtp_ehlo_dis_maps,
state->session->addr, 0)) == 0)
STR(iter->addr), 0)) == 0)
ehlo_words = var_smtp_ehlo_dis_words;
if (smtp_ehlo_dis_maps && smtp_ehlo_dis_maps->error) {
msg_warn("%s: %s map lookup error for %s",
session->state->request->queue_id,
smtp_ehlo_dis_maps->title, state->session->addr);
smtp_ehlo_dis_maps->title, STR(iter->addr));
vstream_longjmp(session->stream, SMTP_ERR_DATA);
}
discard_mask = ehlo_mask(ehlo_words);
@ -643,7 +644,7 @@ int smtp_helo(SMTP_STATE *state)
if ((session->features & SMTP_FEATURE_STARTTLS) &&
var_smtp_tls_note_starttls_offer &&
session->tls->level <= TLS_LEV_NONE)
msg_info("Host offered STARTTLS: [%s]", session->host);
msg_info("Host offered STARTTLS: [%s]", STR(iter->host));
/*
* Decide whether or not to send STARTTLS.
@ -690,7 +691,7 @@ int smtp_helo(SMTP_STATE *state)
*/
session->features &= ~SMTP_FEATURE_STARTTLS;
if (TLS_REQUIRED(session->tls->level))
return (smtp_site_fail(state, session->host, resp,
return (smtp_site_fail(state, STR(iter->host), resp,
"TLS is required, but host %s refused to start TLS: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
@ -739,6 +740,7 @@ int smtp_helo(SMTP_STATE *state)
static int smtp_start_tls(SMTP_STATE *state)
{
SMTP_SESSION *session = state->session;
SMTP_ITERATOR *iter = state->iterator;
TLS_CLIENT_START_PROPS tls_props;
VSTRING *serverid;
SMTP_RESP fake;
@ -805,7 +807,7 @@ static int smtp_start_tls(SMTP_STATE *state)
timeout = var_smtp_starttls_tmout,
tls_level = session->tls->level,
nexthop = session->tls_nexthop,
host = session->host,
host = STR(iter->host),
namaddr = session->namaddrport,
serverid = vstring_str(serverid),
helo = session->helo,
@ -1142,6 +1144,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
const char *myname = "smtp_loop";
DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session;
SMTP_ITERATOR *iter = state->iterator;
SMTP_RESP *resp;
RECIPIENT *rcpt;
VSTRING *next_command = vstring_alloc(100);
@ -1652,7 +1655,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
*/
case SMTP_STATE_MAIL:
if (resp->code / 100 != 2) {
smtp_mesg_fail(state, session->host, resp,
smtp_mesg_fail(state, STR(iter->host), resp,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
@ -1724,7 +1727,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
smtp_rcpt_done(state, resp, rcpt);
}
} else {
smtp_rcpt_fail(state, rcpt, session->host, resp,
smtp_rcpt_fail(state, rcpt, STR(iter->host), resp,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
@ -1746,7 +1749,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
case SMTP_STATE_DATA:
if (resp->code / 100 != 3) {
if (nrcpt > 0)
smtp_mesg_fail(state, session->host, resp,
smtp_mesg_fail(state, STR(iter->host), resp,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
@ -1770,7 +1773,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
if (smtp_mode) {
if (nrcpt > 0) {
if (resp->code / 100 != 2) {
smtp_mesg_fail(state, session->host, resp,
smtp_mesg_fail(state, STR(iter->host), resp,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
@ -1797,7 +1800,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
rcpt = request->rcpt_list.info
+ survivors[recv_done++];
if (resp->code / 100 != 2) {
smtp_rcpt_fail(state, rcpt, session->host, resp,
smtp_rcpt_fail(state, rcpt, STR(iter->host), resp,
"host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),

View File

@ -132,6 +132,7 @@ void smtp_rcpt_done(SMTP_STATE *state, SMTP_RESP *resp, RECIPIENT *rcpt)
{
DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session;
SMTP_ITERATOR *iter = state->iterator;
DSN_BUF *why = state->why;
const char *dsn_action = "relayed";
int status;
@ -162,7 +163,7 @@ void smtp_rcpt_done(SMTP_STATE *state, SMTP_RESP *resp, RECIPIENT *rcpt)
*
* Note: the DSN action is ignored in case of address probes.
*/
dsb_update(why, resp->dsn, dsn_action, DSB_MTYPE_DNS, session->host,
dsb_update(why, resp->dsn, dsn_action, DSB_MTYPE_DNS, STR(iter->host),
DSB_DTYPE_SMTP, resp->str, "%s", resp->str);
status = sent(DEL_REQ_TRACE_FLAGS(request->flags),

View File

@ -155,6 +155,7 @@ static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd,
const char *label)
{
const char *myname = "smtp_reuse_common";
SMTP_ITERATOR *iter = state->iterator;
SMTP_SESSION *session;
/*
@ -200,7 +201,7 @@ static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd,
/*
* Update the list of used cached addresses.
*/
htable_enter(state->cache_used, session->addr, (char *) 0);
htable_enter(state->cache_used, STR(iter->addr), (char *) 0);
return (session);
}

View File

@ -230,11 +230,12 @@ static int smtp_sasl_auth_cache_valid_value(SMTP_SASL_AUTH_CACHE *auth_cache,
int smtp_sasl_auth_cache_find(SMTP_SASL_AUTH_CACHE *auth_cache,
const SMTP_SESSION *session)
{
SMTP_ITERATOR *iter = session->iterator;
char *key;
const char *entry;
int valid = 0;
key = smtp_sasl_auth_cache_make_key(session->host, session->sasl_username);
key = smtp_sasl_auth_cache_make_key(STR(iter->host), session->sasl_username);
if ((entry = dict_get(auth_cache->dict, key)) != 0)
if ((valid = smtp_sasl_auth_cache_valid_value(auth_cache, entry,
session->sasl_passwd)) == 0)
@ -255,10 +256,11 @@ void smtp_sasl_auth_cache_store(SMTP_SASL_AUTH_CACHE *auth_cache,
const SMTP_SESSION *session,
const SMTP_RESP *resp)
{
SMTP_ITERATOR *iter = session->iterator;
char *key;
char *value;
key = smtp_sasl_auth_cache_make_key(session->host, session->sasl_username);
key = smtp_sasl_auth_cache_make_key(STR(iter->host), session->sasl_username);
value = smtp_sasl_auth_cache_make_value(session->sasl_passwd,
resp->dsn, resp->str);
dict_put(auth_cache->dict, key, value);

View File

@ -158,6 +158,7 @@ int smtp_sasl_passwd_lookup(SMTP_SESSION *session)
{
const char *myname = "smtp_sasl_passwd_lookup";
SMTP_STATE *state = session->state;
SMTP_ITERATOR *iter = session->iterator;
const char *value;
char *passwd;
@ -187,10 +188,10 @@ int smtp_sasl_passwd_lookup(SMTP_SESSION *session)
state->request->sender, (char **) 0)) != 0)
|| (smtp_sasl_passwd_map->error == 0
&& (value = maps_find(smtp_sasl_passwd_map,
session->host, 0)) != 0)
STR(iter->host), 0)) != 0)
|| (smtp_sasl_passwd_map->error == 0
&& (value = maps_find(smtp_sasl_passwd_map,
session->dest, 0)) != 0)) {
STR(iter->dest), 0)) != 0)) {
if (session->sasl_username)
myfree(session->sasl_username);
session->sasl_username = mystrdup(value);
@ -200,7 +201,7 @@ int smtp_sasl_passwd_lookup(SMTP_SESSION *session)
session->sasl_passwd = mystrdup(passwd ? passwd : "");
if (msg_verbose)
msg_info("%s: host `%s' user `%s' pass `%s'",
myname, session->host,
myname, STR(iter->host),
session->sasl_username, session->sasl_passwd);
return (1);
} else if (smtp_sasl_passwd_map->error) {
@ -210,7 +211,7 @@ int smtp_sasl_passwd_lookup(SMTP_SESSION *session)
} else {
if (msg_verbose)
msg_info("%s: no auth info found (sender=`%s', host=`%s')",
myname, state->request->sender, session->host);
myname, state->request->sender, STR(iter->host));
return (0);
}
}
@ -284,6 +285,7 @@ void smtp_sasl_start(SMTP_SESSION *session, const char *sasl_opts_name,
const char *sasl_opts_val)
{
XSASL_CLIENT_CREATE_ARGS create_args;
SMTP_ITERATOR *iter = session->iterator;
if (msg_verbose)
msg_info("starting new SASL client");
@ -291,7 +293,7 @@ void smtp_sasl_start(SMTP_SESSION *session, const char *sasl_opts_name,
XSASL_CLIENT_CREATE(smtp_sasl_impl, &create_args,
stream = session->stream,
service = var_procname,
server_name = session->host,
server_name = STR(iter->host),
security_options = sasl_opts_val)) == 0)
msg_fatal("SASL per-connection initialization failed");
session->sasl_reply = vstring_alloc(20);
@ -302,6 +304,7 @@ void smtp_sasl_start(SMTP_SESSION *session, const char *sasl_opts_name,
int smtp_sasl_authenticate(SMTP_SESSION *session, DSN_BUF *why)
{
const char *myname = "smtp_sasl_authenticate";
SMTP_ITERATOR *iter = session->iterator;
SMTP_RESP *resp;
const char *mechanism;
int result;
@ -330,9 +333,9 @@ int smtp_sasl_authenticate(SMTP_SESSION *session, DSN_BUF *why)
if (var_smtp_sasl_auth_soft_bounce && resp_dsn[0] == '5')
resp_dsn[0] = '4';
dsb_update(why, resp_dsn, DSB_DEF_ACTION, DSB_MTYPE_DNS,
session->host, var_procname, resp_str,
STR(iter->host), var_procname, resp_str,
"SASL [CACHED] authentication failed; server %s said: %s",
session->host, resp_str);
STR(iter->host), resp_str);
return (0);
}
#endif
@ -416,7 +419,7 @@ int smtp_sasl_authenticate(SMTP_SESSION *session, DSN_BUF *why)
if (var_smtp_sasl_auth_soft_bounce && resp->code / 100 == 5)
STR(resp->dsn_buf)[0] = '4';
dsb_update(why, resp->dsn, DSB_DEF_ACTION,
DSB_MTYPE_DNS, session->host,
DSB_MTYPE_DNS, STR(iter->host),
var_procname, resp->str,
"SASL authentication failed; server %s said: %s",
session->namaddr, resp->str);

View File

@ -123,16 +123,13 @@ SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, SMTP_ITERATOR *iter,
time_t start, int flags)
{
SMTP_SESSION *session;
const char *dest = STR(iter->dest);
const char *host = STR(iter->host);
const char *addr = STR(iter->addr);
unsigned port = iter->port;
session = (SMTP_SESSION *) mymalloc(sizeof(*session));
session->stream = stream;
session->dest = mystrdup(dest);
session->host = mystrdup(host);
session->addr = mystrdup(addr);
session->iterator = iter;
session->namaddr = concatenate(host, "[", addr, "]", (char *) 0);
session->helo = 0;
session->port = port;
@ -191,9 +188,6 @@ void smtp_session_free(SMTP_SESSION *session)
#endif
if (session->stream)
vstream_fclose(session->stream);
myfree(session->dest);
myfree(session->host);
myfree(session->addr);
myfree(session->namaddr);
myfree(session->namaddrport);
if (session->helo)
@ -221,6 +215,7 @@ void smtp_session_free(SMTP_SESSION *session)
int smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop,
VSTRING *endp_prop)
{
SMTP_ITERATOR *iter = session->iterator;
int fd;
/*
@ -238,7 +233,7 @@ int smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop,
*
*/
vstring_sprintf(dest_prop, "%s\n%s\n%s\n%u",
session->dest, session->host, session->addr,
STR(iter->dest), STR(iter->host), STR(iter->addr),
session->features & SMTP_FEATURE_DESTINATION_MASK);
/*

View File

@ -19,7 +19,7 @@ INCL =
LIB = libtls.a
TESTPROG= tls_dh tls_mgr tls_rsa tls_dane
LIBS = ../../lib/libglobal.a ../../lib/libutil.a ../../lib/libdns.a
LIBS = ../../lib/libdns.a ../../lib/libglobal.a ../../lib/libutil.a
LIB_DIR = ../../lib
INC_DIR = ../../include
MAKES =

View File

@ -705,11 +705,11 @@ const char *dict_cache_name(DICT_CACHE *cp)
"\n\treset (discard pending requests)" \
"\n\trun (execute pending requests in interleaved order)" \
"\n\n\tTo add a pending request:" \
"\n\tquery <key-prefix> <count> (negative to reverse order)" \
"\n\tupdate <key-prefix> <count> (negative to reverse order)" \
"\n\tdelete <key-prefix> <count> (negative to reverse order)" \
"\n\tpurge <key-prefix>" \
"\n\tcount <key-prefix>"
"\n\tquery <key-suffix> <count> (negative to reverse order)" \
"\n\tupdate <key-suffix> <count> (negative to reverse order)" \
"\n\tdelete <key-suffix> <count> (negative to reverse order)" \
"\n\tpurge <key-suffix>" \
"\n\tcount <key-suffix>"
/*
* For realism, open the cache with the same flags as postscreen(8) and
@ -725,7 +725,7 @@ typedef struct DICT_CACHE_SREQ {
int flags; /* per-request: reverse, purge */
char *cmd; /* command for status report */
void (*action) (struct DICT_CACHE_SREQ *, DICT_CACHE *, VSTRING *);
char *prefix; /* key prefix */
char *suffix; /* key suffix */
int done; /* progress indicator */
int todo; /* number of entries to process */
int first_next; /* first/next */
@ -772,9 +772,9 @@ static void make_tagged_key(VSTRING *bp, DICT_CACHE_SREQ *cp)
msg_panic("make_tagged_key: bad done count: %d", cp->done);
if (cp->todo < 1)
msg_panic("make_tagged_key: bad todo count: %d", cp->todo);
vstring_sprintf(bp, "%s-%d", cp->prefix,
vstring_sprintf(bp, "%d-%s",
(cp->flags & DICT_CACHE_SREQ_FLAG_REVERSE) ?
cp->todo - cp->done - 1 : cp->done);
cp->todo - cp->done - 1 : cp->done, cp->suffix);
}
/* create_requests - create request list */
@ -793,7 +793,7 @@ static DICT_CACHE_TEST *create_requests(int count)
cp->flags = 0;
cp->cmd = 0;
cp->action = 0;
cp->prefix = 0;
cp->suffix = 0;
cp->todo = 0;
cp->first_next = DICT_SEQ_FUN_FIRST;
}
@ -815,9 +815,9 @@ static void reset_requests(DICT_CACHE_TEST *tp)
cp->cmd = 0;
}
cp->action = 0;
if (cp->prefix) {
myfree(cp->prefix);
cp->prefix = 0;
if (cp->suffix) {
myfree(cp->suffix);
cp->suffix = 0;
}
cp->todo = 0;
cp->first_next = DICT_SEQ_FUN_FIRST;
@ -876,8 +876,11 @@ static void show_status(DICT_CACHE_TEST *tp, DICT_CACHE *dp)
#endif
vstream_printf("cache\t%s\n", dp ? dp->name : "(none)");
vstream_printf("%s\t%s\t%s\t%s\t%s\t%s\n",
"cmd", "dir", "prefix", "count", "done", "first/next");
if (tp->used == 0)
vstream_printf("No pending requests\n");
else
vstream_printf("%s\t%s\t%s\t%s\t%s\t%s\n",
"cmd", "dir", "suffix", "count", "done", "first/next");
for (cp = tp->job_list; cp < tp->job_list + tp->used; cp++)
if (cp->todo > 0)
@ -885,7 +888,7 @@ static void show_status(DICT_CACHE_TEST *tp, DICT_CACHE *dp)
cp->cmd,
(cp->flags & DICT_CACHE_SREQ_FLAG_REVERSE) ?
"reverse" : "forward",
cp->prefix ? cp->prefix : "(null)", cp->todo,
cp->suffix ? cp->suffix : "(null)", cp->todo,
cp->done, cp->first_next);
}
@ -936,21 +939,21 @@ static void delete_action(DICT_CACHE_SREQ *cp, DICT_CACHE *dp, VSTRING *bp)
cp->done += 1;
}
/* iter_action - iterate over cache and act on entries with given prefix */
/* iter_action - iterate over cache and act on entries with given suffix */
static void iter_action(DICT_CACHE_SREQ *cp, DICT_CACHE *dp, VSTRING *bp)
{
const char *cache_key;
const char *cache_val;
const char *what;
int len;
const char *suffix;
if (dict_cache_sequence(dp, cp->first_next, &cache_key, &cache_val) == 0) {
if (strcmp(cache_key, cache_val) != 0)
msg_warn("value \"%s\" differs from key \"%s\"",
cache_val, cache_key);
len = strlen(cp->prefix);
if (strncmp(cache_key, cp->prefix, len) == 0 && cache_key[len] == '-') {
suffix = cache_key + strspn(cache_key, "0123456789");
if (suffix[0] == '-' && strcmp(suffix + 1, cp->suffix) == 0) {
cp->done += 1;
cp->todo = cp->done + 1; /* XXX */
if ((cp->flags & DICT_CACHE_SREQ_FLAG_PURGE)
@ -967,7 +970,7 @@ static void iter_action(DICT_CACHE_SREQ *cp, DICT_CACHE *dp, VSTRING *bp)
if (dp->error)
msg_warn("%s error after %d: %m", what, cp->done);
else
vstream_printf("prefix=%s %s=%d\n", cp->prefix, what, cp->done);
vstream_printf("suffix=%s %s=%d\n", cp->suffix, what, cp->done);
cp->todo = 0;
}
}
@ -1001,7 +1004,7 @@ static void add_request(DICT_CACHE_TEST *tp, ARGV *argv)
int req_flags;
int count;
char *cmd = argv->argv[0];
char *prefix = (argv->argc > 1 ? argv->argv[1] : 0);
char *suffix = (argv->argc > 1 ? argv->argv[1] : 0);
char *todo = (argv->argc > 2 ? argv->argv[2] : "1"); /* XXX */
if (tp->used >= tp->size) {
@ -1034,8 +1037,8 @@ static void add_request(DICT_CACHE_TEST *tp, ARGV *argv)
cp = tp->job_list + tp->used;
cp->cmd = mystrdup(cmd);
cp->action = rp->action;
if (prefix)
cp->prefix = mystrdup(prefix);
if (suffix)
cp->suffix = mystrdup(suffix);
cp->done = 0;
cp->flags = req_flags;
cp->todo = count;

View File

@ -82,7 +82,8 @@
/*
/* slmdb_cursor_get() is an mdb_cursor_get() wrapper with
/* automatic error recovery. The result value is an LMDB
/* status code (zero in case of success).
/* status code (zero in case of success). This wrapper supports
/* only one cursor per database.
/*
/* slmdb_fd() returns the file descriptor for the specified
/* database. This may be used for file status queries or
@ -198,6 +199,8 @@
#include <unistd.h>
#include <limits.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
/* Application-specific. */
@ -256,6 +259,80 @@
return (status); \
} while (0)
/*
* We must close the cursor's read transaction before writing to the
* database with MDB_NOLOCK, and before changing the memory map size. Our
* database iterator saves the key under the last cursor position, and
* restores the cursor if needed. This supports only one cursor per
* database.
*/
/* slmdb_cursor_close - close cursor and its read transaction */
static void slmdb_cursor_close(SLMDB *slmdb)
{
MDB_txn *txn;
/*
* Close the cursor and its read transaction. We can restore it later
* from the saved key information.
*/
txn = mdb_cursor_txn(slmdb->cursor);
mdb_cursor_close(slmdb->cursor);
slmdb->cursor = 0;
mdb_txn_abort(txn);
}
/* slmdb_saved_key_init - initialize saved key info */
static void slmdb_saved_key_init(SLMDB *slmdb)
{
slmdb->saved_key.mv_data = 0;
slmdb->saved_key_size = 0;
}
/* slmdb_saved_key_free - destroy saved key info */
static void slmdb_saved_key_free(SLMDB *slmdb)
{
free(slmdb->saved_key.mv_data);
slmdb->saved_key.mv_data = 0;
slmdb->saved_key_size = 0;
}
#define HAVE_SLMDB_SAVED_KEY(s) ((s)->saved_key.mv_data != 0)
/* slmdb_saved_key_assign - copy the saved key */
static int slmdb_saved_key_assign(SLMDB *slmdb, MDB_val *key_val)
{
/*
* Extend the buffer to fit the key, so that we can avoid malloc()
* overhead most of the time.
*/
if (slmdb->saved_key_size < key_val->mv_size) {
if (slmdb->saved_key.mv_data == 0)
slmdb->saved_key.mv_data = malloc(key_val->mv_size);
else
slmdb->saved_key.mv_data =
realloc(slmdb->saved_key.mv_data, key_val->mv_size);
if (slmdb->saved_key.mv_data == 0) {
slmdb->saved_key_size = 0;
return (ENOMEM);
} else {
slmdb->saved_key_size = key_val->mv_size;
}
}
/*
* Copy the key under the cursor.
*/
memcpy(slmdb->saved_key.mv_data, key_val->mv_data, key_val->mv_size);
slmdb->saved_key.mv_size = key_val->mv_size;
return (0);
}
/* slmdb_prepare - LMDB-specific (re)initialization before actual access */
static int slmdb_prepare(SLMDB *slmdb)
@ -295,6 +372,13 @@ static int slmdb_recover(SLMDB *slmdb, int status)
{
MDB_envinfo info;
/*
* Close the cursor and its read transaction before changing the memory
* map size. We can restore it later with the saved key information.
*/
if (slmdb->cursor != 0)
slmdb_cursor_close(slmdb);
/*
* Recover bulk transactions only if they can be restarted. Limit the
* number of recovery attempts per slmdb(3) API request.
@ -456,6 +540,15 @@ int slmdb_put(SLMDB *slmdb, MDB_val *mdb_key,
else if ((status = slmdb_txn_begin(slmdb, 0, &txn)) != 0)
SLMDB_API_RETURN(slmdb, status);
/*
* Before doing a non-bulk write transaction in MDB_NOLOCK mode, close a
* cursor and its read transaction. We can restore it later with the
* saved key information.
*/
if (slmdb->cursor != 0 && slmdb->txn == 0
&& (slmdb->lmdb_flags & MDB_NOLOCK))
slmdb_cursor_close(slmdb);
/*
* Do the update.
*/
@ -493,6 +586,15 @@ int slmdb_del(SLMDB *slmdb, MDB_val *mdb_key)
else if ((status = slmdb_txn_begin(slmdb, 0, &txn)) != 0)
SLMDB_API_RETURN(slmdb, status);
/*
* Before doing a non-bulk write transaction in MDB_NOLOCK mode, close a
* cursor and its read transaction. We can restore it later with the
* saved key information.
*/
if (slmdb->cursor != 0 && slmdb->txn == 0
&& (slmdb->lmdb_flags & MDB_NOLOCK))
slmdb_cursor_close(slmdb);
/*
* Do the update.
*/
@ -527,13 +629,27 @@ int slmdb_cursor_get(SLMDB *slmdb, MDB_val *mdb_key,
* Open a read transaction and cursor if needed.
*/
if (slmdb->cursor == 0) {
slmdb_txn_begin(slmdb, MDB_RDONLY, &txn);
if ((status = slmdb_txn_begin(slmdb, MDB_RDONLY, &txn)) != 0)
SLMDB_API_RETURN(slmdb, status);
if ((status = mdb_cursor_open(txn, slmdb->dbi, &slmdb->cursor)) != 0) {
mdb_txn_abort(txn);
if ((status = slmdb_recover(slmdb, status)) == 0)
status = slmdb_cursor_get(slmdb, mdb_key, mdb_value, op);
SLMDB_API_RETURN(slmdb, status);
}
/*
* Restore the cursor to the saved key position.
*/
if (HAVE_SLMDB_SAVED_KEY(slmdb) && op != MDB_FIRST) {
if ((status = mdb_cursor_get(slmdb->cursor, &slmdb->saved_key,
(MDB_val *) 0, MDB_SET)) != 0) {
slmdb_cursor_close(slmdb);
if ((status = slmdb_recover(slmdb, status)) == 0)
status = slmdb_cursor_get(slmdb, mdb_key, mdb_value, op);
SLMDB_API_RETURN(slmdb, status);
}
}
}
/*
@ -541,15 +657,20 @@ int slmdb_cursor_get(SLMDB *slmdb, MDB_val *mdb_key,
*/
status = mdb_cursor_get(slmdb->cursor, mdb_key, mdb_value, op);
/*
* Save the cursor position. This can fail only with ENOMEM.
*/
if (status == 0)
status = slmdb_saved_key_assign(slmdb, mdb_key);
/*
* Handle end-of-database or other error.
*/
if (status != 0) {
else {
if (status == MDB_NOTFOUND) {
txn = mdb_cursor_txn(slmdb->cursor);
mdb_cursor_close(slmdb->cursor);
mdb_txn_abort(txn);
slmdb->cursor = 0;
slmdb_cursor_close(slmdb);
if (HAVE_SLMDB_SAVED_KEY(slmdb))
slmdb_saved_key_free(slmdb);
} else {
if ((status = slmdb_recover(slmdb, status)) == 0)
status = slmdb_cursor_get(slmdb, mdb_key, mdb_value, op);
@ -613,14 +734,17 @@ int slmdb_close(SLMDB *slmdb)
/*
* Clean up after an unfinished sequence() operation.
*/
if (slmdb->cursor) {
MDB_txn *txn = mdb_cursor_txn(slmdb->cursor);
if (slmdb->cursor != 0)
slmdb_cursor_close(slmdb);
mdb_cursor_close(slmdb->cursor);
mdb_txn_abort(txn);
}
mdb_env_close(slmdb->env);
/*
* Clean up the saved key position.
*/
if (HAVE_SLMDB_SAVED_KEY(slmdb))
slmdb_saved_key_free(slmdb);
SLMDB_API_RETURN(slmdb, status);
}
@ -703,6 +827,7 @@ int slmdb_open(SLMDB *slmdb, const char *path, int open_flags,
slmdb->dbi = dbi;
slmdb->db_fd = db_fd;
slmdb->cursor = 0;
slmdb_saved_key_init(slmdb);
slmdb->api_retry_count = 0;
slmdb->bulk_retry_count = 0;
slmdb->api_retry_limit = SLMDB_DEF_API_RETRY_LIMIT;
@ -718,4 +843,20 @@ int slmdb_open(SLMDB *slmdb, const char *path, int open_flags,
return (status);
}
#endif
/*
* Implementation-dependent workaround to debug LMDB assert() failures. The
* code below prevents daemons from disappearing without logfile record.
*/
#ifdef LMDB_ASSERT_WORKAROUND
#include <assert.h>
void __assert(const char *func, const char *file, int line, const char *text)
{
msg_panic("Assertion failed: %s, function %s, file %s, line %d.",
text, func, file, line);
}
#endif

View File

@ -31,6 +31,9 @@
#define SLMDB_JMP_BUF sigjmp_buf
#endif
/*
* All data structure members are private.
*/
typedef struct {
size_t curr_limit; /* database soft size limit */
int size_incr; /* database expansion factor */
@ -43,6 +46,8 @@ typedef struct {
MDB_txn *txn; /* bulk transaction */
int db_fd; /* database file handle */
MDB_cursor *cursor; /* iterator */
MDB_val saved_key; /* saved cursor key buffer */
size_t saved_key_size; /* saved cursor key buffer size */
void (*longjmp_fn) (void *, int);/* exception handling */
void (*notify_fn) (void *, int,...); /* workaround notification */
void *cb_context; /* call-back context */