Client TLS limitations
@@ -1908,6 +1910,49 @@ when the certificate is needed.
+
+
+ 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.
+
+
The remote SMTP server and the Postfix SMTP client negotiate a
diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto
index 23f4f63e6..f45809385 100644
--- a/postfix/proto/postconf.proto
+++ b/postfix/proto/postconf.proto
@@ -11050,11 +11050,13 @@ security are:
may
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.
@@ -11062,9 +11064,11 @@ implementations.
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.
@@ -11078,7 +11082,9 @@ href="TLS_README.html#client_tls_encrypt">encrypt. When 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.
+2.11 and later. The optional "connection_reuse" attribute (Postfix
+≥ 3.4) overrides the main.cf smtp_tls_connection_reuse parameter.
+
dane-only
Mandatory DANE TLS. The TLS policy for the destination is
@@ -11087,7 +11093,9 @@ or none are usable, no connection is made to the server. When
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.
+
fingerprint
Certificate fingerprint
@@ -11103,7 +11111,8 @@ algorithm used to calculate the fingerprint is selected by the
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.
verify
Mandatory TLS verification. At this security
@@ -11113,7 +11122,9 @@ unauthenticated DNS MX lookups. The optional "match" attribute overrides
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.
secure
Secure-channel TLS. At this security level, DNS
@@ -11131,7 +11142,9 @@ routing the secondary domains to the primary nexthop also allow secure
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.
@@ -16582,6 +16595,25 @@ Postfix versions.
This feature is available in Postfix 3.0 and later.
+%PARAM smtp_tls_connection_reuse no
+
+ 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.
+
+ See "Client-side
+TLS connection reuse" for background details.
+
+ This feature is available in Postfix 3.4 and later.
+
+%PARAM lmtp_tls_connection_reuse no
+
+ The LMTP-specific version of the smtp_tls_connection_reuse configuration
+parameter. See there for details.
+
+ This feature is available in Postfix 3.4 and later.
+
%PARAM virtual_alias_address_length_limit 1000
diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h
index dd2589fe0..211d7cf74 100644
--- a/postfix/src/global/mail_params.h
+++ b/postfix/src/global/mail_params.h
@@ -4008,6 +4008,15 @@ extern char *var_smtp_dns_re_filter;
#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.
*
diff --git a/postfix/src/global/mail_proto.h b/postfix/src/global/mail_proto.h
index 363bc71ec..9fc9ab68e 100644
--- a/postfix/src/global/mail_proto.h
+++ b/postfix/src/global/mail_proto.h
@@ -276,25 +276,6 @@ extern char *mail_pathname(const char *, const char *);
#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.
*/
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index ae82be407..762261a23 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20180603"
+#define MAIL_RELEASE_DATE "20180617"
#define MAIL_VERSION_NUMBER "3.4"
#ifdef SNAPSHOT
diff --git a/postfix/src/local/mailbox.c b/postfix/src/local/mailbox.c
index d4f01bf71..d31bc6bd5 100644
--- a/postfix/src/local/mailbox.c
+++ b/postfix/src/local/mailbox.c
@@ -41,6 +41,11 @@
/* 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. */
diff --git a/postfix/src/local/unknown.c b/postfix/src/local/unknown.c
index c97cef39b..e643aad68 100644
--- a/postfix/src/local/unknown.c
+++ b/postfix/src/local/unknown.c
@@ -45,6 +45,11 @@
/* 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. */
diff --git a/postfix/src/master/mail_server.h b/postfix/src/master/mail_server.h
index 64a394d1d..56a3072b7 100644
--- a/postfix/src/master/mail_server.h
+++ b/postfix/src/master/mail_server.h
@@ -134,4 +134,9 @@ extern NORETURN trigger_server_main(int, char **, TRIGGER_SERVER_FN,...);
/* 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
/*--*/
diff --git a/postfix/src/master/master.h b/postfix/src/master/master.h
index 60f5f81ec..55bdf74db 100644
--- a/postfix/src/master/master.h
+++ b/postfix/src/master/master.h
@@ -237,4 +237,9 @@ extern int master_monitor(int);
/* 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
/*--*/
diff --git a/postfix/src/master/master_sig.c b/postfix/src/master/master_sig.c
index 0560b1ab3..db5e39d2f 100644
--- a/postfix/src/master/master_sig.c
+++ b/postfix/src/master/master_sig.c
@@ -33,6 +33,11 @@
/* 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. */
diff --git a/postfix/src/postscreen/postscreen_starttls.c b/postfix/src/postscreen/postscreen_starttls.c
index a5d6906e6..3056d7130 100644
--- a/postfix/src/postscreen/postscreen_starttls.c
+++ b/postfix/src/postscreen/postscreen_starttls.c
@@ -37,6 +37,11 @@
/* 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. */
@@ -226,10 +231,11 @@ void psc_starttls_open(PSC_STATE *smtp_state, EVENT_NOTIFY_FN resume_event)
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);
diff --git a/postfix/src/posttls-finger/posttls-finger.c b/postfix/src/posttls-finger/posttls-finger.c
index 5f559b4fd..6162f77a9 100644
--- a/postfix/src/posttls-finger/posttls-finger.c
+++ b/postfix/src/posttls-finger/posttls-finger.c
@@ -245,6 +245,9 @@
/* 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
@@ -299,6 +302,11 @@
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*
/* Viktor Dukhovni
/*--*/
@@ -357,6 +365,7 @@
#include
#include
#include
+#include
/* DNS library. */
@@ -374,6 +383,7 @@
#include
#ifdef USE_TLS
+#include
#include
#endif
@@ -431,6 +441,7 @@ typedef struct STATE {
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 */
@@ -461,6 +472,7 @@ typedef struct STATE {
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;
@@ -699,7 +711,11 @@ static int starttls(STATE *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 */
@@ -743,24 +759,138 @@ static int starttls(STATE *state)
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);
@@ -780,12 +910,14 @@ static int starttls(STATE *state)
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);
}
@@ -879,6 +1011,8 @@ static VSTREAM *connect_sock(int sock, struct sockaddr *sa, int salen,
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.
*/
@@ -1426,9 +1560,14 @@ static int connect_dest(STATE *state)
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);
@@ -1447,6 +1586,10 @@ static void disconnect_dest(STATE *state)
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)
@@ -1491,7 +1634,7 @@ static int finger(STATE *state)
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;
@@ -1620,6 +1763,7 @@ static void tls_init(STATE *state)
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",
@@ -1672,7 +1816,7 @@ static void parse_options(STATE *state, int argc, char *argv[])
#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("");
@@ -1683,6 +1827,7 @@ static void parse_options(STATE *state, int argc, char *argv[])
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;
@@ -1792,6 +1937,8 @@ static void parse_options(STATE *state, int argc, char *argv[])
break;
case 'w':
state->wrapper_mode = 1;
+ case 'X':
+ state->tlsproxy_mode = 1;
break;
#endif
}
diff --git a/postfix/src/smtp/Makefile.in b/postfix/src/smtp/Makefile.in
index acd830264..7d4556496 100644
--- a/postfix/src/smtp/Makefile.in
+++ b/postfix/src/smtp/Makefile.in
@@ -101,8 +101,10 @@ smtp.o: ../../include/flush_clnt.h
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
@@ -124,6 +126,7 @@ smtp.o: ../../include/string_list.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
@@ -168,6 +171,7 @@ smtp_addr.o: ../../include/string_list.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
@@ -217,6 +221,7 @@ smtp_chat.o: ../../include/string_list.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
@@ -266,6 +271,7 @@ smtp_connect.o: ../../include/stringops.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
@@ -305,6 +311,7 @@ smtp_key.o: ../../include/sock_addr.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
@@ -344,6 +351,7 @@ smtp_map11.o: ../../include/sock_addr.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
@@ -405,6 +413,7 @@ smtp_proto.o: ../../include/string_list.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
@@ -450,6 +459,7 @@ smtp_rcpt.o: ../../include/string_list.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
@@ -487,6 +497,7 @@ smtp_reuse.o: ../../include/string_list.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
@@ -527,6 +538,7 @@ smtp_sasl_auth_cache.o: ../../include/string_list.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
@@ -569,6 +581,7 @@ smtp_sasl_glue.o: ../../include/string_list.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
@@ -609,6 +622,7 @@ smtp_sasl_proto.o: ../../include/string_list.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
@@ -648,6 +662,7 @@ smtp_session.o: ../../include/string_list.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
@@ -685,6 +700,7 @@ smtp_state.o: ../../include/sock_addr.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
@@ -724,6 +740,7 @@ smtp_tls_policy.o: ../../include/string_list.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
@@ -768,6 +785,7 @@ smtp_trouble.o: ../../include/string_list.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
@@ -804,6 +822,7 @@ smtp_unalias.o: ../../include/sock_addr.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
diff --git a/postfix/src/smtp/lmtp_params.c b/postfix/src/smtp/lmtp_params.c
index 68d611e26..c4e16966b 100644
--- a/postfix/src/smtp/lmtp_params.c
+++ b/postfix/src/smtp/lmtp_params.c
@@ -61,6 +61,7 @@
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[] = {
@@ -108,6 +109,7 @@
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,
diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c
index ed27be96a..1477a7068 100644
--- a/postfix/src/smtp/smtp.c
+++ b/postfix/src/smtp/smtp.c
@@ -478,6 +478,10 @@
/* 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
@@ -584,6 +588,10 @@
/* 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
@@ -810,6 +818,7 @@
/* Global library. */
#include
+#include
#include
#include
#include
@@ -895,6 +904,8 @@ bool var_smtp_enforce_tls;
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;
@@ -1219,6 +1230,9 @@ static void pre_init(char *unused_name, char **unused_argv)
*
* 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,
diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h
index 11beb4406..041745f5d 100644
--- a/postfix/src/smtp/smtp.h
+++ b/postfix/src/smtp/smtp.h
@@ -38,6 +38,11 @@
*/
#include
+ /*
+ * tlsproxy client.
+ */
+#include
+
/*
* Global iterator support. This is updated by the connection-management
* loop, and contains dynamic context that appears in lookup keys for SASL
@@ -98,6 +103,7 @@ typedef struct SMTP_TLS_POLICY {
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;
/*
@@ -131,6 +137,7 @@ extern void smtp_tls_policy_cache_flush(void);
_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
@@ -614,12 +621,13 @@ char *smtp_key_prefix(VSTRING *, const char *, SMTP_ITERATOR *, int);
#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
@@ -647,7 +655,7 @@ char *smtp_key_prefix(VSTRING *, const char *, SMTP_ITERATOR *, int);
*/
#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
@@ -658,7 +666,7 @@ char *smtp_key_prefix(VSTRING *, const char *, SMTP_ITERATOR *, int);
#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.
@@ -686,6 +694,11 @@ extern int smtp_mode;
/* 111 8th Avenue
/* New York, NY 10011, USA
/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*
/* TLS support originally by:
/* Lutz Jaenicke
/* BTU Cottbus
diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c
index 2bf209d92..abccb57c9 100644
--- a/postfix/src/smtp/smtp_connect.c
+++ b/postfix/src/smtp/smtp_connect.c
@@ -667,13 +667,16 @@ static int smtp_reuse_session(SMTP_STATE *state, DNS_RR **addr_list,
* 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
diff --git a/postfix/src/smtp/smtp_key.c b/postfix/src/smtp/smtp_key.c
index 78d795923..6f72b803e 100644
--- a/postfix/src/smtp/smtp_key.c
+++ b/postfix/src/smtp/smtp_key.c
@@ -76,6 +76,11 @@
/* 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
/*--*/
/*
@@ -191,7 +196,17 @@ char *smtp_key_prefix(VSTRING *buffer, const char *delim_na,
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);
diff --git a/postfix/src/smtp/smtp_params.c b/postfix/src/smtp/smtp_params.c
index ed5ea455b..198119e10 100644
--- a/postfix/src/smtp/smtp_params.c
+++ b/postfix/src/smtp/smtp_params.c
@@ -62,6 +62,7 @@
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[] = {
@@ -112,6 +113,7 @@
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,
diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c
index 15a15db17..0169f1716 100644
--- a/postfix/src/smtp/smtp_proto.c
+++ b/postfix/src/smtp/smtp_proto.c
@@ -848,20 +848,29 @@ static int smtp_start_tls(SMTP_STATE *state)
{
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:
@@ -894,37 +903,164 @@ static int smtp_start_tls(SMTP_STATE *state)
| 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) {
@@ -932,7 +1068,6 @@ static int smtp_start_tls(SMTP_STATE *state)
/*
* 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;
/*
@@ -983,9 +1118,6 @@ static int smtp_start_tls(SMTP_STATE *state)
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.
diff --git a/postfix/src/smtp/smtp_reuse.c b/postfix/src/smtp/smtp_reuse.c
index da23692fd..2bc5c5ed0 100644
--- a/postfix/src/smtp/smtp_reuse.c
+++ b/postfix/src/smtp/smtp_reuse.c
@@ -41,6 +41,8 @@
/* 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:
@@ -64,6 +66,11 @@
/* 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. */
@@ -159,10 +166,9 @@ static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd,
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);
@@ -211,10 +217,10 @@ SMTP_SESSION *smtp_reuse_nexthop(SMTP_STATE *state, int name_key_flags)
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
@@ -244,11 +250,11 @@ SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, int endp_key_flags)
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
diff --git a/postfix/src/smtp/smtp_session.c b/postfix/src/smtp/smtp_session.c
index 806d8de06..9fa8cd285 100644
--- a/postfix/src/smtp/smtp_session.c
+++ b/postfix/src/smtp/smtp_session.c
@@ -84,6 +84,11 @@
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*
/* Viktor Dukhovni
/*--*/
@@ -176,7 +181,11 @@ void smtp_session_free(SMTP_SESSION *session)
#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);
}
@@ -223,9 +232,11 @@ int smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop,
* 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),
diff --git a/postfix/src/smtp/smtp_tls_policy.c b/postfix/src/smtp/smtp_tls_policy.c
index 9dfc41896..13735b210 100644
--- a/postfix/src/smtp/smtp_tls_policy.c
+++ b/postfix/src/smtp/smtp_tls_policy.c
@@ -74,6 +74,11 @@
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*
/* Viktor Dukhovni
/*--*/
@@ -356,6 +361,18 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
}
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);
}
@@ -483,6 +500,7 @@ static void *policy_create(const char *unused_key, void *context)
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
diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c
index 20598992b..5006e7449 100644
--- a/postfix/src/smtpd/smtpd.c
+++ b/postfix/src/smtpd/smtpd.c
@@ -1496,9 +1496,11 @@ static void tls_reset(SMTPD_STATE *);
/*
* TLS initialization status.
*/
+#ifndef USE_TLSPROXY
static TLS_APPL_STATE *smtpd_tls_ctx;
static int ask_client_cert;
+#endif /* USE_TLSPROXY */
#endif
/*
@@ -4704,9 +4706,11 @@ static int starttls_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
#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. */
@@ -4963,10 +4967,12 @@ static void smtpd_proto(SMTPD_STATE *state)
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"
diff --git a/postfix/src/tls/Makefile.in b/postfix/src/tls/Makefile.in
index 55dfd1ecc..16efff4dc 100644
--- a/postfix/src/tls/Makefile.in
+++ b/postfix/src/tls/Makefile.in
@@ -4,13 +4,19 @@ SRCS = tls_prng_dev.c tls_prng_egd.c tls_prng_file.c tls_fprint.c \
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)
@@ -294,6 +300,46 @@ tls_prng_file.o: ../../include/mymalloc.h
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
@@ -318,46 +364,79 @@ tls_proxy_clnt.o: ../../include/vstring.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
diff --git a/postfix/src/tls/tls.h b/postfix/src/tls/tls.h
index 79b8d73a3..a9c4d7885 100644
--- a/postfix/src/tls/tls.h
+++ b/postfix/src/tls/tls.h
@@ -448,6 +448,7 @@ typedef struct {
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 */
@@ -465,6 +466,8 @@ typedef struct {
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))
@@ -477,11 +480,12 @@ extern TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *);
((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
@@ -674,6 +678,11 @@ extern int tls_ext_seed(int);
/* 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
/*--*/
diff --git a/postfix/src/tls/tls_client.c b/postfix/src/tls/tls_client.c
index bbe18f905..6b4dfda71 100644
--- a/postfix/src/tls/tls_client.c
+++ b/postfix/src/tls/tls_client.c
@@ -12,6 +12,10 @@
/* 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;
@@ -96,6 +100,31 @@
/* 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
@@ -116,6 +145,11 @@
/* 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
/*--*/
@@ -837,8 +871,6 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
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;
@@ -1018,7 +1050,8 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
/*
* 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);
@@ -1026,12 +1059,6 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
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
@@ -1046,6 +1073,19 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
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.
@@ -1069,8 +1109,19 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
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);
/*
@@ -1078,7 +1129,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
* 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);
/*
@@ -1125,7 +1176,8 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
* 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.
@@ -1142,7 +1194,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
/*
* 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" :
diff --git a/postfix/src/tls/tls_dane.c b/postfix/src/tls/tls_dane.c
index 5cee6663f..de7ba77ff 100644
--- a/postfix/src/tls/tls_dane.c
+++ b/postfix/src/tls/tls_dane.c
@@ -1346,26 +1346,20 @@ int tls_dane_match(TLS_SESS_STATE *TLScontext, int usage,
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 */
diff --git a/postfix/src/tls/tls_proxy.h b/postfix/src/tls/tls_proxy.h
index e677c67ab..cdfdf6388 100644
--- a/postfix/src/tls/tls_proxy.h
+++ b/postfix/src/tls/tls_proxy.h
@@ -31,13 +31,178 @@
#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
@@ -49,6 +214,11 @@ extern int tls_proxy_context_scan(ATTR_SCAN_MASTER_FN, VSTREAM *, int, void *);
/* 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
diff --git a/postfix/src/tls/tls_proxy_client_print.c b/postfix/src/tls/tls_proxy_client_print.c
new file mode 100644
index 000000000..7ed66b9de
--- /dev/null
+++ b/postfix/src/tls/tls_proxy_client_print.c
@@ -0,0 +1,324 @@
+/*++
+/* NAME
+/* tls_proxy_client_print 3
+/* SUMMARY
+/* write TLS_CLIENT_XXX structures to stream
+/* SYNOPSIS
+/* #include
+/*
+/* 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
+
+/* Utility library */
+
+#include
+#include
+#include
+
+/* TLS library. */
+
+#include
+#include
+
+
+#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
diff --git a/postfix/src/tls/tls_proxy_client_scan.c b/postfix/src/tls/tls_proxy_client_scan.c
new file mode 100644
index 000000000..45779398f
--- /dev/null
+++ b/postfix/src/tls/tls_proxy_client_scan.c
@@ -0,0 +1,568 @@
+/*++
+/* NAME
+/* tls_proxy_client_scan 3
+/* SUMMARY
+/* read TLS_CLIENT_XXX structures from stream
+/* SYNOPSIS
+/* #include
+/*
+/* 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
+
+/* Utility library */
+
+#include
+#include
+#include
+#include
+
+/* TLS library. */
+
+#include
+#include
+
+#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
diff --git a/postfix/src/tls/tls_proxy_clnt.c b/postfix/src/tls/tls_proxy_clnt.c
index 97d096264..3dd0aa364 100644
--- a/postfix/src/tls/tls_proxy_clnt.c
+++ b/postfix/src/tls/tls_proxy_clnt.c
@@ -2,24 +2,37 @@
/* NAME
/* tlsproxy_clnt 3
/* SUMMARY
-/* postscreen TLS proxy support
+/* tlsproxy(8) client support
/* SYNOPSIS
/* #include
/*
/* 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
@@ -44,8 +57,8 @@
/*
/* 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
@@ -66,8 +79,18 @@
/* 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
@@ -81,6 +104,11 @@
/* 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
@@ -119,8 +147,13 @@ VSTREAM *tls_proxy_open(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)
{
+ const char myname[] = "tls_proxy_open";
VSTREAM *tlsproxy_stream;
int status;
int fd;
@@ -146,19 +179,43 @@ VSTREAM *tls_proxy_open(const char *service, int flags,
}
/*
- * 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));
@@ -170,11 +227,12 @@ VSTREAM *tls_proxy_open(const char *service, int flags,
* 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) {
/*
@@ -191,7 +249,7 @@ VSTREAM *tls_proxy_open(const char *service, int flags,
}
/*
- * 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) {
@@ -207,41 +265,22 @@ VSTREAM *tls_proxy_open(const char *service, int flags,
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
diff --git a/postfix/src/tls/tls_proxy_print.c b/postfix/src/tls/tls_proxy_context_print.c
similarity index 63%
rename from postfix/src/tls/tls_proxy_print.c
rename to postfix/src/tls/tls_proxy_context_print.c
index e30e8be88..a597024b2 100644
--- a/postfix/src/tls/tls_proxy_print.c
+++ b/postfix/src/tls/tls_proxy_context_print.c
@@ -1,8 +1,8 @@
/*++
/* NAME
-/* tls_proxy_print
+/* tls_proxy_context_print
/* SUMMARY
-/* write DSN structure to stream
+/* write TLS_ATTR_STATE structure to stream
/* SYNOPSIS
/* #include
/*
@@ -12,10 +12,10 @@
/* 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
@@ -29,6 +29,11 @@
/* 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
@@ -41,10 +46,6 @@
#include
-/* Global library. */
-
-#include
-
/* TLS library. */
#include
@@ -61,25 +62,26 @@ int tls_proxy_context_print(ATTR_PRINT_MASTER_FN print_fn, VSTREAM *fp,
#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);
}
diff --git a/postfix/src/tls/tls_proxy_context_scan.c b/postfix/src/tls/tls_proxy_context_scan.c
new file mode 100644
index 000000000..74f109b31
--- /dev/null
+++ b/postfix/src/tls/tls_proxy_context_scan.c
@@ -0,0 +1,140 @@
+/*++
+/* NAME
+/* tls_proxy_context_scan
+/* SUMMARY
+/* read TLS session state from stream
+/* SYNOPSIS
+/* #include
+/*
+/* 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
+
+/* Utility library */
+
+#include
+#include
+
+/* TLS library. */
+
+#include
+#include
+
+/* 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
diff --git a/postfix/src/tls/tls_proxy_scan.c b/postfix/src/tls/tls_proxy_scan.c
deleted file mode 100644
index 29a0cd9f9..000000000
--- a/postfix/src/tls/tls_proxy_scan.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/*++
-/* NAME
-/* tls_proxy_scan
-/* SUMMARY
-/* read TLS session state from stream
-/* SYNOPSIS
-/* #include
-/*
-/* 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
-
-/* Utility library */
-
-#include
-
-/* Global library. */
-
-#include
-
-/* TLS library. */
-
-#include
-#include
-
-/* 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
diff --git a/postfix/src/tls/tls_proxy_server_print.c b/postfix/src/tls/tls_proxy_server_print.c
new file mode 100644
index 000000000..670f84119
--- /dev/null
+++ b/postfix/src/tls/tls_proxy_server_print.c
@@ -0,0 +1,141 @@
+/*++
+/* NAME
+/* tls_proxy_server_print 3
+/* SUMMARY
+/* write TLS_SERVER_XXX structures to stream
+/* SYNOPSIS
+/* #include
+/*
+/* 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
+
+/* Utility library */
+
+#include
+
+/* TLS library. */
+
+#include
+#include
+
+/* 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
diff --git a/postfix/src/tls/tls_proxy_server_scan.c b/postfix/src/tls/tls_proxy_server_scan.c
new file mode 100644
index 000000000..62487d8ce
--- /dev/null
+++ b/postfix/src/tls/tls_proxy_server_scan.c
@@ -0,0 +1,241 @@
+/*++
+/* NAME
+/* tls_proxy_server_scan 3
+/* SUMMARY
+/* read TLS_SERVER_XXX structures from stream
+/* SYNOPSIS
+/* #include
+/*
+/* 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
+
+/* Utility library */
+
+#include
+
+/* TLS library. */
+
+#include
+#include
+
+/* 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
diff --git a/postfix/src/tlsproxy/Makefile.in b/postfix/src/tlsproxy/Makefile.in
index 087721bf7..e248f8501 100644
--- a/postfix/src/tlsproxy/Makefile.in
+++ b/postfix/src/tlsproxy/Makefile.in
@@ -61,6 +61,7 @@ depend: $(MAKES)
@$(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
@@ -90,9 +91,11 @@ tlsproxy.o: ../../include/vstring.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
@@ -101,9 +104,11 @@ tlsproxy_state.o: ../../include/mymalloc.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
diff --git a/postfix/src/tlsproxy/tlsproxy.c b/postfix/src/tlsproxy/tlsproxy.c
index 2c855ffcc..241e83b2a 100644
--- a/postfix/src/tlsproxy/tlsproxy.c
+++ b/postfix/src/tlsproxy/tlsproxy.c
@@ -6,11 +6,12 @@
/* 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
@@ -309,14 +310,33 @@ int var_tlsp_watchdog;
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
@@ -343,12 +363,17 @@ static int ask_client_cert;
* 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)
@@ -406,7 +431,7 @@ static int tlsp_eval_tls_error(TLSP_STATE *state, int err)
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
@@ -422,7 +447,7 @@ static int tlsp_eval_tls_error(TLSP_STATE *state, int err)
}
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
@@ -438,7 +463,7 @@ static int tlsp_eval_tls_error(TLSP_STATE *state, int err)
}
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
@@ -450,8 +475,61 @@ static int tlsp_eval_tls_error(TLSP_STATE *state, int err)
/* 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. */
@@ -466,33 +544,28 @@ static void tlsp_strategy(TLSP_STATE *state)
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;
}
/*
@@ -581,21 +654,24 @@ static void tlsp_strategy(TLSP_STATE *state)
*/
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)
@@ -634,6 +710,7 @@ static void tlsp_plaintext_event(int event, void *context)
* 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 */
@@ -649,6 +726,7 @@ static void tlsp_ciphertext_event(int event, void *context)
*/
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",
@@ -661,9 +739,23 @@ static void tlsp_ciphertext_event(int event, void *context)
}
}
-/* 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;
@@ -676,8 +768,8 @@ static int tlsp_start_tls(TLSP_STATE *state)
*/
/*
- * 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
@@ -716,7 +808,7 @@ static int tlsp_start_tls(TLSP_STATE *state)
if (state->tls_context == 0) {
tlsp_state_free(state);
- return (-1);
+ return (TLSP_STAT_ERR);
}
/*
@@ -725,68 +817,140 @@ static int tlsp_start_tls(TLSP_STATE *state)
* 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)
{
@@ -797,8 +961,17 @@ 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.
@@ -809,68 +982,87 @@ static void tlsp_get_request_event(int event, void *context)
}
/*
- * 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;
}
}
@@ -906,9 +1098,8 @@ static void tlsp_service(VSTREAM *plaintext_stream,
* 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 */
@@ -1033,7 +1224,7 @@ static void pre_jail_init(char *unused_name, char **unused_argv)
static void post_jail_init(char *unused_name, char **unused_argv)
{
- /* void */ ;
+ tlsp_client_app_cache = htable_create(10);
}
MAIL_VERSION_STAMP_DECLARE;
@@ -1120,7 +1311,7 @@ int main(int argc, char **argv)
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),
diff --git a/postfix/src/tlsproxy/tlsproxy.h b/postfix/src/tlsproxy/tlsproxy.h
index e3e1d11fa..fe560644e 100644
--- a/postfix/src/tlsproxy/tlsproxy.h
+++ b/postfix/src/tlsproxy/tlsproxy.h
@@ -25,16 +25,24 @@
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)
@@ -51,4 +59,9 @@ extern void tlsp_state_free(TLSP_STATE *);
/* 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
/*--*/
diff --git a/postfix/src/tlsproxy/tlsproxy_state.c b/postfix/src/tlsproxy/tlsproxy_state.c
index e32764920..d6b88f598 100644
--- a/postfix/src/tlsproxy/tlsproxy_state.c
+++ b/postfix/src/tlsproxy/tlsproxy_state.c
@@ -18,7 +18,8 @@
/*
/* 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
@@ -60,6 +61,11 @@
/* 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
/*--*/
/*
@@ -85,6 +91,7 @@
#ifdef USE_TLS
#define TLS_INTERNAL /* XXX */
#include
+#include
/*
* Application-specific.
@@ -108,6 +115,10 @@ TLSP_STATE *tlsp_state_create(const char *service,
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);
}
@@ -116,9 +127,16 @@ TLSP_STATE *tlsp_state_create(const char *service,
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);
@@ -134,6 +152,14 @@ void tlsp_state_free(TLSP_STATE *state)
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);
}
diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in
index 32cefbc07..d419cb397 100644
--- a/postfix/src/util/Makefile.in
+++ b/postfix/src/util/Makefile.in
@@ -40,7 +40,7 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
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 \
@@ -82,7 +82,7 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.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.
@@ -111,7 +111,8 @@ HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
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)
@@ -879,6 +880,32 @@ argv.o: argv.h
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
@@ -1785,6 +1812,8 @@ load_file.o: vbuf.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
@@ -2363,6 +2392,7 @@ vbuf.o: vbuf.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
diff --git a/postfix/src/util/argv_attr.h b/postfix/src/util/argv_attr.h
new file mode 100644
index 000000000..65cfa5541
--- /dev/null
+++ b/postfix/src/util/argv_attr.h
@@ -0,0 +1,43 @@
+#ifndef _ARGV_ATTR_H_INCLUDED_
+#define _ARGV_ATTR_H_INCLUDED_
+
+/*++
+/* NAME
+/* argv_attr 3h
+/* SUMMARY
+/* argv serialization/deserialization
+/* SYNOPSIS
+/* #include
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include
+#include
+#include
+#include
+
+ /*
+ * 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
diff --git a/postfix/src/util/argv_attr_print.c b/postfix/src/util/argv_attr_print.c
new file mode 100644
index 000000000..87b086a9b
--- /dev/null
+++ b/postfix/src/util/argv_attr_print.c
@@ -0,0 +1,73 @@
+/*++
+/* NAME
+/* argv_attr_print
+/* SUMMARY
+/* write ARGV to stream
+/* SYNOPSIS
+/* #include
+/*
+/* 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
+
+ /*
+ * Utility library.
+ */
+#include
+#include
+#include
+#include
+#include
+
+/* 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);
+}
diff --git a/postfix/src/util/argv_attr_scan.c b/postfix/src/util/argv_attr_scan.c
new file mode 100644
index 000000000..3005cb2dd
--- /dev/null
+++ b/postfix/src/util/argv_attr_scan.c
@@ -0,0 +1,93 @@
+/*++
+/* NAME
+/* argv_attr_scan
+/* SUMMARY
+/* read ARGV from stream
+/* SYNOPSIS
+/* #include
+/*
+/* 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
+
+ /*
+ * Utility library.
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* 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);
+}
diff --git a/postfix/src/util/killme_after.c b/postfix/src/util/killme_after.c
index 5d461706f..34e04343e 100644
--- a/postfix/src/util/killme_after.c
+++ b/postfix/src/util/killme_after.c
@@ -24,6 +24,11 @@
/* 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. */