diff --git a/postfix/HISTORY b/postfix/HISTORY index eca39850f..77a10aeff 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -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. diff --git a/postfix/README_FILES/FORWARD_SECRECY_README b/postfix/README_FILES/FORWARD_SECRECY_README index 785c572ac..d59c5a682 100644 --- a/postfix/README_FILES/FORWARD_SECRECY_README +++ b/postfix/README_FILES/FORWARD_SECRECY_README @@ -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?? diff --git a/postfix/README_FILES/SASL_README b/postfix/README_FILES/SASL_README index 9e93b2326..dc0ab41e0 100644 --- a/postfix/README_FILES/SASL_README +++ b/postfix/README_FILES/SASL_README @@ -1,5 +1,3 @@ -X - PPoossttffiixx SSAASSLL HHoowwttoo ------------------------------------------------------------------------------- diff --git a/postfix/conf/post-install b/postfix/conf/post-install index 91ff4a677..7e79c92cd 100644 --- a/postfix/conf/post-install +++ b/postfix/conf/post-install @@ -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. diff --git a/postfix/html/FORWARD_SECRECY_README.html b/postfix/html/FORWARD_SECRECY_README.html index 42e132041..40613f1ff 100644 --- a/postfix/html/FORWARD_SECRECY_README.html +++ b/postfix/html/FORWARD_SECRECY_README.html @@ -125,24 +125,26 @@ Presently, there are two flavors of "groups" that work with PFS:

  • Prime-field groups (EDH): 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).

    +The acronym for forward secrecy over prime fields is EDH for Ephemeral +Diffie-Hellman (also abbreviated as DHE for Diffie-Hellman Exchange). +

  • Elliptic-curve groups (EECDH): 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. -

    +is EECDH which is short for Ephemeral Elliptic Curve 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).

    +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 ≥ 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, @@ -200,7 +202,7 @@ parameter file and the prime need not actually be 1024 bits long

    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 ≥ 2048-bit length for the non-export prime. See the quick-start section for the recommended configuration to work around this issue.

    @@ -269,10 +271,12 @@ href="TLS_README.html#client_tls_policy">TLS policy table.

    Getting started, quick and dirty

    - -

    How can I see that a connection has forward secrecy?

    @@ -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.

    +cipher name, and the second shows the key exchange method.

    diff --git a/postfix/html/SASL_README.html b/postfix/html/SASL_README.html
    index e3a7eca41..37b374740 100644
    --- a/postfix/html/SASL_README.html
    +++ b/postfix/html/SASL_README.html
    @@ -1,4 +1,4 @@
    -X
     
     
    diff --git a/postfix/proto/FORWARD_SECRECY_README.html b/postfix/proto/FORWARD_SECRECY_README.html
    index 6b33f61ad..4aa9012fe 100644
    --- a/postfix/proto/FORWARD_SECRECY_README.html
    +++ b/postfix/proto/FORWARD_SECRECY_README.html
    @@ -125,24 +125,26 @@ Presently, there are two flavors of "groups" that work with PFS: 

  • Prime-field groups (EDH): 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).

    +The acronym for forward secrecy over prime fields is EDH for Ephemeral +Diffie-Hellman (also abbreviated as DHE for Diffie-Hellman Exchange). +

  • Elliptic-curve groups (EECDH): 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. -

    +is EECDH which is short for Ephemeral Elliptic Curve 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).

    +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 ≥ 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, @@ -200,7 +202,7 @@ parameter file and the prime need not actually be 1024 bits long

    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 ≥ 2048-bit length for the non-export prime. See the quick-start section for the recommended configuration to work around this issue.

    @@ -269,10 +271,12 @@ href="TLS_README.html#client_tls_policy">TLS policy table.

    Getting started, quick and dirty

    -
      +

      EECDH Client and server support (Postfix ≥ 2.6 with OpenSSL +≥ 1.0.0)

      -
    • Postfix 2.6 and 2.7: Enable elliptic-curve support. This -is the default with Postfix ≥ 2.8. +

      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.

      @@ -282,12 +286,16 @@ 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.

      +

      EDH Client support (Postfix ≥ 2.2)

      + +

      This space intentionally left blank.

      + +

      EDH Server support (Postfix ≥ 2.2)

      + +

      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.

      Execute as root (prime group generation can take a few seconds to a few minutes):

      @@ -295,6 +303,7 @@ few seconds to a few minutes):

       # 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): 

      +

      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 EDH parameters periodically (an hourly or daily -cron job running as root can automate this task).

      +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:

      @@ -332,8 +346,6 @@ need to adjust the submission entry in master.cf accordingly:

  • - -

    How can I see that a connection has forward secrecy?

    @@ -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.

    +cipher name, and the second shows the key exchange method.

    diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
    index c4e730f26..8254d0187 100644
    --- a/postfix/src/global/mail_version.h
    +++ b/postfix/src/global/mail_version.h
    @@ -20,7 +20,7 @@
       * Patches change both the patchlevel and the release date. Snapshots have no
       * patchlevel; they change the release date only.
       */
    -#define MAIL_RELEASE_DATE	"20131228"
    +#define MAIL_RELEASE_DATE	"20140104"
     #define MAIL_VERSION_NUMBER	"2.11"
     
     #ifdef SNAPSHOT
    diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h
    index c69470830..336a4f47f 100644
    --- a/postfix/src/smtp/smtp.h
    +++ b/postfix/src/smtp/smtp.h
    @@ -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 */
    diff --git a/postfix/src/smtp/smtp_chat.c b/postfix/src/smtp/smtp_chat.c
    index 11dc90998..5e2f82c7d 100644
    --- a/postfix/src/smtp/smtp_chat.c
    +++ b/postfix/src/smtp/smtp_chat.c
    @@ -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));
     	}
         }
    diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c
    index 70e8dc56d..ff278c1ff 100644
    --- a/postfix/src/smtp/smtp_connect.c
    +++ b/postfix/src/smtp/smtp_connect.c
    @@ -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)
    diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c
    index 5d442df41..1f9759d38 100644
    --- a/postfix/src/smtp/smtp_proto.c
    +++ b/postfix/src/smtp/smtp_proto.c
    @@ -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", " "),
    diff --git a/postfix/src/smtp/smtp_rcpt.c b/postfix/src/smtp/smtp_rcpt.c
    index ec6d2a47c..acb125236 100644
    --- a/postfix/src/smtp/smtp_rcpt.c
    +++ b/postfix/src/smtp/smtp_rcpt.c
    @@ -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),
    diff --git a/postfix/src/smtp/smtp_reuse.c b/postfix/src/smtp/smtp_reuse.c
    index eeadbb20d..3f1270678 100644
    --- a/postfix/src/smtp/smtp_reuse.c
    +++ b/postfix/src/smtp/smtp_reuse.c
    @@ -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);
     }
    diff --git a/postfix/src/smtp/smtp_sasl_auth_cache.c b/postfix/src/smtp/smtp_sasl_auth_cache.c
    index 562d6f3a5..520f8b6e5 100644
    --- a/postfix/src/smtp/smtp_sasl_auth_cache.c
    +++ b/postfix/src/smtp/smtp_sasl_auth_cache.c
    @@ -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);
    diff --git a/postfix/src/smtp/smtp_sasl_glue.c b/postfix/src/smtp/smtp_sasl_glue.c
    index 1cf6adac9..5254341d5 100644
    --- a/postfix/src/smtp/smtp_sasl_glue.c
    +++ b/postfix/src/smtp/smtp_sasl_glue.c
    @@ -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);
    diff --git a/postfix/src/smtp/smtp_session.c b/postfix/src/smtp/smtp_session.c
    index 2336f0b0a..535e8b628 100644
    --- a/postfix/src/smtp/smtp_session.c
    +++ b/postfix/src/smtp/smtp_session.c
    @@ -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);
     
         /*
    diff --git a/postfix/src/tls/Makefile.in b/postfix/src/tls/Makefile.in
    index 26e5d5a45..1af941902 100644
    --- a/postfix/src/tls/Makefile.in
    +++ b/postfix/src/tls/Makefile.in
    @@ -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	=
    diff --git a/postfix/src/util/dict_cache.c b/postfix/src/util/dict_cache.c
    index 40cfced39..7a8178c93 100644
    --- a/postfix/src/util/dict_cache.c
    +++ b/postfix/src/util/dict_cache.c
    @@ -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   (negative to reverse order)" \
    -		"\n\tupdate   (negative to reverse order)" \
    -		"\n\tdelete   (negative to reverse order)" \
    -		"\n\tpurge " \
    -		"\n\tcount "
    +		"\n\tquery   (negative to reverse order)" \
    +		"\n\tupdate   (negative to reverse order)" \
    +		"\n\tdelete   (negative to reverse order)" \
    +		"\n\tpurge " \
    +		"\n\tcount "
     
      /*
       * 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;
    diff --git a/postfix/src/util/slmdb.c b/postfix/src/util/slmdb.c
    index 5259d3a76..abb5eeef9 100644
    --- a/postfix/src/util/slmdb.c
    +++ b/postfix/src/util/slmdb.c
    @@ -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 
     #include 
     #include 
    +#include 
    +#include 
     
     /* 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 
    +
    +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
    diff --git a/postfix/src/util/slmdb.h b/postfix/src/util/slmdb.h
    index 40fb1aeb6..6f5ad5cfa 100644
    --- a/postfix/src/util/slmdb.h
    +++ b/postfix/src/util/slmdb.h
    @@ -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 */