diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 8f3031c89..6488e500c 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -336,7 +336,6 @@ -TSTATE -TSTRING_LIST -TSTRING_TABLE --TSUMMARY_CLASS -TSYS_EXITS_DETAIL -TTLSMGR_SCACHE -TTLSP_STATE @@ -356,6 +355,7 @@ -TTLS_SESS_STATE -TTLS_TICKET_KEY -TTLS_TLSA +-TTLS_USAGE -TTLS_VINFO -TTLScontext_t -TTOK822 diff --git a/postfix/HISTORY b/postfix/HISTORY index 0ed7db882..ac502c1a5 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -23770,9 +23770,7 @@ Apologies for any names omitted. postmap/postmap.c. Cleanup: don't use ssize_t for boolean result. File: - global/smtp_stream.c. Memory leak: the Berkeley DB client - leaked a small amount of memory asfter failing to open a - table. File: util/dict_db.c. + global/smtp_stream.c. Cleanup: memory leak caused by missing dbenv->close() call after failing to open a Berkeley DB table. File: util/dict_db.c. @@ -23794,3 +23792,26 @@ Apologies for any names omitted. tls/tls_proxy_context_print.c, tls/tls_proxy_context_scan.c, tls/tls_client.c, tls/tls_server.c, smtpd/smtpd.c, posttls-finger/posttls-finger.c. + + Cleanup: vstream_memopen() flags handling. File: + util/vstream.c. + + Cleanup: the SMTP client now uses 'attr_print_plain' + serialization and 'attr_scan_plain' deserialization for + connection cache lookup keys, which now contain a serialized + version of the TLS context. File: smtp/smtp_session.c. + +20181117 + + The Postfix SMTP client now logs whether an SMTP-over-TLS + connection is newly established ("TLS connection established") + or whether the connection is reused ("TLS connection reused"). + Files: smtp/smtp.h, smtp/smtp_proto.c, smtp/smtp_session.c. + +20181118 + + Cleanup, no behavior change: updated comments concerning + connection reuse, and updated some identifiers to reflect + current reality. Files: smtp_reuse.c, smtp_key.c, smtp_proto.c, + smtp_tls_policy.c, smtp.h, smtp_connect.c. + diff --git a/postfix/README_FILES/AAAREADME b/postfix/README_FILES/AAAREADME index d4e10f070..7d72787cb 100644 --- a/postfix/README_FILES/AAAREADME +++ b/postfix/README_FILES/AAAREADME @@ -12,7 +12,6 @@ GGeenneerraall ccoonnffiigguurraattiioonn * TLS_README: TLS Encryption and authentication * FORWARD_SECRECY_README: TLS Forward Secrecy * IPV6_README: IP Version 6 Support - * IPV6_README: IP Version 6 Support * SMTPUTF8_README: SMTPUTF8 Support * COMPATIBILITY_README: Backwards-Compatibility Safety Net * INSTALL: Installation from source code diff --git a/postfix/README_FILES/FORWARD_SECRECY_README b/postfix/README_FILES/FORWARD_SECRECY_README index 2b58e4828..d59b99e92 100644 --- a/postfix/README_FILES/FORWARD_SECRECY_README +++ b/postfix/README_FILES/FORWARD_SECRECY_README @@ -323,7 +323,7 @@ verification status. (No client certificate requested) TLS 1.3 examples. Some of the new attributes may not appear when not - applicable or not available in an older versions of the OpenSSL library. + applicable or not available in older versions of the OpenSSL library. Received: from localhost (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 diff --git a/postfix/html/FORWARD_SECRECY_README.html b/postfix/html/FORWARD_SECRECY_README.html index 3fa0e78c5..18a83818b 100644 --- a/postfix/html/FORWARD_SECRECY_README.html +++ b/postfix/html/FORWARD_SECRECY_README.html @@ -434,7 +434,7 @@ Received: from host.example.com (host.example.com [192.168.0.2])

TLS 1.3 examples. Some of the new attributes may not appear when not -applicable or not available in an older versions of the OpenSSL library.

+applicable or not available in older versions of the OpenSSL library.

diff --git a/postfix/html/index.html b/postfix/html/index.html
index edf9019e7..2989177a6 100644
--- a/postfix/html/index.html
+++ b/postfix/html/index.html
@@ -46,8 +46,6 @@ configuration examples 
 
 
  • IP Version 6 Support -
  • IP Version 6 Support -
  • SMTPUTF8 Support
  • Backwards-Compatibility Safety Net diff --git a/postfix/proto/FORWARD_SECRECY_README.html b/postfix/proto/FORWARD_SECRECY_README.html index abeb31e0a..f549e4a1c 100644 --- a/postfix/proto/FORWARD_SECRECY_README.html +++ b/postfix/proto/FORWARD_SECRECY_README.html @@ -434,7 +434,7 @@ Received: from host.example.com (host.example.com [192.168.0.2])
  • TLS 1.3 examples. Some of the new attributes may not appear when not -applicable or not available in an older versions of the OpenSSL library.

    +applicable or not available in older versions of the OpenSSL library.

    diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
    index cbb127459..ba90a1ddf 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	"20181117"
    +#define MAIL_RELEASE_DATE	"20181118"
     #define MAIL_VERSION_NUMBER	"3.4"
     
     #ifdef SNAPSHOT
    diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h
    index 778a3d524..effc658a1 100644
    --- a/postfix/src/smtp/smtp.h
    +++ b/postfix/src/smtp/smtp.h
    @@ -192,17 +192,25 @@ typedef struct SMTP_STATE {
     } SMTP_STATE;
     
      /*
    -  * TODO: use the new SMTP_ITER name space.
    +  * Primitives to enable/disable/test connection caching and reuse based on
    +  * the delivery request next-hop destination (i.e. not smtp_fallback_relay).
    +  * 
    +  * Connection cache lookup by the request next-hop destination allows a reuse
    +  * request to skip over bad hosts, and may result in a connection to a
    +  * fall-back relay. Once we have found a 'good' host for a request next-hop,
    +  * clear the request next-hop destination, to avoid caching less-preferred
    +  * connections under that same request next-hop.
       */
    -#define SET_NEXTHOP_STATE(state, nexthop) { \
    +#define SET_SCACHE_REQUEST_NEXTHOP(state, nexthop) do { \
     	vstring_strcpy((state)->iterator->request_nexthop, nexthop); \
    -    }
    +    } while (0)
     
    -#define FREE_NEXTHOP_STATE(state) { \
    +#define CLEAR_SCACHE_REQUEST_NEXTHOP(state) do { \
     	STR((state)->iterator->request_nexthop)[0] = 0; \
    -    }
    +    } while (0)
     
    -#define HAVE_NEXTHOP_STATE(state) (STR((state)->iterator->request_nexthop)[0] != 0)
    +#define HAVE_SCACHE_REQUEST_NEXTHOP(state) \
    +	(STR((state)->iterator->request_nexthop)[0] != 0)
     
     
      /*
    @@ -229,6 +237,7 @@ typedef struct SMTP_STATE {
     #define SMTP_FEATURE_EARLY_TLS_MAIL_REPLY (1<<19)	/* CVE-2009-3555 */
     #define SMTP_FEATURE_XFORWARD_IDENT	(1<<20)
     #define SMTP_FEATURE_SMTPUTF8		(1<<21)	/* RFC 6531 */
    +#define SMTP_FEATURE_FROM_PROXY		(1<<22)	/* proxied connection */
     
      /*
       * Features that passivate under the endpoint.
    @@ -617,7 +626,7 @@ char   *smtp_key_prefix(VSTRING *, const char *, SMTP_ITERATOR *, int);
     #define SMTP_KEY_FLAG_SERVICE		(1<<0)	/* service name */
     #define SMTP_KEY_FLAG_SENDER		(1<<1)	/* sender address */
     #define SMTP_KEY_FLAG_REQ_NEXTHOP	(1<<2)	/* request nexthop */
    -#define SMTP_KEY_FLAG_NEXTHOP		(1<<3)	/* current nexthop */
    +#define SMTP_KEY_FLAG_CUR_NEXTHOP	(1<<3)	/* current nexthop */
     #define SMTP_KEY_FLAG_HOSTNAME		(1<<4)	/* remote host name */
     #define SMTP_KEY_FLAG_ADDR		(1<<5)	/* remote address */
     #define SMTP_KEY_FLAG_PORT		(1<<6)	/* remote port */
    @@ -626,7 +635,7 @@ char   *smtp_key_prefix(VSTRING *, const char *, SMTP_ITERATOR *, int);
     #define SMTP_KEY_MASK_ALL \
     	(SMTP_KEY_FLAG_SERVICE | SMTP_KEY_FLAG_SENDER | \
     	SMTP_KEY_FLAG_REQ_NEXTHOP | \
    -	SMTP_KEY_FLAG_NEXTHOP | SMTP_KEY_FLAG_HOSTNAME | \
    +	SMTP_KEY_FLAG_CUR_NEXTHOP | SMTP_KEY_FLAG_HOSTNAME | \
     	SMTP_KEY_FLAG_ADDR | SMTP_KEY_FLAG_PORT | SMTP_KEY_FLAG_TLS_LEVEL)
     
      /*
    @@ -640,14 +649,14 @@ char   *smtp_key_prefix(VSTRING *, const char *, SMTP_ITERATOR *, int);
     	((var_smtp_sender_auth && *var_smtp_sasl_passwd) ? \
     	    SMTP_KEY_FLAG_SENDER : 0)
     
    -#define COND_SASL_SMTP_KEY_FLAG_NEXTHOP \
    -	(*var_smtp_sasl_passwd ? SMTP_KEY_FLAG_NEXTHOP : 0)
    +#define COND_SASL_SMTP_KEY_FLAG_CUR_NEXTHOP \
    +	(*var_smtp_sasl_passwd ? SMTP_KEY_FLAG_CUR_NEXTHOP : 0)
     
     #ifdef USE_TLS
    -#define COND_TLS_SMTP_KEY_FLAG_NEXTHOP \
    -	(TLS_MUST_MATCH(state->tls->level) ? SMTP_KEY_FLAG_NEXTHOP : 0)
    +#define COND_TLS_SMTP_KEY_FLAG_CUR_NEXTHOP \
    +	(TLS_MUST_MATCH(state->tls->level) ? SMTP_KEY_FLAG_CUR_NEXTHOP : 0)
     #else
    -#define COND_TLS_SMTP_KEY_FLAG_NEXTHOP \
    +#define COND_TLS_SMTP_KEY_FLAG_CUR_NEXTHOP \
     	(0)
     #endif
     
    @@ -655,10 +664,13 @@ char   *smtp_key_prefix(VSTRING *, const char *, SMTP_ITERATOR *, int);
     	(*var_smtp_sasl_passwd ? SMTP_KEY_FLAG_HOSTNAME : 0)
     
      /*
    -  * Connection-cache destination lookup key. The SENDER attribute is a proxy
    -  * for sender-dependent SASL credentials (or absence thereof), and prevents
    -  * false connection sharing when different SASL credentials may be required
    -  * for different deliveries to the same domain and port. The SERVICE
    +  * Connection-cache destination lookup key, based on the delivery request
    +  * nexthop. The SENDER attribute is a proxy for sender-dependent SASL
    +  * credentials (or absence thereof), and prevents false connection sharing
    +  * when different SASL credentials may be required for different deliveries
    +  * to the same domain and port. Likewise, the delivery request nexthop
    +  * (REQ_NEXTHOP) prevents false sharing of TLS identities (the destination
    +  * key links only to appropriate endpoint lookup keys). The SERVICE
       * attribute is a proxy for all request-independent configuration details.
       */
     #define SMTP_KEY_MASK_SCACHE_DEST_LABEL \
    @@ -666,15 +678,18 @@ char   *smtp_key_prefix(VSTRING *, const char *, SMTP_ITERATOR *, int);
     	| SMTP_KEY_FLAG_REQ_NEXTHOP)
     
      /*
    -  * Connection-cache endpoint lookup key. The SENDER, NEXTHOP, and HOSTNAME
    -  * attributes are proxies for SASL credentials (or absence thereof), and
    -  * prevent false connection sharing when different SASL credentials may be
    -  * required for different deliveries to the same IP address and port.
    +  * Connection-cache endpoint lookup key. The SENDER, CUR_NEXTHOP, HOSTNAME,
    +  * PORT and TLS_LEVEL attributes are proxies for SASL credentials and TLS
    +  * authentication (or absence thereof), and prevent false connection sharing
    +  * when different SASL credentials or TLS identities may be required for
    +  * different deliveries to the same IP address and port. The SERVICE
    +  * attribute is a proxy for all request-independent configuration details.
       */
     #define SMTP_KEY_MASK_SCACHE_ENDP_LABEL \
     	(SMTP_KEY_FLAG_SERVICE | COND_SASL_SMTP_KEY_FLAG_SENDER \
    -	| COND_SASL_SMTP_KEY_FLAG_NEXTHOP | COND_SASL_SMTP_KEY_FLAG_HOSTNAME \
    -	| COND_TLS_SMTP_KEY_FLAG_NEXTHOP | SMTP_KEY_FLAG_ADDR | \
    +	| COND_SASL_SMTP_KEY_FLAG_CUR_NEXTHOP \
    +	| COND_SASL_SMTP_KEY_FLAG_HOSTNAME \
    +	| COND_TLS_SMTP_KEY_FLAG_CUR_NEXTHOP | SMTP_KEY_FLAG_ADDR | \
     	SMTP_KEY_FLAG_PORT | SMTP_KEY_FLAG_TLS_LEVEL)
     
      /*
    @@ -703,11 +718,6 @@ extern int smtp_mode;
     /*	111 8th Avenue
     /*	New York, NY 10011, USA
     /*
    -/*	Wietse Venema
    -/*	Google, Inc.
    -/*	111 8th Avenue
    -/*	New York, NY 10011, USA
    -/*
     /*	TLS support originally by:
     /*	Lutz Jaenicke
     /*	BTU Cottbus
    diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c
    index b218b2a01..d5693c7c8 100644
    --- a/postfix/src/smtp/smtp_connect.c
    +++ b/postfix/src/smtp/smtp_connect.c
    @@ -417,13 +417,13 @@ static void smtp_cleanup_session(SMTP_STATE *state)
         state->session = 0;
     
         /*
    -     * If this session was good, reset the logical next-hop state, so that we
    -     * won't cache connections to alternate servers under the logical
    -     * next-hop destination. Otherwise we could end up skipping over the
    -     * available and more preferred servers.
    +     * If this session was good, reset the logical next-hop destination, so
    +     * that we won't cache connections to less-preferred servers under the
    +     * logical next-hop destination. Otherwise we could end up skipping over
    +     * the available and more-preferred servers.
          */
    -    if (HAVE_NEXTHOP_STATE(state) && !throttled)
    -	FREE_NEXTHOP_STATE(state);
    +    if (HAVE_SCACHE_REQUEST_NEXTHOP(state) && !throttled)
    +	CLEAR_SCACHE_REQUEST_NEXTHOP(state);
     
         /*
          * Clean up the lists with todo and dropped recipients.
    @@ -678,7 +678,7 @@ static int smtp_reuse_session(SMTP_STATE *state, DNS_RR **addr_list,
     #endif
         SMTP_ITER_SAVE_DEST(state->iterator);
         if (*addr_list && SMTP_RCPT_LEFT(state) > 0
    -	&& HAVE_NEXTHOP_STATE(state)
    +	&& HAVE_SCACHE_REQUEST_NEXTHOP(state)
     	&& (session = smtp_reuse_nexthop(state, SMTP_KEY_MASK_SCACHE_DEST_LABEL)) != 0) {
     	session_count = 1;
     	smtp_update_addr_list(addr_list, STR(iter->addr), session_count);
    @@ -899,7 +899,7 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
     	if (addr_list && (state->misc_flags & SMTP_MISC_FLAG_FIRST_NEXTHOP)) {
     	    smtp_cache_policy(state, domain);
     	    if (state->misc_flags & SMTP_MISC_FLAG_CONN_CACHE_MASK)
    -		SET_NEXTHOP_STATE(state, dest);
    +		SET_SCACHE_REQUEST_NEXTHOP(state, dest);
     	}
     
     	/*
    @@ -1124,8 +1124,8 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
         /*
          * Cleanup.
          */
    -    if (HAVE_NEXTHOP_STATE(state))
    -	FREE_NEXTHOP_STATE(state);
    +    if (HAVE_SCACHE_REQUEST_NEXTHOP(state))
    +	CLEAR_SCACHE_REQUEST_NEXTHOP(state);
         argv_free(sites);
     }
     
    diff --git a/postfix/src/smtp/smtp_key.c b/postfix/src/smtp/smtp_key.c
    index 6f72b803e..8b5db76a2 100644
    --- a/postfix/src/smtp/smtp_key.c
    +++ b/postfix/src/smtp/smtp_key.c
    @@ -53,7 +53,7 @@
     /* .IP SMTP_KEY_FLAG_REQ_NEXTHOP
     /*	The request nexthop destination. This is a proxy for
     /*	destination-dependent, but host-independent context.
    -/* .IP SMTP_KEY_FLAG_NEXTHOP
    +/* .IP SMTP_KEY_FLAG_CUR_NEXTHOP
     /*	The current iterator's nexthop destination (request nexthop
     /*	or fallback nexthop, including optional [] and :port). This
     /*	is the form that users specify in a SASL or TLS lookup
    @@ -183,7 +183,7 @@ char   *smtp_key_prefix(VSTRING *buffer, const char *delim_na,
          */
         if (flags & SMTP_KEY_FLAG_REQ_NEXTHOP)
     	smtp_key_append_str(buffer, STR(iter->request_nexthop), delim_na);
    -    if (flags & SMTP_KEY_FLAG_NEXTHOP)
    +    if (flags & SMTP_KEY_FLAG_CUR_NEXTHOP)
     	smtp_key_append_str(buffer, STR(iter->dest), delim_na);
     
         /*
    diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c
    index 0169f1716..0bac85aba 100644
    --- a/postfix/src/smtp/smtp_proto.c
    +++ b/postfix/src/smtp/smtp_proto.c
    @@ -899,7 +899,7 @@ static int smtp_start_tls(SMTP_STATE *state)
          */
         serverid = vstring_alloc(10);
         smtp_key_prefix(serverid, "&", state->iterator, SMTP_KEY_FLAG_SERVICE
    -		    | SMTP_KEY_FLAG_NEXTHOP	/* With port */
    +		    | SMTP_KEY_FLAG_CUR_NEXTHOP	/* With port */
     		    | SMTP_KEY_FLAG_HOSTNAME
     		    | SMTP_KEY_FLAG_ADDR);
     
    @@ -1018,6 +1018,11 @@ static int smtp_start_tls(SMTP_STATE *state)
     	     * context attributes.
     	     */
     	    session->tls_context = tls_proxy_context_receive(session->stream);
    +	    if (session->tls_context) {
    +		session->features |= SMTP_FEATURE_FROM_PROXY;
    +		tls_log_summary(TLS_ROLE_CLIENT, TLS_USAGE_NEW,
    +				session->tls_context);
    +	    }
     	}
         } else {					/* state->tls->conn_reuse */
     
    diff --git a/postfix/src/smtp/smtp_reuse.c b/postfix/src/smtp/smtp_reuse.c
    index f4b4996c5..f93ba296a 100644
    --- a/postfix/src/smtp/smtp_reuse.c
    +++ b/postfix/src/smtp/smtp_reuse.c
    @@ -23,27 +23,26 @@
     /*	This module implements the SMTP client specific interface to
     /*	the generic session cache infrastructure.
     /*
    -/*	A cached connection is closed when the TLS policy requires
    -/*	that TLS is enabled.
    +/*	The caller needs to include additional state in _key_flags
    +/*	to avoid false sharing of SASL-authenticated or TLS-authenticated
    +/*	sessions.
     /*
     /*	smtp_save_session() stores the current session under the
    -/*	next-hop logical destination (if available) and under the
    -/*	remote server address.  The SMTP_SESSION object is destroyed.
    +/*	delivery request next-hop logical destination (if applicable)
    +/*	and under the remote server address. The SMTP_SESSION object
    +/*	is destroyed.
     /*
    -/*	smtp_reuse_nexthop() looks up a cached session by its logical
    -/*	destination, and verifies that the session is still alive.
    -/*	The restored session information includes the "best MX" bit
    -/*	and overrides the iterator dest, host and addr fields.
    -/*	The result is null in case of failure.
    +/*	smtp_reuse_nexthop() looks up a cached session by its
    +/*	delivery request next-hop destination, and verifies that
    +/*	the session is still alive. The restored session information
    +/*	includes the "best MX" bit and overrides the iterator dest,
    +/*	host and addr fields. The result is null in case of failure.
     /*
     /*	smtp_reuse_addr() looks up a cached session by its server
     /*	address, and verifies that the session is still alive.
     /*	The restored session information does not include the "best
     /*	MX" bit, and does not override the iterator dest, host and
    -/*	addr fields.
    -/*	This function is a NOOP for TLS levels stronger than "encrypt",
    -/*	because stronger levels require certificate checks,
    -/*	The result is null in case of failure.
    +/*	addr fields. The result is null in case of failure.
     /*
     /*	Arguments:
     /* .IP state
    @@ -116,10 +115,16 @@ void    smtp_save_session(SMTP_STATE *state, int name_key_flags,
         int     fd;
     
         /*
    -     * Encode the next-hop logical destination, if available. Reuse storage
    -     * that is also used for cache lookup queries.
    +     * Encode the delivery request next-hop destination, if applicable. Reuse
    +     * storage that is also used for cache lookup queries.
    +     * 
    +     * HAVE_SCACHE_REQUEST_NEXTHOP() controls whether or not to reuse or cache a
    +     * connection by its delivery request next-hop destination. The idea is
    +     * 1) to allow a reuse request to skip over bad hosts, and 2) to avoid
    +     * caching a less-preferred connection when a more-preferred connection
    +     * was possible.
          */
    -    if (HAVE_NEXTHOP_STATE(state))
    +    if (HAVE_SCACHE_REQUEST_NEXTHOP(state))
     	smtp_key_prefix(state->dest_label, SMTP_REUSE_KEY_DELIM_NA,
     			state->iterator, name_key_flags);
     
    @@ -138,16 +143,18 @@ void    smtp_save_session(SMTP_STATE *state, int name_key_flags,
         state->session = 0;
     
         /*
    -     * Save the session under the next-hop name, if available.
    +     * Save the session under the delivery request next-hop name, if
    +     * applicable.
          * 
          * XXX The logical to physical binding can be kept for as long as the DNS
          * allows us to (but that could result in the caching of lots of unused
          * bindings). The session should be idle for no more than 30 seconds or
          * so.
          */
    -    if (HAVE_NEXTHOP_STATE(state))
    -	scache_save_dest(smtp_scache, var_smtp_cache_conn, STR(state->dest_label),
    -			 STR(state->dest_prop), STR(state->endp_label));
    +    if (HAVE_SCACHE_REQUEST_NEXTHOP(state))
    +	scache_save_dest(smtp_scache, var_smtp_cache_conn,
    +			 STR(state->dest_label), STR(state->dest_prop),
    +			 STR(state->endp_label));
     
         /*
          * Save every good session under its physical endpoint address.
    @@ -165,15 +172,6 @@ static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd,
         SMTP_ITERATOR *iter = state->iterator;
         SMTP_SESSION *session;
     
    -    /*
    -     * Obsolete.
    -     */
    -#ifdef notdef
    -    if (state->tls->level > TLS_LEV_NONE)
    -	msg_panic("%s: unexpected plain-text cached session to %s",
    -		  myname, label);
    -#endif
    -
         /*
          * Re-activate the SMTP_SESSION object.
          */
    @@ -216,15 +214,6 @@ SMTP_SESSION *smtp_reuse_nexthop(SMTP_STATE *state, int name_key_flags)
         SMTP_SESSION *session;
         int     fd;
     
    -    /*
    -     * Obsolete: the TLS level and nexthop are part of the connection cache
    -     * key. TODO(tlsproxy) is the port included in the nexthop?
    -     */
    -#ifdef notdef
    -    if (state->tls->level > TLS_LEV_NONE)
    -	return (0);
    -#endif
    -
         /*
          * Look up the session by its logical name.
          */
    @@ -250,18 +239,13 @@ SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, int endp_key_flags)
         int     fd;
     
         /*
    -     * Allow address-based reuse only for security levels that don't require
    -     * certificate checks. Not to be confused with a similar constraint in
    -     * the destination label smtp_key pattern, which conditionally includes
    -     * the nexthop to prevent the reuse of an authenticated connection to the
    -     * same MX hostname and the same IP address, but for a different nexthop
    -     * destination (just in case we start to send SNI with the nexthop, and
    -     * forget to update connection cache lookup key patterns).
    +     * Address-based reuse is safe for security levels that require TLS
    +     * certificate checks, as long as the current nexhop is included in the
    +     * cache lookup key (COND_TLS_SMTP_KEY_FLAG_CUR_NEXTHOP). This is
    +     * sufficient to prevent the reuse of a TLS-authenticated connection to
    +     * the same MX hostname, IP address, and port, but for a different
    +     * current nexthop destination with a different TLS policy.
          */
    -#ifdef USE_TLS
    -    if (TLS_MUST_MATCH(state->tls->level))
    -	return (0);
    -#endif
     
         /*
          * Look up the session by its IP address. This means that we have no
    diff --git a/postfix/src/smtp/smtp_session.c b/postfix/src/smtp/smtp_session.c
    index 2b4ce8675..7ae9afd0c 100644
    --- a/postfix/src/smtp/smtp_session.c
    +++ b/postfix/src/smtp/smtp_session.c
    @@ -113,11 +113,28 @@
     #include 
     #include 
     
    +/* TLS Library. */
    +
    +#include 
    +
     /* Application-specific. */
     
     #include "smtp.h"
     #include "smtp_sasl.h"
     
    + /*
    +  * Local, because these are meaningful only for code in this file.
    +  */
    +#define SESS_ATTR_DEST		"destination"
    +#define SESS_ATTR_HOST		"host_name"
    +#define SESS_ATTR_ADDR		"host_addr"
    +#define SESS_ATTR_DEST_FEATURES	"destination_features"
    +
    +#define SESS_ATTR_TLS_LEVEL	"tls_level"
    +#define SESS_ATTR_REUSE_COUNT	"reuse_count"
    +#define SESS_ATTR_ENDP_FEATURES	"endpoint_features"
    +#define SESS_ATTR_EXPIRE_TIME	"expire_time"
    +
     /* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */
     
     SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, SMTP_ITERATOR *iter,
    @@ -183,7 +200,8 @@ void    smtp_session_free(SMTP_SESSION *session)
     	vstream_fflush(session->stream);
         }
         if (session->tls_context) {
    -	if (session->state->tls->conn_reuse)
    +	if (session->features &
    +	    (SMTP_FEATURE_FROM_CACHE | SMTP_FEATURE_FROM_PROXY))
     	    tls_proxy_context_free(session->tls_context);
     	else
     	    tls_client_stop(smtp_tls_ctx, session->stream,
    @@ -220,6 +238,7 @@ int     smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop,
     			               VSTRING *endp_prop)
     {
         SMTP_ITERATOR *iter = session->iterator;
    +    VSTREAM *mp;
         int     fd;
     
         /*
    @@ -228,55 +247,61 @@ int     smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop,
          * destination (this information is needed for loop handling in
          * smtp_proto()).
          * 
    -     * XXX It would be nice to have a VSTRING to VSTREAM adapter so that we can
    -     * serialize the properties with attr_print() instead of using ad-hoc,
    -     * non-reusable, code and hard-coded format strings.
    -     * 
    -     * TODO(tlsproxy): save TLS_SESS_STATE information so that we can restore
    -     * TLS session properties.
    -     * 
          * TODO: save SASL username and password information so that we can
          * correctly save a reused authenticated connection.
          * 
    -     * Note: the TLS level field is always present.
    +     * These memory writes should never fail.
          */
    -    vstring_sprintf(dest_prop, "%s\n%s\n%s\n%u\n%u",
    -		    STR(iter->dest), STR(iter->host), STR(iter->addr),
    -#ifdef USE_TLS
    -		    iter->parent->tls->level,
    -#else
    -		    0,
    -#endif
    -		    session->features & SMTP_FEATURE_DESTINATION_MASK);
    +    if ((mp = vstream_memopen(dest_prop, O_WRONLY)) == 0
    +	|| attr_print_plain(mp, ATTR_FLAG_NONE,
    +			    SEND_ATTR_STR(SESS_ATTR_DEST, STR(iter->dest)),
    +			    SEND_ATTR_STR(SESS_ATTR_HOST, STR(iter->host)),
    +			    SEND_ATTR_STR(SESS_ATTR_ADDR, STR(iter->addr)),
    +			    SEND_ATTR_INT(SESS_ATTR_DEST_FEATURES,
    +			 session->features & SMTP_FEATURE_DESTINATION_MASK),
    +			    ATTR_TYPE_END) != 0
    +	|| vstream_fclose(mp) != 0)
    +	msg_fatal("smtp_session_passivate: can't save dest properties: %m");
     
         /*
          * Encode the physical endpoint properties: all the session properties
          * except for "session from cache", "best MX", or "RSET failure".
    +     * Plus the TLS level, reuse count, and connection expiration time.
          * 
    -     * XXX It would be nice to have a VSTRING to VSTREAM adapter so that we can
    -     * serialize the properties with attr_print() instead of using obscure
    -     * hard-coded format strings.
    +     * XXX Should also record how many non-delivering mail transactions there
    +     * were during this session, and perhaps other statistics, so that we
    +     * don't reuse a session too much.
          * 
    -     * XXX Should also record an absolute time when a session must be closed,
    -     * how many non-delivering mail transactions there were during this
    -     * session, and perhaps other statistics, so that we don't reuse a
    -     * session too much.
    +     * TODO: passivate SASL username and password information so that we can
    +     * correctly save a reused authenticated connection.
          * 
    -     * XXX Be sure to use unsigned types in the format string. Sign characters
    -     * would be rejected by the alldig() test on the reading end.
    +     * These memory writes should never fail.
          */
    -    vstring_sprintf(endp_prop, "%u\n%u\n%lu",
    -		    session->reuse_count,
    -		    session->features & SMTP_FEATURE_ENDPOINT_MASK,
    -		    (long) session->expire_time);
    +    if ((mp = vstream_memopen(endp_prop, O_WRONLY)) == 0
    +	|| attr_print_plain(mp, ATTR_FLAG_NONE,
    +			    SEND_ATTR_INT(SESS_ATTR_TLS_LEVEL,
    +					  session->state->tls->level),
    +			    SEND_ATTR_INT(SESS_ATTR_REUSE_COUNT,
    +					  session->reuse_count),
    +			    SEND_ATTR_INT(SESS_ATTR_ENDP_FEATURES,
    +			    session->features & SMTP_FEATURE_ENDPOINT_MASK),
    +			    SEND_ATTR_LONG(SESS_ATTR_EXPIRE_TIME,
    +					   (long) session->expire_time),
    +			    ATTR_TYPE_END) != 0
     
         /*
    -     * Append the passivated SASL attributes.
    +     * Append the passivated TLS context. These memory writes should never
    +     * fail.
          */
    -#ifdef notdef
    -    if (smtp_sasl_enable)
    -	smtp_sasl_passivate(endp_prop, session);
    +#ifdef USE_TLS
    +	|| (session->tls_context
    +	    && attr_print_plain(mp, ATTR_FLAG_NONE,
    +				SEND_ATTR_FUNC(tls_proxy_context_print,
    +					     (void *) session->tls_context),
    +				ATTR_TYPE_END) != 0)
     #endif
    +	|| vstream_fclose(mp) != 0)
    +	msg_fatal("smtp_session_passivate: cannot save TLS context: %m");
     
         /*
          * Salvage the underlying file descriptor, and destroy the session
    @@ -297,50 +322,52 @@ SMTP_SESSION *smtp_session_activate(int fd, SMTP_ITERATOR *iter,
     				            VSTRING *endp_prop)
     {
         const char *myname = "smtp_session_activate";
    +    VSTREAM *mp;
         SMTP_SESSION *session;
    -    char   *dest_props;
    -    char   *endp_props;
    -    const char *prop;
    -    const char *dest;
    -    const char *host;
    -    const char *addr;
    -    unsigned features;			/* server features */
    -    time_t  expire_time;		/* session re-use expiration time */
    -    unsigned reuse_count;		/* # times reused */
    +    int     endp_features;		/* server features */
    +    int     dest_features;		/* server features */
    +    long    expire_time;		/* session re-use expiration time */
    +    int     reuse_count;		/* # times reused */
    +    TLS_SESS_STATE *tls_context = 0;
     
     #ifdef USE_TLS
         SMTP_TLS_POLICY *tls = iter->parent->tls;
     
     #endif
     
    +#define SMTP_SESSION_ACTIVATE_ERR_RETURN() do { \
    +	if (tls_context) \
    +	    tls_proxy_context_free(tls_context); \
    +	return (0); \
    +   } while (0)
    +
         /*
    -     * XXX it would be nice to have a VSTRING to VSTREAM adapter so that we
    -     * can de-serialize the properties with attr_scan(), instead of using
    -     * ad-hoc, non-reusable code.
    -     * 
    -     * XXX As a preliminary solution we use mystrtok(), but that function is not
    -     * suitable for zero-length fields.
    +     * Sanity check: if TLS is required, the cached properties must contain a
    +     * TLS context.
          */
    -    endp_props = STR(endp_prop);
    -    if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
    -	msg_warn("%s: bad cached session reuse count property", myname);
    -	return (0);
    -    }
    -    reuse_count = atoi(prop);
    -    if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
    -	msg_warn("%s: bad cached session features property", myname);
    -	return (0);
    -    }
    -    features = atoi(prop);
    -    if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
    -	msg_warn("%s: bad cached session expiration time property", myname);
    -	return (0);
    -    }
    -#ifdef MISSING_STRTOUL
    -    expire_time = strtol(prop, 0, 10);
    -#else
    -    expire_time = strtoul(prop, 0, 10);
    +    if ((mp = vstream_memopen(endp_prop, O_RDONLY)) == 0
    +	|| attr_scan_plain(mp, ATTR_FLAG_NONE,
    +			   RECV_ATTR_INT(SESS_ATTR_TLS_LEVEL,
    +					 &tls->level),
    +			   RECV_ATTR_INT(SESS_ATTR_REUSE_COUNT,
    +					 &reuse_count),
    +			   RECV_ATTR_INT(SESS_ATTR_ENDP_FEATURES,
    +					 &endp_features),
    +			   RECV_ATTR_LONG(SESS_ATTR_EXPIRE_TIME,
    +					  &expire_time),
    +			   ATTR_TYPE_END) != 4
    +#ifdef USE_TLS
    +	|| ((tls->level > TLS_LEV_MAY
    +	     || (tls->level == TLS_LEV_MAY && vstream_peek(mp) > 0))
    +	    && attr_scan_plain(mp, ATTR_FLAG_NONE,
    +			       RECV_ATTR_FUNC(tls_proxy_context_scan,
    +					      (void *) &tls_context),
    +			       ATTR_TYPE_END) != 1)
     #endif
    +	|| vstream_fclose(mp) != 0) {
    +	msg_warn("smtp_session_activate: bad cached endp properties");
    +	SMTP_SESSION_ACTIVATE_ERR_RETURN();
    +    }
     
         /*
          * Clobber the iterator's current nexthop, host and address fields with
    @@ -355,36 +382,25 @@ SMTP_SESSION *smtp_session_activate(int fd, SMTP_ITERATOR *iter,
          * correctly save a reused authenticated connection.
          */
         if (dest_prop && VSTRING_LEN(dest_prop)) {
    -	dest_props = STR(dest_prop);
    -	if ((dest = mystrtok(&dest_props, "\n")) == 0) {
    -	    msg_warn("%s: missing cached session destination property", myname);
    -	    return (0);
    +	if ((mp = vstream_memopen(dest_prop, O_RDONLY)) == 0
    +	    || attr_scan_plain(mp, ATTR_FLAG_NONE,
    +			       RECV_ATTR_STR(SESS_ATTR_DEST, iter->dest),
    +			       RECV_ATTR_STR(SESS_ATTR_HOST, iter->host),
    +			       RECV_ATTR_STR(SESS_ATTR_ADDR, iter->addr),
    +			       RECV_ATTR_INT(SESS_ATTR_DEST_FEATURES,
    +					     &dest_features),
    +			       ATTR_TYPE_END) != 4
    +	    || vstream_fclose(mp) != 0) {
    +	    msg_warn("smtp_session_passivate: bad cached dest properties");
    +	    SMTP_SESSION_ACTIVATE_ERR_RETURN();
     	}
    -	if ((host = mystrtok(&dest_props, "\n")) == 0) {
    -	    msg_warn("%s: missing cached session hostname property", myname);
    -	    return (0);
    -	}
    -	if ((addr = mystrtok(&dest_props, "\n")) == 0) {
    -	    msg_warn("%s: missing cached session address property", myname);
    -	    return (0);
    -	}
    -	/* Note: the TLS level field is always present. */
    -	if ((prop = mystrtok(&dest_props, "\n")) == 0 || !alldig(prop)) {
    -	    msg_warn("%s: bad cached destination TLS level property", myname);
    -	    return (0);
    -	}
    -#ifdef USE_TLS
    -	tls->level = atoi(prop);
    -	if (msg_verbose)
    -	    msg_info("%s: tls_level=%d", myname, tls->level);
    -#endif
    -	if ((prop = mystrtok(&dest_props, "\n")) == 0 || !alldig(prop)) {
    -	    msg_warn("%s: bad cached destination features property", myname);
    -	    return (0);
    -	}
    -	features |= atoi(prop);
    -	SMTP_ITER_CLOBBER(iter, dest, host, addr);
    +    } else {
    +	dest_features = 0;
         }
    +#ifdef USE_TLS
    +    if (msg_verbose)
    +	msg_info("%s: tls_level=%d", myname, tls->level);
    +#endif
     
         /*
          * Allright, bundle up what we have sofar.
    @@ -393,7 +409,9 @@ SMTP_SESSION *smtp_session_activate(int fd, SMTP_ITERATOR *iter,
     
         session = smtp_session_alloc(vstream_fdopen(fd, O_RDWR), iter,
     				 (time_t) 0, NO_FLAGS);
    -    session->features = (features | SMTP_FEATURE_FROM_CACHE);
    +    session->features =
    +	(endp_features | dest_features | SMTP_FEATURE_FROM_CACHE);
    +    session->tls_context = tls_context;
         CACHE_THIS_SESSION_UNTIL(expire_time);
         session->reuse_count = ++reuse_count;
     
    @@ -401,20 +419,15 @@ SMTP_SESSION *smtp_session_activate(int fd, SMTP_ITERATOR *iter,
     	msg_info("%s: dest=%s host=%s addr=%s port=%u features=0x%x, "
     		 "ttl=%ld, reuse=%d",
     		 myname, STR(iter->dest), STR(iter->host),
    -		 STR(iter->addr), ntohs(iter->port), features,
    +		 STR(iter->addr), ntohs(iter->port),
    +		 endp_features | dest_features,
     		 (long) (expire_time - time((time_t *) 0)),
     		 reuse_count);
     
    -    /*
    -     * Re-activate the SASL attributes.
    -     */
    -#ifdef notdef
    -    if (smtp_sasl_enable && smtp_sasl_activate(session, endp_props) < 0) {
    -	vstream_fdclose(session->stream);
    -	session->stream = 0;
    -	smtp_session_free(session);
    -	return (0);
    -    }
    +#if USE_TLS
    +    if (tls_context)
    +	tls_log_summary(TLS_ROLE_CLIENT, TLS_USAGE_USED,
    +			session->tls_context);
     #endif
     
         return (session);
    diff --git a/postfix/src/smtp/smtp_tls_policy.c b/postfix/src/smtp/smtp_tls_policy.c
    index 13735b210..6415e032e 100644
    --- a/postfix/src/smtp/smtp_tls_policy.c
    +++ b/postfix/src/smtp/smtp_tls_policy.c
    @@ -668,7 +668,7 @@ int     smtp_tls_policy_cache_query(DSN_BUF *why, SMTP_TLS_POLICY *tls,
          * values that also appear in other cache and table search keys.
          */
         key = vstring_alloc(100);
    -    smtp_key_prefix(key, ":", iter, SMTP_KEY_FLAG_NEXTHOP
    +    smtp_key_prefix(key, ":", iter, SMTP_KEY_FLAG_CUR_NEXTHOP
     		    | SMTP_KEY_FLAG_HOSTNAME
     		    | SMTP_KEY_FLAG_PORT);
         ctable_newcontext(policy_cache, (void *) iter);
    diff --git a/postfix/src/tls/tls.h b/postfix/src/tls/tls.h
    index e49567562..0f3c5be12 100644
    --- a/postfix/src/tls/tls.h
    +++ b/postfix/src/tls/tls.h
    @@ -115,12 +115,12 @@ extern const char *str_tls_level(int);
     #endif
     
      /*-
    -  * Backwards compatibility with OpenSSL < 1.1.1a (or some later version).
    +  * Backwards compatibility with OpenSSL < 1.1.1a.
       *
    -  * The client-only interface SSL_get_server_tmp_key() is slated to be made to
    -  * work on both client and server, and renamed to SSL_get_peer_tmp_key(), with
    -  * the original name left behind as an alias.  We use the new name when
    -  * available.
    +  * In OpenSSL 1.1.1a the client-only interface SSL_get_server_tmp_key() was
    +  * updated to work on both the client and the server, and was renamed to
    +  * SSL_get_peer_tmp_key(), with the original name left behind as an alias.  We
    +  * use the new name when available.
       */
     #if OPENSSL_VERSION_NUMBER < 0x1010101fUL
     #undef SSL_get_signature_nid
    @@ -159,9 +159,13 @@ extern const char *str_tls_level(int);
      /*
       * TLS role, presently for logging.
       */
    -typedef enum { TLS_ROLE_CLIENT, TLS_ROLE_SERVER, } TLS_ROLE;
    +typedef enum {
    +    TLS_ROLE_CLIENT, TLS_ROLE_SERVER,
    +} TLS_ROLE;
     
    -typedef enum { TLS_USAGE_NEW, TLS_USAGE_USED, } TLS_USAGE;
    +typedef enum {
    +    TLS_USAGE_NEW, TLS_USAGE_USED,
    +} TLS_USAGE;
     
      /*
       * Names of valid tlsmgr(8) session caches.
    diff --git a/postfix/src/tls/tls_client.c b/postfix/src/tls/tls_client.c
    index f48538973..3d771a494 100644
    --- a/postfix/src/tls/tls_client.c
    +++ b/postfix/src/tls/tls_client.c
    @@ -1191,6 +1191,9 @@ TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *TLScontext,
     	&& !TLS_NEVER_SECURED(props->tls_level))
     	TLScontext->peer_status |= TLS_CERT_FLAG_SECURED;
     
    +    /*
    +     * With the handshake done, extract TLS 1.3 signature metadata.
    +     */
         tls_get_signature_params(TLScontext);
     
         if (TLScontext->log_mask & TLS_LOG_SUMMARY)
    diff --git a/postfix/src/tls/tls_misc.c b/postfix/src/tls/tls_misc.c
    index 0ca919f52..9460d9fd6 100644
    --- a/postfix/src/tls/tls_misc.c
    +++ b/postfix/src/tls/tls_misc.c
    @@ -14,6 +14,12 @@
     /*	TLS_USAGE usage;
     /*	TLS_SESS_STATE *TLScontext;
     /*
    +/*	const char *tls_compile_version(void)
    +/*
    +/*	const char *tls_run_version(void)
    +/*
    +/*	const char **tls_pkey_algorithms(void)
    +/*
     /* .SH Internal functions
     /* .nf
     /* .na
    @@ -103,12 +109,6 @@
     /*
     /*	int	tls_validate_digest(dgst)
     /*	const char *dgst;
    -/*
    -/*	const char *tls_compile_version(void)
    -/*
    -/*	const char *tls_run_version(void)
    -/*
    -/*	const char **tls_pkey_algorithms(void)
     /* DESCRIPTION
     /*	This module implements public and internal routines that
     /*	support the TLS client and server.
    @@ -118,6 +118,16 @@
     /*	connections, or TLS_ROLE_SERVER for incoming server connections,
     /*	and the "usage" must be TLS_USAGE_NEW or TLS_USAGE_USED.
     /*
    +/*	tls_compile_version() returns a text string description of
    +/*	the compile-time TLS library.
    +/*
    +/*	tls_run_version() is just tls_compile_version() but with the runtime
    +/*	version instead of the compile-time version.
    +/*
    +/*	tls_pkey_algorithms() returns a pointer to null-terminated
    +/*	array of string constants with the names of the supported
    +/*	public-key algorithms.
    +/*
     /*	tls_alloc_app_context() creates an application context that
     /*	holds the SSL context for the application and related cached state.
     /*
    @@ -190,16 +200,6 @@
     /*
     /*	tls_validate_digest() returns non-zero if the named digest
     /*	is usable and zero otherwise.
    -/*
    -/*	tls_compile_version() returns a text string description of
    -/*	the compile-time TLS library.
    -/*
    -/*	tls_run_version() is just tls_compile_version() but with the runtime
    -/*	version instead of the compile-time version.
    -/*
    -/*	tls_pkey_algorithms() returns a pointer to null-terminated
    -/*	array of string constants with the names of the supported
    -/*	public-key algorithms.
     /* LICENSE
     /* .ad
     /* .fi
    diff --git a/postfix/src/util/vstream.c b/postfix/src/util/vstream.c
    index 674e79604..7af288271 100644
    --- a/postfix/src/util/vstream.c
    +++ b/postfix/src/util/vstream.c
    @@ -1714,7 +1714,7 @@ VSTREAM *vstream_memreopen(VSTREAM *stream, VSTRING *string, int flags)
         stream->write_fn = 0;
         stream->vstring = string;
         memcpy(&stream->buf, &stream->vstring->vbuf, sizeof(stream->buf));
    -    stream->buf.flags |= (flags | VSTREAM_FLAG_MEMORY);
    +    stream->buf.flags |= VSTREAM_FLAG_MEMORY;
         switch (VSTREAM_ACC_MASK(flags)) {
         case O_RDONLY:
     	stream->buf.flags |= VSTREAM_FLAG_READ;