2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-28 12:48:01 +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 Cleanup: DANE support: test script. Viktor Dukhovni. File
tls/tls_dane.sh. 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 Because of the above behavior, LMDB cannot be supported in
testing. Shockingly, LMDB terminates the postcreen daemon the stable Postfix 2.11 release.
without logfile record. Fixing this will require changes
in LMDB or changes in the way Postfix can use LMDB. File: 20140102
util/dict_cache.c.
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 * PPrriimmee--ffiieelldd ggrroouuppss ((EEDDHH)):: The server needs to be configured with a
suitably-large prime and a corresponding "generator". The acronym for suitably-large prime and a corresponding "generator". The acronym for
forward secrecy over prime fields is EDH or Ephemeral Diffie-Hellman forward secrecy over prime fields is EDH for Ephemeral Diffie-Hellman (also
(sometimes also abbreviated as DHE). abbreviated as DHE for Diffie-Hellman Exchange).
* EElllliippttiicc--ccuurrvvee ggrroouuppss ((EEEECCDDHH)):: The server needs to be configured with a * EElllliippttiicc--ccuurrvvee ggrroouuppss ((EEEECCDDHH)):: The server needs to be configured with a
"named curve". These offer better security at lower computational cost than "named curve". These offer better security at lower computational cost than
prime field groups, but are not as widely implemented. The acronym for the 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 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 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 OpenSSL supports EECDH with version 1.0.0 or later. Thus the configuration
parameters related to Elliptic Curve forward secrecy are only available when parameters related to Elliptic-Curve forward secrecy are available when Postfix
Postfix is linked with OpenSSL 1.0.0 or later (provided EC support has not been is linked with OpenSSL >= 1.0.0 (provided EC support has not been disabled by
disabled by the vendor, as in some versions of RedHat Linux). the vendor, as in some versions of RedHat Linux).
Elliptic curves used in cryptography are typically identified by a "name" that 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 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). 1024 bits long (see the quick-start section for details).
It turns out that (inadvisably-patched in some Debian releases) Exim SMTP 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 clients require a >= 2048-bit length for the non-export prime. See the quick-
quick-start section for the recommended configuration to work around this start section for the recommended configuration to work around this issue.
issue.
EEEECCDDHH SSeerrvveerr ssuuppppoorrtt EEEECCDDHH SSeerrvveerr ssuuppppoorrtt
@ -198,50 +198,60 @@ a case-by-case basis via the TLS policy table.
GGeettttiinngg ssttaarrtteedd,, qquuiicckk aanndd ddiirrttyy GGeettttiinngg ssttaarrtteedd,, qquuiicckk aanndd ddiirrttyy
* Postfix 2.6 and 2.7: Enable elliptic-curve support. This is the default EEEECCDDHH CClliieenntt aanndd sseerrvveerr ssuuppppoorrtt ((PPoossttffiixx >>== 22..66 wwiitthh OOppeennSSSSLL >>== 11..00..00))
with Postfix >= 2.8.
/etc/postfix/main.cf: With Postfix 2.6 and 2.7, enable elliptic-curve support in the Postfix SMTP
# Postfix 2.6 or 2.7 only. This is default with Postfix 2.8 and client and server. This is the default with Postfix >= 2.8.
later.
smtpd_tls_eecdh_grade = strong
* Optionally generate non-default EDH parameters for improved security /etc/postfix/main.cf:
against pre-computation attacks and for compatibility with Debian-patched # Postfix 2.6 or 2.7 only. This is default with Postfix 2.8 and later.
EXIM SMTP clients (these require a minimum 2048-bit length for the non- smtpd_tls_eecdh_grade = strong
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.
Execute as root (prime group generation can take a few seconds to a few EEDDHH CClliieenntt ssuuppppoorrtt ((PPoossttffiixx >>== 22..22))
minutes):
# cd /etc/postfix This space intentionally left blank.
# 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
You can improve security against pre-computation attacks further by EEDDHH SSeerrvveerr ssuuppppoorrtt ((PPoossttffiixx >>== 22..22))
regenerating the EDH parameters periodically (an hourly or daily cron job
running as root can automate this task).
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: Execute as root (prime group generation can take a few seconds to a few
smtpd_tls_dh1024_param_file = ${config_directory}/dh2048.pem minutes):
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 # cd /etc/postfix
adjust the submission entry in master.cf accordingly: # 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: The Postfix SMTP server EDH parameter files are not secret, after all these
submission inet n - n - - smtpd parameters are sent to all remote SMTP clients in the clear. Mode 0644 is fine.
# Some submission clients may not yet do 2048-bit EDH, if such
# clients use your MSA, configure 1024-bit EDH instead: You can improve security against pre-computation attacks further by
-o smtpd_tls_dh1024_param_file=${config_directory}/dh1024.pem regenerating the Postfix SMTP server EDH parameters periodically (an hourly or
-o smtpd_tls_security_level=encrypt daily cron job running the above commands as root can automate this task).
-o smtpd_sasl_auth_enable=yes
... 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?? HHooww ccaann II sseeee tthhaatt aa ccoonnnneeccttiioonn hhaass ffoorrwwaarrdd sseeccrreeccyy??

View File

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

View File

@ -40,25 +40,31 @@
# Arguments # Arguments
# .IP create-missing # .IP create-missing
# Create missing queue directories with ownerships and permissions # Create missing queue directories with ownerships and permissions
# according to the contents of $daemon_directory/postfix-files, using # according to the contents of $daemon_directory/postfix-files
# the mail_owner and setgid_group parameter settings from the command # and optionally in $daemon_directory/postfix-files.d/*, using
# line, process environment or from the installed main.cf file. # 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. # This is required at Postfix start-up time.
# .IP set-permissions # .IP set-permissions
# Set all file/directory ownerships and permissions according to the # Set all file/directory ownerships and permissions according to the
# contents of $daemon_directory/postfix-files, using the mail_owner # contents of $daemon_directory/postfix-files and optionally
# and setgid_group parameter settings from the command line, process # in $daemon_directory/postfix-files.d/*, using the mail_owner
# environment or from the installed main.cf file. Implies create-missing. # 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, # This is required when installing Postfix from a pre-built package,
# or when changing the mail_owner or setgid_group installation parameter # or when changing the mail_owner or setgid_group installation parameter
# settings after Postfix is already installed. # settings after Postfix is already installed.
# .IP upgrade-permissions # .IP upgrade-permissions
# Update ownership and permission of existing files/directories as # Update ownership and permission of existing files/directories as
# specified in $daemon_directory/postfix-files, using the mail_owner # specified in $daemon_directory/postfix-files and optionally
# and setgid_group parameter settings from the command line, process # in $daemon_directory/postfix-files.d/*, using the mail_owner
# environment or from the installed main.cf file. Implies create-missing. # 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. # This is required when upgrading an existing Postfix instance.
# .IP upgrade-configuration # .IP upgrade-configuration
@ -174,6 +180,7 @@
# FILES # FILES
# $config_directory/main.cf, Postfix installation parameters. # $config_directory/main.cf, Postfix installation parameters.
# $daemon_directory/postfix-files, installation control file. # $daemon_directory/postfix-files, installation control file.
# $daemon_directory/postfix-files.d/*, optional control files.
# $config_directory/install.cf, obsolete configuration file. # $config_directory/install.cf, obsolete configuration file.
# LICENSE # LICENSE
# .ad # .ad
@ -437,91 +444,96 @@ test -n "$override" && {
# Use file/directory status information in $daemon_directory/postfix-files. # Use file/directory status information in $daemon_directory/postfix-files.
test -n "$create" && { test -n "$create" && {
exec <$daemon_directory/postfix-files || exit 1 postfix_files_d=$daemon_directory/postfix-files.d
while IFS=: read path type owner group mode flags junk for postfix_file in $daemon_directory/postfix-files \
`test -d $postfix_files_d && { find $postfix_files_d -type f | sort; }`
do do
IFS="$BACKUP_IFS" exec <$postfix_file || exit 1
set_permission= while IFS=: read path type owner group mode flags junk
# 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
do do
eval junk=\${$name} IFS="$BACKUP_IFS"
case $junk in set_permission=
[$]*) eval $name=$junk;; # Skip comments. Skip shared files, if updating a secondary instance.
-) eval $name=;; 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 esac
done # Skip hard links and symbolic links.
# Skip uninstalled files. case $type in
case $path in [hl]) continue;;
no|no/*) continue;; [df]) ;;
esac *) echo unknown type $type for $path in $postfix_file 1>&2; exit 1;;
# Pick up the flags. esac
case $flags in *u*) upgrade_flag=1;; *) upgrade_flag=;; esac # Expand $name, and canonicalize null fields.
case $flags in *c*) create_flag=1;; *) create_flag=;; esac for name in path owner group flags
case $flags in *r*) recursive="-R";; *) recursive=;; esac do
case $flags in *o*) obsolete_flag=1;; *) obsolete_flag=;; esac eval junk=\${$name}
case $flags in *[1i]*) test ! -r "$path" -a "$config_directory" != \ case $junk in
"$def_config_directory" && continue;; esac [$]*) eval $name=$junk;;
# Flag obsolete objects. XXX Solaris 2..9 does not have "test -e". -) eval $name=;;
if [ -n "$obsolete_flag" ] *) ;;
then esac
test -r $path -a "$type" != "d" && obsolete="$obsolete $path" done
continue; # Skip uninstalled files.
else case $path in
keep_list="$keep_list $path" no|no/*) continue;;
fi esac
# Create missing directories with proper owner/group/mode settings. # Pick up the flags.
if [ -n "$create" -a "$type" = "d" -a -n "$create_flag" -a ! -d "$path" ] case $flags in *u*) upgrade_flag=1;; *) upgrade_flag=;; esac
then case $flags in *c*) create_flag=1;; *) create_flag=;; esac
mkdir $path || exit 1 case $flags in *r*) recursive="-R";; *) recursive=;; esac
set_permission=1 case $flags in *o*) obsolete_flag=1;; *) obsolete_flag=;; esac
# Update all owner/group/mode settings. case $flags in *[1i]*) test ! -r "$path" -a "$config_directory" != \
elif [ -n "$set_perms" ] "$def_config_directory" && continue;; esac
then # Flag obsolete objects. XXX Solaris 2..9 does not have "test -e".
set_permission=1 if [ -n "$obsolete_flag" ]
# 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 then
find $path -type d -exec chmod $mode "{}" ";" test -r $path -a "$type" != "d" && obsolete="$obsolete $path"
continue;
else else
chmod $mode $path keep_list="$keep_list $path"
fi || exit 1 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 done
IFS="$BACKUP_IFS"
} }
# Upgrade existing Postfix configuration files if necessary. # 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 <li> <p> <b> Prime-field groups (EDH):</b> The server needs to be
configured with a suitably-large prime and a corresponding "generator". configured with a suitably-large prime and a corresponding "generator".
The acronym for forward secrecy over prime fields is EDH or Ephemeral The acronym for forward secrecy over prime fields is EDH for Ephemeral
Diffie-Hellman (sometimes also abbreviated as DHE). </p> Diffie-Hellman (also abbreviated as DHE for Diffie-Hellman Exchange).
</p>
<li> <p> <b> Elliptic-curve groups (EECDH): </b> The server needs <li> <p> <b> Elliptic-curve groups (EECDH): </b> The server needs
to be configured with a "named curve". These offer better security to be configured with a "named curve". These offer better security
at lower computational cost than prime field groups, but are not at lower computational cost than prime field groups, but are not
as widely implemented. The acronym for the elliptic curve version as widely implemented. The acronym for the elliptic curve version
is EECDH which is short for Ephemeral Elliptic Curve Diffie-Hellman. is EECDH which is short for Ephemeral Elliptic Curve Diffie-Hellman
</p> (also abbreviated as ECDHE for Elliptic Curve Diffie-Hellman
Exchange). </p>
</ul> </ul>
<p> It is not essential to know what these are, but one does need <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 to know that OpenSSL supports EECDH with version 1.0.0 or later.
the configuration parameters related to Elliptic Curve forward secrecy Thus the configuration parameters related to Elliptic-Curve forward
are only available when Postfix is linked with OpenSSL 1.0.0 or secrecy are available when Postfix is linked with OpenSSL &ge; 1.0.0
later (provided EC support has not been disabled by the vendor, as (provided EC support has not been disabled by the vendor, as in
in some versions of RedHat Linux). </p> some versions of RedHat Linux). </p>
<p> Elliptic curves used in cryptography are typically identified <p> Elliptic curves used in cryptography are typically identified
by a "name" that stands for a set of well-known parameter values, 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> </ul>
<p> It turns out that (inadvisably-patched in some Debian releases) <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 prime. See the <a href="#quick-start">quick-start</a> section for
the recommended configuration to work around this issue. </p> 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> <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 <p> With Postfix 2.6 and 2.7, enable elliptic-curve support in the
is the default with Postfix &ge; 2.8. Postfix SMTP client and server. This is the default with Postfix
&ge; 2.8.
<blockquote> <blockquote>
<pre> <pre>
@ -282,12 +286,16 @@ is the default with Postfix &ge; 2.8.
</pre> </pre>
</blockquote> </blockquote>
<li> <p> Optionally generate non-default EDH parameters for improved <h3> EDH Client support (Postfix &ge; 2.2) </h3>
security against pre-computation attacks and for compatibility with
Debian-patched EXIM SMTP clients (these require a minimum 2048-bit <p> This space intentionally left blank. </p>
length for the non-export prime). The parameter files are not
secret, after all these parameters are sent to all SMTP clients in <h3> EDH Server support (Postfix &ge; 2.2) </h3>
the clear. Mode 0644 is fine. </p>
<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 <p> Execute as root (prime group generation can take a
few seconds to a few minutes): </p> few seconds to a few minutes): </p>
@ -295,6 +303,7 @@ few seconds to a few minutes): </p>
<blockquote> <blockquote>
<pre> <pre>
# cd /etc/postfix # cd /etc/postfix
# umask 022
# openssl dhparam -out dh512.tmp 512 && mv dh512.tmp dh512.pem # 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 dh1024.tmp 1024 && mv dh1024.tmp dh1024.pem
# openssl dhparam -out dh2048.tmp 2048 && mv dh2048.tmp dh2048.pem # openssl dhparam -out dh2048.tmp 2048 && mv dh2048.tmp dh2048.pem
@ -302,9 +311,14 @@ few seconds to a few minutes): </p>
</pre> </pre>
</blockquote> </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 <p> You can improve security against pre-computation attacks further
by regenerating the EDH parameters periodically (an hourly or daily by regenerating the Postfix SMTP server EDH parameters periodically
cron job running as root can automate this task). </p> (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> <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> </pre>
</blockquote> </blockquote>
</ul>
<h2><a name="test">How can I see that a connection has forward <h2><a name="test">How can I see that a connection has forward
secrecy? </a> </h2> 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 order. It excludes null ciphers that only authenticate and don't
encrypt, together with export and low-grade ciphers whose encryption encrypt, together with export and low-grade ciphers whose encryption
is too weak to offer meaningful secrecy. The first column shows the 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> <blockquote>
<pre> <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"> "http://www.w3.org/TR/html4/loose.dtd">
<head> <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 <li> <p> <b> Prime-field groups (EDH):</b> The server needs to be
configured with a suitably-large prime and a corresponding "generator". configured with a suitably-large prime and a corresponding "generator".
The acronym for forward secrecy over prime fields is EDH or Ephemeral The acronym for forward secrecy over prime fields is EDH for Ephemeral
Diffie-Hellman (sometimes also abbreviated as DHE). </p> Diffie-Hellman (also abbreviated as DHE for Diffie-Hellman Exchange).
</p>
<li> <p> <b> Elliptic-curve groups (EECDH): </b> The server needs <li> <p> <b> Elliptic-curve groups (EECDH): </b> The server needs
to be configured with a "named curve". These offer better security to be configured with a "named curve". These offer better security
at lower computational cost than prime field groups, but are not at lower computational cost than prime field groups, but are not
as widely implemented. The acronym for the elliptic curve version as widely implemented. The acronym for the elliptic curve version
is EECDH which is short for Ephemeral Elliptic Curve Diffie-Hellman. is EECDH which is short for Ephemeral Elliptic Curve Diffie-Hellman
</p> (also abbreviated as ECDHE for Elliptic Curve Diffie-Hellman
Exchange). </p>
</ul> </ul>
<p> It is not essential to know what these are, but one does need <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 to know that OpenSSL supports EECDH with version 1.0.0 or later.
the configuration parameters related to Elliptic Curve forward secrecy Thus the configuration parameters related to Elliptic-Curve forward
are only available when Postfix is linked with OpenSSL 1.0.0 or secrecy are available when Postfix is linked with OpenSSL &ge; 1.0.0
later (provided EC support has not been disabled by the vendor, as (provided EC support has not been disabled by the vendor, as in
in some versions of RedHat Linux). </p> some versions of RedHat Linux). </p>
<p> Elliptic curves used in cryptography are typically identified <p> Elliptic curves used in cryptography are typically identified
by a "name" that stands for a set of well-known parameter values, 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> </ul>
<p> It turns out that (inadvisably-patched in some Debian releases) <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 prime. See the <a href="#quick-start">quick-start</a> section for
the recommended configuration to work around this issue. </p> 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> <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 <p> With Postfix 2.6 and 2.7, enable elliptic-curve support in the
is the default with Postfix &ge; 2.8. Postfix SMTP client and server. This is the default with Postfix
&ge; 2.8.
<blockquote> <blockquote>
<pre> <pre>
@ -282,12 +286,16 @@ is the default with Postfix &ge; 2.8.
</pre> </pre>
</blockquote> </blockquote>
<li> <p> Optionally generate non-default EDH parameters for improved <h3> EDH Client support (Postfix &ge; 2.2) </h3>
security against pre-computation attacks and for compatibility with
Debian-patched EXIM SMTP clients (these require a minimum 2048-bit <p> This space intentionally left blank. </p>
length for the non-export prime). The parameter files are not
secret, after all these parameters are sent to all SMTP clients in <h3> EDH Server support (Postfix &ge; 2.2) </h3>
the clear. Mode 0644 is fine. </p>
<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 <p> Execute as root (prime group generation can take a
few seconds to a few minutes): </p> few seconds to a few minutes): </p>
@ -295,6 +303,7 @@ few seconds to a few minutes): </p>
<blockquote> <blockquote>
<pre> <pre>
# cd /etc/postfix # cd /etc/postfix
# umask 022
# openssl dhparam -out dh512.tmp 512 && mv dh512.tmp dh512.pem # 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 dh1024.tmp 1024 && mv dh1024.tmp dh1024.pem
# openssl dhparam -out dh2048.tmp 2048 && mv dh2048.tmp dh2048.pem # openssl dhparam -out dh2048.tmp 2048 && mv dh2048.tmp dh2048.pem
@ -302,9 +311,14 @@ few seconds to a few minutes): </p>
</pre> </pre>
</blockquote> </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 <p> You can improve security against pre-computation attacks further
by regenerating the EDH parameters periodically (an hourly or daily by regenerating the Postfix SMTP server EDH parameters periodically
cron job running as root can automate this task). </p> (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> <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> </pre>
</blockquote> </blockquote>
</ul>
<h2><a name="test">How can I see that a connection has forward <h2><a name="test">How can I see that a connection has forward
secrecy? </a> </h2> 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 order. It excludes null ciphers that only authenticate and don't
encrypt, together with export and low-grade ciphers whose encryption encrypt, together with export and low-grade ciphers whose encryption
is too weak to offer meaningful secrecy. The first column shows the 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> <blockquote>
<pre> <pre>

View File

@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no * Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only. * patchlevel; they change the release date only.
*/ */
#define MAIL_RELEASE_DATE "20131228" #define MAIL_RELEASE_DATE "20140104"
#define MAIL_VERSION_NUMBER "2.11" #define MAIL_VERSION_NUMBER "2.11"
#ifdef SNAPSHOT #ifdef SNAPSHOT

View File

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

View File

@ -363,7 +363,8 @@ SMTP_RESP *smtp_chat_resp(SMTP_SESSION *session)
session->namaddrport, STR(session->buffer)); session->namaddrport, STR(session->buffer));
if (var_helpful_warnings) if (var_helpful_warnings)
msg_warn("to prevent loss of mail, turn off command pipelining " 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)); 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) if (msg_verbose)
msg_info("%s: trying: %s...", myname, addr); 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)); 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"; const char *myname = "smtp_connect_addr";
struct sockaddr_storage ss; /* remote */ struct sockaddr_storage ss; /* remote */
struct sockaddr *sa = (struct sockaddr *) & ss; struct sockaddr *sa = (struct sockaddr *) &ss;
SOCKADDR_SIZE salen = sizeof(ss); SOCKADDR_SIZE salen = sizeof(ss);
MAI_HOSTADDR_STR hostaddr; MAI_HOSTADDR_STR hostaddr;
DNS_RR *addr = iter->rr; 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 */ /* 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, int salen,
SMTP_ITERATOR *iter, SMTP_ITERATOR *iter,
DSN_BUF *why, 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 if (*addr_list && SMTP_RCPT_LEFT(state) > 0
&& (session = smtp_reuse_nexthop(state, SMTP_KEY_MASK_SCACHE_DEST_LABEL)) != 0) { && (session = smtp_reuse_nexthop(state, SMTP_KEY_MASK_SCACHE_DEST_LABEL)) != 0) {
session_count = 1; 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) if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP)
&& *addr_list == 0) && *addr_list == 0)
state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER; 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) { SMTP_KEY_MASK_SCACHE_ENDP_LABEL)) != 0) {
session->features |= SMTP_FEATURE_BEST_MX; session->features |= SMTP_FEATURE_BEST_MX;
session_count += 1; 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) if (*addr_list == 0)
next = 0; next = 0;
if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP) 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"; const char *myname = "smtp_helo";
SMTP_SESSION *session = state->session; SMTP_SESSION *session = state->session;
DELIVER_REQUEST *request = state->request; DELIVER_REQUEST *request = state->request;
SMTP_ITERATOR *iter = state->iterator;
SMTP_RESP *resp; SMTP_RESP *resp;
SMTP_RESP fake; SMTP_RESP fake;
int except; int except;
@ -322,7 +323,7 @@ int smtp_helo(SMTP_STATE *state)
STR(resp->dsn_buf)[0] = '4'; STR(resp->dsn_buf)[0] = '4';
/* FALLTHROUGH */ /* FALLTHROUGH */
default: 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", "host %s refused to talk to me: %s",
session->namaddr, session->namaddr,
translit(resp->str, "\n", " "))); translit(resp->str, "\n", " ")));
@ -350,7 +351,7 @@ int smtp_helo(SMTP_STATE *state)
if (smtp_pix_bug_maps != 0 if (smtp_pix_bug_maps != 0
&& (pix_bug_words = && (pix_bug_words =
maps_find(smtp_pix_bug_maps, maps_find(smtp_pix_bug_maps,
state->session->addr, 0)) != 0) { STR(iter->addr), 0)) != 0) {
pix_bug_source = SMTP_X(PIX_BUG_MAPS); pix_bug_source = SMTP_X(PIX_BUG_MAPS);
} else { } else {
pix_bug_words = var_smtp_pix_bug_words; 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); smtp_chat_cmd(session, "EHLO %s", var_smtp_helo_name);
if ((resp = smtp_chat_resp(session))->code / 100 != 2) { if ((resp = smtp_chat_resp(session))->code / 100 != 2) {
if (resp->code == 421) 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", "host %s refused to talk to me: %s",
session->namaddr, session->namaddr,
translit(resp->str, "\n", " "))); translit(resp->str, "\n", " ")));
@ -427,7 +428,7 @@ int smtp_helo(SMTP_STATE *state)
where = "performing the HELO handshake"; where = "performing the HELO handshake";
smtp_chat_cmd(session, "HELO %s", var_smtp_helo_name); smtp_chat_cmd(session, "HELO %s", var_smtp_helo_name);
if ((resp = smtp_chat_resp(session))->code / 100 != 2) 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", "host %s refused to talk to me: %s",
session->namaddr, session->namaddr,
translit(resp->str, "\n", " "))); translit(resp->str, "\n", " ")));
@ -436,7 +437,7 @@ int smtp_helo(SMTP_STATE *state)
where = "performing the LHLO handshake"; where = "performing the LHLO handshake";
smtp_chat_cmd(session, "LHLO %s", var_smtp_helo_name); smtp_chat_cmd(session, "LHLO %s", var_smtp_helo_name);
if ((resp = smtp_chat_resp(session))->code / 100 != 2) 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", "host %s refused to talk to me: %s",
session->namaddr, session->namaddr,
translit(resp->str, "\n", " "))); translit(resp->str, "\n", " ")));
@ -454,12 +455,12 @@ int smtp_helo(SMTP_STATE *state)
*/ */
if (smtp_ehlo_dis_maps == 0 if (smtp_ehlo_dis_maps == 0
|| (ehlo_words = maps_find(smtp_ehlo_dis_maps, || (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; ehlo_words = var_smtp_ehlo_dis_words;
if (smtp_ehlo_dis_maps && smtp_ehlo_dis_maps->error) { if (smtp_ehlo_dis_maps && smtp_ehlo_dis_maps->error) {
msg_warn("%s: %s map lookup error for %s", msg_warn("%s: %s map lookup error for %s",
session->state->request->queue_id, 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); vstream_longjmp(session->stream, SMTP_ERR_DATA);
} }
discard_mask = ehlo_mask(ehlo_words); discard_mask = ehlo_mask(ehlo_words);
@ -643,7 +644,7 @@ int smtp_helo(SMTP_STATE *state)
if ((session->features & SMTP_FEATURE_STARTTLS) && if ((session->features & SMTP_FEATURE_STARTTLS) &&
var_smtp_tls_note_starttls_offer && var_smtp_tls_note_starttls_offer &&
session->tls->level <= TLS_LEV_NONE) 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. * Decide whether or not to send STARTTLS.
@ -690,7 +691,7 @@ int smtp_helo(SMTP_STATE *state)
*/ */
session->features &= ~SMTP_FEATURE_STARTTLS; session->features &= ~SMTP_FEATURE_STARTTLS;
if (TLS_REQUIRED(session->tls->level)) 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", "TLS is required, but host %s refused to start TLS: %s",
session->namaddr, session->namaddr,
translit(resp->str, "\n", " "))); translit(resp->str, "\n", " ")));
@ -739,6 +740,7 @@ int smtp_helo(SMTP_STATE *state)
static int smtp_start_tls(SMTP_STATE *state) static int smtp_start_tls(SMTP_STATE *state)
{ {
SMTP_SESSION *session = state->session; SMTP_SESSION *session = state->session;
SMTP_ITERATOR *iter = state->iterator;
TLS_CLIENT_START_PROPS tls_props; TLS_CLIENT_START_PROPS tls_props;
VSTRING *serverid; VSTRING *serverid;
SMTP_RESP fake; SMTP_RESP fake;
@ -805,7 +807,7 @@ static int smtp_start_tls(SMTP_STATE *state)
timeout = var_smtp_starttls_tmout, timeout = var_smtp_starttls_tmout,
tls_level = session->tls->level, tls_level = session->tls->level,
nexthop = session->tls_nexthop, nexthop = session->tls_nexthop,
host = session->host, host = STR(iter->host),
namaddr = session->namaddrport, namaddr = session->namaddrport,
serverid = vstring_str(serverid), serverid = vstring_str(serverid),
helo = session->helo, helo = session->helo,
@ -1142,6 +1144,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
const char *myname = "smtp_loop"; const char *myname = "smtp_loop";
DELIVER_REQUEST *request = state->request; DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session; SMTP_SESSION *session = state->session;
SMTP_ITERATOR *iter = state->iterator;
SMTP_RESP *resp; SMTP_RESP *resp;
RECIPIENT *rcpt; RECIPIENT *rcpt;
VSTRING *next_command = vstring_alloc(100); 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: case SMTP_STATE_MAIL:
if (resp->code / 100 != 2) { 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)", "host %s said: %s (in reply to %s)",
session->namaddr, session->namaddr,
translit(resp->str, "\n", " "), 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); smtp_rcpt_done(state, resp, rcpt);
} }
} else { } 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)", "host %s said: %s (in reply to %s)",
session->namaddr, session->namaddr,
translit(resp->str, "\n", " "), translit(resp->str, "\n", " "),
@ -1746,7 +1749,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
case SMTP_STATE_DATA: case SMTP_STATE_DATA:
if (resp->code / 100 != 3) { if (resp->code / 100 != 3) {
if (nrcpt > 0) 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)", "host %s said: %s (in reply to %s)",
session->namaddr, session->namaddr,
translit(resp->str, "\n", " "), translit(resp->str, "\n", " "),
@ -1770,7 +1773,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
if (smtp_mode) { if (smtp_mode) {
if (nrcpt > 0) { if (nrcpt > 0) {
if (resp->code / 100 != 2) { 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)", "host %s said: %s (in reply to %s)",
session->namaddr, session->namaddr,
translit(resp->str, "\n", " "), translit(resp->str, "\n", " "),
@ -1797,7 +1800,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
rcpt = request->rcpt_list.info rcpt = request->rcpt_list.info
+ survivors[recv_done++]; + survivors[recv_done++];
if (resp->code / 100 != 2) { 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)", "host %s said: %s (in reply to %s)",
session->namaddr, session->namaddr,
translit(resp->str, "\n", " "), 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; DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session; SMTP_SESSION *session = state->session;
SMTP_ITERATOR *iter = state->iterator;
DSN_BUF *why = state->why; DSN_BUF *why = state->why;
const char *dsn_action = "relayed"; const char *dsn_action = "relayed";
int status; 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. * 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); DSB_DTYPE_SMTP, resp->str, "%s", resp->str);
status = sent(DEL_REQ_TRACE_FLAGS(request->flags), 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 *label)
{ {
const char *myname = "smtp_reuse_common"; const char *myname = "smtp_reuse_common";
SMTP_ITERATOR *iter = state->iterator;
SMTP_SESSION *session; 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. * 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); 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, int smtp_sasl_auth_cache_find(SMTP_SASL_AUTH_CACHE *auth_cache,
const SMTP_SESSION *session) const SMTP_SESSION *session)
{ {
SMTP_ITERATOR *iter = session->iterator;
char *key; char *key;
const char *entry; const char *entry;
int valid = 0; 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 ((entry = dict_get(auth_cache->dict, key)) != 0)
if ((valid = smtp_sasl_auth_cache_valid_value(auth_cache, entry, if ((valid = smtp_sasl_auth_cache_valid_value(auth_cache, entry,
session->sasl_passwd)) == 0) 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_SESSION *session,
const SMTP_RESP *resp) const SMTP_RESP *resp)
{ {
SMTP_ITERATOR *iter = session->iterator;
char *key; char *key;
char *value; 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, value = smtp_sasl_auth_cache_make_value(session->sasl_passwd,
resp->dsn, resp->str); resp->dsn, resp->str);
dict_put(auth_cache->dict, key, value); 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"; const char *myname = "smtp_sasl_passwd_lookup";
SMTP_STATE *state = session->state; SMTP_STATE *state = session->state;
SMTP_ITERATOR *iter = session->iterator;
const char *value; const char *value;
char *passwd; char *passwd;
@ -187,10 +188,10 @@ int smtp_sasl_passwd_lookup(SMTP_SESSION *session)
state->request->sender, (char **) 0)) != 0) state->request->sender, (char **) 0)) != 0)
|| (smtp_sasl_passwd_map->error == 0 || (smtp_sasl_passwd_map->error == 0
&& (value = maps_find(smtp_sasl_passwd_map, && (value = maps_find(smtp_sasl_passwd_map,
session->host, 0)) != 0) STR(iter->host), 0)) != 0)
|| (smtp_sasl_passwd_map->error == 0 || (smtp_sasl_passwd_map->error == 0
&& (value = maps_find(smtp_sasl_passwd_map, && (value = maps_find(smtp_sasl_passwd_map,
session->dest, 0)) != 0)) { STR(iter->dest), 0)) != 0)) {
if (session->sasl_username) if (session->sasl_username)
myfree(session->sasl_username); myfree(session->sasl_username);
session->sasl_username = mystrdup(value); session->sasl_username = mystrdup(value);
@ -200,7 +201,7 @@ int smtp_sasl_passwd_lookup(SMTP_SESSION *session)
session->sasl_passwd = mystrdup(passwd ? passwd : ""); session->sasl_passwd = mystrdup(passwd ? passwd : "");
if (msg_verbose) if (msg_verbose)
msg_info("%s: host `%s' user `%s' pass `%s'", msg_info("%s: host `%s' user `%s' pass `%s'",
myname, session->host, myname, STR(iter->host),
session->sasl_username, session->sasl_passwd); session->sasl_username, session->sasl_passwd);
return (1); return (1);
} else if (smtp_sasl_passwd_map->error) { } else if (smtp_sasl_passwd_map->error) {
@ -210,7 +211,7 @@ int smtp_sasl_passwd_lookup(SMTP_SESSION *session)
} else { } else {
if (msg_verbose) if (msg_verbose)
msg_info("%s: no auth info found (sender=`%s', host=`%s')", 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); return (0);
} }
} }
@ -284,6 +285,7 @@ void smtp_sasl_start(SMTP_SESSION *session, const char *sasl_opts_name,
const char *sasl_opts_val) const char *sasl_opts_val)
{ {
XSASL_CLIENT_CREATE_ARGS create_args; XSASL_CLIENT_CREATE_ARGS create_args;
SMTP_ITERATOR *iter = session->iterator;
if (msg_verbose) if (msg_verbose)
msg_info("starting new SASL client"); 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, XSASL_CLIENT_CREATE(smtp_sasl_impl, &create_args,
stream = session->stream, stream = session->stream,
service = var_procname, service = var_procname,
server_name = session->host, server_name = STR(iter->host),
security_options = sasl_opts_val)) == 0) security_options = sasl_opts_val)) == 0)
msg_fatal("SASL per-connection initialization failed"); msg_fatal("SASL per-connection initialization failed");
session->sasl_reply = vstring_alloc(20); 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) int smtp_sasl_authenticate(SMTP_SESSION *session, DSN_BUF *why)
{ {
const char *myname = "smtp_sasl_authenticate"; const char *myname = "smtp_sasl_authenticate";
SMTP_ITERATOR *iter = session->iterator;
SMTP_RESP *resp; SMTP_RESP *resp;
const char *mechanism; const char *mechanism;
int result; 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') if (var_smtp_sasl_auth_soft_bounce && resp_dsn[0] == '5')
resp_dsn[0] = '4'; resp_dsn[0] = '4';
dsb_update(why, resp_dsn, DSB_DEF_ACTION, DSB_MTYPE_DNS, 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", "SASL [CACHED] authentication failed; server %s said: %s",
session->host, resp_str); STR(iter->host), resp_str);
return (0); return (0);
} }
#endif #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) if (var_smtp_sasl_auth_soft_bounce && resp->code / 100 == 5)
STR(resp->dsn_buf)[0] = '4'; STR(resp->dsn_buf)[0] = '4';
dsb_update(why, resp->dsn, DSB_DEF_ACTION, dsb_update(why, resp->dsn, DSB_DEF_ACTION,
DSB_MTYPE_DNS, session->host, DSB_MTYPE_DNS, STR(iter->host),
var_procname, resp->str, var_procname, resp->str,
"SASL authentication failed; server %s said: %s", "SASL authentication failed; server %s said: %s",
session->namaddr, resp->str); 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) time_t start, int flags)
{ {
SMTP_SESSION *session; SMTP_SESSION *session;
const char *dest = STR(iter->dest);
const char *host = STR(iter->host); const char *host = STR(iter->host);
const char *addr = STR(iter->addr); const char *addr = STR(iter->addr);
unsigned port = iter->port; unsigned port = iter->port;
session = (SMTP_SESSION *) mymalloc(sizeof(*session)); session = (SMTP_SESSION *) mymalloc(sizeof(*session));
session->stream = stream; session->stream = stream;
session->dest = mystrdup(dest); session->iterator = iter;
session->host = mystrdup(host);
session->addr = mystrdup(addr);
session->namaddr = concatenate(host, "[", addr, "]", (char *) 0); session->namaddr = concatenate(host, "[", addr, "]", (char *) 0);
session->helo = 0; session->helo = 0;
session->port = port; session->port = port;
@ -191,9 +188,6 @@ void smtp_session_free(SMTP_SESSION *session)
#endif #endif
if (session->stream) if (session->stream)
vstream_fclose(session->stream); vstream_fclose(session->stream);
myfree(session->dest);
myfree(session->host);
myfree(session->addr);
myfree(session->namaddr); myfree(session->namaddr);
myfree(session->namaddrport); myfree(session->namaddrport);
if (session->helo) if (session->helo)
@ -221,6 +215,7 @@ void smtp_session_free(SMTP_SESSION *session)
int smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop, int smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop,
VSTRING *endp_prop) VSTRING *endp_prop)
{ {
SMTP_ITERATOR *iter = session->iterator;
int fd; 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", 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); session->features & SMTP_FEATURE_DESTINATION_MASK);
/* /*

View File

@ -19,7 +19,7 @@ INCL =
LIB = libtls.a LIB = libtls.a
TESTPROG= tls_dh tls_mgr tls_rsa tls_dane 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 LIB_DIR = ../../lib
INC_DIR = ../../include INC_DIR = ../../include
MAKES = MAKES =

View File

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

View File

@ -82,7 +82,8 @@
/* /*
/* slmdb_cursor_get() is an mdb_cursor_get() wrapper with /* slmdb_cursor_get() is an mdb_cursor_get() wrapper with
/* automatic error recovery. The result value is an LMDB /* 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 /* slmdb_fd() returns the file descriptor for the specified
/* database. This may be used for file status queries or /* database. This may be used for file status queries or
@ -198,6 +199,8 @@
#include <unistd.h> #include <unistd.h>
#include <limits.h> #include <limits.h>
#include <stdarg.h> #include <stdarg.h>
#include <string.h>
#include <stdlib.h>
/* Application-specific. */ /* Application-specific. */
@ -256,6 +259,80 @@
return (status); \ return (status); \
} while (0) } 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 */ /* slmdb_prepare - LMDB-specific (re)initialization before actual access */
static int slmdb_prepare(SLMDB *slmdb) static int slmdb_prepare(SLMDB *slmdb)
@ -295,6 +372,13 @@ static int slmdb_recover(SLMDB *slmdb, int status)
{ {
MDB_envinfo info; 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 * Recover bulk transactions only if they can be restarted. Limit the
* number of recovery attempts per slmdb(3) API request. * 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) else if ((status = slmdb_txn_begin(slmdb, 0, &txn)) != 0)
SLMDB_API_RETURN(slmdb, status); 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. * 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) else if ((status = slmdb_txn_begin(slmdb, 0, &txn)) != 0)
SLMDB_API_RETURN(slmdb, status); 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. * 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. * Open a read transaction and cursor if needed.
*/ */
if (slmdb->cursor == 0) { 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) { if ((status = mdb_cursor_open(txn, slmdb->dbi, &slmdb->cursor)) != 0) {
mdb_txn_abort(txn); mdb_txn_abort(txn);
if ((status = slmdb_recover(slmdb, status)) == 0) if ((status = slmdb_recover(slmdb, status)) == 0)
status = slmdb_cursor_get(slmdb, mdb_key, mdb_value, op); status = slmdb_cursor_get(slmdb, mdb_key, mdb_value, op);
SLMDB_API_RETURN(slmdb, status); 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); 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. * Handle end-of-database or other error.
*/ */
if (status != 0) { else {
if (status == MDB_NOTFOUND) { if (status == MDB_NOTFOUND) {
txn = mdb_cursor_txn(slmdb->cursor); slmdb_cursor_close(slmdb);
mdb_cursor_close(slmdb->cursor); if (HAVE_SLMDB_SAVED_KEY(slmdb))
mdb_txn_abort(txn); slmdb_saved_key_free(slmdb);
slmdb->cursor = 0;
} else { } else {
if ((status = slmdb_recover(slmdb, status)) == 0) if ((status = slmdb_recover(slmdb, status)) == 0)
status = slmdb_cursor_get(slmdb, mdb_key, mdb_value, op); 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. * Clean up after an unfinished sequence() operation.
*/ */
if (slmdb->cursor) { if (slmdb->cursor != 0)
MDB_txn *txn = mdb_cursor_txn(slmdb->cursor); slmdb_cursor_close(slmdb);
mdb_cursor_close(slmdb->cursor);
mdb_txn_abort(txn);
}
mdb_env_close(slmdb->env); 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); SLMDB_API_RETURN(slmdb, status);
} }
@ -703,6 +827,7 @@ int slmdb_open(SLMDB *slmdb, const char *path, int open_flags,
slmdb->dbi = dbi; slmdb->dbi = dbi;
slmdb->db_fd = db_fd; slmdb->db_fd = db_fd;
slmdb->cursor = 0; slmdb->cursor = 0;
slmdb_saved_key_init(slmdb);
slmdb->api_retry_count = 0; slmdb->api_retry_count = 0;
slmdb->bulk_retry_count = 0; slmdb->bulk_retry_count = 0;
slmdb->api_retry_limit = SLMDB_DEF_API_RETRY_LIMIT; 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); 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 #endif

View File

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