TLS handshake error. Found during code maintenance. File:
tlsproxy/tlsproxy.c.
+ Connection reuse for TLS-encrypted SMTP sessions. This is
+ work-in-progress, #ifdef USE_TLSPROXY, to avoid contamination
+ of existing code.
+
+ The idea is to have smtp(8) talk plaintext while tlsproxy(8)
+ converts between local plaintext and remote ciphertext.
+ Then, smtp(8) can save plaintext connections to the cache,
+ and scache(8) holds the handles to the tlsproxy(8) processes.
+
+ This preliminary implementation does not yet support proxying
+ of DANE attributes from smtp(8) to tlsproxy(8). tlsproxy(8)
+ does not have permissions to read private key files that
+ smtp(8) can read. And the name of a connection cache entry
+ does not yet depend on whether the cached connection uses
+ TLS, nor does it depend on DANE information.
+
+ Files: global/mail_proto.h, postscreen/postscreen_starttls.c,
+ posttls-finger/posttls-finger.c, smtp/smtp.c, smtp/smtp.h,
+ smtp/smtp_params.c, smtp/smtp_proto.c, smtp/smtp_session.c,
+ smtpd/smtpd.c, tls/tls.h, tls/tls_client.c, tls/tls_proxy.h,
+ tls/tls_proxy_client_init_print.c,
+ tls/tls_proxy_client_init_scan.c,
+ tls/tls_proxy_client_start_print.c,
+ tls/tls_proxy_client_start_scan.c, tls/tls_proxy_clnt.c,
+ tls/tls_proxy_context_print.c, tls/tls_proxy_context_scan.c,
+ tls/tls_proxy_server_init_print.c,
+ tls/tls_proxy_server_init_scan.c,
+ tls/tls_proxy_server_start_print.c,
+ tls/tls_proxy_server_start_scan.c, tlsproxy/tlsproxy.c,
+ tlsproxy/tlsproxy.h, tlsproxy/tlsproxy_state.c, util/argv_attr.h,
+ util/argv_attr_print.c, util/argv_attr_scan.c.
+
20180425
Cleanup: dnsblog proccesses now retire voluntarily after
Documentation: bash syntax to eliminate or view default
settings in "postconf -n" output. File: postconf/postconf.c.
Contributed by various postfix-users list members.
+
+20180603
+
+ TLS reuse: serializer/deserializer support for TLS_DANE and
+ related data structures. Files: tls/tls_proxy_client_print.c,
+ tls/tls_proxy_client_scan.c, tls/tls_proxy.h, util/argv_attr.h,
+ util/argv_attr_print.c, util/argv_attr_scan.c.
+
+ TLS reuse: posttls-finger -X test flag for quick tests.
+ File: posttls-finger/posttls-finger.c.
+
+ TLS reuse: smtp_use_tlsproxy boolean parameter. This is a
+ preliminary implementation that should support override via
+ smtp_tls_policy_maps. Files: smtp.c, smtp_connect.c,
+ smtp_params.c, smtp_proto.c, smtp_session.c.
+
+ TLS reuse: the SMTP client now includes the requested TLS
+ security level in the scache(8) key.
+
+ TLS reuse: address-based reuse is allowed only for TLS
+ levels that require no certificate checks. Perhaps it still
+ makes sense to save such sessions for reuse by less sensitive
+ deliveries. Files: smtp/smtp.h smtp/smtp_reuse.c.
+
+20180604
+
+ TLS reuse: smtp_tls_connection_reuse boolean parameter, and
+ corresponding override with "connection_reuse" boolean
+ attribute in smtp_policy_maps. Files: global/mail_params.h,
+ smtp.c, smtp.h, smtp_params.c, smtp_proto.c, smtp_session.c,
+ smtp_tls_policy.c. proto/postconf.proto. mantools/postlink.
+
+20180605
+
+ TLS reuse: updated TLS_README and CONNECTION_CACHE_README,
+ added comments in tlsproxy.c to explain why it works.
+
+20180617
+
+ Bugfix (introduced: Postfix 2.11): minor memory leak when
+ minting issuer certs. This affects a tiny minority of use
+ cases. Fix by Viktor Dukhovni, based on a fix by Juan
+ Altmayer Pizzorno for Viktor's ssl_dane library.
+
+ Cleanup: support for longer timeouts after the TLS handshake,
+ so that the tlsproxy server won't time out too soon, while
+ the SMTP client waits for the end-of-data response. This
+ tlxproxy timeout is a redundant safety feature for the case
+ that the SMTP client does not enforce the SMTP-level time
+ limit. Files: tls/tls_proxy.h, tls/tls_proxy_clnt.c,
+ tlsproxy/tlsproxy.c, posttls-finger/posttls-finger.c,
+ postcreen/postscreen_starttls.c, smtp/smtp_proto.c.
+
+ Cleanup: earlier purging of unexpected plaintext. Files:
+ posttls-finger/posttls-finger.c, smtp/smtp_proto.c.
+
+ Release: first production snapshot with multiple outbound
+ deliveries per TLS-encrypted connection.
This document describes the Postfix connection cache implementation, which is
available with Postfix version 2.2 and later.
+See Client-side TLS connection reuse for how this connection cache is used to
+implement multiple deliveries per TLS-encrypted connection.
+
Topics covered in this document:
* What SMTP connection caching can do for you
/-- smtp(8) --> Internet
- qmgr(8) |
- |
- \-- | smtp(8) --> Internet
- |
- ^
- |
+ qmgr(8)
+ |
+ \-- | smtp(8) --> Internet
+ |
+ ^
+ |
- scache(8)
+ scache(8)
When SMTP connection caching is enabled (see next section), the smtp(8) client
does not disconnect after a mail transaction, but gives the connection to the
* Configuring TLS in the SMTP/LMTP client
* Client-side TLS activity logging
* Client-side certificate and private key configuration
+ * Client-side TLS connection reuse
* Client-side TLS session cache
* Client TLS limitations
* Per-destination TLS policy
smtp_tls_CAfile = /etc/postfix/CAcert.pem
smtp_tls_CApath = /etc/postfix/certs
+C\bCl\bli\bie\ben\bnt\bt-\b-s\bsi\bid\bde\be T\bTL\bLS\bS c\bco\bon\bnn\bne\bec\bct\bti\bio\bon\bn r\bre\beu\bus\bse\be
+
+Historically, the Postfix SMTP client has supported multiple deliveries per
+connection only for plaintext connections. Postfix 3.4 introduces the ability
+to make multiple deliveries per TLS-encrypted connection. This is primarily to
+improve mail delivery performance for destinations that throttle clients when
+they don't combine deliveries.
+
+To enable multiple deliveries per TLS connection, specify:
+
+ /etc/postfix/main.cf:
+ smtp_tls_connection_reuse = yes
+
+Alternatively, specify the attribute "connection_reuse=yes" in an
+smtp_tls_policy_maps entry.
+
+The implementation of TLS connection reuse relies on the same scache(8) service
+as used for delivering plaintext SMTP mail, the same tlsproxy(8) daemon as used
+by the postscreen(8) service, and relies on the same hints from the qmgr(8)
+daemon. See "Postfix Connection Cache" for a description of the underlying
+connection reuse infrastructure.
+
+Initial SMTP handshake:
+
+ smtp(8) -> remote SMTP server
+
+Reused SMTP/TLS connection, or new SMTP/TLS connection:
+
+ smtp(8) -> tlsproxy(8) -> remote SMTP server
+
+Cached SMTP/TLS connection:
+
+ scache(8) -> tlsproxy(8) -> remote SMTP server
+
+As of Postfix 3.4, TLS connection reuse is disabled by default. This may change
+once the impact on over-all performance is undestood.
+
C\bCl\bli\bie\ben\bnt\bt-\b-s\bsi\bid\bde\be T\bTL\bLS\bS s\bse\bes\bss\bsi\bio\bon\bn c\bca\bac\bch\bhe\be
The remote SMTP server and the Postfix SMTP client negotiate a session, which
more recent Eclipse Public License 2.0. Recipients can choose to take
the software under the license of their choice. Those who are more
comfortable with the IPL can continue with that license.
+
+Major changes with snapshot 20180617
+====================================
+
+Preliminary Postfix SMTP client support for multiple deliveries per
+TLS-encrypted connection. This is primarily to improve mail delivery
+performance for destinations that throttle clients when they don't
+combine deliveries.
+
+This feature is enabled with "smtp_tls_connection_reuse=yes" in
+main.cf, or with "tls_connection_reuse=yes" in smtp_tls_policy_maps.
+It supports all Postfix TLS security levels including dane and
+dane-only.
+
+With connection reuse enabled as described above, the Postfix SMTP
+client uses the tlsproxy(8) server to encrypt a connection (even under
+low-traffic conditions). The tlsproxy(8) service was introduced in
+Postfix 2.8, to support STARTTLS in postscreen(8).
+
+Under high-traffic conditions, the Postfix SMTP client will use the
+scache(8) connection cache to store and retrieve open connections.
+This part already existed for plaintext SMTP, and it works in the
+same way for TLS-encryped connections.
+
+The following illustrates how TLS connections are reused:
+
+ Initial plaintext SMTP handshake:
+ smtp(8) -> remote SMTP server
+
+ Reused SMTP/TLS connection, or new SMTP/TLS connection:
+ smtp(8) -> tlsproxy(8) -> remote SMTP server
+
+ Cached SMTP/TLS connection:
+ scache(8) -> tlsproxy(8) -> remote SMTP server
+
+There are a few refinements planned:
+
+- Log the TLS properties every time a connection is reused.
+ Currently, the properties are logged when a TLS session is created.
+
+- Retire a tlsproxy(8) process after max_idle*max_use seconds, even
+ if it is not idle. This limits the impact of memory leaks in
+ libraries or in Postfix itself.
Wish list:
+ Add 'retire after max_use * max_idle' support to the
+ event-server, so that tlsproxy processes will terminate
+ even on a busy server. This can build on the retirement
+ support for the dnsblog server.
+
+ With smtpd_reject_footer=$foo in master.cf, and foo defined
+ in main.cf, postconf complains about an unused setting in
+ main.cf. Note that "postconf -Px" will expand $name in
+ the parameter value, which is probably why the warning
+ exists.
+
+ Optionally save application attributes in scache(8) connection
+ store requests, and optionally request such attributes upon
+ connection retrieval. This would allow the SMTP client to
+ log the TLS properties of a reused session.
+
Things to do before the stable release:
Spell-check, double-word check, HTML validator check,
<p> This document describes the Postfix connection cache implementation,
which is available with Postfix version 2.2 and later. </p>
+<p> See <a href="TLS_README.html#client_tls_reuse">Client-side TLS
+connection reuse</a> for how this connection cache is used to
+implement multiple deliveries per TLS-encrypted connection. </p>
+
<p> Topics covered in this document: </p>
<ul>
<li><a href="#client_cert_key">Client-side certificate and private
key configuration </a>
+<li><a href="#client_tls_reuse">Client-side TLS connection reuse</a>
+
<li><a href="#client_tls_cache">Client-side TLS session cache</a>
<li><a href="#client_tls_limits"> Client TLS limitations </a>
</pre>
</blockquote>
+<h3><a name="client_tls_reuse">Client-side TLS connection reuse</a> </h3>
+
+<p> Historically, the Postfix SMTP client has supported multiple
+deliveries per connection only for plaintext connections. Postfix
+3.4 introduces the ability to make multiple deliveries per TLS-encrypted
+connection. This is primarily to improve mail delivery performance
+for destinations that throttle clients when they don't combine
+deliveries. </p>
+
+<p> To enable multiple deliveries per TLS connection, specify:</p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+ <a href="postconf.5.html#smtp_tls_connection_reuse">smtp_tls_connection_reuse</a> = yes
+</pre>
+</blockquote>
+
+<p> Alternatively, specify the attribute "connection_reuse=yes" in
+an <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> entry. </p>
+
+<p> The implementation of TLS connection reuse relies on the same
+<a href="scache.8.html">scache(8)</a> service as used for delivering plaintext SMTP mail, the
+same <a href="tlsproxy.8.html">tlsproxy(8)</a> daemon as used by the <a href="postscreen.8.html">postscreen(8)</a> service, and
+relies on the same hints from the <a href="qmgr.8.html">qmgr(8)</a> daemon.
+
+See "<a href="CONNECTION_CACHE_README.html">Postfix Connection
+Cache</a>" for a description of the underlying connection reuse
+infrastructure. </p>
+
+<p> Initial SMTP handshake:</p>
+<pre> <a href="smtp.8.html">smtp(8)</a> -> remote SMTP server</pre>
+
+<p> Reused SMTP/TLS connection, or new SMTP/TLS connection: </p>
+<pre> <a href="smtp.8.html">smtp(8)</a> -> <a href="tlsproxy.8.html">tlsproxy(8)</a> -> remote SMTP server </pre>
+
+<p> Cached SMTP/TLS connection:</p>
+<pre> <a href="scache.8.html">scache(8)</a> -> <a href="tlsproxy.8.html">tlsproxy(8)</a> -> remote SMTP server</pre>
+
+<p> As of Postfix 3.4, TLS connection reuse is disabled by default.
+This may change once the impact on over-all performance is undestood.
+</p>
+
<h3><a name="client_tls_cache">Client-side TLS session cache</a> </h3>
<p> The remote SMTP server and the Postfix SMTP client negotiate a
nexthop destination security level is <b>dane</b>, but the MX record
was found via an "insecure" MX lookup.
+ Available in Postfix version 3.4 and later:
+
+ <b><a href="postconf.5.html#smtp_tls_connection_reuse">smtp_tls_connection_reuse</a> (no)</b>
+ Try to make multiple deliveries per TLS-encrypted connection.
+
<b>OBSOLETE STARTTLS CONTROLS</b>
The following configuration parameters exist for compatibility with
Postfix versions before 2.3. Support for these will be removed in a
that an SMTP session may be reused before it is closed, or zero
(no limit).
+ Available in Postfix version 3.4 and later:
+
+ <b><a href="postconf.5.html#smtp_tls_connection_reuse">smtp_tls_connection_reuse</a> (no)</b>
+ Try to make multiple deliveries per TLS-encrypted connection.
+
Implemented in the <a href="qmgr.8.html">qmgr(8)</a> daemon:
<b>transport_destination_concurrency_limit ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destination_concur</a>-</b>
<p> This feature is available in Postfix 2.6 and later. </p>
+</DD>
+
+<DT><b><a name="lmtp_tls_connection_reuse">lmtp_tls_connection_reuse</a>
+(default: no)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_connection_reuse">smtp_tls_connection_reuse</a> configuration
+parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 3.4 and later. </p>
+
+
</DD>
<DT><b><a name="lmtp_tls_dcert_file">lmtp_tls_dcert_file</a>
and opportunistic TLS always uses "export" or better (i.e. all) ciphers. </p>
+</DD>
+
+<DT><b><a name="smtp_tls_connection_reuse">smtp_tls_connection_reuse</a>
+(default: no)</b></DT><DD>
+
+<p> Try to make multiple deliveries per TLS-encrypted connection.
+This uses the <a href="tlsproxy.8.html">tlsproxy(8)</a> service to encrypt an SMTP connection,
+uses the <a href="scache.8.html">scache(8)</a> service to save that connection, and relies on
+hints from the <a href="qmgr.8.html">qmgr(8)</a> daemon. </p>
+
+<p> See "<a href="TLS_README.html#client_tls_reuse">Client-side
+TLS connection reuse</a>" for background details. </p>
+
+<p> This feature is available in Postfix 3.4 and later. </p>
+
+
</DD>
<DT><b><a name="smtp_tls_dane_insecure_mx_policy">smtp_tls_dane_insecure_mx_policy</a>
<dt><b><a href="TLS_README.html#client_tls_may">may</a></b></dt>
<dd>Opportunistic TLS. Since sending in the clear is acceptable,
demanding stronger than default TLS security merely reduces
-interoperability. The optional "ciphers", "exclude" and "protocols"
+interoperability. The optional "ciphers", "exclude", and "protocols"
attributes (available for opportunistic TLS with Postfix ≥ 2.6)
-override the "<a href="postconf.5.html#smtp_tls_ciphers">smtp_tls_ciphers</a>", "<a href="postconf.5.html#smtp_tls_exclude_ciphers">smtp_tls_exclude_ciphers</a>" and
-"<a href="postconf.5.html#smtp_tls_protocols">smtp_tls_protocols</a>" configuration parameters. When opportunistic TLS
-handshakes fail, Postfix retries the connection with TLS disabled.
+and "connection_reuse" attribute (Postfix ≥ 3.4) override the
+"<a href="postconf.5.html#smtp_tls_ciphers">smtp_tls_ciphers</a>", "<a href="postconf.5.html#smtp_tls_exclude_ciphers">smtp_tls_exclude_ciphers</a>", "<a href="postconf.5.html#smtp_tls_protocols">smtp_tls_protocols</a>",
+and
+"<a href="postconf.5.html#smtp_tls_connection_reuse">smtp_tls_connection_reuse</a>" configuration parameters. When opportunistic
+TLS handshakes fail, Postfix retries the connection with TLS disabled.
This allows mail delivery to sites with non-interoperable TLS
implementations.</dd>
<dd>Mandatory TLS encryption. At this level
and higher, the optional "protocols" attribute overrides the <a href="postconf.5.html">main.cf</a>
<a href="postconf.5.html#smtp_tls_mandatory_protocols">smtp_tls_mandatory_protocols</a> parameter, the optional "ciphers" attribute
-overrides the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> parameter, and the
+overrides the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_mandatory_ciphers">smtp_tls_mandatory_ciphers</a> parameter, the
optional "exclude" attribute (Postfix ≥ 2.6) overrides the <a href="postconf.5.html">main.cf</a>
-<a href="postconf.5.html#smtp_tls_mandatory_exclude_ciphers">smtp_tls_mandatory_exclude_ciphers</a> parameter. In the policy table,
+<a href="postconf.5.html#smtp_tls_mandatory_exclude_ciphers">smtp_tls_mandatory_exclude_ciphers</a> parameter, and the optional
+"connection_reuse" attribute (Postfix ≥ 3.4) overrides the
+<a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_connection_reuse">smtp_tls_connection_reuse</a> parameter. In the policy table,
multiple protocols or excluded ciphers must be separated by colons,
as attribute values may not contain whitespace or commas. </dd>
TLSA records are obtained for the remote SMTP server, the
server certificate must match the TLSA records. <a href="http://tools.ietf.org/html/rfc7672">RFC 7672</a> (DANE)
TLS authentication and DNSSEC support is available with Postfix
-2.11 and later. </dd>
+2.11 and later. The optional "connection_reuse" attribute (Postfix
+≥ 3.4) overrides the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_connection_reuse">smtp_tls_connection_reuse</a> parameter.
+</dd>
<dt><b><a href="TLS_README.html#client_tls_dane">dane-only</a></b></dt>
<dd>Mandatory DANE TLS. The TLS policy for the destination is
usable TLSA records are obtained for the remote SMTP server, the
server certificate must match the TLSA records. <a href="http://tools.ietf.org/html/rfc7672">RFC 7672</a> (DANE) TLS
authentication and DNSSEC support is available with Postfix 2.11
-and later. </dd>
+and later. The optional "connection_reuse" attribute (Postfix ≥
+3.4) overrides the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_connection_reuse">smtp_tls_connection_reuse</a> parameter.
+</dd>
<dt><b><a href="TLS_README.html#client_tls_fprint">fingerprint</a></b></dt>
<dd>Certificate fingerprint
be combined with a "|" delimiter in a single match attribute, or multiple
match attributes can be employed. The ":" character is not used as a
delimiter as it occurs between each pair of fingerprint (hexadecimal)
-digits. </dd>
+digits. The optional "connection_reuse" attribute (Postfix ≥ 3.4)
+overrides the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_connection_reuse">smtp_tls_connection_reuse</a> parameter. </dd>
<dt><b><a href="TLS_README.html#client_tls_verify">verify</a></b></dt>
<dd>Mandatory TLS verification. At this security
the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a> parameter. In the policy table,
multiple match patterns and strategies must be separated by colons.
In practice explicit control over matching is more common with the
-"secure" policy, described below. </dd>
+"secure" policy, described below. The optional "connection_reuse"
+attribute (Postfix ≥ 3.4) overrides the <a href="postconf.5.html">main.cf</a>
+<a href="postconf.5.html#smtp_tls_connection_reuse">smtp_tls_connection_reuse</a> parameter. </dd>
<dt><b><a href="TLS_README.html#client_tls_secure">secure</a></b></dt>
<dd>Secure-channel TLS. At this security level, DNS
verification, they risk delivery to the wrong destination when domains
change hands or are re-assigned to new gateways. With the "match"
attribute approach, routing is not perturbed, and mail is deferred if
-verification of a new MX host fails. </dd>
+verification of a new MX host fails. The optional "connection_reuse"
+attribute (Postfix ≥ 3.4) overrides the <a href="postconf.5.html">main.cf</a>
+<a href="postconf.5.html#smtp_tls_connection_reuse">smtp_tls_connection_reuse</a> parameter. </dd>
</dl>
STARTTLS protocol. The destination <i>domain</i>:<i>port</i> should of course
provide such a service.
+ <b>-X</b> Enable <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> mode. This is an unsupported mode, for pro-
+ gram development only.
+
[<b>inet:</b>]<i>domain</i>[:<i>port</i>]
Connect via TCP to domain <i>domain</i>, port <i>port</i>. The default port is
- <b>smtp</b> (or 24 with LMTP). With SMTP an MX lookup is performed to
- resolve the domain to a host, unless the domain is enclosed in
- <b>[]</b>. If you want to connect to a specific MX host, for instance
- <i>mx1.example.com</i>, specify [<i>mx1.example.com</i>] as the destination
+ <b>smtp</b> (or 24 with LMTP). With SMTP an MX lookup is performed to
+ resolve the domain to a host, unless the domain is enclosed in
+ <b>[]</b>. If you want to connect to a specific MX host, for instance
+ <i>mx1.example.com</i>, specify [<i>mx1.example.com</i>] as the destination
and <i>example.com</i> as a <b>match</b> argument. When using DNS, the desti-
- nation domain is assumed fully qualified and no <a href="ADDRESS_CLASS_README.html#default_domain_class">default domain</a>
- or search suffixes are applied; you must use fully-qualified
- names or also enable <b>native</b> host lookups (these don't support
- <b>dane</b> or <b>dane-only</b> as no DNSSEC validation information is avail-
+ nation domain is assumed fully qualified and no default domain
+ or search suffixes are applied; you must use fully-qualified
+ names or also enable <b>native</b> host lookups (these don't support
+ <b>dane</b> or <b>dane-only</b> as no DNSSEC validation information is avail-
able via <b>native</b> lookups).
<b>unix:</b><i>pathname</i>
<b>match ...</b>
With no match arguments specified, certificate peername matching
uses the compiled-in default strategies for each security level.
- If you specify one or more arguments, these will be used as the
- list of certificate or public-key digests to match for the <b>fin-</b>
+ If you specify one or more arguments, these will be used as the
+ list of certificate or public-key digests to match for the <b>fin-</b>
<b>gerprint</b> level, or as the list of DNS names to match in the cer-
tificate at the <b>verify</b> and <b>secure</b> levels. If the security level
is <b>dane</b>, or <b>dane-only</b> the match names are ignored, and <b>hostname,</b>
P.O. Box 704
Yorktown Heights, NY 10598, USA
+ Wietse Venema
+ Google, Inc.
+ 111 8th Avenue
+ New York, NY 10011, USA
+
Viktor Dukhovni
POSTTLS-FINGER(1)
nexthop destination security level is <b>dane</b>, but the MX record
was found via an "insecure" MX lookup.
+ Available in Postfix version 3.4 and later:
+
+ <b><a href="postconf.5.html#smtp_tls_connection_reuse">smtp_tls_connection_reuse</a> (no)</b>
+ Try to make multiple deliveries per TLS-encrypted connection.
+
<b>OBSOLETE STARTTLS CONTROLS</b>
The following configuration parameters exist for compatibility with
Postfix versions before 2.3. Support for these will be removed in a
that an SMTP session may be reused before it is closed, or zero
(no limit).
+ Available in Postfix version 3.4 and later:
+
+ <b><a href="postconf.5.html#smtp_tls_connection_reuse">smtp_tls_connection_reuse</a> (no)</b>
+ Try to make multiple deliveries per TLS-encrypted connection.
+
Implemented in the <a href="qmgr.8.html">qmgr(8)</a> daemon:
<b>transport_destination_concurrency_limit ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destination_concur</a>-</b>
<b>tlsproxy</b> [generic Postfix daemon options]
<b>DESCRIPTION</b>
- The <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server implements a server-side TLS proxy. It is used
- by <a href="postscreen.8.html"><b>postscreen</b>(8)</a> to talk SMTP-over-TLS with remote SMTP clients that
- are not whitelisted (including clients whose whitelist status has
- expired), but it should also work for non-SMTP protocols.
-
- Although one <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> process can serve multiple sessions at the
- same time, it is a good idea to allow the number of processes to
+ The <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server implements a two-way TLS proxy. It is used by
+ the <a href="postscreen.8.html"><b>postscreen</b>(8)</a> server to talk SMTP-over-TLS with remote SMTP clients
+ that are not whitelisted (including clients whose whitelist status has
+ expired), and by the <a href="smtp.8.html"><b>smtp</b>(8)</a> client to support TLS connection reuse,
+ but it should also work for non-SMTP protocols.
+
+ Although one <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> process can serve multiple sessions at the
+ same time, it is a good idea to allow the number of processes to
increase with load, so that the service remains responsive.
<b>PROTOCOL EXAMPLE</b>
- The example below concerns <a href="postscreen.8.html"><b>postscreen</b>(8)</a>. However, the <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a>
+ The example below concerns <a href="postscreen.8.html"><b>postscreen</b>(8)</a>. However, the <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a>
server is agnostic of the application protocol, and the example is eas-
ily adapted to other applications.
- After receiving a valid remote SMTP client STARTTLS command, the
- <a href="postscreen.8.html"><b>postscreen</b>(8)</a> server sends the remote SMTP client endpoint string, the
- requested role (server), and the requested timeout to <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a>.
- <a href="postscreen.8.html"><b>postscreen</b>(8)</a> then receives a "TLS available" indication from
- <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a>. If the TLS service is available, <a href="postscreen.8.html"><b>postscreen</b>(8)</a> sends the
+ After receiving a valid remote SMTP client STARTTLS command, the
+ <a href="postscreen.8.html"><b>postscreen</b>(8)</a> server sends the remote SMTP client endpoint string, the
+ requested role (server), and the requested timeout to <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a>.
+ <a href="postscreen.8.html"><b>postscreen</b>(8)</a> then receives a "TLS available" indication from
+ <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a>. If the TLS service is available, <a href="postscreen.8.html"><b>postscreen</b>(8)</a> sends the
remote SMTP client file descriptor to <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a>, and sends the plain-
text 220 greeting to the remote SMTP client. This triggers TLS negoti-
ations between the remote SMTP client and <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a>. Upon completion
- of the TLS-level handshake, <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> translates between plaintext
+ of the TLS-level handshake, <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> translates between plaintext
from/to <a href="postscreen.8.html"><b>postscreen</b>(8)</a> and ciphertext to/from the remote SMTP client.
<b>SECURITY</b>
- The <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server is moderately security-sensitive. It talks to
- untrusted clients on the network. The process can be run chrooted at
+ The <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server is moderately security-sensitive. It talks to
+ untrusted clients on the network. The process can be run chrooted at
fixed low privilege.
<b>DIAGNOSTICS</b>
<b>CONFIGURATION PARAMETERS</b>
Changes to <a href="postconf.5.html"><b>main.cf</b></a> are not picked up automatically, as <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> pro-
- cesses may run for a long time depending on mail server load. Use the
+ cesses may run for a long time depending on mail server load. Use the
command "<b>postfix reload</b>" to speed up a change.
- The text below provides only a parameter summary. See <a href="postconf.5.html"><b>postconf</b>(5)</a> for
+ The text below provides only a parameter summary. See <a href="postconf.5.html"><b>postconf</b>(5)</a> for
more details including examples.
<b>STARTTLS SUPPORT CONTROLS</b>
<b><a href="postconf.5.html#tlsproxy_tls_CAfile">tlsproxy_tls_CAfile</a> ($<a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a>)</b>
- A file containing (PEM format) CA certificates of root CAs
+ A file containing (PEM format) CA certificates of root CAs
trusted to sign either remote SMTP client certificates or inter-
mediate CA certificates.
<b><a href="postconf.5.html#tlsproxy_tls_CApath">tlsproxy_tls_CApath</a> ($<a href="postconf.5.html#smtpd_tls_CApath">smtpd_tls_CApath</a>)</b>
- A directory containing (PEM format) CA certificates of root CAs
+ A directory containing (PEM format) CA certificates of root CAs
trusted to sign either remote SMTP client certificates or inter-
mediate CA certificates.
<b><a href="postconf.5.html#tlsproxy_tls_always_issue_session_ids">tlsproxy_tls_always_issue_session_ids</a> ($<a href="postconf.5.html#smtpd_tls_always_issue_session_ids">smtpd_tls_always_issue_ses</a>-</b>
<b><a href="postconf.5.html#smtpd_tls_always_issue_session_ids">sion_ids</a>)</b>
- Force the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server to issue a TLS session id,
+ Force the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server to issue a TLS session id,
even when TLS session caching is turned off.
<b><a href="postconf.5.html#tlsproxy_tls_ask_ccert">tlsproxy_tls_ask_ccert</a> ($<a href="postconf.5.html#smtpd_tls_ask_ccert">smtpd_tls_ask_ccert</a>)</b>
The verification depth for remote SMTP client certificates.
<b><a href="postconf.5.html#tlsproxy_tls_cert_file">tlsproxy_tls_cert_file</a> ($<a href="postconf.5.html#smtpd_tls_cert_file">smtpd_tls_cert_file</a>)</b>
- File with the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server RSA certificate in PEM
+ File with the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server RSA certificate in PEM
format.
<b><a href="postconf.5.html#tlsproxy_tls_ciphers">tlsproxy_tls_ciphers</a> ($<a href="postconf.5.html#smtpd_tls_ciphers">smtpd_tls_ciphers</a>)</b>
will use with opportunistic TLS encryption.
<b><a href="postconf.5.html#tlsproxy_tls_dcert_file">tlsproxy_tls_dcert_file</a> ($<a href="postconf.5.html#smtpd_tls_dcert_file">smtpd_tls_dcert_file</a>)</b>
- File with the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server DSA certificate in PEM
+ File with the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server DSA certificate in PEM
format.
<b><a href="postconf.5.html#tlsproxy_tls_dh1024_param_file">tlsproxy_tls_dh1024_param_file</a> ($<a href="postconf.5.html#smtpd_tls_dh1024_param_file">smtpd_tls_dh1024_param_file</a>)</b>
- File with DH parameters that the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server
+ File with DH parameters that the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server
should use with non-export EDH ciphers.
<b><a href="postconf.5.html#tlsproxy_tls_dh512_param_file">tlsproxy_tls_dh512_param_file</a> ($<a href="postconf.5.html#smtpd_tls_dh512_param_file">smtpd_tls_dh512_param_file</a>)</b>
- File with DH parameters that the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server
+ File with DH parameters that the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server
should use with export-grade EDH ciphers.
<b><a href="postconf.5.html#tlsproxy_tls_dkey_file">tlsproxy_tls_dkey_file</a> ($<a href="postconf.5.html#smtpd_tls_dkey_file">smtpd_tls_dkey_file</a>)</b>
- File with the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server DSA private key in PEM
+ File with the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server DSA private key in PEM
format.
<b><a href="postconf.5.html#tlsproxy_tls_eccert_file">tlsproxy_tls_eccert_file</a> ($<a href="postconf.5.html#smtpd_tls_eccert_file">smtpd_tls_eccert_file</a>)</b>
- File with the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server ECDSA certificate in
+ File with the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server ECDSA certificate in
PEM format.
<b><a href="postconf.5.html#tlsproxy_tls_eckey_file">tlsproxy_tls_eckey_file</a> ($<a href="postconf.5.html#smtpd_tls_eckey_file">smtpd_tls_eckey_file</a>)</b>
- File with the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server ECDSA private key in
+ File with the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server ECDSA private key in
PEM format.
<b><a href="postconf.5.html#tlsproxy_tls_eecdh_grade">tlsproxy_tls_eecdh_grade</a> ($<a href="postconf.5.html#smtpd_tls_eecdh_grade">smtpd_tls_eecdh_grade</a>)</b>
- The Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server security grade for ephemeral
+ The Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server security grade for ephemeral
elliptic-curve Diffie-Hellman (EECDH) key exchange.
<b><a href="postconf.5.html#tlsproxy_tls_exclude_ciphers">tlsproxy_tls_exclude_ciphers</a> ($<a href="postconf.5.html#smtpd_tls_exclude_ciphers">smtpd_tls_exclude_ciphers</a>)</b>
- List of ciphers or cipher types to exclude from the <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a>
+ List of ciphers or cipher types to exclude from the <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a>
server cipher list at all TLS security levels.
<b><a href="postconf.5.html#tlsproxy_tls_fingerprint_digest">tlsproxy_tls_fingerprint_digest</a> ($<a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a>)</b>
- The message digest algorithm to construct remote SMTP
+ The message digest algorithm to construct remote SMTP
client-certificate fingerprints.
<b><a href="postconf.5.html#tlsproxy_tls_key_file">tlsproxy_tls_key_file</a> ($<a href="postconf.5.html#smtpd_tls_key_file">smtpd_tls_key_file</a>)</b>
- File with the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server RSA private key in PEM
+ File with the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server RSA private key in PEM
format.
<b><a href="postconf.5.html#tlsproxy_tls_loglevel">tlsproxy_tls_loglevel</a> ($<a href="postconf.5.html#smtpd_tls_loglevel">smtpd_tls_loglevel</a>)</b>
- Enable additional Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server logging of TLS
+ Enable additional Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server logging of TLS
activity.
<b><a href="postconf.5.html#tlsproxy_tls_mandatory_ciphers">tlsproxy_tls_mandatory_ciphers</a> ($<a href="postconf.5.html#smtpd_tls_mandatory_ciphers">smtpd_tls_mandatory_ciphers</a>)</b>
<b><a href="postconf.5.html#tlsproxy_tls_mandatory_exclude_ciphers">tlsproxy_tls_mandatory_exclude_ciphers</a> ($<a href="postconf.5.html#smtpd_tls_mandatory_exclude_ciphers">smtpd_tls_manda</a>-</b>
<b><a href="postconf.5.html#smtpd_tls_mandatory_exclude_ciphers">tory_exclude_ciphers</a>)</b>
- Additional list of ciphers or cipher types to exclude from the
+ Additional list of ciphers or cipher types to exclude from the
<a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server cipher list at mandatory TLS security levels.
<b><a href="postconf.5.html#tlsproxy_tls_mandatory_protocols">tlsproxy_tls_mandatory_protocols</a> ($<a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a>)</b>
with mandatory TLS encryption.
<b><a href="postconf.5.html#tlsproxy_tls_protocols">tlsproxy_tls_protocols</a> ($<a href="postconf.5.html#smtpd_tls_protocols">smtpd_tls_protocols</a>)</b>
- List of TLS protocols that the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server will
+ List of TLS protocols that the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server will
exclude or include with opportunistic TLS encryption.
<b><a href="postconf.5.html#tlsproxy_tls_req_ccert">tlsproxy_tls_req_ccert</a> ($<a href="postconf.5.html#smtpd_tls_req_ccert">smtpd_tls_req_ccert</a>)</b>
- With mandatory TLS encryption, require a trusted remote SMTP
+ With mandatory TLS encryption, require a trusted remote SMTP
client certificate in order to allow TLS connections to proceed.
<b><a href="postconf.5.html#tlsproxy_tls_security_level">tlsproxy_tls_security_level</a> ($<a href="postconf.5.html#smtpd_tls_security_level">smtpd_tls_security_level</a>)</b>
- The SMTP TLS security level for the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server;
+ The SMTP TLS security level for the Postfix <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a> server;
when a non-empty value is specified, this overrides the obsolete
parameters <a href="postconf.5.html#smtpd_use_tls">smtpd_use_tls</a> and <a href="postconf.5.html#smtpd_enforce_tls">smtpd_enforce_tls</a>.
The name of the <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a> service entry in <a href="master.5.html">master.cf</a>.
<b>OBSOLETE STARTTLS SUPPORT CONTROLS</b>
- These parameters are supported for compatibility with <a href="smtpd.8.html"><b>smtpd</b>(8)</a> legacy
+ These parameters are supported for compatibility with <a href="smtpd.8.html"><b>smtpd</b>(8)</a> legacy
parameters.
<b><a href="postconf.5.html#tlsproxy_use_tls">tlsproxy_use_tls</a> ($<a href="postconf.5.html#smtpd_use_tls">smtpd_use_tls</a>)</b>
- Opportunistic TLS: announce STARTTLS support to remote SMTP
+ Opportunistic TLS: announce STARTTLS support to remote SMTP
clients, but do not require that clients use TLS encryption.
<b><a href="postconf.5.html#tlsproxy_enforce_tls">tlsproxy_enforce_tls</a> ($<a href="postconf.5.html#smtpd_enforce_tls">smtpd_enforce_tls</a>)</b>
<b>MISCELLANEOUS CONTROLS</b>
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
figuration files.
<b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
- A prefix that is prepended to the process name in syslog
+ A prefix that is prepended to the process name in syslog
records, so that, for example, "smtpd" becomes "prefix/smtpd".
Available in Postfix 3.3 and later:
SMTP in SSL protocol, rather than the standard STARTTLS protocol.
The destination \fIdomain\fR:\fIport\fR should of course provide such
a service.
+.IP "\fB\-X\fR"
+Enable \fBtlsproxy\fR(8) mode. This is an unsupported mode,
+for program development only.
.IP "[\fBinet:\fR]\fIdomain\fR[:\fIport\fR]"
Connect via TCP to domain \fIdomain\fR, port \fIport\fR. The default
port is \fBsmtp\fR (or 24 with LMTP). With SMTP an MX lookup is
P.O. Box 704
Yorktown Heights, NY 10598, USA
+Wietse Venema
+Google, Inc.
+111 8th Avenue
+New York, NY 10011, USA
+
Viktor Dukhovni
parameter. See there for details.
.PP
This feature is available in Postfix 2.6 and later.
+.SH lmtp_tls_connection_reuse (default: no)
+The LMTP\-specific version of the smtp_tls_connection_reuse configuration
+parameter. See there for details.
+.PP
+This feature is available in Postfix 3.4 and later.
.SH lmtp_tls_dcert_file (default: empty)
The LMTP\-specific version of the smtp_tls_dcert_file
configuration parameter. See there for details.
This feature is available in Postfix 2.6 and later. With earlier Postfix
releases only the smtp_tls_mandatory_ciphers parameter is implemented,
and opportunistic TLS always uses "export" or better (i.e. all) ciphers.
+.SH smtp_tls_connection_reuse (default: no)
+Try to make multiple deliveries per TLS\-encrypted connection.
+This uses the \fBtlsproxy\fR(8) service to encrypt an SMTP connection,
+uses the \fBscache\fR(8) service to save that connection, and relies on
+hints from the \fBqmgr\fR(8) daemon.
+.PP
+See "Client\-side
+TLS connection reuse" for background details.
+.PP
+This feature is available in Postfix 3.4 and later.
.SH smtp_tls_dane_insecure_mx_policy (default: dane)
The TLS policy for MX hosts with "secure" TLSA records when the
nexthop destination security level is \fBdane\fR, but the MX
.IP "\fBmay\fR"
Opportunistic TLS. Since sending in the clear is acceptable,
demanding stronger than default TLS security merely reduces
-interoperability. The optional "ciphers", "exclude" and "protocols"
+interoperability. The optional "ciphers", "exclude", and "protocols"
attributes (available for opportunistic TLS with Postfix >= 2.6)
-override the "smtp_tls_ciphers", "smtp_tls_exclude_ciphers" and
-"smtp_tls_protocols" configuration parameters. When opportunistic TLS
-handshakes fail, Postfix retries the connection with TLS disabled.
+and "connection_reuse" attribute (Postfix >= 3.4) override the
+"smtp_tls_ciphers", "smtp_tls_exclude_ciphers", "smtp_tls_protocols",
+and
+"smtp_tls_connection_reuse" configuration parameters. When opportunistic
+TLS handshakes fail, Postfix retries the connection with TLS disabled.
This allows mail delivery to sites with non\-interoperable TLS
implementations.
.br
Mandatory TLS encryption. At this level
and higher, the optional "protocols" attribute overrides the main.cf
smtp_tls_mandatory_protocols parameter, the optional "ciphers" attribute
-overrides the main.cf smtp_tls_mandatory_ciphers parameter, and the
+overrides the main.cf smtp_tls_mandatory_ciphers parameter, the
optional "exclude" attribute (Postfix >= 2.6) overrides the main.cf
-smtp_tls_mandatory_exclude_ciphers parameter. In the policy table,
+smtp_tls_mandatory_exclude_ciphers parameter, and the optional
+"connection_reuse" attribute (Postfix >= 3.4) overrides the
+main.cf smtp_tls_connection_reuse parameter. In the policy table,
multiple protocols or excluded ciphers must be separated by colons,
as attribute values may not contain whitespace or commas.
.br
TLSA records are obtained for the remote SMTP server, the
server certificate must match the TLSA records. RFC 7672 (DANE)
TLS authentication and DNSSEC support is available with Postfix
-2.11 and later.
+2.11 and later. The optional "connection_reuse" attribute (Postfix
+>= 3.4) overrides the main.cf smtp_tls_connection_reuse parameter.
.br
.IP "\fBdane\-only\fR"
Mandatory DANE TLS. The TLS policy for the destination is
usable TLSA records are obtained for the remote SMTP server, the
server certificate must match the TLSA records. RFC 7672 (DANE) TLS
authentication and DNSSEC support is available with Postfix 2.11
-and later.
+and later. The optional "connection_reuse" attribute (Postfix >=
+3.4) overrides the main.cf smtp_tls_connection_reuse parameter.
.br
.IP "\fBfingerprint\fR"
Certificate fingerprint
be combined with a "|" delimiter in a single match attribute, or multiple
match attributes can be employed. The ":" character is not used as a
delimiter as it occurs between each pair of fingerprint (hexadecimal)
-digits.
+digits. The optional "connection_reuse" attribute (Postfix >= 3.4)
+overrides the main.cf smtp_tls_connection_reuse parameter.
.br
.IP "\fBverify\fR"
Mandatory TLS verification. At this security
the main.cf smtp_tls_verify_cert_match parameter. In the policy table,
multiple match patterns and strategies must be separated by colons.
In practice explicit control over matching is more common with the
-"secure" policy, described below.
+"secure" policy, described below. The optional "connection_reuse"
+attribute (Postfix >= 3.4) overrides the main.cf
+smtp_tls_connection_reuse parameter.
.br
.IP "\fBsecure\fR"
Secure\-channel TLS. At this security level, DNS
verification, they risk delivery to the wrong destination when domains
change hands or are re\-assigned to new gateways. With the "match"
attribute approach, routing is not perturbed, and mail is deferred if
-verification of a new MX host fails.
+verification of a new MX host fails. The optional "connection_reuse"
+attribute (Postfix >= 3.4) overrides the main.cf
+smtp_tls_connection_reuse parameter.
.br
.br
.PP
The TLS policy for MX hosts with "secure" TLSA records when the
nexthop destination security level is \fBdane\fR, but the MX
record was found via an "insecure" MX lookup.
+.PP
+Available in Postfix version 3.4 and later:
+.IP "\fBsmtp_tls_connection_reuse (no)\fR"
+Try to make multiple deliveries per TLS\-encrypted connection.
.SH "OBSOLETE STARTTLS CONTROLS"
.na
.nf
that an SMTP session may be reused before it is closed, or zero (no
limit).
.PP
+Available in Postfix version 3.4 and later:
+.IP "\fBsmtp_tls_connection_reuse (no)\fR"
+Try to make multiple deliveries per TLS\-encrypted connection.
+.PP
Implemented in the qmgr(8) daemon:
.IP "\fBtransport_destination_concurrency_limit ($default_destination_concurrency_limit)\fR"
A transport\-specific override for the
.SH DESCRIPTION
.ad
.fi
-The \fBtlsproxy\fR(8) server implements a server\-side TLS
-proxy. It is used by \fBpostscreen\fR(8) to talk SMTP\-over\-TLS
+The \fBtlsproxy\fR(8) server implements a two\-way TLS proxy. It
+is used by the \fBpostscreen\fR(8) server to talk SMTP\-over\-TLS
with remote SMTP clients that are not whitelisted (including
-clients whose whitelist status has expired),
-but it should also work for non\-SMTP protocols.
+clients whose whitelist status has expired), and by the
+\fBsmtp\fR(8) client to support TLS connection reuse, but it
+should also work for non\-SMTP protocols.
Although one \fBtlsproxy\fR(8) process can serve multiple
sessions at the same time, it is a good idea to allow the
s;\bsmtp_per_record_deadline\b;<a href="postconf.5.html#smtp_per_record_deadline">$&</a>;g;
s;\bsmtp_send_dummy_mail_auth\b;<a href="postconf.5.html#smtp_send_dummy_mail_auth">$&</a>;g;
s;\bsmtp_balance_inet_protocols\b;<a href="postconf.5.html#smtp_balance_inet_protocols">$&</a>;g;
+ s;\bsmtp_tls_connection_reuse\b;<a href="postconf.5.html#smtp_tls_connection_reuse">$&</a>;g;
+ s;\blmtp_tls_connection_reuse\b;<a href="postconf.5.html#lmtp_tls_connection_reuse">$&</a>;g;
s;\bsmtpd_enforce_tls\b;<a href="postconf.5.html#smtpd_enforce_tls">$&</a>;g;
s;\bsmtpd_sasl_tls_security_options\b;<a href="postconf.5.html#smtpd_sasl_tls_security_options">$&</a>;g;
s;\bsmtpd_sasl_type\b;<a href="postconf.5.html#smtpd_sasl_type">$&</a>;g;
<p> This document describes the Postfix connection cache implementation,
which is available with Postfix version 2.2 and later. </p>
+<p> See <a href="TLS_README.html#client_tls_reuse">Client-side TLS
+connection reuse</a> for how this connection cache is used to
+implement multiple deliveries per TLS-encrypted connection. </p>
+
<p> Topics covered in this document: </p>
<ul>
<li><a href="#client_cert_key">Client-side certificate and private
key configuration </a>
+<li><a href="#client_tls_reuse">Client-side TLS connection reuse</a>
+
<li><a href="#client_tls_cache">Client-side TLS session cache</a>
<li><a href="#client_tls_limits"> Client TLS limitations </a>
</pre>
</blockquote>
+<h3><a name="client_tls_reuse">Client-side TLS connection reuse</a> </h3>
+
+<p> Historically, the Postfix SMTP client has supported multiple
+deliveries per connection only for plaintext connections. Postfix
+3.4 introduces the ability to make multiple deliveries per TLS-encrypted
+connection. This is primarily to improve mail delivery performance
+for destinations that throttle clients when they don't combine
+deliveries. </p>
+
+<p> To enable multiple deliveries per TLS connection, specify:</p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+ smtp_tls_connection_reuse = yes
+</pre>
+</blockquote>
+
+<p> Alternatively, specify the attribute "connection_reuse=yes" in
+an smtp_tls_policy_maps entry. </p>
+
+<p> The implementation of TLS connection reuse relies on the same
+scache(8) service as used for delivering plaintext SMTP mail, the
+same tlsproxy(8) daemon as used by the postscreen(8) service, and
+relies on the same hints from the qmgr(8) daemon.
+
+See "<a href="CONNECTION_CACHE_README.html">Postfix Connection
+Cache</a>" for a description of the underlying connection reuse
+infrastructure. </p>
+
+<p> Initial SMTP handshake:</p>
+<pre> smtp(8) -> remote SMTP server</pre>
+
+<p> Reused SMTP/TLS connection, or new SMTP/TLS connection: </p>
+<pre> smtp(8) -> tlsproxy(8) -> remote SMTP server </pre>
+
+<p> Cached SMTP/TLS connection:</p>
+<pre> scache(8) -> tlsproxy(8) -> remote SMTP server</pre>
+
+<p> As of Postfix 3.4, TLS connection reuse is disabled by default.
+This may change once the impact on over-all performance is undestood.
+</p>
+
<h3><a name="client_tls_cache">Client-side TLS session cache</a> </h3>
<p> The remote SMTP server and the Postfix SMTP client negotiate a
<dt><b><a href="TLS_README.html#client_tls_may">may</a></b></dt>
<dd>Opportunistic TLS. Since sending in the clear is acceptable,
demanding stronger than default TLS security merely reduces
-interoperability. The optional "ciphers", "exclude" and "protocols"
+interoperability. The optional "ciphers", "exclude", and "protocols"
attributes (available for opportunistic TLS with Postfix ≥ 2.6)
-override the "smtp_tls_ciphers", "smtp_tls_exclude_ciphers" and
-"smtp_tls_protocols" configuration parameters. When opportunistic TLS
-handshakes fail, Postfix retries the connection with TLS disabled.
+and "connection_reuse" attribute (Postfix ≥ 3.4) override the
+"smtp_tls_ciphers", "smtp_tls_exclude_ciphers", "smtp_tls_protocols",
+and
+"smtp_tls_connection_reuse" configuration parameters. When opportunistic
+TLS handshakes fail, Postfix retries the connection with TLS disabled.
This allows mail delivery to sites with non-interoperable TLS
implementations.</dd>
<dd>Mandatory TLS encryption. At this level
and higher, the optional "protocols" attribute overrides the main.cf
smtp_tls_mandatory_protocols parameter, the optional "ciphers" attribute
-overrides the main.cf smtp_tls_mandatory_ciphers parameter, and the
+overrides the main.cf smtp_tls_mandatory_ciphers parameter, the
optional "exclude" attribute (Postfix ≥ 2.6) overrides the main.cf
-smtp_tls_mandatory_exclude_ciphers parameter. In the policy table,
+smtp_tls_mandatory_exclude_ciphers parameter, and the optional
+"connection_reuse" attribute (Postfix ≥ 3.4) overrides the
+main.cf smtp_tls_connection_reuse parameter. In the policy table,
multiple protocols or excluded ciphers must be separated by colons,
as attribute values may not contain whitespace or commas. </dd>
TLSA records are obtained for the remote SMTP server, the
server certificate must match the TLSA records. RFC 7672 (DANE)
TLS authentication and DNSSEC support is available with Postfix
-2.11 and later. </dd>
+2.11 and later. The optional "connection_reuse" attribute (Postfix
+≥ 3.4) overrides the main.cf smtp_tls_connection_reuse parameter.
+</dd>
<dt><b><a href="TLS_README.html#client_tls_dane">dane-only</a></b></dt>
<dd>Mandatory DANE TLS. The TLS policy for the destination is
usable TLSA records are obtained for the remote SMTP server, the
server certificate must match the TLSA records. RFC 7672 (DANE) TLS
authentication and DNSSEC support is available with Postfix 2.11
-and later. </dd>
+and later. The optional "connection_reuse" attribute (Postfix ≥
+3.4) overrides the main.cf smtp_tls_connection_reuse parameter.
+</dd>
<dt><b><a href="TLS_README.html#client_tls_fprint">fingerprint</a></b></dt>
<dd>Certificate fingerprint
be combined with a "|" delimiter in a single match attribute, or multiple
match attributes can be employed. The ":" character is not used as a
delimiter as it occurs between each pair of fingerprint (hexadecimal)
-digits. </dd>
+digits. The optional "connection_reuse" attribute (Postfix ≥ 3.4)
+overrides the main.cf smtp_tls_connection_reuse parameter. </dd>
<dt><b><a href="TLS_README.html#client_tls_verify">verify</a></b></dt>
<dd>Mandatory TLS verification. At this security
the main.cf smtp_tls_verify_cert_match parameter. In the policy table,
multiple match patterns and strategies must be separated by colons.
In practice explicit control over matching is more common with the
-"secure" policy, described below. </dd>
+"secure" policy, described below. The optional "connection_reuse"
+attribute (Postfix ≥ 3.4) overrides the main.cf
+smtp_tls_connection_reuse parameter. </dd>
<dt><b><a href="TLS_README.html#client_tls_secure">secure</a></b></dt>
<dd>Secure-channel TLS. At this security level, DNS
verification, they risk delivery to the wrong destination when domains
change hands or are re-assigned to new gateways. With the "match"
attribute approach, routing is not perturbed, and mail is deferred if
-verification of a new MX host fails. </dd>
+verification of a new MX host fails. The optional "connection_reuse"
+attribute (Postfix ≥ 3.4) overrides the main.cf
+smtp_tls_connection_reuse parameter. </dd>
</dl>
<p> This feature is available in Postfix 3.0 and later. </p>
+%PARAM smtp_tls_connection_reuse no
+
+<p> Try to make multiple deliveries per TLS-encrypted connection.
+This uses the tlsproxy(8) service to encrypt an SMTP connection,
+uses the scache(8) service to save that connection, and relies on
+hints from the qmgr(8) daemon. </p>
+
+<p> See "<a href="TLS_README.html#client_tls_reuse">Client-side
+TLS connection reuse</a>" for background details. </p>
+
+<p> This feature is available in Postfix 3.4 and later. </p>
+
+%PARAM lmtp_tls_connection_reuse no
+
+<p> The LMTP-specific version of the smtp_tls_connection_reuse configuration
+parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 3.4 and later. </p>
+
%PARAM virtual_alias_address_length_limit 1000
<p>
#define DEF_SMTPD_DNS_RE_FILTER ""
extern char *var_smtpd_dns_re_filter;
+ /*
+ * Share TLS sessions through tlproxy(8).
+ */
+#define VAR_SMTP_TLS_CONN_REUSE "smtp_tls_connection_reuse"
+#define DEF_SMTP_TLS_CONN_REUSE 0
+#define VAR_LMTP_TLS_CONN_REUSE "lmtp_tls_connection_reuse"
+#define DEF_LMTP_TLS_CONN_REUSE 0
+extern bool var_smtp_tls_conn_reuse;
+
/*
* Location of shared-library files.
*
#define MAIL_ATTR_DSN_ORCPT "dsn_orig_rcpt" /* dsn original recipient */
#define MAIL_ATTR_SMTPUTF8 "smtputf8" /* RFC6531 support */
- /*
- * TLSPROXY support.
- */
-#define MAIL_ATTR_REMOTE_ENDPT "remote_endpoint" /* name[addr]:port */
-#define MAIL_ATTR_ROLE "role" /* requested role */
-#define MAIL_ATTR_ROLE_SERVER "server"
-#define MAIL_ATTR_ROLE_CLIENT "client"
-#define MAIL_ATTR_TIMEOUT "timeout"
-#define MAIL_ATTR_PEER_CN "peer_CN"
-#define MAIL_ATTR_ISSUER_CN "issuer_CN"
-#define MAIL_ATTR_PEER_CERT_FPT "peer_fingerprint"
-#define MAIL_ATTR_PEER_PKEY_FPT "peer_pubkey_fingerprint"
-#define MAIL_ATTR_PEER_STATUS "peer_status"
-#define MAIL_ATTR_CIPHER_PROTOCOL "cipher_protocol"
-#define MAIL_ATTR_CIPHER_NAME "cipher_name"
-#define MAIL_ATTR_CIPHER_USEBITS "cipher_usebits"
-#define MAIL_ATTR_CIPHER_ALGBITS "cipher_algbits"
-#define MAIL_ATTR_SERVER_ID "server_id"
-
/*
* SMTP reply footer support.
*/
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20180603"
+#define MAIL_RELEASE_DATE "20180617"
#define MAIL_VERSION_NUMBER "3.4"
#ifdef SNAPSHOT
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/* System library. */
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/* System library. */
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/* System libraries. */
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/* System library. */
vstring_sprintf(remote_endpt, "[%s]:%s", smtp_state->smtp_client_addr,
smtp_state->smtp_client_port);
attr_print(tlsproxy_stream, ATTR_FLAG_NONE,
- SEND_ATTR_STR(MAIL_ATTR_REMOTE_ENDPT, STR(remote_endpt)),
- SEND_ATTR_INT(MAIL_ATTR_FLAGS, TLS_PROXY_FLAG_ROLE_SERVER),
- SEND_ATTR_INT(MAIL_ATTR_TIMEOUT, psc_normal_cmd_time_limit),
- SEND_ATTR_STR(MAIL_ATTR_SERVER_ID, MAIL_SERVICE_SMTPD), /* XXX */
+ SEND_ATTR_STR(TLS_ATTR_REMOTE_ENDPT, STR(remote_endpt)),
+ SEND_ATTR_INT(TLS_ATTR_FLAGS, TLS_PROXY_FLAG_ROLE_SERVER),
+ SEND_ATTR_INT(TLS_ATTR_TIMEOUT, psc_normal_cmd_time_limit),
+ SEND_ATTR_INT(TLS_ATTR_TIMEOUT, psc_normal_cmd_time_limit),
+ SEND_ATTR_STR(TLS_ATTR_SERVERID, MAIL_SERVICE_SMTPD), /* XXX */
ATTR_TYPE_END);
if (vstream_fflush(tlsproxy_stream) != 0) {
msg_warn("error sending request to %s service: %m", psc_tlsp_service);
/* SMTP in SSL protocol, rather than the standard STARTTLS protocol.
/* The destination \fIdomain\fR:\fIport\fR should of course provide such
/* a service.
+/* .IP "\fB-X\fR"
+/* Enable \fBtlsproxy\fR(8) mode. This is an unsupported mode,
+/* for program development only.
/* .IP "[\fBinet:\fR]\fIdomain\fR[:\fIport\fR]"
/* Connect via TCP to domain \fIdomain\fR, port \fIport\fR. The default
/* port is \fBsmtp\fR (or 24 with LMTP). With SMTP an MX lookup is
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*
/* Viktor Dukhovni
/*--*/
#include <smtp_stream.h>
#include <dsn_buf.h>
#include <mail_parm_split.h>
+#include <mail_proto.h>
/* DNS library. */
#include <tls.h>
#ifdef USE_TLS
+#include <tls_proxy.h>
#include <openssl/engine.h>
#endif
int force_tlsa; /* -f option */
unsigned port; /* TCP port */
char *dest; /* Full destination spec */
+ char *paddr; /* XXX printable addr for proxy */
char *addrport; /* [addr]:port */
char *namaddrport; /* name[addr]:port */
char *nexthop; /* Nexthop domain for verification */
char *grade; /* Minimum cipher grade */
char *protocols; /* Protocol inclusion/exclusion */
int mxinsec_level; /* DANE for insecure MX RRs? */
+ int tlsproxy_mode;
#endif
OPTIONS options; /* JCL */
} STATE;
int except;
RESPONSE *resp;
VSTREAM *stream = state->stream;
- TLS_CLIENT_START_PROPS tls_props;
+ TLS_CLIENT_START_PROPS start_props;
+ TLS_CLIENT_INIT_PROPS init_props;
+ VSTREAM *tlsproxy;
+ VSTRING *port_buf;
+ int cwd_fd;
if (state->wrapper_mode == 0) {
/* SMTP stream with deadline timeouts */
else
ADD_EXCLUDE(cipher_exclusions, "eNULL");
- state->tls_context =
- TLS_CLIENT_START(&tls_props,
- ctx = state->tls_ctx,
- stream = stream,
- timeout = smtp_tmout,
- tls_level = state->level,
- nexthop = state->nexthop,
- host = state->hostname,
- namaddr = state->namaddrport,
- serverid = state->addrport,
- helo = state->helo ? state->helo : "",
- protocols = state->protocols,
- cipher_grade = state->grade,
- cipher_exclusions
- = vstring_str(cipher_exclusions),
- matchargv = state->match,
- mdalg = state->mdalg,
- dane = state->ddane ? state->ddane : state->dane);
+ if (state->tlsproxy_mode) {
+
+ /*
+ * Send all our wishes in one big request.
+ */
+ TLS_PROXY_CLIENT_INIT_PROPS(&init_props,
+ log_param = "-L option",
+ log_level = state->options.logopts,
+ verifydepth = DEF_SMTP_TLS_SCERT_VD,
+ cache_type = "memory",
+ cert_file = state->certfile,
+ key_file = state->keyfile,
+ dcert_file = "",
+ dkey_file = "",
+ eccert_file = "",
+ eckey_file = "",
+ CAfile = state->CAfile,
+ CApath = state->CApath,
+ mdalg = state->mdalg);
+ TLS_PROXY_CLIENT_START_PROPS(&start_props,
+ timeout = smtp_tmout,
+ tls_level = state->level,
+ nexthop = state->nexthop,
+ host = state->hostname,
+ namaddr = state->namaddrport,
+ serverid = state->addrport,
+ helo = state->helo ? state->helo : "",
+ protocols = state->protocols,
+ cipher_grade = state->grade,
+ cipher_exclusions
+ = vstring_str(cipher_exclusions),
+ matchargv = state->match,
+ mdalg = state->mdalg,
+ dane = state->ddane ?
+ state->ddane : state->dane);
+
+#define PROXY_OPEN_FLAGS \
+ (TLS_PROXY_FLAG_ROLE_CLIENT | TLS_PROXY_FLAG_SEND_CONTEXT)
+#define var_tlsproxy_service
+
+ if ((cwd_fd = open(".", O_RDONLY)) < 0)
+ msg_fatal("open(\".\", O_RDONLY): %m");
+ if (chdir(var_queue_dir) < 0)
+ msg_fatal("chdir(%s): %m", var_queue_dir);
+ port_buf = vstring_alloc(100);
+ vstring_sprintf(port_buf, "%d", ntohs(state->port));
+ tlsproxy =
+ tls_proxy_open(DEF_TLSPROXY_SERVICE /* TODO */ , PROXY_OPEN_FLAGS,
+ state->stream, state->paddr,
+ STR(port_buf), smtp_tmout, smtp_tmout,
+ state->addrport, &init_props, &start_props);
+ vstring_free(port_buf);
+ if (fchdir(cwd_fd) < 0)
+ msg_fatal("fchdir: %m");
+ (void) close(cwd_fd);
+
+ /*
+ * To insert tlsproxy(8) between this process and the remote SMTP
+ * server, we swap the file descriptors between the tlsproxy and
+ * session->stream VSTREAMS, so that we don't lose all the
+ * user-configurable session->stream attributes (such as longjump
+ * buffers or timeouts).
+ *
+ * TODO: the tlsproxy RPCs should return more error detail than a "NO"
+ * result.
+ */
+ if (tlsproxy == 0) {
+ state->tls_context = 0;
+ } else {
+ vstream_control(tlsproxy,
+ CA_VSTREAM_CTL_DOUBLE,
+ CA_VSTREAM_CTL_END);
+ vstream_control(state->stream,
+ CA_VSTREAM_CTL_SWAP_FD(tlsproxy),
+ CA_VSTREAM_CTL_END);
+ (void) vstream_fclose(tlsproxy); /* direct-to-server stream! */
+
+ /*
+ * There must not be any pending data in the stream buffers
+ * before we read the TLS context attributes.
+ */
+ vstream_fpurge(state->stream, VSTREAM_PURGE_BOTH);
+
+ /*
+ * After plumbing the plaintext stream, receive the TLS context
+ * object. For this we use the same VSTREAM buffer that we also
+ * use to receive subsequent SMTP commands, therefore we must be
+ * prepared for the possibility that the remote SMTP server
+ * starts talking immediately. The tlsproxy implementation sends
+ * the TLS context before remote content. The attribute protocol
+ * is robust enough that an adversary cannot insert their own TLS
+ * context attributes.
+ */
+ state->tls_context = tls_proxy_context_receive(state->stream);
+ msg_info("%s: subject_CN=%s, issuer_CN=%s, "
+ "fingerprint=%s, pkey_fingerprint=%s",
+ state->namaddrport, state->tls_context->peer_CN,
+ state->tls_context->issuer_CN,
+ state->tls_context->peer_cert_fprint,
+ state->tls_context->peer_pkey_fprint);
+ msg_info("%s TLS connection established to %s: %s with cipher %s "
+ "(%d/%d bits)",
+ !TLS_CERT_IS_PRESENT(state->tls_context) ? "Anonymous" :
+ TLS_CERT_IS_SECURED(state->tls_context) ? "Verified" :
+ TLS_CERT_IS_TRUSTED(state->tls_context) ? "Trusted" :
+ "Untrusted", state->namaddrport,
+ state->tls_context->protocol,
+ state->tls_context->cipher_name,
+ state->tls_context->cipher_usebits,
+ state->tls_context->cipher_algbits);
+ }
+ } else { /* tls_proxy_mode */
+ state->tls_context =
+ TLS_CLIENT_START(&start_props,
+ ctx = state->tls_ctx,
+ stream = stream,
+ fd = -1,
+ timeout = smtp_tmout,
+ tls_level = state->level,
+ nexthop = state->nexthop,
+ host = state->hostname,
+ namaddr = state->namaddrport,
+ serverid = state->addrport,
+ helo = state->helo ? state->helo : "",
+ protocols = state->protocols,
+ cipher_grade = state->grade,
+ cipher_exclusions
+ = vstring_str(cipher_exclusions),
+ matchargv = state->match,
+ mdalg = state->mdalg,
+ dane = state->ddane ? state->ddane : state->dane);
+ } /* tlsproxy_mode */
vstring_free(cipher_exclusions);
if (state->helo) {
myfree(state->helo);
ehlo(state);
if (!TLS_CERT_IS_PRESENT(state->tls_context))
msg_info("Server is anonymous");
- else if (state->print_trust)
- print_trust_info(state);
- state->log_mask &= ~(TLS_LOG_CERTMATCH | TLS_LOG_PEERCERT |
- TLS_LOG_VERBOSE | TLS_LOG_UNTRUSTED);
- state->log_mask |= TLS_LOG_CACHE | TLS_LOG_SUMMARY;
- tls_update_app_logmask(state->tls_ctx, state->log_mask);
+ else if (state->tlsproxy_mode == 0) {
+ if (state->print_trust)
+ print_trust_info(state);
+ state->log_mask &= ~(TLS_LOG_CERTMATCH | TLS_LOG_PEERCERT |
+ TLS_LOG_VERBOSE | TLS_LOG_UNTRUSTED);
+ state->log_mask |= TLS_LOG_CACHE | TLS_LOG_SUMMARY;
+ tls_update_app_logmask(state->tls_ctx, state->log_mask);
+ }
}
return (0);
}
vstring_sprintf(vstring_alloc(10), "[%s]:%u",
addr, ntohs(state->port)));
+ state->paddr = mystrdup(addr); /* XXX for tlsproxy */
+
/*
* Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE.
*/
static void disconnect_dest(STATE *state)
{
#ifdef USE_TLS
- if (state->tls_context)
- tls_client_stop(state->tls_ctx, state->stream,
- smtp_tmout, 0, state->tls_context);
+ if (state->tls_context) {
+ if (state->tlsproxy_mode) {
+ tls_proxy_context_free(state->tls_context);
+ } else {
+ tls_client_stop(state->tls_ctx, state->stream,
+ smtp_tmout, 0, state->tls_context);
+ }
+ }
state->tls_context = 0;
if (state->ddane)
tls_dane_free(state->ddane);
myfree(state->addrport);
state->addrport = 0;
+ if (state->paddr)
+ myfree(state->paddr);
+ state->paddr = 0;
+
/* Reused on reconnect */
if (state->reconnect <= 0) {
if (state->addr)
return (1);
#ifdef USE_TLS
- if (state->reconnect > 0) {
+ if (state->tlsproxy_mode == 0 && state->reconnect > 0) {
int cache_enabled;
int cache_count;
int cache_hits;
if (state->level <= TLS_LEV_NONE)
return;
+ /* Needed for tls_dane_avail() and other DANE-related processing. */
state->tls_ctx =
TLS_CLIENT_INIT(&props,
log_param = "-L option",
#define OPTS "a:ch:o:St:T:v"
#ifdef USE_TLS
-#define TLSOPTS "A:Cd:fF:g:k:K:l:L:m:M:p:P:r:w"
+#define TLSOPTS "A:Cd:fF:g:k:K:l:L:m:M:p:P:r:wX"
state->mdalg = mystrdup("sha1");
state->CApath = mystrdup("");
state->options.logopts = 0;
state->level = TLS_LEV_DANE;
state->mxinsec_level = TLS_LEV_DANE;
+ state->tlsproxy_mode = 0;
#else
#define TLSOPTS ""
state->level = TLS_LEV_NONE;
break;
case 'w':
state->wrapper_mode = 1;
+ case 'X':
+ state->tlsproxy_mode = 1;
break;
#endif
}
smtp.o: ../../include/header_body_checks.h
smtp.o: ../../include/header_opts.h
smtp.o: ../../include/htable.h
+smtp.o: ../../include/iostuff.h
smtp.o: ../../include/mail_conf.h
smtp.o: ../../include/mail_params.h
+smtp.o: ../../include/mail_proto.h
smtp.o: ../../include/mail_server.h
smtp.o: ../../include/mail_version.h
smtp.o: ../../include/maps.h
smtp.o: ../../include/stringops.h
smtp.o: ../../include/sys_defs.h
smtp.o: ../../include/tls.h
+smtp.o: ../../include/tls_proxy.h
smtp.o: ../../include/tok822.h
smtp.o: ../../include/vbuf.h
smtp.o: ../../include/vstream.h
smtp_addr.o: ../../include/stringops.h
smtp_addr.o: ../../include/sys_defs.h
smtp_addr.o: ../../include/tls.h
+smtp_addr.o: ../../include/tls_proxy.h
smtp_addr.o: ../../include/tok822.h
smtp_addr.o: ../../include/vbuf.h
smtp_addr.o: ../../include/vstream.h
smtp_chat.o: ../../include/stringops.h
smtp_chat.o: ../../include/sys_defs.h
smtp_chat.o: ../../include/tls.h
+smtp_chat.o: ../../include/tls_proxy.h
smtp_chat.o: ../../include/tok822.h
smtp_chat.o: ../../include/vbuf.h
smtp_chat.o: ../../include/vstream.h
smtp_connect.o: ../../include/sys_defs.h
smtp_connect.o: ../../include/timed_connect.h
smtp_connect.o: ../../include/tls.h
+smtp_connect.o: ../../include/tls_proxy.h
smtp_connect.o: ../../include/tok822.h
smtp_connect.o: ../../include/vbuf.h
smtp_connect.o: ../../include/vstream.h
smtp_key.o: ../../include/string_list.h
smtp_key.o: ../../include/sys_defs.h
smtp_key.o: ../../include/tls.h
+smtp_key.o: ../../include/tls_proxy.h
smtp_key.o: ../../include/tok822.h
smtp_key.o: ../../include/vbuf.h
smtp_key.o: ../../include/vstream.h
smtp_map11.o: ../../include/string_list.h
smtp_map11.o: ../../include/sys_defs.h
smtp_map11.o: ../../include/tls.h
+smtp_map11.o: ../../include/tls_proxy.h
smtp_map11.o: ../../include/tok822.h
smtp_map11.o: ../../include/vbuf.h
smtp_map11.o: ../../include/vstream.h
smtp_proto.o: ../../include/stringops.h
smtp_proto.o: ../../include/sys_defs.h
smtp_proto.o: ../../include/tls.h
+smtp_proto.o: ../../include/tls_proxy.h
smtp_proto.o: ../../include/tok822.h
smtp_proto.o: ../../include/uxtext.h
smtp_proto.o: ../../include/vbuf.h
smtp_rcpt.o: ../../include/stringops.h
smtp_rcpt.o: ../../include/sys_defs.h
smtp_rcpt.o: ../../include/tls.h
+smtp_rcpt.o: ../../include/tls_proxy.h
smtp_rcpt.o: ../../include/tok822.h
smtp_rcpt.o: ../../include/vbuf.h
smtp_rcpt.o: ../../include/vstream.h
smtp_reuse.o: ../../include/stringops.h
smtp_reuse.o: ../../include/sys_defs.h
smtp_reuse.o: ../../include/tls.h
+smtp_reuse.o: ../../include/tls_proxy.h
smtp_reuse.o: ../../include/tok822.h
smtp_reuse.o: ../../include/vbuf.h
smtp_reuse.o: ../../include/vstream.h
smtp_sasl_auth_cache.o: ../../include/stringops.h
smtp_sasl_auth_cache.o: ../../include/sys_defs.h
smtp_sasl_auth_cache.o: ../../include/tls.h
+smtp_sasl_auth_cache.o: ../../include/tls_proxy.h
smtp_sasl_auth_cache.o: ../../include/tok822.h
smtp_sasl_auth_cache.o: ../../include/vbuf.h
smtp_sasl_auth_cache.o: ../../include/vstream.h
smtp_sasl_glue.o: ../../include/stringops.h
smtp_sasl_glue.o: ../../include/sys_defs.h
smtp_sasl_glue.o: ../../include/tls.h
+smtp_sasl_glue.o: ../../include/tls_proxy.h
smtp_sasl_glue.o: ../../include/tok822.h
smtp_sasl_glue.o: ../../include/vbuf.h
smtp_sasl_glue.o: ../../include/vstream.h
smtp_sasl_proto.o: ../../include/stringops.h
smtp_sasl_proto.o: ../../include/sys_defs.h
smtp_sasl_proto.o: ../../include/tls.h
+smtp_sasl_proto.o: ../../include/tls_proxy.h
smtp_sasl_proto.o: ../../include/tok822.h
smtp_sasl_proto.o: ../../include/vbuf.h
smtp_sasl_proto.o: ../../include/vstream.h
smtp_session.o: ../../include/stringops.h
smtp_session.o: ../../include/sys_defs.h
smtp_session.o: ../../include/tls.h
+smtp_session.o: ../../include/tls_proxy.h
smtp_session.o: ../../include/tok822.h
smtp_session.o: ../../include/vbuf.h
smtp_session.o: ../../include/vstream.h
smtp_state.o: ../../include/string_list.h
smtp_state.o: ../../include/sys_defs.h
smtp_state.o: ../../include/tls.h
+smtp_state.o: ../../include/tls_proxy.h
smtp_state.o: ../../include/tok822.h
smtp_state.o: ../../include/vbuf.h
smtp_state.o: ../../include/vstream.h
smtp_tls_policy.o: ../../include/stringops.h
smtp_tls_policy.o: ../../include/sys_defs.h
smtp_tls_policy.o: ../../include/tls.h
+smtp_tls_policy.o: ../../include/tls_proxy.h
smtp_tls_policy.o: ../../include/tok822.h
smtp_tls_policy.o: ../../include/valid_hostname.h
smtp_tls_policy.o: ../../include/valid_utf8_hostname.h
smtp_trouble.o: ../../include/stringops.h
smtp_trouble.o: ../../include/sys_defs.h
smtp_trouble.o: ../../include/tls.h
+smtp_trouble.o: ../../include/tls_proxy.h
smtp_trouble.o: ../../include/tok822.h
smtp_trouble.o: ../../include/vbuf.h
smtp_trouble.o: ../../include/vstream.h
smtp_unalias.o: ../../include/string_list.h
smtp_unalias.o: ../../include/sys_defs.h
smtp_unalias.o: ../../include/tls.h
+smtp_unalias.o: ../../include/tls_proxy.h
smtp_unalias.o: ../../include/tok822.h
smtp_unalias.o: ../../include/vbuf.h
smtp_unalias.o: ../../include/vstream.h
VAR_LMTP_DNS_RES_OPT, DEF_LMTP_DNS_RES_OPT, &var_smtp_dns_res_opt, 0, 0,
VAR_LMTP_DSN_FILTER, DEF_LMTP_DSN_FILTER, &var_smtp_dsn_filter, 0, 0,
VAR_LMTP_DNS_RE_FILTER, DEF_LMTP_DNS_RE_FILTER, &var_smtp_dns_re_filter, 0, 0,
+ VAR_TLSPROXY_SERVICE, DEF_TLSPROXY_SERVICE, &var_tlsproxy_service, 1, 0,
0,
};
static const CONFIG_TIME_TABLE lmtp_time_table[] = {
VAR_LMTP_CACHE_DEMAND, DEF_LMTP_CACHE_DEMAND, &var_smtp_cache_demand,
VAR_LMTP_USE_TLS, DEF_LMTP_USE_TLS, &var_smtp_use_tls,
VAR_LMTP_ENFORCE_TLS, DEF_LMTP_ENFORCE_TLS, &var_smtp_enforce_tls,
+ VAR_LMTP_TLS_CONN_REUSE, DEF_LMTP_TLS_CONN_REUSE, &var_smtp_tls_conn_reuse,
#ifdef USE_TLS
VAR_LMTP_TLS_ENFORCE_PN, DEF_LMTP_TLS_ENFORCE_PN, &var_smtp_tls_enforce_peername,
VAR_LMTP_TLS_NOTEOFFER, DEF_LMTP_TLS_NOTEOFFER, &var_smtp_tls_note_starttls_offer,
/* The TLS policy for MX hosts with "secure" TLSA records when the
/* nexthop destination security level is \fBdane\fR, but the MX
/* record was found via an "insecure" MX lookup.
+/* .PP
+/* Available in Postfix version 3.4 and later:
+/* .IP "\fBsmtp_tls_connection_reuse (no)\fR"
+/* Try to make multiple deliveries per TLS-encrypted connection.
/* OBSOLETE STARTTLS CONTROLS
/* .ad
/* .fi
/* that an SMTP session may be reused before it is closed, or zero (no
/* limit).
/* .PP
+/* Available in Postfix version 3.4 and later:
+/* .IP "\fBsmtp_tls_connection_reuse (no)\fR"
+/* Try to make multiple deliveries per TLS-encrypted connection.
+/* .PP
/* Implemented in the qmgr(8) daemon:
/* .IP "\fBtransport_destination_concurrency_limit ($default_destination_concurrency_limit)\fR"
/* A transport-specific override for the
/* Global library. */
#include <deliver_request.h>
+#include <mail_proto.h>
#include <mail_params.h>
#include <mail_version.h>
#include <mail_conf.h>
char *var_smtp_tls_per_site;
char *var_smtp_tls_policy;
bool var_smtp_tls_wrappermode;
+bool var_smtp_tls_conn_reuse;
+char *var_tlsproxy_service;
#ifdef USE_TLS
char *var_smtp_sasl_tls_opts;
*
* Large parameter lists are error-prone, so we emulate a language
* feature that C does not have natively: named parameter lists.
+ *
+ * With tlsproxy(8) turned on, this is still needed for DANE-related
+ * initializations.
*/
smtp_tls_ctx =
TLS_CLIENT_INIT(&props,
*/
#include <tls.h>
+ /*
+ * tlsproxy client.
+ */
+#include <tls_proxy.h>
+
/*
* Global iterator support. This is updated by the connection-management
* loop, and contains dynamic context that appears in lookup keys for SASL
ARGV *matchargv; /* Cert match patterns */
DSN_BUF *why; /* Lookup error status */
TLS_DANE *dane; /* DANE TLSA digests */
+ int conn_reuse; /* enable connection reuse */
} SMTP_TLS_POLICY;
/*
_tls_policy_init_tmp->matchargv = 0; \
_tls_policy_init_tmp->why = (w); \
_tls_policy_init_tmp->dane = 0; \
+ _tls_policy_init_tmp->conn_reuse = 0; \
} while (0)
#endif
#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 */
+#define SMTP_KEY_FLAG_TLS_LEVEL (1<<7) /* requested TLS level */
#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_ADDR | SMTP_KEY_FLAG_PORT)
+ SMTP_KEY_FLAG_ADDR | SMTP_KEY_FLAG_PORT | SMTP_KEY_FLAG_TLS_LEVEL)
/*
* Conditional lookup-key flags for cached connections that may be
*/
#define SMTP_KEY_MASK_SCACHE_DEST_LABEL \
(SMTP_KEY_FLAG_SERVICE | COND_SASL_SMTP_KEY_FLAG_SENDER \
- | SMTP_KEY_FLAG_REQ_NEXTHOP)
+ | SMTP_KEY_FLAG_REQ_NEXTHOP | SMTP_KEY_FLAG_TLS_LEVEL)
/*
* Connection-cache endpoint lookup key. The SENDER, NEXTHOP, and HOSTNAME
#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 \
- | SMTP_KEY_FLAG_ADDR | SMTP_KEY_FLAG_PORT)
+ | SMTP_KEY_FLAG_ADDR | SMTP_KEY_FLAG_PORT | SMTP_KEY_FLAG_TLS_LEVEL)
/*
* Silly little macros.
/* 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
* and restore it here, so that subsequent connections will use the
* proper nexthop information.
*
- * We request a dummy "TLS disabled" policy for connection-cache lookup by
- * request nexthop only. If we find a saved connection, then we know that
- * plaintext was permitted, because we never save a connection after
- * turning on TLS.
+ * If TLS is proxied, lookup the TLS policy now so that we reuse only
+ * matching sessions. Otherwise, request a dummy "TLS disabled" policy
+ * for connection-cache lookup by request nexthop only.
*/
#ifdef USE_TLS
- smtp_tls_policy_dummy(state->tls);
+ if (!smtp_tls_policy_cache_query(why, state->tls, iter)) {
+ msg_warn("TLS policy lookup error for %s/%s: %s",
+ STR(iter->dest), STR(iter->host), STR(why->reason));
+ return (0); /* XXX */
+ }
#endif
SMTP_ITER_SAVE_DEST(state->iterator);
if (*addr_list && SMTP_RCPT_LEFT(state) > 0
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/*
if (flags & SMTP_KEY_FLAG_PORT)
smtp_key_append_uint(buffer, ntohs(iter->port), delim_na);
- /* Similarly, provide unique TLS fingerprint when applicable. */
+ /*
+ * Requested TLS level, if applicable. TODO(tlsproxy) should the lookup
+ * engine also try the requested TLS level and 'stronger', in case a
+ * server hosts multiple domains with different TLS requirements?
+ */
+ if (flags & SMTP_KEY_FLAG_TLS_LEVEL)
+#ifdef USE_TLS
+ smtp_key_append_uint(buffer, state->tls->level, delim_na);
+#else
+ smtp_key_append_na(buffer, delim_na);
+#endif
VSTRING_TERMINATE(buffer);
VAR_SMTP_DNS_RES_OPT, DEF_SMTP_DNS_RES_OPT, &var_smtp_dns_res_opt, 0, 0,
VAR_SMTP_DSN_FILTER, DEF_SMTP_DSN_FILTER, &var_smtp_dsn_filter, 0, 0,
VAR_SMTP_DNS_RE_FILTER, DEF_SMTP_DNS_RE_FILTER, &var_smtp_dns_re_filter, 0, 0,
+ VAR_TLSPROXY_SERVICE, DEF_TLSPROXY_SERVICE, &var_tlsproxy_service, 1, 0,
0,
};
static const CONFIG_TIME_TABLE smtp_time_table[] = {
VAR_SMTP_CACHE_DEMAND, DEF_SMTP_CACHE_DEMAND, &var_smtp_cache_demand,
VAR_SMTP_USE_TLS, DEF_SMTP_USE_TLS, &var_smtp_use_tls,
VAR_SMTP_ENFORCE_TLS, DEF_SMTP_ENFORCE_TLS, &var_smtp_enforce_tls,
+ VAR_SMTP_TLS_CONN_REUSE, DEF_SMTP_TLS_CONN_REUSE, &var_smtp_tls_conn_reuse,
#ifdef USE_TLS
VAR_SMTP_TLS_ENFORCE_PN, DEF_SMTP_TLS_ENFORCE_PN, &var_smtp_tls_enforce_peername,
VAR_SMTP_TLS_NOTEOFFER, DEF_SMTP_TLS_NOTEOFFER, &var_smtp_tls_note_starttls_offer,
{
SMTP_SESSION *session = state->session;
SMTP_ITERATOR *iter = state->iterator;
- TLS_CLIENT_START_PROPS tls_props;
+ TLS_CLIENT_START_PROPS start_props;
VSTRING *serverid;
SMTP_RESP fake;
+ TLS_CLIENT_INIT_PROPS init_props;
+ VSTREAM *tlsproxy;
+ VSTRING *port_buf;
/*
- * Turn off SMTP connection caching. When the TLS handshake succeeds, we
- * can't reuse the SMTP connection. Reason: we can't turn off TLS in one
- * process, save the connection to the cache which is shared with all
- * SMTP clients, migrate the connection to another SMTP client, and
- * resume TLS there. When the TLS handshake fails, we can't reuse the
- * SMTP connection either, because the conversation is in an unknown
- * state.
+ * When the TLS handshake succeeds, we can reuse a connection only if TLS
+ * remains turned on for the lifetime of that connection. This requires
+ * that the TLS library state is maintained in some proxy process, for
+ * example, in tlsproxy(8). We then store the proxy file handle in the
+ * connection cache, and reuse that file handle.
+ *
+ * Otherwise, we must turn off connection caching. We can't turn off TLS in
+ * one SMTP client process, save the open connection to a cache which is
+ * shared with all SMTP clients, migrate the connection to another SMTP
+ * client, and resume TLS there. When the TLS handshake fails, we can't
+ * reuse the SMTP connection either, because the conversation is in an
+ * unknown state.
*/
- DONT_CACHE_THIS_SESSION;
+ if (state->tls->conn_reuse == 0)
+ DONT_CACHE_THIS_SESSION;
/*
* The following assumes sites that use TLS in a perverse configuration:
| SMTP_KEY_FLAG_HOSTNAME
| SMTP_KEY_FLAG_ADDR);
- /*
- * As of Postfix 2.5, tls_client_start() tries hard to always complete
- * the TLS handshake. It records the verification and match status in the
- * resulting TLScontext. It is now up to the application to abort the TLS
- * connection if it chooses.
- *
- * XXX When tls_client_start() fails then we don't know what state the SMTP
- * connection is in, so we give up on this connection even if we are not
- * required to use TLS.
- *
- * Large parameter lists are error-prone, so we emulate a language feature
- * that C does not have natively: named parameter lists.
- */
- session->tls_context =
- TLS_CLIENT_START(&tls_props,
- ctx = smtp_tls_ctx,
- stream = session->stream,
- timeout = var_smtp_starttls_tmout,
- tls_level = state->tls->level,
- nexthop = session->tls_nexthop,
- host = STR(iter->host),
- namaddr = session->namaddrport,
- serverid = vstring_str(serverid),
- helo = session->helo,
- protocols = state->tls->protocols,
- cipher_grade = state->tls->grade,
- cipher_exclusions
- = vstring_str(state->tls->exclusions),
- matchargv = state->tls->matchargv,
- mdalg = var_smtp_tls_fpt_dgst,
- dane = state->tls->dane);
+ if (state->tls->conn_reuse) {
+
+ /*
+ * Send all our wishes in one big request.
+ */
+ TLS_PROXY_CLIENT_INIT_PROPS(&init_props,
+ log_param = VAR_LMTP_SMTP(TLS_LOGLEVEL),
+ log_level = var_smtp_tls_loglevel,
+ verifydepth = var_smtp_tls_scert_vd,
+ cache_type
+ = LMTP_SMTP_SUFFIX(TLS_MGR_SCACHE),
+ cert_file = var_smtp_tls_cert_file,
+ key_file = var_smtp_tls_key_file,
+ dcert_file = var_smtp_tls_dcert_file,
+ dkey_file = var_smtp_tls_dkey_file,
+ eccert_file = var_smtp_tls_eccert_file,
+ eckey_file = var_smtp_tls_eckey_file,
+ CAfile = var_smtp_tls_CAfile,
+ CApath = var_smtp_tls_CApath,
+ mdalg = var_smtp_tls_fpt_dgst);
+ TLS_PROXY_CLIENT_START_PROPS(&start_props,
+ timeout = var_smtp_starttls_tmout,
+ tls_level = state->tls->level,
+ nexthop = session->tls_nexthop,
+ host = STR(iter->host),
+ namaddr = session->namaddrport,
+ serverid = vstring_str(serverid),
+ helo = session->helo,
+ protocols = state->tls->protocols,
+ cipher_grade = state->tls->grade,
+ cipher_exclusions
+ = vstring_str(state->tls->exclusions),
+ matchargv = state->tls->matchargv,
+ mdalg = var_smtp_tls_fpt_dgst,
+ dane = state->tls->dane);
+
+ /*
+ * The tlsproxy(8) server enforces timeouts that are larger than
+ * those specified by the tlsproxy(8) client. These timeouts are a
+ * safety net for the case that the tlsproxy(8) client fails to
+ * enforce time limits. Normally, the tlsproxy(8) client would time
+ * out and trigger a plaintext event in the tlsproxy(8) server, and
+ * cause it to tear down the session.
+ *
+ * However, the tlsproxy(8) server has no insight into the SMTP
+ * protocol, and therefore it cannot by itself support different
+ * timeouts at different SMTP protocol stages. Instead, we specify
+ * the largest timeout (end-of-data) and rely on the SMTP client to
+ * time out first, which normally results in a plaintext event in the
+ * tlsproxy(8) server. Unfortunately, we cannot permit plaintext
+ * events during the TLS handshake, so we specify a separate timeout
+ * for that stage (the end-of-data timeout would be unreasonably
+ * large anyway).
+ */
+#define PROXY_OPEN_FLAGS \
+ (TLS_PROXY_FLAG_ROLE_CLIENT | TLS_PROXY_FLAG_SEND_CONTEXT)
+
+ port_buf = vstring_alloc(100); /* minimize fragmentation */
+ vstring_sprintf(port_buf, "%d", ntohs(iter->port));
+ tlsproxy =
+ tls_proxy_open(var_tlsproxy_service, PROXY_OPEN_FLAGS,
+ session->stream, STR(iter->addr),
+ STR(port_buf), var_smtp_starttls_tmout,
+ var_smtp_data2_tmout, state->service,
+ &init_props, &start_props);
+ vstring_free(port_buf);
+
+ /*
+ * To insert tlsproxy(8) between this process and the remote SMTP
+ * server, we swap the file descriptors between the tlsproxy and
+ * session->stream VSTREAMS, so that we don't lose all the
+ * user-configurable session->stream attributes (such as longjump
+ * buffers or timeouts).
+ *
+ * TODO: the tlsproxy RPCs should return more error detail than a "NO"
+ * result. OTOH, the in-process TLS engine does not return such info
+ * either.
+ *
+ * If the tlsproxy request fails we do not fall back to the in-process
+ * TLS stack. Reason: the admin enabled connection reuse to respect
+ * receiver policy; silently violating such policy would not be
+ * useful.
+ *
+ * We also don't fall back to the in-process TLS stack under low-traffic
+ * conditions, to avoid frustrating attempts to debug a problem with
+ * using the tlsproxy(8) service.
+ */
+ if (tlsproxy == 0) {
+ session->tls_context = 0;
+ } else {
+ vstream_control(tlsproxy,
+ CA_VSTREAM_CTL_DOUBLE,
+ CA_VSTREAM_CTL_END);
+ vstream_control(session->stream,
+ CA_VSTREAM_CTL_SWAP_FD(tlsproxy),
+ CA_VSTREAM_CTL_END);
+ (void) vstream_fclose(tlsproxy); /* direct-to-server stream! */
+
+ /*
+ * There must not be any pending data in the stream buffers
+ * before we read the TLS context attributes.
+ */
+ vstream_fpurge(session->stream, VSTREAM_PURGE_BOTH);
+
+ /*
+ * After plumbing the plaintext stream, receive the TLS context
+ * object. For this we use the same VSTREAM buffer that we also
+ * use to receive subsequent SMTP commands, therefore we must be
+ * prepared for the possibility that the remote SMTP server
+ * starts talking immediately. The tlsproxy implementation sends
+ * the TLS context before remote content. The attribute protocol
+ * is robust enough that an adversary cannot insert their own TLS
+ * context attributes.
+ */
+ session->tls_context = tls_proxy_context_receive(session->stream);
+ }
+ } else { /* state->tls->conn_reuse */
+
+ /*
+ * As of Postfix 2.5, tls_client_start() tries hard to always
+ * complete the TLS handshake. It records the verification and match
+ * status in the resulting TLScontext. It is now up to the
+ * application to abort the TLS connection if it chooses.
+ *
+ * XXX When tls_client_start() fails then we don't know what state the
+ * SMTP connection is in, so we give up on this connection even if we
+ * are not required to use TLS.
+ *
+ * Large parameter lists are error-prone, so we emulate a language
+ * feature that C does not have natively: named parameter lists.
+ */
+ session->tls_context =
+ TLS_CLIENT_START(&start_props,
+ ctx = smtp_tls_ctx,
+ stream = session->stream,
+ fd = -1,
+ timeout = var_smtp_starttls_tmout,
+ tls_level = state->tls->level,
+ nexthop = session->tls_nexthop,
+ host = STR(iter->host),
+ namaddr = session->namaddrport,
+ serverid = vstring_str(serverid),
+ helo = session->helo,
+ protocols = state->tls->protocols,
+ cipher_grade = state->tls->grade,
+ cipher_exclusions
+ = vstring_str(state->tls->exclusions),
+ matchargv = state->tls->matchargv,
+ mdalg = var_smtp_tls_fpt_dgst,
+ dane = state->tls->dane);
+
+ /*
+ * At this point there must not be any pending data in the stream
+ * buffers.
+ */
+ vstream_fpurge(session->stream, VSTREAM_PURGE_BOTH);
+ } /* state->tls->conn_reuse */
+
vstring_free(serverid);
if (session->tls_context == 0) {
/*
* We must avoid further I/O, the peer is in an undefined state.
*/
- (void) vstream_fpurge(session->stream, VSTREAM_PURGE_BOTH);
DONT_USE_FORBIDDEN_SESSION;
/*
SMTP_RESP_FAKE(&fake, "4.7.5"),
"Server certificate not verified"));
- /* At this point there must not be any pending plaintext. */
- vstream_fpurge(session->stream, VSTREAM_PURGE_BOTH);
-
/*
* At this point we have to re-negotiate the "EHLO" to reget the
* feature-list.
/* 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.
/*
/* Arguments:
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/* System library. */
SMTP_SESSION *session;
/*
- * Can't happen. Both smtp_reuse_nexthop() and smtp_reuse_addr() decline
- * the request when the TLS policy is not TLS_LEV_NONE.
+ * Obsolete.
*/
-#ifdef USE_TLS
+#ifdef notdef
if (state->tls->level > TLS_LEV_NONE)
msg_panic("%s: unexpected plain-text cached session to %s",
myname, label);
int fd;
/*
- * Don't look up an existing plaintext connection when a new connection
- * would (try to) use TLS.
+ * Obsolete: the TLS level and nexthop are part of the connection cache
+ * key. TODO(tlsproxy) is the port included in the nexthop?
*/
-#ifdef USE_TLS
+#ifdef notdef
if (state->tls->level > TLS_LEV_NONE)
return (0);
#endif
int fd;
/*
- * Don't look up an existing plaintext connection when a new connection
- * would (try to) use TLS.
+ * Allow address-based reuse only for security levels that don't require
+ * certificate checks.
*/
#ifdef USE_TLS
- if (state->tls->level > TLS_LEV_NONE)
+ if (state->tls->level > TLS_LEV_ENCRYPT)
return (0);
#endif
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*
/* Viktor Dukhovni
/*--*/
#ifdef USE_TLS
if (session->stream) {
vstream_fflush(session->stream);
- if (session->tls_context)
+ }
+ if (session->tls_context) {
+ if (session->state->tls->conn_reuse)
+ tls_proxy_context_free(session->tls_context);
+ else
tls_client_stop(smtp_tls_ctx, session->stream,
var_smtp_starttls_tmout, 0, session->tls_context);
}
* 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.
- *
*/
vstring_sprintf(dest_prop, "%s\n%s\n%s\n%u",
STR(iter->dest), STR(iter->host), STR(iter->addr),
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*
/* Viktor Dukhovni
/*--*/
}
continue;
}
+ /* Last one wins. */
+ if (!strcasecmp(name, "connection_reuse")) {
+ if (strcasecmp(val, "yes") == 0) {
+ tls->conn_reuse = 1;
+ } else if (strcasecmp(val, "no") == 0) {
+ tls->conn_reuse = 0;
+ } else {
+ msg_warn("%s: attribute \"%s\" has bad value: \"%s\"",
+ WHERE, name, val);
+ INVALID_RETURN(tls->why, site_level);
+ }
+ }
msg_warn("%s: invalid attribute name: \"%s\"", WHERE, name);
INVALID_RETURN(tls->why, site_level);
}
SMTP_TLS_POLICY *tls = (SMTP_TLS_POLICY *) mymalloc(sizeof(*tls));
smtp_tls_policy_init(tls, dsb_create());
+ tls->conn_reuse = var_smtp_tls_conn_reuse;
/*
* Compute the per-site TLS enforcement level. For compatibility with the
/*
* TLS initialization status.
*/
+#ifndef USE_TLSPROXY
static TLS_APPL_STATE *smtpd_tls_ctx;
static int ask_client_cert;
+#endif /* USE_TLSPROXY */
#endif
/*
#define PROXY_OPEN_FLAGS \
(TLS_PROXY_FLAG_ROLE_SERVER | TLS_PROXY_FLAG_SEND_CONTEXT)
- state->tlsproxy = tls_proxy_open(var_tlsproxy_service, PROXY_OPEN_FLAGS,
- state->client, state->addr,
- state->port, var_smtpd_tmout);
+ state->tlsproxy =
+ tls_proxy_legacy_open(var_tlsproxy_service, PROXY_OPEN_FLAGS,
+ state->client, state->addr,
+ state->port, var_smtpd_tmout,
+ state->service);
if (state->tlsproxy == 0) {
state->error_mask |= MAIL_ERROR_SOFTWARE;
/* RFC 3207 Section 4. */
if (SMTPD_STAND_ALONE(state) == 0 && var_smtpd_tls_wrappermode) {
#ifdef USE_TLSPROXY
/* We garbage-collect the VSTREAM in smtpd_state_reset() */
- state->tlsproxy = tls_proxy_open(var_tlsproxy_service,
- PROXY_OPEN_FLAGS,
- state->client, state->addr,
- state->port, var_smtpd_tmout);
+ state->tlsproxy =
+ tls_proxy_legacy_open(var_tlsproxy_service,
+ PROXY_OPEN_FLAGS,
+ state->client, state->addr,
+ state->port, var_smtpd_tmout,
+ state->service);
if (state->tlsproxy == 0) {
msg_warn("Wrapper-mode request dropped from %s for service %s."
" TLS context initialization failed. For details see"
tls_rsa.c tls_verify.c tls_dane.c tls_certkey.c tls_session.c \
tls_client.c tls_server.c tls_scache.c tls_mgr.c tls_seed.c \
tls_level.c \
- tls_proxy_clnt.c tls_proxy_print.c tls_proxy_scan.c
+ tls_proxy_clnt.c tls_proxy_context_print.c tls_proxy_context_scan.c \
+ tls_proxy_client_init_print.c tls_proxy_client_init_scan.c \
+ tls_proxy_server_init_print.c tls_proxy_server_init_scan.c \
+ tls_proxy_client_start_print.c tls_proxy_client_start_scan.c \
+ tls_proxy_server_start_print.c tls_proxy_server_start_scan.c
OBJS = tls_prng_dev.o tls_prng_egd.o tls_prng_file.o tls_fprint.o \
tls_prng_exch.o tls_stream.o tls_bio_ops.o tls_misc.o tls_dh.o \
tls_rsa.o tls_verify.o tls_dane.o tls_certkey.o tls_session.o \
tls_client.o tls_server.o tls_scache.o tls_mgr.o tls_seed.o \
tls_level.o \
- tls_proxy_clnt.o tls_proxy_print.o tls_proxy_scan.o
+ tls_proxy_clnt.o tls_proxy_context_print.o tls_proxy_context_scan.o \
+ tls_proxy_client_print.o tls_proxy_client_scan.o \
+ tls_proxy_server_print.o tls_proxy_server_scan.o
HDRS = tls.h tls_prng.h tls_scache.h tls_mgr.h tls_proxy.h
TESTSRC =
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
tls_prng_file.o: ../../include/sys_defs.h
tls_prng_file.o: tls_prng.h
tls_prng_file.o: tls_prng_file.c
+tls_proxy_client_print.o: ../../include/argv.h
+tls_proxy_client_print.o: ../../include/argv_attr.h
+tls_proxy_client_print.o: ../../include/attr.h
+tls_proxy_client_print.o: ../../include/check_arg.h
+tls_proxy_client_print.o: ../../include/dns.h
+tls_proxy_client_print.o: ../../include/htable.h
+tls_proxy_client_print.o: ../../include/msg.h
+tls_proxy_client_print.o: ../../include/myaddrinfo.h
+tls_proxy_client_print.o: ../../include/mymalloc.h
+tls_proxy_client_print.o: ../../include/name_code.h
+tls_proxy_client_print.o: ../../include/name_mask.h
+tls_proxy_client_print.o: ../../include/nvtable.h
+tls_proxy_client_print.o: ../../include/sock_addr.h
+tls_proxy_client_print.o: ../../include/sys_defs.h
+tls_proxy_client_print.o: ../../include/vbuf.h
+tls_proxy_client_print.o: ../../include/vstream.h
+tls_proxy_client_print.o: ../../include/vstring.h
+tls_proxy_client_print.o: tls.h
+tls_proxy_client_print.o: tls_proxy.h
+tls_proxy_client_print.o: tls_proxy_client_print.c
+tls_proxy_client_scan.o: ../../include/argv.h
+tls_proxy_client_scan.o: ../../include/argv_attr.h
+tls_proxy_client_scan.o: ../../include/attr.h
+tls_proxy_client_scan.o: ../../include/check_arg.h
+tls_proxy_client_scan.o: ../../include/dns.h
+tls_proxy_client_scan.o: ../../include/htable.h
+tls_proxy_client_scan.o: ../../include/msg.h
+tls_proxy_client_scan.o: ../../include/myaddrinfo.h
+tls_proxy_client_scan.o: ../../include/mymalloc.h
+tls_proxy_client_scan.o: ../../include/name_code.h
+tls_proxy_client_scan.o: ../../include/name_mask.h
+tls_proxy_client_scan.o: ../../include/nvtable.h
+tls_proxy_client_scan.o: ../../include/sock_addr.h
+tls_proxy_client_scan.o: ../../include/sys_defs.h
+tls_proxy_client_scan.o: ../../include/vbuf.h
+tls_proxy_client_scan.o: ../../include/vstream.h
+tls_proxy_client_scan.o: ../../include/vstring.h
+tls_proxy_client_scan.o: tls.h
+tls_proxy_client_scan.o: tls_proxy.h
+tls_proxy_client_scan.o: tls_proxy_client_scan.c
tls_proxy_clnt.o: ../../include/argv.h
tls_proxy_clnt.o: ../../include/attr.h
tls_proxy_clnt.o: ../../include/check_arg.h
tls_proxy_clnt.o: tls.h
tls_proxy_clnt.o: tls_proxy.h
tls_proxy_clnt.o: tls_proxy_clnt.c
-tls_proxy_print.o: ../../include/argv.h
-tls_proxy_print.o: ../../include/attr.h
-tls_proxy_print.o: ../../include/check_arg.h
-tls_proxy_print.o: ../../include/dns.h
-tls_proxy_print.o: ../../include/htable.h
-tls_proxy_print.o: ../../include/iostuff.h
-tls_proxy_print.o: ../../include/mail_proto.h
-tls_proxy_print.o: ../../include/myaddrinfo.h
-tls_proxy_print.o: ../../include/mymalloc.h
-tls_proxy_print.o: ../../include/name_code.h
-tls_proxy_print.o: ../../include/name_mask.h
-tls_proxy_print.o: ../../include/nvtable.h
-tls_proxy_print.o: ../../include/sock_addr.h
-tls_proxy_print.o: ../../include/sys_defs.h
-tls_proxy_print.o: ../../include/vbuf.h
-tls_proxy_print.o: ../../include/vstream.h
-tls_proxy_print.o: ../../include/vstring.h
-tls_proxy_print.o: tls.h
-tls_proxy_print.o: tls_proxy.h
-tls_proxy_print.o: tls_proxy_print.c
-tls_proxy_scan.o: ../../include/argv.h
-tls_proxy_scan.o: ../../include/attr.h
-tls_proxy_scan.o: ../../include/check_arg.h
-tls_proxy_scan.o: ../../include/dns.h
-tls_proxy_scan.o: ../../include/htable.h
-tls_proxy_scan.o: ../../include/iostuff.h
-tls_proxy_scan.o: ../../include/mail_proto.h
-tls_proxy_scan.o: ../../include/myaddrinfo.h
-tls_proxy_scan.o: ../../include/mymalloc.h
-tls_proxy_scan.o: ../../include/name_code.h
-tls_proxy_scan.o: ../../include/name_mask.h
-tls_proxy_scan.o: ../../include/nvtable.h
-tls_proxy_scan.o: ../../include/sock_addr.h
-tls_proxy_scan.o: ../../include/sys_defs.h
-tls_proxy_scan.o: ../../include/vbuf.h
-tls_proxy_scan.o: ../../include/vstream.h
-tls_proxy_scan.o: ../../include/vstring.h
-tls_proxy_scan.o: tls.h
-tls_proxy_scan.o: tls_proxy.h
-tls_proxy_scan.o: tls_proxy_scan.c
+tls_proxy_context_print.o: ../../include/argv.h
+tls_proxy_context_print.o: ../../include/attr.h
+tls_proxy_context_print.o: ../../include/check_arg.h
+tls_proxy_context_print.o: ../../include/dns.h
+tls_proxy_context_print.o: ../../include/htable.h
+tls_proxy_context_print.o: ../../include/myaddrinfo.h
+tls_proxy_context_print.o: ../../include/mymalloc.h
+tls_proxy_context_print.o: ../../include/name_code.h
+tls_proxy_context_print.o: ../../include/name_mask.h
+tls_proxy_context_print.o: ../../include/nvtable.h
+tls_proxy_context_print.o: ../../include/sock_addr.h
+tls_proxy_context_print.o: ../../include/sys_defs.h
+tls_proxy_context_print.o: ../../include/vbuf.h
+tls_proxy_context_print.o: ../../include/vstream.h
+tls_proxy_context_print.o: ../../include/vstring.h
+tls_proxy_context_print.o: tls.h
+tls_proxy_context_print.o: tls_proxy.h
+tls_proxy_context_print.o: tls_proxy_context_print.c
+tls_proxy_context_scan.o: ../../include/argv.h
+tls_proxy_context_scan.o: ../../include/attr.h
+tls_proxy_context_scan.o: ../../include/check_arg.h
+tls_proxy_context_scan.o: ../../include/dns.h
+tls_proxy_context_scan.o: ../../include/htable.h
+tls_proxy_context_scan.o: ../../include/msg.h
+tls_proxy_context_scan.o: ../../include/myaddrinfo.h
+tls_proxy_context_scan.o: ../../include/mymalloc.h
+tls_proxy_context_scan.o: ../../include/name_code.h
+tls_proxy_context_scan.o: ../../include/name_mask.h
+tls_proxy_context_scan.o: ../../include/nvtable.h
+tls_proxy_context_scan.o: ../../include/sock_addr.h
+tls_proxy_context_scan.o: ../../include/sys_defs.h
+tls_proxy_context_scan.o: ../../include/vbuf.h
+tls_proxy_context_scan.o: ../../include/vstream.h
+tls_proxy_context_scan.o: ../../include/vstring.h
+tls_proxy_context_scan.o: tls.h
+tls_proxy_context_scan.o: tls_proxy.h
+tls_proxy_context_scan.o: tls_proxy_context_scan.c
+tls_proxy_server_print.o: ../../include/argv.h
+tls_proxy_server_print.o: ../../include/attr.h
+tls_proxy_server_print.o: ../../include/check_arg.h
+tls_proxy_server_print.o: ../../include/dns.h
+tls_proxy_server_print.o: ../../include/htable.h
+tls_proxy_server_print.o: ../../include/myaddrinfo.h
+tls_proxy_server_print.o: ../../include/mymalloc.h
+tls_proxy_server_print.o: ../../include/name_code.h
+tls_proxy_server_print.o: ../../include/name_mask.h
+tls_proxy_server_print.o: ../../include/nvtable.h
+tls_proxy_server_print.o: ../../include/sock_addr.h
+tls_proxy_server_print.o: ../../include/sys_defs.h
+tls_proxy_server_print.o: ../../include/vbuf.h
+tls_proxy_server_print.o: ../../include/vstream.h
+tls_proxy_server_print.o: ../../include/vstring.h
+tls_proxy_server_print.o: tls.h
+tls_proxy_server_print.o: tls_proxy.h
+tls_proxy_server_print.o: tls_proxy_server_print.c
+tls_proxy_server_scan.o: ../../include/argv.h
+tls_proxy_server_scan.o: ../../include/attr.h
+tls_proxy_server_scan.o: ../../include/check_arg.h
+tls_proxy_server_scan.o: ../../include/dns.h
+tls_proxy_server_scan.o: ../../include/htable.h
+tls_proxy_server_scan.o: ../../include/myaddrinfo.h
+tls_proxy_server_scan.o: ../../include/mymalloc.h
+tls_proxy_server_scan.o: ../../include/name_code.h
+tls_proxy_server_scan.o: ../../include/name_mask.h
+tls_proxy_server_scan.o: ../../include/nvtable.h
+tls_proxy_server_scan.o: ../../include/sock_addr.h
+tls_proxy_server_scan.o: ../../include/sys_defs.h
+tls_proxy_server_scan.o: ../../include/vbuf.h
+tls_proxy_server_scan.o: ../../include/vstream.h
+tls_proxy_server_scan.o: ../../include/vstring.h
+tls_proxy_server_scan.o: tls.h
+tls_proxy_server_scan.o: tls_proxy.h
+tls_proxy_server_scan.o: tls_proxy_server_scan.c
tls_rsa.o: ../../include/argv.h
tls_rsa.o: ../../include/check_arg.h
tls_rsa.o: ../../include/dns.h
typedef struct {
TLS_APPL_STATE *ctx;
VSTREAM *stream;
+ int fd; /* Event-driven file descriptor */
int timeout;
int tls_level; /* Security level */
const char *nexthop; /* destination domain */
extern TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *);
extern TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *);
+extern TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *,
+ const TLS_CLIENT_START_PROPS *);
#define tls_client_stop(ctx, stream, timeout, failure, TLScontext) \
tls_session_stop(ctx, (stream), (timeout), (failure), (TLScontext))
((props)->a12), ((props)->a13), (props)))
#define TLS_CLIENT_START(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, \
- a10, a11, a12, a13, a14, a15) \
+ a10, a11, a12, a13, a14, a15, a16) \
tls_client_start((((props)->a1), ((props)->a2), ((props)->a3), \
((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \
((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \
- ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15), (props)))
+ ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15), \
+ ((props)->a16), (props)))
/*
* tls_server.c
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*
/* Victor Duchovni
/* Morgan Stanley
/*--*/
/* TLS_SESS_STATE *tls_client_start(start_props)
/* const TLS_CLIENT_START_PROPS *start_props;
/*
+/* TLS_SESS_STATE *tls_client_post_connect(TLScontext, start_props)
+/* TLS_SESS_STATE *TLScontext;
+/* const TLS_CLIENT_START_PROPS *start_props;
+/*
/* void tls_client_stop(app_ctx, stream, failure, TLScontext)
/* TLS_APPL_STATE *app_ctx;
/* VSTREAM *stream;
/* the fingerprint of the certificate.
/* .PP
/* If no peer certificate is presented the peer_status is set to 0.
+/* EVENT_DRIVEN APPLICATIONS
+/* .ad
+/* .fi
+/* Event-driven programs manage multiple I/O channels. Such
+/* programs cannot use the synchronous VSTREAM-over-TLS
+/* implementation that the TLS library historically provides,
+/* including tls_client_stop() and the underlying tls_stream(3)
+/* and tls_bio_ops(3) routines.
+/*
+/* With the current TLS library implementation, this means
+/* that an event-driven application is responsible for calling
+/* and retrying SSL_connect(), SSL_read(), SSL_write() and
+/* SSL_shutdown().
+/*
+/* To maintain control over TLS I/O, an event-driven client
+/* invokes tls_client_start() with a null VSTREAM argument and
+/* with an fd argument that specifies the I/O file descriptor.
+/* Then, tls_client_start() performs all the necessary
+/* preparations before the TLS handshake and returns a partially
+/* populated TLS context. The event-driven application is then
+/* responsible for invoking SSL_connect(), and if successful,
+/* for invoking tls_client_post_connect() to finish the work
+/* that was started by tls_client_start(). In case of unrecoverable
+/* failure, tls_client_post_connect() destroys the TLS context
+/* and returns a null pointer value.
/* LICENSE
/* .ad
/* .fi
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*
/* Victor Duchovni
/* Morgan Stanley
/*--*/
int protomask;
const char *cipher_list;
SSL_SESSION *session = 0;
- SSL_CIPHER_const SSL_CIPHER *cipher;
- X509 *peercert;
TLS_SESS_STATE *TLScontext;
TLS_APPL_STATE *app_ctx = props->ctx;
char *myserverid;
/*
* Connect the SSL connection with the network socket.
*/
- if (SSL_set_fd(TLScontext->con, vstream_fileno(props->stream)) != 1) {
+ if (SSL_set_fd(TLScontext->con, props->stream == 0 ? props->fd :
+ vstream_fileno(props->stream)) != 1) {
msg_info("SSL_set_fd error to %s", props->namaddr);
tls_print_errors();
uncache_session(app_ctx->ssl_ctx, TLScontext);
return (0);
}
- /*
- * Turn on non-blocking I/O so that we can enforce timeouts on network
- * I/O.
- */
- non_blocking(vstream_fileno(props->stream), NON_BLOCKING);
-
/*
* If the debug level selected is high enough, all of the data is dumped:
* TLS_LOG_TLSPKTS will dump the SSL negotiation, TLS_LOG_ALLPKTS will
tls_dane_set_callback(app_ctx->ssl_ctx, TLScontext);
+ /*
+ * If we don't trigger the handshake in the library, leave control over
+ * SSL_connect/read/write/etc with the application.
+ */
+ if (props->stream == 0)
+ return (TLScontext);
+
+ /*
+ * Turn on non-blocking I/O so that we can enforce timeouts on network
+ * I/O.
+ */
+ non_blocking(vstream_fileno(props->stream), NON_BLOCKING);
+
/*
* Start TLS negotiations. This process is a black box that invokes our
* call-backs for certificate verification.
tls_free_context(TLScontext);
return (0);
}
+ return (tls_client_post_connect(TLScontext, props));
+}
+
+/* tls_client_post_connect - post-handshake processing */
+
+TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *TLScontext,
+ const TLS_CLIENT_START_PROPS *props)
+{
+ SSL_CIPHER_const SSL_CIPHER *cipher;
+ X509 *peercert;
+
/* Turn off packet dump if only dumping the handshake */
- if ((log_mask & TLS_LOG_ALLPKTS) == 0)
+ if ((TLScontext->log_mask & TLS_LOG_ALLPKTS) == 0)
BIO_set_callback(SSL_get_rbio(TLScontext->con), 0);
/*
* session was negotiated.
*/
TLScontext->session_reused = SSL_session_reused(TLScontext->con);
- if ((log_mask & TLS_LOG_CACHE) && TLScontext->session_reused)
+ if ((TLScontext->log_mask & TLS_LOG_CACHE) && TLScontext->session_reused)
msg_info("%s: Reusing old session", TLScontext->namaddr);
/*
* The TLS engine is active. Switch to the tls_timed_read/write()
* functions and make the TLScontext available to those functions.
*/
- tls_stream_start(props->stream, TLScontext);
+ if (TLScontext->stream != 0)
+ tls_stream_start(props->stream, TLScontext);
/*
* Fully secured only if trusted, matched and not insecure like halfdane.
/*
* All the key facts in a single log entry.
*/
- if (log_mask & TLS_LOG_SUMMARY)
+ if (TLScontext->log_mask & TLS_LOG_SUMMARY)
msg_info("%s TLS connection established to %s: %s with cipher %s "
"(%d/%d bits)",
!TLS_CERT_IS_PRESENT(TLScontext) ? "Anonymous" :
return (matched);
}
-/* push_ext - push extension onto certificate's stack, else free it */
-
-static int push_ext(X509 *cert, X509_EXTENSION *ext)
-{
- if (ext) {
- if (X509_add_ext(cert, ext, -1))
- return 1;
- X509_EXTENSION_free(ext);
- }
- return 0;
-}
-
/* add_ext - add simple extension (no config section references) */
static int add_ext(X509 *issuer, X509 *subject, int ext_nid, char *ext_val)
{
+ int ret = 0;
X509V3_CTX v3ctx;
+ X509_EXTENSION *ext;
X509V3_set_ctx(&v3ctx, issuer, subject, 0, 0, 0);
- return push_ext(subject, X509V3_EXT_conf_nid(0, &v3ctx, ext_nid, ext_val));
+ if ((ext = X509V3_EXT_conf_nid(0, &v3ctx, ext_nid, ext_val)) != 0) {
+ ret = X509_add_ext(subject, ext, -1);
+ X509_EXTENSION_free(ext);
+ }
+ return ret;
}
/* set_serial - set serial number to match akid or use subject's plus 1 */
#ifdef USE_TLS
+#define tls_proxy_legacy_open(service, flags, peer_stream, peer_addr, \
+ peer_port, timeout, serverid) \
+ tls_proxy_open((service), (flags), (peer_stream), (peer_addr), \
+ (peer_port), (timeout), (timeout), (serverid), (void *) 0, (void *) 0)
+
extern VSTREAM *tls_proxy_open(const char *, int, VSTREAM *, const char *,
- const char *, int);
+ const char *, int, int, const char *,
+ void *, void *);
+
+#define TLS_PROXY_CLIENT_INIT_PROPS(props, a1, a2, a3, a4, a5, a6, a7, a8, \
+ a9, a10, a11, a12, a13) \
+ (((props)->a1), ((props)->a2), ((props)->a3), \
+ ((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \
+ ((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \
+ ((props)->a12), ((props)->a13))
+
+#define TLS_PROXY_CLIENT_START_PROPS(props, a1, a2, a3, a4, a5, a6, a7, a8, \
+ a9, a10, a11, a12, a13) \
+ (((props)->a1), ((props)->a2), ((props)->a3), \
+ ((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \
+ ((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \
+ ((props)->a12), ((props)->a13))
+
extern TLS_SESS_STATE *tls_proxy_context_receive(VSTREAM *);
extern void tls_proxy_context_free(TLS_SESS_STATE *);
extern int tls_proxy_context_print(ATTR_PRINT_MASTER_FN, VSTREAM *, int, void *);
extern int tls_proxy_context_scan(ATTR_SCAN_MASTER_FN, VSTREAM *, int, void *);
+extern int tls_proxy_client_init_print(ATTR_PRINT_MASTER_FN, VSTREAM *, int, void *);
+extern int tls_proxy_client_init_scan(ATTR_SCAN_MASTER_FN, VSTREAM *, int, void *);
+extern void tls_proxy_client_init_free(TLS_CLIENT_INIT_PROPS *);
+extern char *tls_proxy_client_init_to_string(VSTRING *, TLS_CLIENT_INIT_PROPS *);
+
+extern int tls_proxy_client_start_print(ATTR_PRINT_MASTER_FN, VSTREAM *, int, void *);
+extern int tls_proxy_client_start_scan(ATTR_SCAN_MASTER_FN, VSTREAM *, int, void *);
+extern void tls_proxy_client_start_free(TLS_CLIENT_START_PROPS *);
+
+extern int tls_proxy_server_init_print(ATTR_PRINT_MASTER_FN, VSTREAM *, int, void *);
+extern int tls_proxy_server_init_scan(ATTR_SCAN_MASTER_FN, VSTREAM *, int, void *);
+extern void tls_proxy_server_init_free(TLS_SERVER_INIT_PROPS *);
+
+extern int tls_proxy_server_start_print(ATTR_PRINT_MASTER_FN, VSTREAM *, int, void *);
+extern int tls_proxy_server_start_scan(ATTR_SCAN_MASTER_FN, VSTREAM *, int, void *);
+
+extern void tls_proxy_server_start_free(TLS_SERVER_START_PROPS *);
+
+#endif /* USE_TLS */
+
+ /*
+ * TLSPROXY attributes, unconditionally exposed.
+ */
+#define TLS_ATTR_REMOTE_ENDPT "remote_endpoint" /* name[addr]:port */
+#define TLS_ATTR_FLAGS "flags"
+#define TLS_ATTR_TIMEOUT "timeout"
+#define TLS_ATTR_SERVERID "serverid"
+
+#ifdef USE_TLS
+
+ /*
+ * Misc attributes.
+ */
+#define TLS_ATTR_COUNT "count"
+
+ /*
+ * TLS_SESS_STATE attributes.
+ */
+#define TLS_ATTR_PEER_CN "peer_CN"
+#define TLS_ATTR_ISSUER_CN "issuer_CN"
+#define TLS_ATTR_PEER_CERT_FPT "peer_fingerprint"
+#define TLS_ATTR_PEER_PKEY_FPT "peer_pubkey_fingerprint"
+#define TLS_ATTR_PEER_STATUS "peer_status"
+#define TLS_ATTR_CIPHER_PROTOCOL "cipher_protocol"
+#define TLS_ATTR_CIPHER_NAME "cipher_name"
+#define TLS_ATTR_CIPHER_USEBITS "cipher_usebits"
+#define TLS_ATTR_CIPHER_ALGBITS "cipher_algbits"
+
+ /*
+ * TLS_SERVER_INIT_PROPS attributes.
+ */
+#define TLS_ATTR_LOG_PARAM "log_param"
+#define TLS_ATTR_LOG_LEVEL "log_level"
+#define TLS_ATTR_VERIFYDEPTH "verifydepth"
+#define TLS_ATTR_CACHE_TYPE "cache_type"
+#define TLS_ATTR_SET_SESSID "set_sessid"
+#define TLS_ATTR_CERT_FILE "cert_file"
+#define TLS_ATTR_KEY_FILE "key_file"
+#define TLS_ATTR_DCERT_FILE "dcert_file"
+#define TLS_ATTR_DKEY_FILE "dkey_file"
+#define TLS_ATTR_ECCERT_FILE "eccert_file"
+#define TLS_ATTR_ECKEY_FILE "eckey_file"
+#define TLS_ATTR_CAFILE "CAfile"
+#define TLS_ATTR_CAPATH "CApath"
+#define TLS_ATTR_PROTOCOLS "protocols"
+#define TLS_ATTR_EECDH_GRADE "eecdh_grade"
+#define TLS_ATTR_DH1K_PARAM_FILE "dh1024_param_file"
+#define TLS_ATTR_DH512_PARAM_FILE "dh512_param_file"
+#define TLS_ATTR_ASK_CCERT "ask_ccert"
+#define TLS_ATTR_MDALG "mdalg"
+
+ /*
+ * TLS_SERVER_START_PROPS attributes.
+ */
+#define TLS_ATTR_TIMEOUT "timeout"
+#define TLS_ATTR_REQUIRECERT "requirecert"
+#define TLS_ATTR_SERVERID "serverid"
+#define TLS_ATTR_NAMADDR "namaddr"
+#define TLS_ATTR_CIPHER_GRADE "cipher_grade"
+#define TLS_ATTR_CIPHER_EXCLUSIONS "cipher_exclusions"
+#define TLS_ATTR_MDALG "mdalg"
+
+ /*
+ * TLS_CLIENT_INIT_PROPS attributes.
+ */
+#define TLS_ATTR_LOG_PARAM "log_param"
+#define TLS_ATTR_LOG_LEVEL "log_level"
+#define TLS_ATTR_VERIFYDEPTH "verifydepth"
+#define TLS_ATTR_CACHE_TYPE "cache_type"
+#define TLS_ATTR_CERT_FILE "cert_file"
+#define TLS_ATTR_KEY_FILE "key_file"
+#define TLS_ATTR_DCERT_FILE "dcert_file"
+#define TLS_ATTR_DKEY_FILE "dkey_file"
+#define TLS_ATTR_ECCERT_FILE "eccert_file"
+#define TLS_ATTR_ECKEY_FILE "eckey_file"
+#define TLS_ATTR_CAFILE "CAfile"
+#define TLS_ATTR_CAPATH "CApath"
+#define TLS_ATTR_MDALG "mdalg"
+
+ /*
+ * TLS_CLIENT_START_PROPS attributes.
+ */
+#define TLS_ATTR_TIMEOUT "timeout"
+#define TLS_ATTR_TLS_LEVEL "tls_level"
+#define TLS_ATTR_NEXTHOP "nexthop"
+#define TLS_ATTR_HOST "host"
+#define TLS_ATTR_NAMADDR "namaddr"
+#define TLS_ATTR_SERVERID "serverid"
+#define TLS_ATTR_HELO "helo"
+#define TLS_ATTR_PROTOCOLS "protocols"
+#define TLS_ATTR_CIPHER_GRADE "cipher_grade"
+#define TLS_ATTR_CIPHER_EXCLUSIONS "cipher_exclusions"
+#define TLS_ATTR_MATCHARGV "matchargv"
+#define TLS_ATTR_MDALG "mdalg"
+#define TLS_ATTR_DANE "dane"
+
+ /*
+ * TLS_TLSA attributes.
+ */
+#define TLS_ATTR_MDALG "mdalg"
+#define TLS_ATTR_CERTS "certs"
+#define TLS_ATTR_PKEYS "pkeys"
+
+ /*
+ * TLS_CERTS attributes.
+ */
+#define TLS_ATTR_CERT "cert"
+
+ /*
+ * TLS_PKEYS attributes.
+ */
+#define TLS_ATTR_PKEY "pkey"
+
+ /*
+ * TLS_DANE attributes.
+ */
+#define TLS_ATTR_TA "ta"
+#define TLS_ATTR_EE "ee"
+#define TLS_ATTR_CERTS "certs"
+#define TLS_ATTR_PKEYS "pkeys"
+#define TLS_ATTR_DOMAIN "domain"
+#define TLS_ATTR_FLAGS "flags"
+#define TLS_ATTR_EXP "exp"
+
#endif
/* LICENSE
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
#endif
--- /dev/null
+/*++
+/* NAME
+/* tls_proxy_client_print 3
+/* SUMMARY
+/* write TLS_CLIENT_XXX structures to stream
+/* SYNOPSIS
+/* #include <tls_proxy.h>
+/*
+/* int tls_proxy_client_init_print(print_fn, stream, flags, ptr)
+/* ATTR_PRINT_MASTER_FN print_fn;
+/* VSTREAM *stream;
+/* int flags;
+/* void *ptr;
+/*
+/* int tls_proxy_client_start_print(print_fn, stream, flags, ptr)
+/* ATTR_PRINT_MASTER_FN print_fn;
+/* VSTREAM *stream;
+/* int flags;
+/* void *ptr;
+/* DESCRIPTION
+/* tls_proxy_client_init_print() writes a full TLS_CLIENT_INIT_PROPS
+/* structure to the named stream using the specified attribute
+/* print routine. tls_proxy_client_init_print() is meant to
+/* be passed as a call-back to attr_print(), thusly:
+/*
+/* SEND_ATTR_FUNC(tls_proxy_client_init_print, (void *) init_props), ...
+/*
+/* tls_proxy_client_start_print() writes a TLS_CLIENT_START_PROPS
+/* structure, without stream or file descriptor members, to
+/* the named stream using the specified attribute print routine.
+/* tls_proxy_client_start_print() is meant to be passed as a
+/* call-back to attr_print(), thusly:
+/*
+/* SEND_ATTR_FUNC(tls_proxy_client_start_print, (void *) start_props), ...
+/* DIAGNOSTICS
+/* Fatal: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+#ifdef USE_TLS
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library */
+
+#include <argv_attr.h>
+#include <attr.h>
+#include <msg.h>
+
+/* TLS library. */
+
+#include <tls.h>
+#include <tls_proxy.h>
+
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* tls_proxy_client_init_print - send TLS_CLIENT_INIT_PROPS over stream */
+
+int tls_proxy_client_init_print(ATTR_PRINT_MASTER_FN print_fn, VSTREAM *fp,
+ int flags, void *ptr)
+{
+ TLS_CLIENT_INIT_PROPS *props = (TLS_CLIENT_INIT_PROPS *) ptr;
+ int ret;
+
+ if (msg_verbose)
+ msg_info("begin tls_proxy_client_init_print");
+
+#define STRING_OR_EMPTY(s) ((s) ? (s) : "")
+
+ ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+ SEND_ATTR_STR(TLS_ATTR_LOG_PARAM,
+ STRING_OR_EMPTY(props->log_param)),
+ SEND_ATTR_STR(TLS_ATTR_LOG_LEVEL,
+ STRING_OR_EMPTY(props->log_level)),
+ SEND_ATTR_INT(TLS_ATTR_VERIFYDEPTH, props->verifydepth),
+ SEND_ATTR_STR(TLS_ATTR_CACHE_TYPE,
+ STRING_OR_EMPTY(props->cache_type)),
+ SEND_ATTR_STR(TLS_ATTR_CERT_FILE,
+ STRING_OR_EMPTY(props->cert_file)),
+ SEND_ATTR_STR(TLS_ATTR_KEY_FILE,
+ STRING_OR_EMPTY(props->key_file)),
+ SEND_ATTR_STR(TLS_ATTR_DCERT_FILE,
+ STRING_OR_EMPTY(props->dcert_file)),
+ SEND_ATTR_STR(TLS_ATTR_DKEY_FILE,
+ STRING_OR_EMPTY(props->dkey_file)),
+ SEND_ATTR_STR(TLS_ATTR_ECCERT_FILE,
+ STRING_OR_EMPTY(props->eccert_file)),
+ SEND_ATTR_STR(TLS_ATTR_ECKEY_FILE,
+ STRING_OR_EMPTY(props->eckey_file)),
+ SEND_ATTR_STR(TLS_ATTR_CAFILE,
+ STRING_OR_EMPTY(props->CAfile)),
+ SEND_ATTR_STR(TLS_ATTR_CAPATH,
+ STRING_OR_EMPTY(props->CApath)),
+ SEND_ATTR_STR(TLS_ATTR_MDALG,
+ STRING_OR_EMPTY(props->mdalg)),
+ ATTR_TYPE_END);
+ /* Do not flush the stream. */
+ if (msg_verbose)
+ msg_info("tls_proxy_client_init_print ret=%d", ret);
+ return (ret);
+}
+
+/* tls_proxy_client_certs_print - send x509 certificates over stream */
+
+static int tls_proxy_client_certs_print(ATTR_PRINT_MASTER_FN print_fn,
+ VSTREAM *fp, int flags, void *ptr)
+{
+ TLS_CERTS *tls_certs = (TLS_CERTS *) ptr;
+ TLS_CERTS *tp;
+ int count;
+ int ret;
+
+ for (tp = tls_certs, count = 0; tp != 0; tp = tp->next, count++)
+ /* void */ ;
+ if (msg_verbose)
+ msg_info("tls_proxy_client_certs_print count=%d", count);
+
+ ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+ SEND_ATTR_INT(TLS_ATTR_COUNT, count),
+ ATTR_TYPE_END);
+
+ if (ret == 0 && count > 0) {
+ VSTRING *buf = vstring_alloc(100);
+ int n;
+
+ for (tp = tls_certs, n = 0; ret == 0 && n < count; tp = tp->next, n++) {
+ size_t len = i2d_X509(tp->cert, (unsigned char **) 0);
+ unsigned char *bp;
+
+ VSTRING_RESET(buf);
+ VSTRING_SPACE(buf, len);
+ bp = (unsigned char *) STR(buf);
+ i2d_X509(tp->cert, &bp);
+ if ((char *) bp - STR(buf) != len)
+ msg_panic("i2d_X509 failed to encode certificate");
+ ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+ SEND_ATTR_DATA(TLS_ATTR_CERT, LEN(buf), STR(buf)),
+ ATTR_TYPE_END);
+ }
+ vstring_free(buf);
+ }
+ /* Do not flush the stream. */
+ if (msg_verbose)
+ msg_info("tls_proxy_client_certs_print ret=%d", count);
+ return (ret);
+}
+
+/* tls_proxy_client_pkeys_print - send public keys over stream */
+
+static int tls_proxy_client_pkeys_print(ATTR_PRINT_MASTER_FN print_fn,
+ VSTREAM *fp, int flags, void *ptr)
+{
+ TLS_PKEYS *tls_pkeys = (TLS_PKEYS *) ptr;
+ TLS_PKEYS *tp;
+ int count;
+ int ret;
+
+ for (tp = tls_pkeys, count = 0; tp != 0; tp = tp->next, count++)
+ /* void */ ;
+ if (msg_verbose)
+ msg_info("tls_proxy_client_pkeys_print count=%d", count);
+
+ ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+ SEND_ATTR_INT(TLS_ATTR_COUNT, count),
+ ATTR_TYPE_END);
+
+ if (ret == 0 && count > 0) {
+ VSTRING *buf = vstring_alloc(100);
+ int n;
+
+ for (tp = tls_pkeys, n = 0; ret == 0 && n < count; tp = tp->next, n++) {
+ size_t len = i2d_PUBKEY(tp->pkey, (unsigned char **) 0);
+ unsigned char *bp;
+
+ VSTRING_RESET(buf);
+ VSTRING_SPACE(buf, len);
+ bp = (unsigned char *) STR(buf);
+ i2d_PUBKEY(tp->pkey, &bp);
+ if ((char *) bp - STR(buf) != len)
+ msg_panic("i2d_PUBKEY failed to encode public key");
+ ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+ SEND_ATTR_DATA(TLS_ATTR_PKEY, LEN(buf), STR(buf)),
+ ATTR_TYPE_END);
+ }
+ vstring_free(buf);
+ }
+ /* Do not flush the stream. */
+ if (msg_verbose)
+ msg_info("tls_proxy_client_pkeys_print ret=%d", count);
+ return (ret);
+}
+
+/* tls_proxy_client_tlsa_print - send TLS_TLSA over stream */
+
+static int tls_proxy_client_tlsa_print(ATTR_PRINT_MASTER_FN print_fn,
+ VSTREAM *fp, int flags, void *ptr)
+{
+ TLS_TLSA *tls_tlsa = (TLS_TLSA *) ptr;
+ TLS_TLSA *tp;
+ int count;
+ int ret;
+
+ for (tp = tls_tlsa, count = 0; tp != 0; tp = tp->next, count++)
+ /* void */ ;
+ if (msg_verbose)
+ msg_info("tls_proxy_client_tlsa_print count=%d", count);
+
+ ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+ SEND_ATTR_INT(TLS_ATTR_COUNT, count),
+ ATTR_TYPE_END);
+
+ if (ret == 0 && count > 0) {
+ int n;
+
+ for (tp = tls_tlsa, n = 0; ret == 0 && n < count; tp = tp->next, n++) {
+ ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+ SEND_ATTR_STR(TLS_ATTR_MDALG, tp->mdalg),
+ SEND_ATTR_FUNC(argv_attr_print,
+ (void *) tp->certs),
+ SEND_ATTR_FUNC(argv_attr_print,
+ (void *) tp->pkeys),
+ ATTR_TYPE_END);
+ }
+ }
+ /* Do not flush the stream. */
+ if (msg_verbose)
+ msg_info("tls_proxy_client_tlsa_print ret=%d", count);
+ return (ret);
+}
+
+/* tls_proxy_client_dane_print - send TLS_DANE over stream */
+
+static int tls_proxy_client_dane_print(ATTR_PRINT_MASTER_FN print_fn,
+ VSTREAM *fp, int flags, void *ptr)
+{
+ TLS_DANE *dane = (TLS_DANE *) ptr;
+ int ret;
+
+ ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+ SEND_ATTR_INT(TLS_ATTR_DANE, dane != 0),
+ ATTR_TYPE_END);
+ if (msg_verbose)
+ msg_info("tls_proxy_client_dane_print dane=%d", dane != 0);
+
+ if (ret == 0 && dane != 0) {
+ ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+ SEND_ATTR_FUNC(tls_proxy_client_tlsa_print,
+ (void *) dane->ta),
+ SEND_ATTR_FUNC(tls_proxy_client_tlsa_print,
+ (void *) dane->ee),
+ SEND_ATTR_FUNC(tls_proxy_client_certs_print,
+ (void *) dane->certs),
+ SEND_ATTR_FUNC(tls_proxy_client_pkeys_print,
+ (void *) dane->pkeys),
+ SEND_ATTR_STR(TLS_ATTR_DOMAIN,
+ STRING_OR_EMPTY(dane->base_domain)),
+ SEND_ATTR_INT(TLS_ATTR_FLAGS, dane->flags),
+ SEND_ATTR_LONG(TLS_ATTR_EXP, dane->expires),
+ ATTR_TYPE_END);
+ }
+ /* Do not flush the stream. */
+ if (msg_verbose)
+ msg_info("tls_proxy_client_dane_print ret=%d", ret);
+ return (ret);
+}
+
+/* tls_proxy_client_start_print - send TLS_CLIENT_START_PROPS over stream */
+
+int tls_proxy_client_start_print(ATTR_PRINT_MASTER_FN print_fn,
+ VSTREAM *fp, int flags, void *ptr)
+{
+ TLS_CLIENT_START_PROPS *props = (TLS_CLIENT_START_PROPS *) ptr;
+ int ret;
+
+ if (msg_verbose)
+ msg_info("begin tls_proxy_client_start_print");
+
+#define STRING_OR_EMPTY(s) ((s) ? (s) : "")
+
+ ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+ SEND_ATTR_INT(TLS_ATTR_TIMEOUT, props->timeout),
+ SEND_ATTR_INT(TLS_ATTR_TLS_LEVEL, props->tls_level),
+ SEND_ATTR_STR(TLS_ATTR_NEXTHOP,
+ STRING_OR_EMPTY(props->nexthop)),
+ SEND_ATTR_STR(TLS_ATTR_HOST,
+ STRING_OR_EMPTY(props->host)),
+ SEND_ATTR_STR(TLS_ATTR_NAMADDR,
+ STRING_OR_EMPTY(props->namaddr)),
+ SEND_ATTR_STR(TLS_ATTR_SERVERID,
+ STRING_OR_EMPTY(props->serverid)),
+ SEND_ATTR_STR(TLS_ATTR_HELO,
+ STRING_OR_EMPTY(props->helo)),
+ SEND_ATTR_STR(TLS_ATTR_PROTOCOLS,
+ STRING_OR_EMPTY(props->protocols)),
+ SEND_ATTR_STR(TLS_ATTR_CIPHER_GRADE,
+ STRING_OR_EMPTY(props->cipher_grade)),
+ SEND_ATTR_STR(TLS_ATTR_CIPHER_EXCLUSIONS,
+ STRING_OR_EMPTY(props->cipher_exclusions)),
+ SEND_ATTR_FUNC(argv_attr_print,
+ (void *) props->matchargv),
+ SEND_ATTR_STR(TLS_ATTR_MDALG,
+ STRING_OR_EMPTY(props->mdalg)),
+ SEND_ATTR_FUNC(tls_proxy_client_dane_print,
+ (void *) props->dane),
+ ATTR_TYPE_END);
+ /* Do not flush the stream. */
+ if (msg_verbose)
+ msg_info("tls_proxy_client_start_print ret=%d", ret);
+ return (ret);
+}
+
+#endif
--- /dev/null
+/*++
+/* NAME
+/* tls_proxy_client_scan 3
+/* SUMMARY
+/* read TLS_CLIENT_XXX structures from stream
+/* SYNOPSIS
+/* #include <tls_proxy.h>
+/*
+/* int tls_proxy_client_init_scan(scan_fn, stream, flags, ptr)
+/* ATTR_SCAN_MASTER_FN scan_fn;
+/* VSTREAM *stream;
+/* int flags;
+/* void *ptr;
+/*
+/* char *tls_proxy_client_init_to_string(buf, init_props)
+/* VSTRING *buf;
+/* TLS_CLIENT_INIT_PROPS *init_props;
+/*
+/* void tls_proxy_client_init_free(init_props)
+/* TLS_CLIENT_INIT_PROPS *init_props;
+/*
+/* int tls_proxy_client_start_scan(scan_fn, stream, flags, ptr)
+/* ATTR_SCAN_MASTER_FN scan_fn;
+/* VSTREAM *stream;
+/* int flags;
+/* void *ptr;
+/*
+/* void tls_proxy_client_start_free(start_props)
+/* TLS_CLIENT_START_PROPS *start_props;
+/* DESCRIPTION
+/* tls_proxy_client_init_scan() reads a full TLS_CLIENT_INIT_PROPS
+/* structure from the named stream using the specified attribute
+/* scan routine. tls_proxy_client_init_scan() is meant to be passed
+/* as a call-back function to attr_scan(), as shown below.
+/*
+/* tls_proxy_client_init_to_string() produces a lookup key
+/* that is unique for the properties received by
+/* tls_proxy_client_init_scan().
+/*
+/* tls_proxy_client_init_free() destroys a TLS_CLIENT_INIT_PROPS
+/* structure that was created by tls_proxy_client_init_scan().
+/*
+/* TLS_CLIENT_INIT_PROPS *init_props = 0;
+/* ...
+/* ... RECV_ATTR_FUNC(tls_proxy_client_init_scan, (void *) &init_props)
+/* ...
+/* if (init_props != 0)
+/* tls_proxy_client_init_free(init_props);
+/*
+/* tls_proxy_client_start_scan() reads a TLS_CLIENT_START_PROPS
+/* structure, without the stream of file descriptor members,
+/* from the named stream using the specified attribute scan
+/* routine. tls_proxy_client_start_scan() is meant to be passed
+/* as a call-back function to attr_scan(), as shown below.
+/*
+/* tls_proxy_client_start_free() destroys a TLS_CLIENT_START_PROPS
+/* structure that was created by tls_proxy_client_start_scan().
+/*
+/* TLS_CLIENT_START_PROPS *start_props = 0;
+/* ...
+/* ... RECV_ATTR_FUNC(tls_proxy_client_start_scan, (void *) &start_props)
+/* ...
+/* if (start_props != 0)
+/* tls_proxy_client_start_free(start_props);
+/* DIAGNOSTICS
+/* Fatal: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+#ifdef USE_TLS
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library */
+
+#include <argv_attr.h>
+#include <attr.h>
+#include <msg.h>
+#include <vstring.h>
+
+/* TLS library. */
+
+#include <tls.h>
+#include <tls_proxy.h>
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* tls_proxy_client_init_free - destroy TLS_CLIENT_INIT_PROPS structure */
+
+void tls_proxy_client_init_free(TLS_CLIENT_INIT_PROPS *props)
+{
+ myfree((void *) props->log_param);
+ myfree((void *) props->log_level);
+ myfree((void *) props->cache_type);
+ myfree((void *) props->cert_file);
+ myfree((void *) props->key_file);
+ myfree((void *) props->dcert_file);
+ myfree((void *) props->dkey_file);
+ myfree((void *) props->eccert_file);
+ myfree((void *) props->eckey_file);
+ myfree((void *) props->CAfile);
+ myfree((void *) props->CApath);
+ myfree((void *) props->mdalg);
+ myfree((void *) props);
+}
+
+/* tls_proxy_client_init_scan - receive TLS_CLIENT_INIT_PROPS from stream */
+
+int tls_proxy_client_init_scan(ATTR_SCAN_MASTER_FN scan_fn, VSTREAM *fp,
+ int flags, void *ptr)
+{
+ TLS_CLIENT_INIT_PROPS *props
+ = (TLS_CLIENT_INIT_PROPS *) mymalloc(sizeof(*props));
+ int ret;
+ VSTRING *log_param = vstring_alloc(25);
+ VSTRING *log_level = vstring_alloc(25);
+ VSTRING *cache_type = vstring_alloc(25);
+ VSTRING *cert_file = vstring_alloc(25);
+ VSTRING *key_file = vstring_alloc(25);
+ VSTRING *dcert_file = vstring_alloc(25);
+ VSTRING *dkey_file = vstring_alloc(25);
+ VSTRING *eccert_file = vstring_alloc(25);
+ VSTRING *eckey_file = vstring_alloc(25);
+ VSTRING *CAfile = vstring_alloc(25);
+ VSTRING *CApath = vstring_alloc(25);
+ VSTRING *mdalg = vstring_alloc(25);
+
+ if (msg_verbose)
+ msg_info("begin tls_proxy_client_init_scan");
+
+ /*
+ * Note: memset() is not a portable way to initialize non-integer types.
+ */
+ memset(props, 0, sizeof(*props));
+ ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+ RECV_ATTR_STR(TLS_ATTR_LOG_PARAM, log_param),
+ RECV_ATTR_STR(TLS_ATTR_LOG_LEVEL, log_level),
+ RECV_ATTR_INT(TLS_ATTR_VERIFYDEPTH, &props->verifydepth),
+ RECV_ATTR_STR(TLS_ATTR_CACHE_TYPE, cache_type),
+ RECV_ATTR_STR(TLS_ATTR_CERT_FILE, cert_file),
+ RECV_ATTR_STR(TLS_ATTR_KEY_FILE, key_file),
+ RECV_ATTR_STR(TLS_ATTR_DCERT_FILE, dcert_file),
+ RECV_ATTR_STR(TLS_ATTR_DKEY_FILE, dkey_file),
+ RECV_ATTR_STR(TLS_ATTR_ECCERT_FILE, eccert_file),
+ RECV_ATTR_STR(TLS_ATTR_ECKEY_FILE, eckey_file),
+ RECV_ATTR_STR(TLS_ATTR_CAFILE, CAfile),
+ RECV_ATTR_STR(TLS_ATTR_CAPATH, CApath),
+ RECV_ATTR_STR(TLS_ATTR_MDALG, mdalg),
+ ATTR_TYPE_END);
+ /* Always construct a well-formed structure. */
+ props->log_param = vstring_export(log_param);
+ props->log_level = vstring_export(log_level);
+ props->cache_type = vstring_export(cache_type);
+ props->cert_file = vstring_export(cert_file);
+ props->key_file = vstring_export(key_file);
+ props->dcert_file = vstring_export(dcert_file);
+ props->dkey_file = vstring_export(dkey_file);
+ props->eccert_file = vstring_export(eccert_file);
+ props->eckey_file = vstring_export(eckey_file);
+ props->CAfile = vstring_export(CAfile);
+ props->CApath = vstring_export(CApath);
+ props->mdalg = vstring_export(mdalg);
+ ret = (ret == 13 ? 1 : -1);
+ if (ret != 1) {
+ tls_proxy_client_init_free(props);
+ props = 0;
+ }
+ *(TLS_CLIENT_INIT_PROPS **) ptr = props;
+ if (msg_verbose)
+ msg_info("tls_proxy_client_init_scan ret=%d", ret);
+ return (ret);
+}
+
+/* tls_proxy_client_init_to_string - serialize to string */
+
+char *tls_proxy_client_init_to_string(VSTRING *buf,
+ TLS_CLIENT_INIT_PROPS *props)
+{
+ vstring_sprintf(buf, "%s\n%s\n%d\n%s\n%s\n%s\n%s\n%s\n"
+ "%s\n%s\n%s\n%s\n%s\n", props->log_param,
+ props->log_level, props->verifydepth,
+ props->cache_type, props->cert_file, props->key_file,
+ props->dcert_file, props->dkey_file,
+ props->eccert_file, props->eckey_file,
+ props->CAfile, props->CApath, props->mdalg);
+ return (vstring_str(buf));
+}
+
+/* tls_proxy_client_certs_free - destroy TLS_PKEYS from stream */
+
+static void tls_proxy_client_certs_free(TLS_CERTS *tp)
+{
+ if (tp->next)
+ tls_proxy_client_certs_free(tp->next);
+ if (tp->cert)
+ X509_free(tp->cert);
+ myfree((void *) tp);
+}
+
+/* tls_proxy_client_pkeys_free - destroy TLS_PKEYS from stream */
+
+static void tls_proxy_client_pkeys_free(TLS_PKEYS *tp)
+{
+ if (tp->next)
+ tls_proxy_client_pkeys_free(tp->next);
+ if (tp->pkey)
+ EVP_PKEY_free(tp->pkey);
+ myfree((void *) tp);
+}
+
+/* tls_proxy_client_tlsa_free - destroy TLS_TLSA from stream */
+
+static void tls_proxy_client_tlsa_free(TLS_TLSA *tp)
+{
+ if (tp->next)
+ tls_proxy_client_tlsa_free(tp->next);
+ myfree(tp->mdalg);
+ if (tp->certs)
+ argv_free(tp->certs);
+ if (tp->pkeys)
+ argv_free(tp->pkeys);
+ myfree((void *) tp);
+}
+
+/* tls_proxy_client_dane_free - destroy TLS_DANE from stream */
+
+static void tls_proxy_client_dane_free(TLS_DANE *dane)
+{
+ if (dane->ta)
+ tls_proxy_client_tlsa_free(dane->ta);
+ if (dane->ee)
+ tls_proxy_client_tlsa_free(dane->ee);
+ if (dane->certs)
+ tls_proxy_client_certs_free(dane->certs);
+ if (dane->pkeys)
+ tls_proxy_client_pkeys_free(dane->pkeys);
+ myfree(dane->base_domain);
+ if (dane->refs-- == 1)
+ myfree((void *) dane);
+}
+
+/* tls_proxy_client_start_free - destroy TLS_CLIENT_START_PROPS structure */
+
+void tls_proxy_client_start_free(TLS_CLIENT_START_PROPS *props)
+{
+ myfree((void *) props->nexthop);
+ myfree((void *) props->host);
+ myfree((void *) props->namaddr);
+ myfree((void *) props->serverid);
+ myfree((void *) props->helo);
+ myfree((void *) props->protocols);
+ myfree((void *) props->cipher_grade);
+ myfree((void *) props->cipher_exclusions);
+ if (props->matchargv)
+ argv_free((ARGV *) props->matchargv);
+ myfree((void *) props->mdalg);
+ if (props->dane)
+ tls_proxy_client_dane_free((TLS_DANE *) props->dane);
+ myfree((void *) props);
+}
+
+/* tls_proxy_client_certs_scan - receive TLS_CERTS from stream */
+
+static int tls_proxy_client_certs_scan(ATTR_SCAN_MASTER_FN scan_fn,
+ VSTREAM *fp, int flags, void *ptr)
+{
+ int ret;
+ int count;
+ VSTRING *buf = 0;
+ TLS_CERTS **tpp;
+ TLS_CERTS *head = 0;
+ TLS_CERTS *tp;
+ int n;
+
+ ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+ RECV_ATTR_INT(TLS_ATTR_COUNT, &count),
+ ATTR_TYPE_END);
+ if (msg_verbose)
+ msg_info("tls_proxy_client_certs_scan count=%d", count);
+
+ for (tpp = &head, n = 0; ret == 1 && n < count; n++, tpp = &tp->next) {
+ *tpp = tp = (TLS_CERTS *) mymalloc(sizeof(*tp));
+ D2I_const unsigned char *bp;
+
+ if (buf == 0)
+ buf = vstring_alloc(100);
+
+ /*
+ * Note: memset() is not a portable way to initialize non-integer
+ * types.
+ */
+ memset(tp, 0, sizeof(*tp));
+ ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+ RECV_ATTR_DATA(TLS_ATTR_CERT, buf),
+ ATTR_TYPE_END);
+ /* Always construct a well-formed structure. */
+ if (ret == 1) {
+ bp = (D2I_const unsigned char *) STR(buf);
+ if (d2i_X509(&tp->cert, &bp, LEN(buf)) == 0
+ || LEN(buf) != ((char *) bp) - STR(buf)) {
+ msg_warn("malformed certificate in TLS_CERTS");
+ ret = -1;
+ }
+ } else {
+ tp->cert = 0;
+ }
+ tp->next = 0;
+ }
+ if (buf)
+ vstring_free(buf);
+ if (ret != 1) {
+ tls_proxy_client_certs_free(head);
+ head = 0;
+ }
+ *(TLS_CERTS **) ptr = head;
+ if (msg_verbose)
+ msg_info("tls_proxy_client_certs_scan ret=%d", ret);
+ return (ret);
+}
+
+/* tls_proxy_client_pkeys_scan - receive TLS_PKEYS from stream */
+
+static int tls_proxy_client_pkeys_scan(ATTR_SCAN_MASTER_FN scan_fn,
+ VSTREAM *fp, int flags, void *ptr)
+{
+ int ret;
+ int count;
+ VSTRING *buf = vstring_alloc(100);
+ TLS_PKEYS **tpp;
+ TLS_PKEYS *head = 0;
+ TLS_PKEYS *tp;
+ int n;
+
+ ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+ RECV_ATTR_INT(TLS_ATTR_COUNT, &count),
+ ATTR_TYPE_END);
+ if (msg_verbose)
+ msg_info("tls_proxy_client_pkeys_scan count=%d", count);
+
+ for (tpp = &head, n = 0; ret == 1 && n < count; n++, tpp = &tp->next) {
+ *tpp = tp = (TLS_PKEYS *) mymalloc(sizeof(*tp));
+ D2I_const unsigned char *bp;
+
+ if (buf == 0)
+ buf = vstring_alloc(100);
+
+ /*
+ * Note: memset() is not a portable way to initialize non-integer
+ * types.
+ */
+ memset(tp, 0, sizeof(*tp));
+ ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+ RECV_ATTR_DATA(TLS_ATTR_PKEY, buf),
+ ATTR_TYPE_END);
+ /* Always construct a well-formed structure. */
+ if (ret == 1) {
+ bp = (D2I_const unsigned char *) STR(buf);
+ if (d2i_PUBKEY(&tp->pkey, &bp, LEN(buf)) == 0
+ || LEN(buf) != (char *) bp - STR(buf)) {
+ msg_warn("malformed public key in TLS_PKEYS");
+ ret = -1;
+ }
+ } else {
+ tp->pkey = 0;
+ }
+ tp->next = 0;
+ }
+ if (buf)
+ vstring_free(buf);
+ if (ret != 1) {
+ tls_proxy_client_pkeys_free(head);
+ head = 0;
+ }
+ *(TLS_PKEYS **) ptr = head;
+ if (msg_verbose)
+ msg_info("tls_proxy_client_pkeys_scan ret=%d", ret);
+ return (ret);
+}
+
+/* tls_proxy_client_tlsa_scan - receive TLS_TLSA from stream */
+
+static int tls_proxy_client_tlsa_scan(ATTR_SCAN_MASTER_FN scan_fn,
+ VSTREAM *fp, int flags, void *ptr)
+{
+ int ret;
+ int count;
+ TLS_TLSA **tpp;
+ TLS_TLSA *head = 0;
+ TLS_TLSA *tp;
+ int n;
+
+ ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+ RECV_ATTR_INT(TLS_ATTR_COUNT, &count),
+ ATTR_TYPE_END);
+ if (msg_verbose)
+ msg_info("tls_proxy_client_tlsa_scan count=%d", count);
+
+ for (tpp = &head, n = 0; ret == 1 && n < count; n++, tpp = &tp->next) {
+ *tpp = tp = (TLS_TLSA *) mymalloc(sizeof(*tp));
+ VSTRING *mdalg = vstring_alloc(25);
+
+ /*
+ * Note: memset() is not a portable way to initialize non-integer
+ * types.
+ */
+ memset(tp, 0, sizeof(*tp));
+ /* Always construct a well-formed structure. */
+ tp->certs = 0; /* scan_fn may return early */
+ tp->pkeys = 0;
+ ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+ RECV_ATTR_STR(TLS_ATTR_MDALG, mdalg),
+ RECV_ATTR_FUNC(argv_attr_scan, &tp->certs),
+ RECV_ATTR_FUNC(argv_attr_scan, &tp->pkeys),
+ ATTR_TYPE_END);
+ tp->mdalg = vstring_export(mdalg);
+ tp->next = 0;
+ ret = (ret == 3 ? 1 : -1);
+ }
+ if (ret != 1) {
+ tls_proxy_client_tlsa_free(head);
+ head = 0;
+ }
+ *(TLS_TLSA **) ptr = head;
+ if (msg_verbose)
+ msg_info("tls_proxy_client_tlsa_scan ret=%d", ret);
+ return (ret);
+}
+
+/* tls_proxy_client_dane_scan - receive TLS_DANE from stream */
+
+static int tls_proxy_client_dane_scan(ATTR_SCAN_MASTER_FN scan_fn,
+ VSTREAM *fp, int flags, void *ptr)
+{
+ TLS_DANE *dane = 0;
+ int ret;
+ int have_dane = 0;
+
+ ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+ RECV_ATTR_INT(TLS_ATTR_DANE, &have_dane),
+ ATTR_TYPE_END);
+ if (msg_verbose)
+ msg_info("tls_proxy_client_dane_scan have_dane=%d", have_dane);
+
+ if (ret == 1 && have_dane) {
+ VSTRING *base_domain = vstring_alloc(25);
+ long expires;
+
+ dane = (TLS_DANE *) mymalloc(sizeof(*dane));
+
+ /*
+ * Note: memset() is not a portable way to initialize non-integer
+ * types.
+ */
+ memset(dane, 0, sizeof(*dane));
+ /* Always construct a well-formed structure. */
+ dane->ta = 0; /* scan_fn may return early */
+ dane->ee = 0;
+ dane->certs = 0;
+ dane->pkeys = 0;
+ ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+ RECV_ATTR_FUNC(tls_proxy_client_tlsa_scan,
+ &dane->ta),
+ RECV_ATTR_FUNC(tls_proxy_client_tlsa_scan,
+ &dane->ee),
+ RECV_ATTR_FUNC(tls_proxy_client_certs_scan,
+ &dane->certs),
+ RECV_ATTR_FUNC(tls_proxy_client_pkeys_scan,
+ &dane->pkeys),
+ RECV_ATTR_STR(TLS_ATTR_DOMAIN, base_domain),
+ RECV_ATTR_INT(TLS_ATTR_FLAGS, &dane->flags),
+ RECV_ATTR_LONG(TLS_ATTR_EXP, &expires),
+ ATTR_TYPE_END);
+ /* Always construct a well-formed structure. */
+ dane->base_domain = vstring_export(base_domain);
+ dane->expires = expires;
+ dane->refs = 1;
+ ret = (ret == 7 ? 1 : -1);
+ /* XXX if scan_fn() completed normally, sanity check dane->flags. */
+ if (ret != 1) {
+ tls_proxy_client_dane_free(dane);
+ dane = 0;
+ }
+ }
+ *(TLS_DANE **) ptr = dane;
+ if (msg_verbose)
+ msg_info("tls_proxy_client_dane_scan ret=%d", ret);
+ return (ret);
+}
+
+/* tls_proxy_client_start_scan - receive TLS_CLIENT_START_PROPS from stream */
+
+int tls_proxy_client_start_scan(ATTR_SCAN_MASTER_FN scan_fn, VSTREAM *fp,
+ int flags, void *ptr)
+{
+ TLS_CLIENT_START_PROPS *props
+ = (TLS_CLIENT_START_PROPS *) mymalloc(sizeof(*props));
+ int ret;
+ VSTRING *nexthop = vstring_alloc(25);
+ VSTRING *host = vstring_alloc(25);
+ VSTRING *namaddr = vstring_alloc(25);
+ VSTRING *serverid = vstring_alloc(25);
+ VSTRING *helo = vstring_alloc(25);
+ VSTRING *protocols = vstring_alloc(25);
+ VSTRING *cipher_grade = vstring_alloc(25);
+ VSTRING *cipher_exclusions = vstring_alloc(25);
+ VSTRING *mdalg = vstring_alloc(25);
+
+ if (msg_verbose)
+ msg_info("begin tls_proxy_client_start_scan");
+
+ /*
+ * Note: memset() is not a portable way to initialize non-integer types.
+ */
+ memset(props, 0, sizeof(*props));
+ props->ctx = 0;
+ props->stream = 0;
+ props->fd = -1;
+ props->dane = 0; /* scan_fn may return early */
+ ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+ RECV_ATTR_INT(TLS_ATTR_TIMEOUT, &props->timeout),
+ RECV_ATTR_INT(TLS_ATTR_TLS_LEVEL, &props->tls_level),
+ RECV_ATTR_STR(TLS_ATTR_NEXTHOP, nexthop),
+ RECV_ATTR_STR(TLS_ATTR_HOST, host),
+ RECV_ATTR_STR(TLS_ATTR_NAMADDR, namaddr),
+ RECV_ATTR_STR(TLS_ATTR_SERVERID, serverid),
+ RECV_ATTR_STR(TLS_ATTR_HELO, helo),
+ RECV_ATTR_STR(TLS_ATTR_PROTOCOLS, protocols),
+ RECV_ATTR_STR(TLS_ATTR_CIPHER_GRADE, cipher_grade),
+ RECV_ATTR_STR(TLS_ATTR_CIPHER_EXCLUSIONS,
+ cipher_exclusions),
+ RECV_ATTR_FUNC(argv_attr_scan, &props->matchargv),
+ RECV_ATTR_STR(TLS_ATTR_MDALG, mdalg),
+ RECV_ATTR_FUNC(tls_proxy_client_dane_scan,
+ &props->dane),
+ ATTR_TYPE_END);
+ /* Always construct a well-formed structure. */
+ props->nexthop = vstring_export(nexthop);
+ props->host = vstring_export(host);
+ props->namaddr = vstring_export(namaddr);
+ props->serverid = vstring_export(serverid);
+ props->helo = vstring_export(helo);
+ props->protocols = vstring_export(protocols);
+ props->cipher_grade = vstring_export(cipher_grade);
+ props->cipher_exclusions = vstring_export(cipher_exclusions);
+ props->mdalg = vstring_export(mdalg);
+ ret = (ret == 13 ? 1 : -1);
+ if (ret != 1) {
+ tls_proxy_client_start_free(props);
+ props = 0;
+ }
+ *(TLS_CLIENT_START_PROPS **) ptr = props;
+ if (msg_verbose)
+ msg_info("tls_proxy_client_start_scan ret=%d", ret);
+ return (ret);
+}
+
+#endif
/* NAME
/* tlsproxy_clnt 3
/* SUMMARY
-/* postscreen TLS proxy support
+/* tlsproxy(8) client support
/* SYNOPSIS
/* #include <tlsproxy_clnt.h>
/*
/* VSTREAM *tls_proxy_open(service, flags, peer_stream, peer_addr,
-/* peer_port, timeout)
+/* peer_port, handshake_timeout, session_timeout,
+ serverid, init_props, start_props)
/* const char *service;
/* int flags;
/* VSTREAM *peer_stream;
/* const char *peer_addr;
/* const char *peer_port;
-/* int timeout;
+/* int handshake_timeout;
+/* int session_timeout;
+/* const char *serverid;
+/* void *init_props;
+/* void *start_props;
/*
/* TLS_SESS_STATE *tls_proxy_context_receive(proxy_stream)
/* VSTREAM *proxy_stream;
-/*
-/* void tls_proxy_context_free(tls_context)
-/* TLS_SESS_STATE *tls_context;
+/* AUXILIARY FUNCTIONS
+/* VSTREAM *tls_proxy_legacy_open(service, flags, peer_stream,
+/* peer_addr, peer_port,
+/* timeout, serverid)
+/* const char *service;
+/* int flags;
+/* VSTREAM *peer_stream;
+/* const char *peer_addr;
+/* const char *peer_port;
+/* int timeout;
+/* const char *serverid;
/* DESCRIPTION
/* tls_proxy_open() prepares for inserting the tlsproxy(8)
/* daemon between the current process and a remote peer (the
/*
/* After this, the proxy_stream is ready for plain-text I/O.
/*
-/* tls_proxy_context_free() destroys a TLS context object that
-/* was received with tls_proxy_context_receive().
+/* tls_proxy_legacy_open() is a backwards-compatibility feature
+/* that provides a historical interface.
/*
/* Arguments:
/* .IP service
/* Printable IP address of the remote peer_stream endpoint.
/* .IP peer_port
/* Printable TCP port of the remote peer_stream endpoint.
-/* .IP timeout
-/* Time limit that the tlsproxy(8) daemon should use.
+/* .IP handshake_timeout
+/* Time limit that the tlsproxy(8) daemon should use during
+/* the TLS handshake.
+/* .IP session_timeout
+/* Time limit that the tlsproxy(8) daemon should use after the
+/* TLS handshake.
+/* .IP serverid
+/* Unique service identifier.
+/* .IP init_props
+/* Pointer to TLS_CLIENT_INIT_PROPS or TLS_SERVER_INIT_PROPS.
+/* .IP start_props
+/* Pointer to TLS_CLIENT_START_PROPS or TLS_SERVER_START_PROPS.
/* .IP proxy_stream
/* Stream from tls_proxy_open().
/* .IP tls_context
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
#ifdef USE_TLS
VSTREAM *peer_stream,
const char *peer_addr,
const char *peer_port,
- int timeout)
+ int handshake_timeout,
+ int session_timeout,
+ const char *serverid,
+ void *init_props,
+ void *start_props)
{
+ const char myname[] = "tls_proxy_open";
VSTREAM *tlsproxy_stream;
int status;
int fd;
}
/*
- * Initial handshake. Send the data attributes now, and send the client
- * file descriptor in a later transaction.
- *
- * XXX The formatted endpoint should be a state member. Then, we can
- * simplify all the format strings throughout the program.
+ * Initial handshake. Send common data attributes now, and send the
+ * remote peer file descriptor in a later transaction.
*/
tlsproxy_stream = vstream_fdopen(fd, O_RDWR);
vstring_sprintf(remote_endpt, "[%s]:%s", peer_addr, peer_port);
attr_print(tlsproxy_stream, ATTR_FLAG_NONE,
- SEND_ATTR_STR(MAIL_ATTR_REMOTE_ENDPT, STR(remote_endpt)),
- SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags),
- SEND_ATTR_INT(MAIL_ATTR_TIMEOUT, timeout),
+ SEND_ATTR_STR(TLS_ATTR_REMOTE_ENDPT, STR(remote_endpt)),
+ SEND_ATTR_INT(TLS_ATTR_FLAGS, flags),
+ SEND_ATTR_INT(TLS_ATTR_TIMEOUT, handshake_timeout),
+ SEND_ATTR_INT(TLS_ATTR_TIMEOUT, session_timeout),
+ SEND_ATTR_STR(TLS_ATTR_SERVERID, serverid),
ATTR_TYPE_END);
+ /* Do not flush the stream yet. */
+ if (vstream_ferror(tlsproxy_stream) != 0) {
+ msg_warn("error sending request to %s service: %m",
+ STR(tlsproxy_service));
+ vstream_fclose(tlsproxy_stream);
+ return (0);
+ }
+ switch (flags & (TLS_PROXY_FLAG_ROLE_CLIENT | TLS_PROXY_FLAG_ROLE_SERVER)) {
+ case TLS_PROXY_FLAG_ROLE_CLIENT:
+ attr_print(tlsproxy_stream, ATTR_FLAG_NONE,
+ SEND_ATTR_FUNC(tls_proxy_client_init_print, init_props),
+ SEND_ATTR_FUNC(tls_proxy_client_start_print, start_props),
+ ATTR_TYPE_END);
+ break;
+ case TLS_PROXY_FLAG_ROLE_SERVER:
+#if 0
+ attr_print(tlsproxy_stream, ATTR_FLAG_NONE,
+ SEND_ATTR_FUNC(tls_proxy_server_init_print, init_props),
+ SEND_ATTR_FUNC(tls_proxy_server_start_print, start_props),
+ ATTR_TYPE_END);
+#endif
+ break;
+ default:
+ msg_panic("%s: bad flags: 0x%x", myname, flags);
+ }
if (vstream_fflush(tlsproxy_stream) != 0) {
msg_warn("error sending request to %s service: %m",
STR(tlsproxy_service));
* Receive the "TLS is available" indication.
*
* This may seem out of order, but we must have a read transaction between
- * sending the request attributes and sending the SMTP client file
+ * sending the request attributes and sending the plaintext file
* descriptor. We can't assume UNIX-domain socket semantics here.
*/
if (attr_scan(tlsproxy_stream, ATTR_FLAG_STRICT,
RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
+ /* TODO: informative message. */
ATTR_TYPE_END) != 1 || status == 0) {
/*
}
/*
- * Send the remote SMTP client file descriptor.
+ * Send the remote peer file descriptor.
*/
if (LOCAL_SEND_FD(vstream_fileno(tlsproxy_stream),
vstream_fileno(peer_stream)) < 0) {
return (tlsproxy_stream);
}
+
/* tls_proxy_context_receive - receive TLS session object from tlsproxy(8) */
TLS_SESS_STATE *tls_proxy_context_receive(VSTREAM *proxy_stream)
{
- TLS_SESS_STATE *tls_context;
-
- tls_context = (TLS_SESS_STATE *) mymalloc(sizeof(*tls_context));
+ TLS_SESS_STATE *tls_context = 0;
if (attr_scan(proxy_stream, ATTR_FLAG_STRICT,
- RECV_ATTR_FUNC(tls_proxy_context_scan, (void *) tls_context),
+ RECV_ATTR_FUNC(tls_proxy_context_scan, (void *) &tls_context),
ATTR_TYPE_END) != 1) {
- tls_proxy_context_free(tls_context);
+ if (tls_context)
+ tls_proxy_context_free(tls_context);
return (0);
} else {
return (tls_context);
}
}
-/* tls_proxy_context_free - destroy object from tls_proxy_context_receive() */
-
-void tls_proxy_context_free(TLS_SESS_STATE *tls_context)
-{
- if (tls_context->peer_CN)
- myfree(tls_context->peer_CN);
- if (tls_context->issuer_CN)
- myfree(tls_context->issuer_CN);
- if (tls_context->peer_cert_fprint)
- myfree(tls_context->peer_cert_fprint);
- if (tls_context->peer_pkey_fprint)
- myfree(tls_context->peer_pkey_fprint);
- if (tls_context->protocol)
- myfree((void *) tls_context->protocol);
- if (tls_context->cipher_name)
- myfree((void *) tls_context->cipher_name);
- myfree((void *) tls_context);
-}
-
#endif
/*++
/* NAME
-/* tls_proxy_print
+/* tls_proxy_context_print
/* SUMMARY
-/* write DSN structure to stream
+/* write TLS_ATTR_STATE structure to stream
/* SYNOPSIS
/* #include <tls_proxy.h>
/*
/* int flags;
/* void *ptr;
/* DESCRIPTION
-/* tls_proxy_context_print() writes a TLS_SESS_STATE structure
-/* to the named stream using the specified attribute print
-/* routine. TLS_SESS_STATE() is meant to be passed as a call-back
-/* to attr_print(), thusly:
+/* tls_proxy_context_print() writes the public members of a
+/* TLS_ATTR_STATE structure to the named stream using the
+/* specified attribute print routine. tls_proxy_context_print()
+/* is meant to be passed as a call-back to attr_print(), thusly:
/*
/* ... SEND_ATTR_FUNC(tls_proxy_context_print, (void *) tls_context), ...
/* DIAGNOSTICS
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
#ifdef USE_TLS
#include <attr.h>
-/* Global library. */
-
-#include <mail_proto.h>
-
/* TLS library. */
#include <tls.h>
#define STRING_OR_EMPTY(s) ((s) ? (s) : "")
ret = print_fn(fp, flags | ATTR_FLAG_MORE,
- SEND_ATTR_STR(MAIL_ATTR_PEER_CN,
+ SEND_ATTR_STR(TLS_ATTR_PEER_CN,
STRING_OR_EMPTY(tp->peer_CN)),
- SEND_ATTR_STR(MAIL_ATTR_ISSUER_CN,
+ SEND_ATTR_STR(TLS_ATTR_ISSUER_CN,
STRING_OR_EMPTY(tp->issuer_CN)),
- SEND_ATTR_STR(MAIL_ATTR_PEER_CERT_FPT,
+ SEND_ATTR_STR(TLS_ATTR_PEER_CERT_FPT,
STRING_OR_EMPTY(tp->peer_cert_fprint)),
- SEND_ATTR_STR(MAIL_ATTR_PEER_PKEY_FPT,
+ SEND_ATTR_STR(TLS_ATTR_PEER_PKEY_FPT,
STRING_OR_EMPTY(tp->peer_pkey_fprint)),
- SEND_ATTR_INT(MAIL_ATTR_PEER_STATUS,
+ SEND_ATTR_INT(TLS_ATTR_PEER_STATUS,
tp->peer_status),
- SEND_ATTR_STR(MAIL_ATTR_CIPHER_PROTOCOL,
+ SEND_ATTR_STR(TLS_ATTR_CIPHER_PROTOCOL,
STRING_OR_EMPTY(tp->protocol)),
- SEND_ATTR_STR(MAIL_ATTR_CIPHER_NAME,
+ SEND_ATTR_STR(TLS_ATTR_CIPHER_NAME,
STRING_OR_EMPTY(tp->cipher_name)),
- SEND_ATTR_INT(MAIL_ATTR_CIPHER_USEBITS,
+ SEND_ATTR_INT(TLS_ATTR_CIPHER_USEBITS,
tp->cipher_usebits),
- SEND_ATTR_INT(MAIL_ATTR_CIPHER_ALGBITS,
+ SEND_ATTR_INT(TLS_ATTR_CIPHER_ALGBITS,
tp->cipher_algbits),
ATTR_TYPE_END);
+ /* Do not flush the stream. */
return (ret);
}
--- /dev/null
+/*++
+/* NAME
+/* tls_proxy_context_scan
+/* SUMMARY
+/* read TLS session state from stream
+/* SYNOPSIS
+/* #include <tls_proxy.h>
+/*
+/* int tls_proxy_context_scan(scan_fn, stream, flags, ptr)
+/* ATTR_SCAN_MASTER_FN scan_fn;
+/* VSTREAM *stream;
+/* int flags;
+/* void *ptr;
+/*
+/* void tls_proxy_context_free(tls_context)
+/* TLS_SESS_STATE *tls_context;
+/* DESCRIPTION
+/* tls_proxy_context_scan() reads the public members of a
+/* TLS_ATTR_STATE structure from the named stream using the
+/* specified attribute scan routine. tls_proxy_context_scan()
+/* is meant to be passed as a call-back to attr_scan() as shown
+/* below.
+/*
+/* tls_proxy_context_free() destroys a TLS context object that
+/* was received with tls_proxy_context_scan().
+/*
+/* TLS_ATTR_STATE *tls_context = 0;
+/* ...
+/* ... RECV_ATTR_FUNC(tls_proxy_context_scan, (void *) &tls_context), ...
+/* ...
+/* if (tls_context)
+/* tls_proxy_context_free(tls_context);
+/* DIAGNOSTICS
+/* Fatal: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+#ifdef USE_TLS
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library */
+
+#include <attr.h>
+#include <msg.h>
+
+/* TLS library. */
+
+#include <tls.h>
+#include <tls_proxy.h>
+
+/* tls_proxy_context_scan - receive TLS session state from stream */
+
+int tls_proxy_context_scan(ATTR_SCAN_MASTER_FN scan_fn, VSTREAM *fp,
+ int flags, void *ptr)
+{
+ TLS_SESS_STATE *tls_context
+ = (TLS_SESS_STATE *) mymalloc(sizeof(*tls_context));;
+ int ret;
+ VSTRING *peer_CN = vstring_alloc(25);
+ VSTRING *issuer_CN = vstring_alloc(25);
+ VSTRING *peer_cert_fprint = vstring_alloc(60); /* 60 for SHA-1 */
+ VSTRING *peer_pkey_fprint = vstring_alloc(60); /* 60 for SHA-1 */
+ VSTRING *protocol = vstring_alloc(25);
+ VSTRING *cipher_name = vstring_alloc(25);
+
+ if (msg_verbose)
+ msg_info("begin tls_proxy_context_scan");
+
+ /*
+ * Note: memset() is not a portable way to initialize non-integer types.
+ */
+ memset(tls_context, 0, sizeof(*tls_context));
+ ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+ RECV_ATTR_STR(TLS_ATTR_PEER_CN, peer_CN),
+ RECV_ATTR_STR(TLS_ATTR_ISSUER_CN, issuer_CN),
+ RECV_ATTR_STR(TLS_ATTR_PEER_CERT_FPT, peer_cert_fprint),
+ RECV_ATTR_STR(TLS_ATTR_PEER_PKEY_FPT, peer_pkey_fprint),
+ RECV_ATTR_INT(TLS_ATTR_PEER_STATUS,
+ &tls_context->peer_status),
+ RECV_ATTR_STR(TLS_ATTR_CIPHER_PROTOCOL, protocol),
+ RECV_ATTR_STR(TLS_ATTR_CIPHER_NAME, cipher_name),
+ RECV_ATTR_INT(TLS_ATTR_CIPHER_USEBITS,
+ &tls_context->cipher_usebits),
+ RECV_ATTR_INT(TLS_ATTR_CIPHER_ALGBITS,
+ &tls_context->cipher_algbits),
+ ATTR_TYPE_END);
+ /* Always construct a well-formed structure. */
+ tls_context->peer_CN = vstring_export(peer_CN);
+ tls_context->issuer_CN = vstring_export(issuer_CN);
+ tls_context->peer_cert_fprint = vstring_export(peer_cert_fprint);
+ tls_context->peer_pkey_fprint = vstring_export(peer_pkey_fprint);
+ tls_context->protocol = vstring_export(protocol);
+ tls_context->cipher_name = vstring_export(cipher_name);
+ ret = (ret == 9 ? 1 : -1);
+ if (ret != 1) {
+ tls_proxy_context_free(tls_context);
+ tls_context = 0;
+ }
+ *(TLS_SESS_STATE **) ptr = tls_context;
+ if (msg_verbose)
+ msg_info("tls_proxy_context_scan ret=%d", ret);
+ return (ret);
+}
+
+/* tls_proxy_context_free - destroy object from tls_proxy_context_receive() */
+
+void tls_proxy_context_free(TLS_SESS_STATE *tls_context)
+{
+ if (tls_context->peer_CN)
+ myfree(tls_context->peer_CN);
+ if (tls_context->issuer_CN)
+ myfree(tls_context->issuer_CN);
+ if (tls_context->peer_cert_fprint)
+ myfree(tls_context->peer_cert_fprint);
+ if (tls_context->peer_pkey_fprint)
+ myfree(tls_context->peer_pkey_fprint);
+ if (tls_context->protocol)
+ myfree((void *) tls_context->protocol);
+ if (tls_context->cipher_name)
+ myfree((void *) tls_context->cipher_name);
+ myfree((void *) tls_context);
+}
+
+#endif
+++ /dev/null
-/*++
-/* NAME
-/* tls_proxy_scan
-/* SUMMARY
-/* read TLS session state from stream
-/* SYNOPSIS
-/* #include <tls_proxy.h>
-/*
-/* int tls_proxy_context_scan(scan_fn, stream, flags, ptr)
-/* ATTR_SCAN_MASTER_FN scan_fn;
-/* VSTREAM *stream;
-/* int flags;
-/* void *ptr;
-/* DESCRIPTION
-/* tls_proxy_context_scan() reads a TLS_SESS_STATE structure
-/* from the named stream using the specified attribute scan
-/* routine. tls_proxy_context_scan() is meant to be passed as
-/* a call-back to attr_scan(), thusly:
-/*
-/* ... RECV_ATTR_FUNC(tls_proxy_context_scan, (void *) tls_context), ...
-/* DIAGNOSTICS
-/* Fatal: out of memory.
-/* LICENSE
-/* .ad
-/* .fi
-/* The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/* Wietse Venema
-/* IBM T.J. Watson Research
-/* P.O. Box 704
-/* Yorktown Heights, NY 10598, USA
-/*--*/
-
-#ifdef USE_TLS
-
-/* System library. */
-
-#include <sys_defs.h>
-
-/* Utility library */
-
-#include <attr.h>
-
-/* Global library. */
-
-#include <mail_proto.h>
-
-/* TLS library. */
-
-#include <tls.h>
-#include <tls_proxy.h>
-
-/* tls_proxy_context_scan - receive TLS session state from stream */
-
-int tls_proxy_context_scan(ATTR_SCAN_MASTER_FN scan_fn, VSTREAM *fp,
- int flags, void *ptr)
-{
- TLS_SESS_STATE *tls_context = (TLS_SESS_STATE *) ptr;
- int ret;
- VSTRING *peer_CN = vstring_alloc(25);
- VSTRING *issuer_CN = vstring_alloc(25);
- VSTRING *peer_cert_fprint = vstring_alloc(60); /* 60 for SHA-1 */
- VSTRING *peer_pkey_fprint = vstring_alloc(60); /* 60 for SHA-1 */
- VSTRING *protocol = vstring_alloc(25);
- VSTRING *cipher_name = vstring_alloc(25);
-
- /*
- * Note: memset() is not a portable way to initialize non-integer types.
- */
- memset(ptr, 0, sizeof(TLS_SESS_STATE));
- ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
- RECV_ATTR_STR(MAIL_ATTR_PEER_CN, peer_CN),
- RECV_ATTR_STR(MAIL_ATTR_ISSUER_CN, issuer_CN),
- RECV_ATTR_STR(MAIL_ATTR_PEER_CERT_FPT, peer_cert_fprint),
- RECV_ATTR_STR(MAIL_ATTR_PEER_PKEY_FPT, peer_pkey_fprint),
- RECV_ATTR_INT(MAIL_ATTR_PEER_STATUS,
- &tls_context->peer_status),
- RECV_ATTR_STR(MAIL_ATTR_CIPHER_PROTOCOL, protocol),
- RECV_ATTR_STR(MAIL_ATTR_CIPHER_NAME, cipher_name),
- RECV_ATTR_INT(MAIL_ATTR_CIPHER_USEBITS,
- &tls_context->cipher_usebits),
- RECV_ATTR_INT(MAIL_ATTR_CIPHER_ALGBITS,
- &tls_context->cipher_algbits),
- ATTR_TYPE_END);
- tls_context->peer_CN = vstring_export(peer_CN);
- tls_context->issuer_CN = vstring_export(issuer_CN);
- tls_context->peer_cert_fprint = vstring_export(peer_cert_fprint);
- tls_context->peer_pkey_fprint = vstring_export(peer_pkey_fprint);
- tls_context->protocol = vstring_export(protocol);
- tls_context->cipher_name = vstring_export(cipher_name);
- return (ret == 9 ? 1 : -1);
-}
-
-#endif
--- /dev/null
+/*++
+/* NAME
+/* tls_proxy_server_print 3
+/* SUMMARY
+/* write TLS_SERVER_XXX structures to stream
+/* SYNOPSIS
+/* #include <tls_proxy.h>
+/*
+/* int tls_proxy_server_init_print(print_fn, stream, flags, ptr)
+/* ATTR_PRINT_MASTER_FN print_fn;
+/* VSTREAM *stream;
+/* int flags;
+/* void *ptr;
+/*
+/* int tls_proxy_server_start_print(print_fn, stream, flags, ptr)
+/* ATTR_PRINT_MASTER_FN print_fn;
+/* VSTREAM *stream;
+/* int flags;
+/* void *ptr;
+/* DESCRIPTION
+/* tls_proxy_server_init_print() writes a TLS_SERVER_INIT_PROPS
+/* structure to the named stream using the specified attribute print
+/* routine. tls_proxy_server_init_print() is meant to be passed as
+/* a call-back to attr_print(), thusly:
+/*
+/* ... SEND_ATTR_FUNC(tls_proxy_server_init_print, (void *) init_props), ...
+/*
+/* tls_proxy_server_start_print() writes a TLS_SERVER_START_PROPS
+/* structure to the named stream using the specified attribute print
+/* routine. tls_proxy_server_start_print() is meant to be passed as
+/* a call-back to attr_print(), thusly:
+/*
+/* ... SEND_ATTR_FUNC(tls_proxy_server_start_print, (void *) start_props), ...
+/* DIAGNOSTICS
+/* Fatal: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+#ifdef USE_TLS
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library */
+
+#include <attr.h>
+
+/* TLS library. */
+
+#include <tls.h>
+#include <tls_proxy.h>
+
+/* tls_proxy_server_init_print - send TLS_SERVER_INIT_PROPS over stream */
+
+int tls_proxy_server_init_print(ATTR_PRINT_MASTER_FN print_fn, VSTREAM *fp,
+ int flags, void *ptr)
+{
+ TLS_SERVER_INIT_PROPS *props = (TLS_SERVER_INIT_PROPS *) ptr;
+ int ret;
+
+#define STRING_OR_EMPTY(s) ((s) ? (s) : "")
+
+ ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+ SEND_ATTR_STR(TLS_ATTR_LOG_PARAM,
+ STRING_OR_EMPTY(props->log_param)),
+ SEND_ATTR_STR(TLS_ATTR_LOG_LEVEL,
+ STRING_OR_EMPTY(props->log_level)),
+ SEND_ATTR_INT(TLS_ATTR_VERIFYDEPTH, props->verifydepth),
+ SEND_ATTR_STR(TLS_ATTR_CACHE_TYPE,
+ STRING_OR_EMPTY(props->cache_type)),
+ SEND_ATTR_INT(TLS_ATTR_SET_SESSID, props->set_sessid),
+ SEND_ATTR_STR(TLS_ATTR_CERT_FILE,
+ STRING_OR_EMPTY(props->cert_file)),
+ SEND_ATTR_STR(TLS_ATTR_KEY_FILE,
+ STRING_OR_EMPTY(props->key_file)),
+ SEND_ATTR_STR(TLS_ATTR_DCERT_FILE,
+ STRING_OR_EMPTY(props->dcert_file)),
+ SEND_ATTR_STR(TLS_ATTR_DKEY_FILE,
+ STRING_OR_EMPTY(props->dkey_file)),
+ SEND_ATTR_STR(TLS_ATTR_ECCERT_FILE,
+ STRING_OR_EMPTY(props->eccert_file)),
+ SEND_ATTR_STR(TLS_ATTR_ECKEY_FILE,
+ STRING_OR_EMPTY(props->eckey_file)),
+ SEND_ATTR_STR(TLS_ATTR_CAFILE,
+ STRING_OR_EMPTY(props->CAfile)),
+ SEND_ATTR_STR(TLS_ATTR_CAPATH,
+ STRING_OR_EMPTY(props->CApath)),
+ SEND_ATTR_STR(TLS_ATTR_PROTOCOLS,
+ STRING_OR_EMPTY(props->protocols)),
+ SEND_ATTR_STR(TLS_ATTR_EECDH_GRADE,
+ STRING_OR_EMPTY(props->eecdh_grade)),
+ SEND_ATTR_STR(TLS_ATTR_DH1K_PARAM_FILE,
+ STRING_OR_EMPTY(props->dh1024_param_file)),
+ SEND_ATTR_STR(TLS_ATTR_DH512_PARAM_FILE,
+ STRING_OR_EMPTY(props->dh512_param_file)),
+ SEND_ATTR_INT(TLS_ATTR_ASK_CCERT, props->ask_ccert),
+ SEND_ATTR_STR(TLS_ATTR_MDALG,
+ STRING_OR_EMPTY(props->mdalg)),
+ ATTR_TYPE_END);
+ /* Do not flush the stream. */
+ return (ret);
+}
+
+/* tls_proxy_server_start_print - send TLS_SERVER_START_PROPS over stream */
+
+int tls_proxy_server_start_print(ATTR_PRINT_MASTER_FN print_fn, VSTREAM *fp,
+ int flags, void *ptr)
+{
+ TLS_SERVER_START_PROPS *props = (TLS_SERVER_START_PROPS *) ptr;
+ int ret;
+
+#define STRING_OR_EMPTY(s) ((s) ? (s) : "")
+
+ ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+ SEND_ATTR_INT(TLS_ATTR_TIMEOUT, props->timeout),
+ SEND_ATTR_INT(TLS_ATTR_REQUIRECERT, props->requirecert),
+ SEND_ATTR_STR(TLS_ATTR_SERVERID,
+ STRING_OR_EMPTY(props->serverid)),
+ SEND_ATTR_STR(TLS_ATTR_NAMADDR,
+ STRING_OR_EMPTY(props->namaddr)),
+ SEND_ATTR_STR(TLS_ATTR_CIPHER_GRADE,
+ STRING_OR_EMPTY(props->cipher_grade)),
+ SEND_ATTR_STR(TLS_ATTR_CIPHER_EXCLUSIONS,
+ STRING_OR_EMPTY(props->cipher_exclusions)),
+ SEND_ATTR_STR(TLS_ATTR_MDALG,
+ STRING_OR_EMPTY(props->mdalg)),
+ ATTR_TYPE_END);
+ /* Do not flush the stream. */
+ return (ret);
+}
+
+#endif
--- /dev/null
+/*++
+/* NAME
+/* tls_proxy_server_scan 3
+/* SUMMARY
+/* read TLS_SERVER_XXX structures from stream
+/* SYNOPSIS
+/* #include <tls_proxy.h>
+/*
+/* int tls_proxy_server_init_scan(scan_fn, stream, flags, ptr)
+/* ATTR_SCAN_MASTER_FN scan_fn;
+/* VSTREAM *stream;
+/* int flags;
+/* void *ptr;
+/*
+/* tls_proxy_server_init_free(init_props)
+/* TLS_SERVER_INIT_PROPS *init_props;
+/*
+/* int tls_proxy_server_start_scan(scan_fn, stream, flags, ptr)
+/* ATTR_SCAN_MASTER_FN scan_fn;
+/* VSTREAM *stream;
+/* int flags;
+/* void *ptr;
+/*
+/* void tls_proxy_server_start_free(start_props)
+/* TLS_SERVER_START_PROPS *start_props;
+/* DESCRIPTION
+/* tls_proxy_server_init_scan() reads a TLS_SERVER_INIT_PROPS
+/* structure from the named stream using the specified attribute
+/* scan routine. tls_proxy_server_init_scan() is meant to be passed
+/* as a call-back function to attr_scan(), as shown below.
+/*
+/* tls_proxy_server_init_free() destroys a TLS_SERVER_INIT_PROPS
+/* structure that was created by tls_proxy_server_init_scan().
+/*
+/* TLS_SERVER_INIT_PROPS *init_props = 0;
+/* ...
+/* ... RECV_ATTR_FUNC(tls_proxy_server_init_scan, (void *) &init_props)
+/* ...
+/* if (init_props)
+/* tls_proxy_client_init_free(init_props);
+/*
+/* tls_proxy_server_start_scan() reads a TLS_SERVER_START_PROPS
+/* structure from the named stream using the specified attribute
+/* scan routine. tls_proxy_server_start_scan() is meant to be passed
+/* as a call-back function to attr_scan(), as shown below.
+/*
+/* tls_proxy_server_start_free() destroys a TLS_SERVER_START_PROPS
+/* structure that was created by tls_proxy_server_start_scan().
+/*
+/* TLS_SERVER_START_PROPS *start_props = 0;
+/* ...
+/* ... RECV_ATTR_FUNC(tls_proxy_server_start_scan, (void *) &start_props)
+/* ...
+/* if (start_props)
+/* tls_proxy_server_start_free(start_props);
+/* DIAGNOSTICS
+/* Fatal: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+#ifdef USE_TLS
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library */
+
+#include <attr.h>
+
+/* TLS library. */
+
+#include <tls.h>
+#include <tls_proxy.h>
+
+/* tls_proxy_server_init_scan - receive TLS_SERVER_INIT_PROPS from stream */
+
+int tls_proxy_server_init_scan(ATTR_SCAN_MASTER_FN scan_fn, VSTREAM *fp,
+ int flags, void *ptr)
+{
+ TLS_SERVER_INIT_PROPS *props
+ = (TLS_SERVER_INIT_PROPS *) mymalloc(sizeof(*props));
+ int ret;
+ VSTRING *log_param = vstring_alloc(25);
+ VSTRING *log_level = vstring_alloc(25);
+ VSTRING *cache_type = vstring_alloc(25);
+ VSTRING *cert_file = vstring_alloc(25);
+ VSTRING *key_file = vstring_alloc(25);
+ VSTRING *dcert_file = vstring_alloc(25);
+ VSTRING *dkey_file = vstring_alloc(25);
+ VSTRING *eccert_file = vstring_alloc(25);
+ VSTRING *eckey_file = vstring_alloc(25);
+ VSTRING *CAfile = vstring_alloc(25);
+ VSTRING *CApath = vstring_alloc(25);
+ VSTRING *protocols = vstring_alloc(25);
+ VSTRING *eecdh_grade = vstring_alloc(25);
+ VSTRING *dh1024_param_file = vstring_alloc(25);
+ VSTRING *dh512_param_file = vstring_alloc(25);
+ VSTRING *mdalg = vstring_alloc(25);
+
+ /*
+ * Note: memset() is not a portable way to initialize non-integer types.
+ */
+ memset(props, 0, sizeof(*props));
+ ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+ RECV_ATTR_STR(TLS_ATTR_LOG_PARAM, log_param),
+ RECV_ATTR_STR(TLS_ATTR_LOG_LEVEL, log_level),
+ RECV_ATTR_INT(TLS_ATTR_VERIFYDEPTH, &props->verifydepth),
+ RECV_ATTR_STR(TLS_ATTR_CACHE_TYPE, cache_type),
+ RECV_ATTR_INT(TLS_ATTR_SET_SESSID, &props->set_sessid),
+ RECV_ATTR_STR(TLS_ATTR_CERT_FILE, cert_file),
+ RECV_ATTR_STR(TLS_ATTR_KEY_FILE, key_file),
+ RECV_ATTR_STR(TLS_ATTR_DCERT_FILE, dcert_file),
+ RECV_ATTR_STR(TLS_ATTR_DKEY_FILE, dkey_file),
+ RECV_ATTR_STR(TLS_ATTR_ECCERT_FILE, eccert_file),
+ RECV_ATTR_STR(TLS_ATTR_ECKEY_FILE, eckey_file),
+ RECV_ATTR_STR(TLS_ATTR_CAFILE, CAfile),
+ RECV_ATTR_STR(TLS_ATTR_CAPATH, CApath),
+ RECV_ATTR_STR(TLS_ATTR_PROTOCOLS, protocols),
+ RECV_ATTR_STR(TLS_ATTR_EECDH_GRADE, eecdh_grade),
+ RECV_ATTR_STR(TLS_ATTR_DH1K_PARAM_FILE, dh1024_param_file),
+ RECV_ATTR_STR(TLS_ATTR_DH512_PARAM_FILE, dh512_param_file),
+ RECV_ATTR_INT(TLS_ATTR_ASK_CCERT, &props->ask_ccert),
+ RECV_ATTR_STR(TLS_ATTR_MDALG, mdalg),
+ ATTR_TYPE_END);
+ /* Always construct a well-formed structure. */
+ props->log_param = vstring_export(log_param);
+ props->log_level = vstring_export(log_level);
+ props->cache_type = vstring_export(cache_type);
+ props->cert_file = vstring_export(cert_file);
+ props->key_file = vstring_export(key_file);
+ props->dcert_file = vstring_export(dcert_file);
+ props->dkey_file = vstring_export(dkey_file);
+ props->eccert_file = vstring_export(eccert_file);
+ props->eckey_file = vstring_export(eckey_file);
+ props->CAfile = vstring_export(CAfile);
+ props->CApath = vstring_export(CApath);
+ props->protocols = vstring_export(protocols);
+ props->eecdh_grade = vstring_export(eecdh_grade);
+ props->dh1024_param_file = vstring_export(dh1024_param_file);
+ props->dh512_param_file = vstring_export(dh512_param_file);
+ props->mdalg = vstring_export(mdalg);
+ ret = (ret == 19 ? 1 : -1);
+ if (ret != 1) {
+ tls_proxy_server_init_free(props);
+ props = 0;
+ }
+ *(TLS_SERVER_INIT_PROPS **) ptr = props;
+ return (ret);
+}
+
+/* tls_proxy_server_init_free - destroy TLS_SERVER_INIT_PROPS structure */
+
+void tls_proxy_server_init_free(TLS_SERVER_INIT_PROPS *props)
+{
+ myfree((void *) props->log_param);
+ myfree((void *) props->log_level);
+ myfree((void *) props->cache_type);
+ myfree((void *) props->cert_file);
+ myfree((void *) props->key_file);
+ myfree((void *) props->dcert_file);
+ myfree((void *) props->dkey_file);
+ myfree((void *) props->eccert_file);
+ myfree((void *) props->eckey_file);
+ myfree((void *) props->CAfile);
+ myfree((void *) props->CApath);
+ myfree((void *) props->protocols);
+ myfree((void *) props->eecdh_grade);
+ myfree((void *) props->dh1024_param_file);
+ myfree((void *) props->dh512_param_file);
+ myfree((void *) props->mdalg);
+ myfree((void *) props);
+}
+
+/* tls_proxy_server_start_scan - receive TLS_SERVER_START_PROPS from stream */
+
+int tls_proxy_server_start_scan(ATTR_SCAN_MASTER_FN scan_fn, VSTREAM *fp,
+ int flags, void *ptr)
+{
+ TLS_SERVER_START_PROPS *props
+ = (TLS_SERVER_START_PROPS *) mymalloc(sizeof(*props));
+ int ret;
+ VSTRING *serverid = vstring_alloc(25);
+ VSTRING *namaddr = vstring_alloc(25);
+ VSTRING *cipher_grade = vstring_alloc(25);
+ VSTRING *cipher_exclusions = vstring_alloc(25);
+ VSTRING *mdalg = vstring_alloc(25);
+
+ /*
+ * Note: memset() is not a portable way to initialize non-integer types.
+ */
+ memset(props, 0, sizeof(*props));
+ props->ctx = 0;
+ props->stream = 0;
+ /* XXX Caller sets fd. */
+ ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+ RECV_ATTR_INT(TLS_ATTR_TIMEOUT, &props->timeout),
+ RECV_ATTR_INT(TLS_ATTR_REQUIRECERT, &props->requirecert),
+ RECV_ATTR_STR(TLS_ATTR_SERVERID, serverid),
+ RECV_ATTR_STR(TLS_ATTR_NAMADDR, namaddr),
+ RECV_ATTR_STR(TLS_ATTR_CIPHER_GRADE, cipher_grade),
+ RECV_ATTR_STR(TLS_ATTR_CIPHER_EXCLUSIONS,
+ cipher_exclusions),
+ RECV_ATTR_STR(TLS_ATTR_MDALG, mdalg),
+ ATTR_TYPE_END);
+ props->serverid = vstring_export(serverid);
+ props->namaddr = vstring_export(namaddr);
+ props->cipher_grade = vstring_export(cipher_grade);
+ props->cipher_exclusions = vstring_export(cipher_exclusions);
+ props->mdalg = vstring_export(mdalg);
+ ret = (ret == 7 ? 1 : -1);
+ if (ret != 1) {
+ tls_proxy_server_start_free(props);
+ props = 0;
+ }
+ *(TLS_SERVER_START_PROPS **) ptr = props;
+ return (ret);
+}
+
+/* tls_proxy_server_start_free - destroy TLS_SERVER_START_PROPS structure */
+
+void tls_proxy_server_start_free(TLS_SERVER_START_PROPS *props)
+{
+ /* XXX Caller closes fd. */
+ myfree((void *) props->serverid);
+ myfree((void *) props->namaddr);
+ myfree((void *) props->cipher_grade);
+ myfree((void *) props->cipher_exclusions);
+ myfree((void *) props->mdalg);
+ myfree((void *) props);
+}
+
+#endif
@$(EXPORT) make -f Makefile.in Makefile 1>&2
# do not edit below this line - it is generated by 'make depend'
+tls_client.o: tls_client.c
tlsproxy.o: ../../include/argv.h
tlsproxy.o: ../../include/attr.h
tlsproxy.o: ../../include/check_arg.h
tlsproxy.o: tlsproxy.c
tlsproxy.o: tlsproxy.h
tlsproxy_state.o: ../../include/argv.h
+tlsproxy_state.o: ../../include/attr.h
tlsproxy_state.o: ../../include/check_arg.h
tlsproxy_state.o: ../../include/dns.h
tlsproxy_state.o: ../../include/events.h
+tlsproxy_state.o: ../../include/htable.h
tlsproxy_state.o: ../../include/mail_conf.h
tlsproxy_state.o: ../../include/mail_server.h
tlsproxy_state.o: ../../include/msg.h
tlsproxy_state.o: ../../include/name_code.h
tlsproxy_state.o: ../../include/name_mask.h
tlsproxy_state.o: ../../include/nbbio.h
+tlsproxy_state.o: ../../include/nvtable.h
tlsproxy_state.o: ../../include/sock_addr.h
tlsproxy_state.o: ../../include/sys_defs.h
tlsproxy_state.o: ../../include/tls.h
+tlsproxy_state.o: ../../include/tls_proxy.h
tlsproxy_state.o: ../../include/vbuf.h
tlsproxy_state.o: ../../include/vstream.h
tlsproxy_state.o: ../../include/vstring.h
/* SYNOPSIS
/* \fBtlsproxy\fR [generic Postfix daemon options]
/* DESCRIPTION
-/* The \fBtlsproxy\fR(8) server implements a server-side TLS
-/* proxy. It is used by \fBpostscreen\fR(8) to talk SMTP-over-TLS
+/* The \fBtlsproxy\fR(8) server implements a two-way TLS proxy. It
+/* is used by the \fBpostscreen\fR(8) server to talk SMTP-over-TLS
/* with remote SMTP clients that are not whitelisted (including
-/* clients whose whitelist status has expired),
-/* but it should also work for non-SMTP protocols.
+/* clients whose whitelist status has expired), and by the
+/* \fBsmtp\fR(8) client to support TLS connection reuse, but it
+/* should also work for non-SMTP protocols.
/*
/* Although one \fBtlsproxy\fR(8) process can serve multiple
/* sessions at the same time, it is a good idea to allow the
static TLS_APPL_STATE *tlsp_server_ctx;
static int ask_client_cert;
+ /*
+ * TLS per-client status.
+ */
+static HTABLE *tlsp_client_app_cache;
+
+ /*
+ * Error handling: if a function detects an error, then that function is
+ * responsible for destroying TLSP_STATE. Exceptions to this principle are
+ * indicated in the code.
+ */
+
+ /*
+ * Internal status API.
+ */
+#define TLSP_STAT_OK 0
+#define TLSP_STAT_ERR (-1)
+
/*
* SLMs.
*/
#define STR(x) vstring_str(x)
/*
- * This code looks simpler than expected. That is the result of a great deal
- * of effort, mainly in design and analysis.
+ * The code that implements the TLS engine looks simpler than expected. That
+ * is the result of a great deal of effort, mainly in design and analysis.
+ *
+ * The initial use case was to provide TLS support for postscreen(8).
*
* By design, postscreen(8) is an event-driven server that must scale up to a
* large number of clients. This means that postscreen(8) must avoid doing
* public mailing lists. After some field experience with this code, we may
* be able to factor it out as a library module, like nbbio(3), that can
* become part of the TLS library.
+ *
+ * Later in the life cycle, tlsproxy(8) has also become an enabler for TLS
+ * connection reuse across different SMTP client processes.
*/
static void tlsp_ciphertext_event(int, void *);
#define TLSP_INIT_TIMEOUT 100
+static void tlsp_plaintext_event(int event, void *context);
+
/* tlsp_drain - delayed exit after "postfix reload" */
static void tlsp_drain(char *unused_service, char **unused_argv)
state->timeout);
state->ssl_last_err = SSL_ERROR_NONE;
}
- return (0);
+ return (TLSP_STAT_OK);
/*
* The TLS engine wants to write to the network. Turn on
}
event_request_timer(tlsp_ciphertext_event, (void *) state,
state->timeout);
- return (0);
+ return (TLSP_STAT_OK);
/*
* The TLS engine wants to read from the network. Turn on
}
event_request_timer(tlsp_ciphertext_event, (void *) state,
state->timeout);
- return (0);
+ return (TLSP_STAT_OK);
/*
* Some error. Self-destruct. This automagically cleans up all
/* FALLTHROUGH */
default:
tlsp_state_free(state);
- return (-1);
+ return (TLSP_STAT_ERR);
+ }
+}
+
+/* tlsp_post_handshake - post-handshake processing */
+
+static int tlsp_post_handshake(TLSP_STATE *state)
+{
+
+ /*
+ * Do not assume that tls_server_post_accept() and
+ * tls_client_post_connect() will always succeed.
+ */
+ if (state->is_server_role)
+ state->tls_context = tls_server_post_accept(state->tls_context);
+ else
+ state->tls_context = tls_client_post_connect(state->tls_context,
+ state->client_start_props);
+ if (state->tls_context == 0) {
+ tlsp_state_free(state);
+ return (TLSP_STAT_ERR);
+ }
+
+ /*
+ * Report TLS handshake results to the tlsproxy client.
+ *
+ * Security: this sends internal data over the same local plaintext stream
+ * that will also be used for sending decrypted remote content from an
+ * arbitrary remote peer. For this reason we enable decrypted I/O only
+ * after reporting the TLS handshake results. The Postfix attribute
+ * protocol is robust enough that an attacker cannot append content.
+ */
+ if ((state->req_flags & TLS_PROXY_FLAG_SEND_CONTEXT) != 0
+ && (attr_print(state->plaintext_stream, ATTR_FLAG_NONE,
+ SEND_ATTR_FUNC(tls_proxy_context_print,
+ (void *) state->tls_context),
+ ATTR_TYPE_END) != 0
+ || vstream_fflush(state->plaintext_stream) != 0)) {
+ msg_warn("cannot send TLS context: %m");
+ tlsp_state_free(state);
+ return (TLSP_STAT_ERR);
}
+
+ /*
+ * Initialize plaintext-related session state. Once we have this behind
+ * us, the TLSP_STATE destructor will automagically clean up requests for
+ * plaintext read/write/timeout events, which makes error recovery
+ * easier.
+ */
+ state->plaintext_buf =
+ nbbio_create(vstream_fileno(state->plaintext_stream),
+ VSTREAM_BUFSIZE, state->server_id,
+ tlsp_plaintext_event,
+ (void *) state);
+ return (TLSP_STAT_OK);
}
/* tlsp_strategy - decide what to read or write next. */
int handshake_err;
/*
- * Be sure to complete the TLS handshake before enabling plain-text I/O.
- * In case of an unrecoverable error, this automagically cleans up all
- * pending read/write and timeout event requests.
+ * Do not enable plain-text I/O before completing the TLS handshake.
+ * Otherwise the remote peer can prepend plaintext to the optional
+ * TLS_SESS_STATE object.
*/
if (state->flags & TLSP_FLAG_DO_HANDSHAKE) {
- ssl_stat = SSL_accept(tls_context->con);
+ state->timeout = state->handshake_timeout;
+ if (state->is_server_role)
+ ssl_stat = SSL_accept(tls_context->con);
+ else
+ ssl_stat = SSL_connect(tls_context->con);
if (ssl_stat != 1) {
handshake_err = SSL_get_error(tls_context->con, ssl_stat);
tlsp_eval_tls_error(state, handshake_err);
/* At this point, state could be a dangling pointer. */
return;
}
- if ((state->tls_context = tls_server_post_accept(tls_context)) == 0) {
- tlsp_state_free(state);
- return;
- }
- if ((state->req_flags & TLS_PROXY_FLAG_SEND_CONTEXT) != 0
- && (attr_print(state->plaintext_stream, ATTR_FLAG_NONE,
- SEND_ATTR_FUNC(tls_proxy_context_print,
- (void *) state->tls_context),
- ATTR_TYPE_END) != 0
- || vstream_fflush(state->plaintext_stream) != 0)) {
- msg_warn("cannot send TLS context: %m");
- tlsp_state_free(state);
+ state->flags &= ~TLSP_FLAG_DO_HANDSHAKE;
+ state->timeout = state->session_timeout;
+ if (tlsp_post_handshake(state) != TLSP_STAT_OK) {
+ /* At this point, state is a dangling pointer. */
return;
}
- state->flags &= ~TLSP_FLAG_DO_HANDSHAKE;
}
/*
*/
if (tlsp_eval_tls_error(state, ssl_write_err != SSL_ERROR_NONE ?
ssl_write_err : ssl_read_err) < 0)
+ /* At this point, state is a dangling pointer. */
return;
/*
* Try to enable/disable plaintext read/write events. Basically, if we
- * have nothing to write to the postscreen(8) server, see if there is
+ * have nothing to write to the plaintext stream, see if there is
* something to read. If the write buffer is empty and the read buffer is
* full, suspend plaintext I/O until conditions change (but keep the
* timer active, as a safety mechanism in case ciphertext I/O gets
* stuck).
*
- * XXX In theory, if the client keeps writing fast enough then we would
- * never read from postscreen(8), and cause postscreen(8) to block. In
- * practice, postscreen(8) limits the number of client commands, and thus
- * postscreen(8)'s output will fit in a kernel buffer. This may not be
- * true in other scenarios where the tlsproxy(8) server could be used.
+ * XXX In theory, if the ciphertext peer keeps writing fast enough then we
+ * would never read from the plaintext stream and cause the latter to
+ * block. In practice, postscreen(8) limits the number of client
+ * commands, and thus postscreen(8)'s output will fit in a kernel buffer.
+ * A remote SMTP server is not supposed to flood the local SMTP client
+ * with massive replies; it it does, then the local SMTP client should
+ * deal with it.
*/
if (NBBIO_WRITE_PEND(plaintext_buf) > 0) {
if (NBBIO_ACTIVE_FLAGS(plaintext_buf) & NBBIO_FLAG_READ)
* want to read or write more plaintext.
*/
tlsp_strategy(state);
+ /* At this point, state could be a dangling pointer. */
}
/* tlsp_ciphertext_event - ciphertext is ready to read/write */
*/
if (event == EVENT_READ || event == EVENT_WRITE) {
tlsp_strategy(state);
+ /* At this point, state could be a dangling pointer. */
} else {
if (event == EVENT_TIME && state->ssl_last_err == SSL_ERROR_NONE)
msg_warn("deadlock on plaintext stream for %s",
}
}
-/* tlsp_start_tls - turn on TLS or force disconnect */
+/* tlsp_client_start_pre_handshake - turn on TLS or force disconnect */
-static int tlsp_start_tls(TLSP_STATE *state)
+static int tlsp_client_start_pre_handshake(TLSP_STATE *state)
+{
+ state->client_start_props->ctx = state->appl_state;
+ state->client_start_props->fd = state->ciphertext_fd;
+ state->tls_context = tls_client_start(state->client_start_props);
+ if (state->tls_context != 0)
+ return (TLSP_STAT_OK);
+
+ tlsp_state_free(state);
+ return (TLSP_STAT_ERR);
+}
+
+/* tlsp_server_start_pre_handshake - turn on TLS or force disconnect */
+
+static int tlsp_server_start_pre_handshake(TLSP_STATE *state)
{
TLS_SERVER_START_PROPS props;
static char *cipher_grade;
*/
/*
- * Perform the before-handshake portion of the per-session initialization.
- * Pass a null VSTREAM to indicate that this program, will do the
+ * Perform the before-handshake portion of per-session initialization.
+ * Pass a null VSTREAM to indicate that this program will do the
* ciphertext I/O, not libtls.
*
* The cipher grade and exclusions don't change between sessions. Compute
if (state->tls_context == 0) {
tlsp_state_free(state);
- return (-1);
+ return (TLSP_STAT_ERR);
}
/*
* whitelist status, but bad clients hammering the server can suck up
* lots of CPU cycles. Per-client concurrency limits in postscreen(8)
* will divert only naive security "researchers".
- *
- * XXX Do we care about certificate verification results? Not as long as
- * postscreen(8) doesn't actually receive email.
*/
- return (0);
+ return (TLSP_STAT_OK);
+}
+
+ /*
+ * From here on down is low-level code that sets up the plumbing before
+ * passing control to the TLS engine above.
+ */
+
+/* tlsp_request_read_event - pre-handshake event boiler plate */
+
+static void tlsp_request_read_event(int fd, EVENT_NOTIFY_FN handler,
+ int timeout, void *context)
+{
+ event_enable_read(fd, handler, context);
+ event_request_timer(handler, context, timeout);
+}
+
+/* tlsp_accept_event - pre-handshake event boiler plate */
+
+static void tlsp_accept_event(int event, EVENT_NOTIFY_FN handler,
+ void *context)
+{
+ if (event != EVENT_TIME)
+ event_cancel_timer(handler, context);
+ else
+ errno = ETIMEDOUT;
+ /* tlsp_state_free() disables pre-handshake plaintext I/O events. */
}
-/* tlsp_get_fd_event - receive final postscreen(8) hand-off information */
+/* tlsp_get_fd_event - receive final connection hand-off information */
static void tlsp_get_fd_event(int event, void *context)
{
const char *myname = "tlsp_get_fd_event";
TLSP_STATE *state = (TLSP_STATE *) context;
int plaintext_fd = vstream_fileno(state->plaintext_stream);
+ int status;
/*
* At this point we still manually manage plaintext read/write/timeout
- * events. Disable I/O and timer events. Don't assume that the first
- * plaintext request will be a read.
+ * events. Disable I/O events on the plaintext stream until the TLS
+ * handshake is completed. Every code path must either destroy state, or
+ * request the next event, otherwise we have a file and memory leak.
*/
+ tlsp_accept_event(event, tlsp_get_fd_event, (void *) state);
event_disable_readwrite(plaintext_fd);
- if (event != EVENT_TIME)
- event_cancel_timer(tlsp_get_fd_event, (void *) state);
- else
- errno = ETIMEDOUT;
- /*
- * Initialize plaintext-related session state. Once we have this behind
- * us, the TLSP_STATE destructor will automagically clean up requests for
- * read/write/timeout events, which makes error recovery easier.
- *
- * Register the plaintext event handler for timer cleanup in the TLSP_STATE
- * destructor. Insert the NBBIO event-driven I/O layer between the
- * postscreen(8) server and the TLS engine.
- */
if (event != EVENT_READ
|| (state->ciphertext_fd = LOCAL_RECV_FD(plaintext_fd)) < 0) {
- msg_warn("%s: receive SMTP client file descriptor: %m", myname);
+ msg_warn("%s: receive remote SMTP peer file descriptor: %m", myname);
tlsp_state_free(state);
return;
}
- non_blocking(state->ciphertext_fd, NON_BLOCKING);
+
+ /*
+ * This is a bit early, to ensure that timer events for this file handle
+ * are guaranteed to be turned off by the TLSP_STATE destructor.
+ */
state->ciphertext_timer = tlsp_ciphertext_event;
- state->plaintext_buf = nbbio_create(plaintext_fd,
- VSTREAM_BUFSIZE, "postscreen",
- tlsp_plaintext_event,
- (void *) state);
+ non_blocking(state->ciphertext_fd, NON_BLOCKING);
/*
* Perform the TLS layer before-handshake initialization. We perform the
- * remainder after the TLS handshake completes.
+ * remainder after the actual TLS handshake completes.
*/
- if (tlsp_start_tls(state) < 0)
+ if (state->is_server_role)
+ status = tlsp_server_start_pre_handshake(state);
+ else
+ status = tlsp_client_start_pre_handshake(state);
+ if (status != TLSP_STAT_OK)
+ /* At this point, state is a dangling pointer. */
return;
/*
* Trigger the initial proxy server I/Os.
*/
tlsp_strategy(state);
+ /* At this point, state could be a dangling pointer. */
+}
+
+ /*
+ * This function does not destroy TLSP_STATE in case of error, because that
+ * would complicate the caller.
+ */
+
+/* tlsp_client_init_no_tlsp_state_free - initialize a TLS client engine */
+
+static int tlsp_client_init_no_tlsp_state_free(TLSP_STATE *state)
+{
+ VSTRING *buf;
+ char *key;
+
+ /*
+ * Share a TLS_APPL_STATE object among multiple requests that specify the
+ * same TLS_CLIENT_INIT_PROPS. TLS_APPL_STATE owns an SSL_CTX which is
+ * expensive.
+ */
+ buf = vstring_alloc(100);
+ key = tls_proxy_client_init_to_string(buf, state->client_init_props);
+ if ((state->appl_state = (TLS_APPL_STATE *)
+ htable_find(tlsp_client_app_cache, key)) == 0
+ && (state->appl_state =
+ tls_client_init(state->client_init_props)) != 0) {
+ (void) htable_enter(tlsp_client_app_cache, key,
+ (void *) state->appl_state);
+
+ /*
+ * To maintain sanity, allow partial SSL_write() operations, and
+ * allow SSL_write() buffer pointers to change after a WANT_READ or
+ * WANT_WRITE result. This is based on OpenSSL developers talking on
+ * a mailing list, but is not supported by documentation. If this
+ * code stops working then no-one can be held responsible.
+ */
+ if (state->appl_state)
+ SSL_CTX_set_mode(state->appl_state->ssl_ctx,
+ SSL_MODE_ENABLE_PARTIAL_WRITE
+ | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+ }
+ vstring_free(buf);
+ return (state->appl_state != 0);
+}
+
+/* tlsp_close_event - pre-handshake plaintext-client close event */
+
+static void tlsp_close_event(int event, void *context)
+{
+ TLSP_STATE *state = (TLSP_STATE *) context;
+
+ tlsp_accept_event(event, tlsp_close_event, (void *) state);
+ tlsp_state_free(state);
}
-/* tlsp_get_request_event - receive initial postscreen(8) hand-off info */
+/* tlsp_get_request_event - receive initial hand-off info */
static void tlsp_get_request_event(int event, void *context)
{
static VSTRING *remote_endpt;
static VSTRING *server_id;
int req_flags;
- int timeout;
- int ready;
+ int handshake_timeout;
+ int session_timeout;
+ int ready = 0;
+
+ /*
+ * At this point we still manually manage plaintext read/write/timeout
+ * events. Every code path must either destroy state or request the next
+ * event, otherwise this pseudo-thread is idle until the client goes
+ * away.
+ */
+ tlsp_accept_event(event, tlsp_get_request_event, (void *) state);
/*
* One-time initialization.
}
/*
- * At this point we still manually manage plaintext read/write/timeout
- * events. Turn off timer events. Below we disable read events on error,
- * and redefine read events on success.
- */
- if (event != EVENT_TIME)
- event_cancel_timer(tlsp_get_request_event, (void *) state);
- else
- errno = ETIMEDOUT;
-
- /*
- * We must send some data, after receiving the request attributes and
- * before receiving the remote file descriptor. We can't assume
- * UNIX-domain socket semantics here.
+ * Receive the initial request attributes. Receive the remainder after we
+ * figure out what role we are expected to play.
*/
if (event != EVENT_READ
|| attr_scan(plaintext_stream, ATTR_FLAG_STRICT,
- RECV_ATTR_STR(MAIL_ATTR_REMOTE_ENDPT, remote_endpt),
- RECV_ATTR_INT(MAIL_ATTR_FLAGS, &req_flags),
- RECV_ATTR_INT(MAIL_ATTR_TIMEOUT, &timeout),
- RECV_ATTR_STR(MAIL_ATTR_SERVER_ID, server_id),
- ATTR_TYPE_END) != 4) {
+ RECV_ATTR_STR(TLS_ATTR_REMOTE_ENDPT, remote_endpt),
+ RECV_ATTR_INT(TLS_ATTR_FLAGS, &req_flags),
+ RECV_ATTR_INT(TLS_ATTR_TIMEOUT, &handshake_timeout),
+ RECV_ATTR_INT(TLS_ATTR_TIMEOUT, &session_timeout),
+ RECV_ATTR_STR(TLS_ATTR_SERVERID, server_id),
+ ATTR_TYPE_END) != 5) {
msg_warn("%s: receive request attributes: %m", myname);
- event_disable_readwrite(plaintext_fd);
tlsp_state_free(state);
return;
}
/*
+ * XXX We use the same fixed timeout throughout the entire session for
+ * both plaintext and ciphertext communication. This timeout is just a
+ * safety feature; the real timeout will be enforced by our plaintext
+ * peer (except during TLS the handshake, when we intentionally disable
+ * plaintext I/O).
+ */
+ state->remote_endpt = mystrdup(STR(remote_endpt));
+ state->server_id = mystrdup(STR(server_id));
+ msg_info("CONNECT %s %s",
+ (req_flags & TLS_PROXY_FLAG_ROLE_SERVER) ? "from" :
+ (req_flags & TLS_PROXY_FLAG_ROLE_CLIENT) ? "to" :
+ "(bogus_direction)", state->remote_endpt);
+ state->req_flags = req_flags;
+ /* state->is_server_role is set below. */
+ state->handshake_timeout = handshake_timeout + 10; /* XXX */
+ state->session_timeout = session_timeout + 10; /* XXX */
+
+ /*
+ * Receive the TLS preferences now, to reduce the number of protocol
+ * roundtrips.
+ */
+ switch (req_flags & (TLS_PROXY_FLAG_ROLE_CLIENT | TLS_PROXY_FLAG_ROLE_SERVER)) {
+ case TLS_PROXY_FLAG_ROLE_CLIENT:
+ state->is_server_role = 0;
+ if (attr_scan(plaintext_stream, ATTR_FLAG_STRICT,
+ RECV_ATTR_FUNC(tls_proxy_client_init_scan,
+ (void *) &state->client_init_props),
+ RECV_ATTR_FUNC(tls_proxy_client_start_scan,
+ (void *) &state->client_start_props),
+ ATTR_TYPE_END) != 2) {
+ msg_warn("%s: receive client TLS settings: %m", myname);
+ tlsp_state_free(state);
+ return;
+ }
+ ready = tlsp_client_init_no_tlsp_state_free(state);
+ break;
+ case TLS_PROXY_FLAG_ROLE_SERVER:
+ state->is_server_role = 1;
+ ready = (tlsp_server_ctx != 0);
+ break;
+ default:
+ state->is_server_role = 0;
+ msg_warn("%s: bad request flags: 0x%x", myname, req_flags);
+ ready = 0;
+ }
+
+ /*
+ * For portability we must send some data, after receiving the request
+ * attributes and before receiving the remote file descriptor.
+ *
* If the requested TLS engine is unavailable, hang up after making sure
* that the plaintext peer has received our "sorry" indication.
*/
- ready = ((req_flags & TLS_PROXY_FLAG_ROLE_SERVER) != 0
- && tlsp_server_ctx != 0);
if (attr_print(plaintext_stream, ATTR_FLAG_NONE,
SEND_ATTR_INT(MAIL_ATTR_STATUS, ready),
ATTR_TYPE_END) != 0
|| vstream_fflush(plaintext_stream) != 0
|| ready == 0) {
- read_wait(plaintext_fd, TLSP_INIT_TIMEOUT); /* XXX */
- event_disable_readwrite(plaintext_fd);
- tlsp_state_free(state);
+ tlsp_request_read_event(plaintext_fd, tlsp_close_event,
+ TLSP_INIT_TIMEOUT, (void *) state);
return;
- }
-
- /*
- * XXX We use the same fixed timeout throughout the entire session for
- * both plaintext and ciphertext communication. This timeout is just a
- * safety feature; the real timeout will be enforced by our plaintext
- * peer.
- */
- else {
- state->remote_endpt = mystrdup(STR(remote_endpt));
- state->server_id = mystrdup(STR(server_id));
- msg_info("CONNECT %s %s",
- (req_flags & TLS_PROXY_FLAG_ROLE_SERVER) ? "from" :
- (req_flags & TLS_PROXY_FLAG_ROLE_CLIENT) ? "to" :
- "(bogus_direction)", state->remote_endpt);
- state->req_flags = req_flags;
- state->timeout = timeout + 10; /* XXX */
- event_enable_read(plaintext_fd, tlsp_get_fd_event, (void *) state);
- event_request_timer(tlsp_get_fd_event, (void *) state,
- TLSP_INIT_TIMEOUT);
+ } else {
+ tlsp_request_read_event(plaintext_fd, tlsp_get_fd_event,
+ TLSP_INIT_TIMEOUT, (void *) state);
return;
}
}
* Receive postscreen's remote SMTP client address/port and socket.
*/
state = tlsp_state_create(service, plaintext_stream);
- event_enable_read(plaintext_fd, tlsp_get_request_event, (void *) state);
- event_request_timer(tlsp_get_request_event, (void *) state,
- TLSP_INIT_TIMEOUT);
+ tlsp_request_read_event(plaintext_fd, tlsp_get_request_event,
+ TLSP_INIT_TIMEOUT, (void *) state);
}
/* pre_jail_init - pre-jail initialization */
static void post_jail_init(char *unused_name, char **unused_argv)
{
- /* void */ ;
+ tlsp_client_app_cache = htable_create(10);
}
MAIL_VERSION_STAMP_DECLARE;
MAIL_VERSION_STAMP_ALLOCATE;
/*
- * Pass control to the single-threaded service skeleton.
+ * Pass control to the event-driven service skeleton.
*/
event_server_main(argc, argv, tlsp_service,
CA_MAIL_SERVER_INT_TABLE(int_table),
typedef struct {
int flags; /* see below */
int req_flags; /* request flags, see tls_proxy.h */
+ int is_server_role; /* avoid clumsy handler code */
char *service; /* argv[0] */
VSTREAM *plaintext_stream; /* local peer: postscreen(8), etc. */
NBBIO *plaintext_buf; /* plaintext buffer */
int ciphertext_fd; /* remote peer */
EVENT_NOTIFY_FN ciphertext_timer; /* kludge */
int timeout; /* read/write time limit */
+ int handshake_timeout; /* in-handshake time limit */
+ int session_timeout; /* post-handshake time limit */
char *remote_endpt; /* printable remote endpoint */
char *server_id; /* cache management */
- TLS_SESS_STATE *tls_context; /* llibtls state */
+ TLS_APPL_STATE *appl_state; /* libtls state */
+ TLS_SESS_STATE *tls_context; /* libtls state */
int ssl_last_err; /* TLS I/O state */
+ TLS_SERVER_INIT_PROPS *server_init_props;
+ TLS_SERVER_START_PROPS *server_start_props;
+ TLS_CLIENT_INIT_PROPS *client_init_props;
+ TLS_CLIENT_START_PROPS *client_start_props;
} TLSP_STATE;
#define TLSP_FLAG_DO_HANDSHAKE (1<<0)
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/*
/* tlsp_state_create() initializes session context.
/*
-/* tlsp_state_free() destroys session context.
+/* tlsp_state_free() destroys session context. If the handshake
+/* was in progress, it logs a 'handshake failed' message.
/*
/* Arguments:
/* .IP service
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/*
#ifdef USE_TLS
#define TLS_INTERNAL /* XXX */
#include <tls.h>
+#include <tls_proxy.h>
/*
* Application-specific.
state->remote_endpt = 0;
state->server_id = 0;
state->tls_context = 0;
+ state->server_init_props = 0;
+ state->server_start_props = 0;
+ state->client_init_props = 0;
+ state->client_start_props = 0;
return (state);
}
void tlsp_state_free(TLSP_STATE *state)
{
+ /* Don't log failure after plaintext EOF. */
+ if (state->remote_endpt && state->server_id
+ && (state->flags & TLSP_FLAG_DO_HANDSHAKE))
+ msg_info("TLS handshake failed for service=%s peer=%s",
+ state->server_id, state->remote_endpt);
myfree(state->service);
if (state->plaintext_buf) /* turns off plaintext events */
nbbio_free(state->plaintext_buf);
+ else
+ event_disable_readwrite(vstream_fileno(state->plaintext_stream));
event_server_disconnect(state->plaintext_stream);
if (state->ciphertext_fd >= 0) {
event_disable_readwrite(state->ciphertext_fd);
myfree(state->server_id);
if (state->tls_context)
tls_free_context(state->tls_context);
+ if (state->server_init_props)
+ tls_proxy_server_init_free(state->server_init_props);
+ if (state->server_start_props)
+ tls_proxy_server_start_free(state->server_start_props);
+ if (state->client_init_props)
+ tls_proxy_client_init_free(state->client_init_props);
+ if (state->client_start_props)
+ tls_proxy_client_start_free(state->client_start_props);
myfree((void *) state);
}
poll_fd.c timecmp.c slmdb.c dict_pipe.c dict_random.c \
valid_utf8_hostname.c midna_domain.c argv_splitq.c balpar.c dict_union.c \
extpar.c dict_inline.c casefold.c dict_utf8.c strcasecmp_utf8.c \
- split_qnameval.c
+ split_qnameval.c argv_attr_print.c argv_attr_scan.c
OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
poll_fd.o timecmp.o $(NON_PLUGIN_MAP_OBJ) dict_pipe.o dict_random.o \
valid_utf8_hostname.o midna_domain.o argv_splitq.o balpar.o dict_union.o \
extpar.o dict_inline.o casefold.o dict_utf8.o strcasecmp_utf8.o \
- split_qnameval.o
+ split_qnameval.o argv_attr_print.o argv_attr_scan.o
# MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
# When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
# otherwise it sets the PLUGIN_* macros.
edit_file.h dict_cache.h dict_thash.h ip_match.h nbbio.h base32_code.h \
dict_fail.h warn_stat.h dict_sockmap.h line_number.h timecmp.h \
slmdb.h compat_va_copy.h dict_pipe.h dict_random.h \
- valid_utf8_hostname.h midna_domain.h dict_union.h dict_inline.h check_arg.h
+ valid_utf8_hostname.h midna_domain.h dict_union.h dict_inline.h \
+ check_arg.h argv_attr.h
TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
stream_test.c dup2_pass_on_exec.c
DEFS = -I. -D$(SYSTYPE)
argv.o: msg.h
argv.o: mymalloc.h
argv.o: sys_defs.h
+argv_attr_print.o: argv.h
+argv_attr_print.o: argv_attr.h
+argv_attr_print.o: argv_attr_print.c
+argv_attr_print.o: attr.h
+argv_attr_print.o: check_arg.h
+argv_attr_print.o: htable.h
+argv_attr_print.o: msg.h
+argv_attr_print.o: mymalloc.h
+argv_attr_print.o: nvtable.h
+argv_attr_print.o: sys_defs.h
+argv_attr_print.o: vbuf.h
+argv_attr_print.o: vstream.h
+argv_attr_print.o: vstring.h
+argv_attr_scan.o: argv.h
+argv_attr_scan.o: argv_attr.h
+argv_attr_scan.o: argv_attr_scan.c
+argv_attr_scan.o: attr.h
+argv_attr_scan.o: check_arg.h
+argv_attr_scan.o: htable.h
+argv_attr_scan.o: msg.h
+argv_attr_scan.o: mymalloc.h
+argv_attr_scan.o: nvtable.h
+argv_attr_scan.o: sys_defs.h
+argv_attr_scan.o: vbuf.h
+argv_attr_scan.o: vstream.h
+argv_attr_scan.o: vstring.h
argv_split.o: argv.h
argv_split.o: argv_split.c
argv_split.o: check_arg.h
load_file.o: vstream.h
load_file.o: warn_stat.h
load_lib.o: load_lib.c
+load_lib.o: load_lib.h
+load_lib.o: msg.h
load_lib.o: sys_defs.h
lowercase.o: check_arg.h
lowercase.o: lowercase.c
vbuf.o: vbuf.h
vbuf_print.o: check_arg.h
vbuf_print.o: msg.h
+vbuf_print.o: mymalloc.h
vbuf_print.o: sys_defs.h
vbuf_print.o: vbuf.h
vbuf_print.o: vbuf_print.c
--- /dev/null
+#ifndef _ARGV_ATTR_H_INCLUDED_
+#define _ARGV_ATTR_H_INCLUDED_
+
+/*++
+/* NAME
+/* argv_attr 3h
+/* SUMMARY
+/* argv serialization/deserialization
+/* SYNOPSIS
+/* #include <argv_attr.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <argv.h>
+#include <attr.h>
+#include <check_arg.h>
+#include <vstream.h>
+
+ /*
+ * External API.
+ */
+#define ARGV_ATTR_SIZE "argv_size"
+#define ARGV_ATTR_VALUE "argv_value"
+#define ARGV_ATTR_MAX 1024
+
+extern int argv_attr_print(ATTR_PRINT_MASTER_FN, VSTREAM *, int, void *);
+extern int argv_attr_scan(ATTR_SCAN_MASTER_FN, VSTREAM *, int, void *);
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+#endif
--- /dev/null
+/*++
+/* NAME
+/* argv_attr_print
+/* SUMMARY
+/* write ARGV to stream
+/* SYNOPSIS
+/* #include <argv_attr.h>
+/*
+/* int argv_attr_print(print_fn, stream, flags, ptr)
+/* ATTR_PRINT_MASTER_FN print_fn;
+/* VSTREAM *stream;
+/* int flags;
+/* void *ptr;
+/* DESCRIPTION
+/* argv_attr_print() writes an ARGV to the named stream using
+/* the specified attribute print routine. argv_attr_print() is meant
+/* to be passed as a call-back to attr_print(), thusly:
+/*
+/* ... SEND_ATTR_FUNC(argv_attr_print, (void *) argv), ...
+/* DIAGNOSTICS
+/* Fatal: out of memory.
+/*
+/* The result value is zero in case of success, non-zero
+/* otherwise.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+ /*
+ * System library.
+ */
+#include <sys_defs.h>
+
+ /*
+ * Utility library.
+ */
+#include <argv.h>
+#include <argv_attr.h>
+#include <attr.h>
+#include <vstream.h>
+#include <msg.h>
+
+/* argv_attr_print - write ARGV to stream */
+
+int argv_attr_print(ATTR_PRINT_MASTER_FN print_fn, VSTREAM *fp,
+ int flags, void *ptr)
+{
+ ARGV *argv = (ARGV *) ptr;
+ int n;
+ int ret;
+ int argc = argv ? argv->argc : 0;
+
+ ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+ SEND_ATTR_INT(ARGV_ATTR_SIZE, argc),
+ ATTR_TYPE_END);
+ if (msg_verbose)
+ msg_info("argv_attr_print count=%d", argc);
+ for (n = 0; ret == 0 && n < argc; n++)
+ ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+ SEND_ATTR_STR(ARGV_ATTR_VALUE, argv->argv[n]),
+ ATTR_TYPE_END);
+ if (msg_verbose)
+ msg_info("argv_attr_print ret=%d", ret);
+ /* Do not flush the stream. */
+ return (ret);
+}
--- /dev/null
+/*++
+/* NAME
+/* argv_attr_scan
+/* SUMMARY
+/* read ARGV from stream
+/* SYNOPSIS
+/* #include <argv_attr.h>
+/*
+/* int argv_attr_scan(scan_fn, stream, flags, ptr)
+/* ATTR_SCAN_MASTER_FN scan_fn;
+/* VSTREAM *stream;
+/* int flags;
+/* void *ptr;
+/* DESCRIPTION
+/* argv_attr_scan() creates an ARGV and reads its contents
+/* from the named stream using the specified attribute scan
+/* routine. argv_attr_scan() is meant to be passed as a call-back
+/* to attr_scan(), thusly:
+/*
+/* ARGV *argv = 0;
+/* ...
+/* ... RECV_ATTR_FUNC(argv_attr_scan, (void *) &argv), ...
+/* ...
+/* if (argv)
+/* argv_free(argv);
+/* DIAGNOSTICS
+/* Fatal: out of memory.
+/*
+/* In case of error, this function returns non-zero and creates
+/* an ARGV null pointer.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+ /*
+ * System library.
+ */
+#include <sys_defs.h>
+
+ /*
+ * Utility library.
+ */
+#include <argv.h>
+#include <argv_attr.h>
+#include <attr.h>
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+
+/* argv_attr_scan - write ARGV to stream */
+
+int argv_attr_scan(ATTR_PRINT_MASTER_FN scan_fn, VSTREAM *fp,
+ int flags, void *ptr)
+{
+ ARGV *argv = 0;
+ int size;
+ int ret;
+
+ if ((ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+ RECV_ATTR_INT(ARGV_ATTR_SIZE, &size),
+ ATTR_TYPE_END)) == 1) {
+ if (msg_verbose)
+ msg_info("argv_attr_scan count=%d", size);
+ if (size < 0 || size > ARGV_ATTR_MAX) {
+ msg_warn("invalid size %d from %s while reading ARGV",
+ size, VSTREAM_PATH(fp));
+ ret = -1;
+ } else if (size > 0) {
+ VSTRING *buffer = vstring_alloc(100);
+
+ argv = argv_alloc(size);
+ while (ret == 1 && size-- > 0) {
+ if ((ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+ RECV_ATTR_STR(ARGV_ATTR_VALUE, buffer),
+ ATTR_TYPE_END)) == 1)
+ argv_add(argv, vstring_str(buffer), ARGV_END);
+ }
+ argv_terminate(argv);
+ vstring_free(buffer);
+ }
+ }
+ *(ARGV **) ptr = argv;
+ if (msg_verbose)
+ msg_info("argv_attr_scan ret=%d", ret);
+ return (ret);
+}
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/* System library. */