smtp_xforward_timeout
diff --git a/postfix/html/posttls-finger.1.html b/postfix/html/posttls-finger.1.html
index 44dbd9eaa..db83e928d 100644
--- a/postfix/html/posttls-finger.1.html
+++ b/postfix/html/posttls-finger.1.html
@@ -280,17 +280,20 @@ POSTTLS-FINGER(1) POSTTLS-FINGER(1)
STARTTLS protocol. The destination domain:port should of course
provide such a service.
+ -X Enable tlsproxy(8) mode. This is an unsupported mode, for pro-
+ gram development only.
+
[inet:]domain[:port]
Connect via TCP to domain domain, port port. The default port is
- smtp (or 24 with LMTP). With SMTP an MX lookup is performed to
- resolve the domain to a host, unless the domain is enclosed in
- []. If you want to connect to a specific MX host, for instance
- mx1.example.com, specify [mx1.example.com] as the destination
+ smtp (or 24 with LMTP). With SMTP an MX lookup is performed to
+ resolve the domain to a host, unless the domain is enclosed in
+ []. If you want to connect to a specific MX host, for instance
+ mx1.example.com, specify [mx1.example.com] as the destination
and example.com as a match argument. When using DNS, the desti-
- nation domain is assumed fully qualified and no default domain
- or search suffixes are applied; you must use fully-qualified
- names or also enable native host lookups (these don't support
- dane or dane-only as no DNSSEC validation information is avail-
+ nation domain is assumed fully qualified and no default domain
+ or search suffixes are applied; you must use fully-qualified
+ names or also enable native host lookups (these don't support
+ dane or dane-only as no DNSSEC validation information is avail-
able via native lookups).
unix:pathname
@@ -299,8 +302,8 @@ POSTTLS-FINGER(1) POSTTLS-FINGER(1)
match ...
With no match arguments specified, certificate peername matching
uses the compiled-in default strategies for each security level.
- If you specify one or more arguments, these will be used as the
- list of certificate or public-key digests to match for the fin-
+ If you specify one or more arguments, these will be used as the
+ list of certificate or public-key digests to match for the fin-
gerprint level, or as the list of DNS names to match in the cer-
tificate at the verify and secure levels. If the security level
is dane, or dane-only the match names are ignored, and hostname,
diff --git a/postfix/makedefs b/postfix/makedefs
index 5449732b0..26e950d4b 100644
--- a/postfix/makedefs
+++ b/postfix/makedefs
@@ -878,7 +878,7 @@ CCARGS="$CCARGS -DSNAPSHOT"
# Non-production: needs thorough testing, or major changes are still
# needed before the code stabilizes.
-#CCARGS="$CCARGS -DNONPROD"
+CCARGS="$CCARGS -DNONPROD"
# Workaround: prepend Postfix include files before other include files.
CCARGS="-I. -I../../include $CCARGS"
diff --git a/postfix/man/man1/posttls-finger.1 b/postfix/man/man1/posttls-finger.1
index ca773102a..2515089b7 100644
--- a/postfix/man/man1/posttls-finger.1
+++ b/postfix/man/man1/posttls-finger.1
@@ -251,6 +251,9 @@ provided on port 465 by servers that are compatible with the ad\-hoc
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
diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5
index b2dff862e..3ca7bcfc5 100644
--- a/postfix/man/man5/postconf.5
+++ b/postfix/man/man5/postconf.5
@@ -8853,6 +8853,11 @@ is a concern for you, use the smtp_tls_per_site feature instead.
.PP
This feature is available in Postfix 2.2 and later. With
Postfix 2.3 and later use smtp_tls_security_level instead.
+.SH smtp_use_tlsproxy (default: no)
+The LMTP\-specific version of the smtp_use_tlsproxy configuration
+parameter. See there for details.
+.PP
+This feature is available in Postfix 3.4 and later.
.SH smtp_xforward_timeout (default: 300s)
The Postfix SMTP client time limit for sending the XFORWARD command,
and for receiving the remote SMTP server response.
diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink
index 4f07119a1..4f7b6adaf 100755
--- a/postfix/mantools/postlink
+++ b/postfix/mantools/postlink
@@ -690,6 +690,8 @@ while (<>) {
s;\bsmtp_per_record_deadline\b;$&;g;
s;\bsmtp_send_dummy_mail_auth\b;$&;g;
s;\bsmtp_balance_inet_protocols\b;$&;g;
+ s;\bsmtp_use_tlsproxy\b;$&;g;
+ s;\blmtp_use_tlsproxy\b;$&;g;
s;\bsmtpd_enforce_tls\b;$&;g;
s;\bsmtpd_sasl_tls_security_options\b;$&;g;
s;\bsmtpd_sasl_type\b;$&;g;
diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto
index 23f4f63e6..76670568e 100644
--- a/postfix/proto/postconf.proto
+++ b/postfix/proto/postconf.proto
@@ -16582,6 +16582,22 @@ Postfix versions.
This feature is available in Postfix 3.0 and later.
+%PARAM smtp_use_tlsproxy no
+
+ Try to use a TLS session multiple times, without reconnecting.
+This uses the tlsproxy(8) service to make an outbound SMTP-over-TLS
+connection, and uses the sache(8) service to save that connection.
+
+
+ This feature is available in Postfix 3.4 and later.
+
+%PARAM smtp_use_tlsproxy no
+
+ The LMTP-specific version of the smtp_use_tlsproxy 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..31843dfca 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_USE_TLSPROXY "smtp_use_tlsproxy"
+#define DEF_SMTP_USE_TLSPROXY 0
+#define VAR_LMTP_USE_TLSPROXY "lmtp_use_tlsproxy"
+#define DEF_LMTP_USE_TLSPROXY 0
+extern bool var_smtp_use_tlsproxy;
+
/*
* 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/postscreen/postscreen_starttls.c b/postfix/src/postscreen/postscreen_starttls.c
index a5d6906e6..863ba8c23 100644
--- a/postfix/src/postscreen/postscreen_starttls.c
+++ b/postfix/src/postscreen/postscreen_starttls.c
@@ -226,10 +226,10 @@ 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_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..d9d128ff1 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
@@ -357,6 +360,7 @@
#include
#include
#include
+#include
/* DNS library. */
@@ -374,6 +378,7 @@
#include
#ifdef USE_TLS
+#include
#include
#endif
@@ -431,6 +436,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 +467,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 +706,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 +754,132 @@ 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,
+ 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! */
+
+ /*
+ * 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 +899,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 +1000,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 +1549,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 +1575,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 +1623,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 +1752,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 +1805,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 +1816,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 +1926,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..d057195d4 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_USE_TLSPROXY, DEF_LMTP_USE_TLSPROXY, &var_smtp_use_tlsproxy,
#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..bbd039972 100644
--- a/postfix/src/smtp/smtp.c
+++ b/postfix/src/smtp/smtp.c
@@ -810,6 +810,7 @@
/* Global library. */
#include
+#include
#include
#include
#include
@@ -895,6 +896,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_use_tlsproxy;
+char *var_tlsproxy_service;
#ifdef USE_TLS
char *var_smtp_sasl_tls_opts;
@@ -1219,6 +1222,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..27e7a8612 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
@@ -614,12 +619,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 +653,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 +664,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 +692,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..7fb8e168a 100644
--- a/postfix/src/smtp/smtp_connect.c
+++ b/postfix/src/smtp/smtp_connect.c
@@ -667,13 +667,19 @@ 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 (var_smtp_use_tlsproxy) {
+ 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 */
+ }
+ } else
+ smtp_tls_policy_dummy(state->tls);
#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..56cff253b 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,13 @@ 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)
+ smtp_key_append_uint(buffer, state->tls->level, delim_na);
VSTRING_TERMINATE(buffer);
diff --git a/postfix/src/smtp/smtp_params.c b/postfix/src/smtp/smtp_params.c
index ed5ea455b..7fef9e707 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_USE_TLSPROXY, DEF_SMTP_USE_TLSPROXY, &var_smtp_use_tlsproxy,
#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..44c9a4d2e 100644
--- a/postfix/src/smtp/smtp_proto.c
+++ b/postfix/src/smtp/smtp_proto.c
@@ -848,9 +848,12 @@ 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
@@ -861,7 +864,8 @@ static int smtp_start_tls(SMTP_STATE *state)
* SMTP connection either, because the conversation is in an unknown
* state.
*/
- DONT_CACHE_THIS_SESSION;
+ if (var_smtp_use_tlsproxy == 0)
+ DONT_CACHE_THIS_SESSION;
/*
* The following assumes sites that use TLS in a perverse configuration:
@@ -894,37 +898,122 @@ 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 (var_smtp_use_tlsproxy) {
+
+ /*
+ * 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);
+
+#define PROXY_OPEN_FLAGS \
+ (TLS_PROXY_FLAG_ROLE_CLIENT | TLS_PROXY_FLAG_SEND_CONTEXT)
+
+ port_buf = vstring_alloc(100);
+ 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,
+ 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.
+ */
+ 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! */
+
+ /*
+ * 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 { /* var_smtp_tls_proxy */
+
+ /*
+ * 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);
+ } /* var_smtp_use_tlsproxy */
+
vstring_free(serverid);
if (session->tls_context == 0) {
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..594567f78 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 (var_smtp_use_tlsproxy)
+ 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/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..01419cb56 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
diff --git a/postfix/src/tls/tls_client.c b/postfix/src/tls/tls_client.c
index bbe18f905..09e523364 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 current TLS library 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
@@ -837,8 +866,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 +1045,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 +1054,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 +1068,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 +1104,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 +1124,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 +1171,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 +1189,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_proxy.h b/postfix/src/tls/tls_proxy.h
index e677c67ab..99f1afe1a 100644
--- a/postfix/src/tls/tls_proxy.h
+++ b/postfix/src/tls/tls_proxy.h
@@ -31,13 +31,173 @@
#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), (serverid), (void *) 0, (void *) 0)
+
extern VSTREAM *tls_proxy_open(const char *, int, VSTREAM *, const char *,
- const char *, int);
+ const char *, 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 *);
+
+ /*
+ * TLSPROXY attributes
+ */
+#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"
+
+ /*
+ * 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 +209,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..19ca7c286
--- /dev/null
+++ b/postfix/src/tls/tls_proxy_client_scan.c
@@ -0,0 +1,566 @@
+/*++
+/* 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);
+ 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);
+ 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..90b21cd50 100644
--- a/postfix/src/tls/tls_proxy_clnt.c
+++ b/postfix/src/tls/tls_proxy_clnt.c
@@ -2,24 +2,36 @@
/* 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, timeout, serverid, init_props,
+/* start_props)
/* const char *service;
/* int flags;
/* VSTREAM *peer_stream;
/* const char *peer_addr;
/* const char *peer_port;
/* int 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 +56,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
@@ -68,6 +80,12 @@
/* Printable TCP port of the remote peer_stream endpoint.
/* .IP timeout
/* Time limit that the tlsproxy(8) daemon should use.
+/* .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 +99,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 +142,12 @@ VSTREAM *tls_proxy_open(const char *service, int flags,
VSTREAM *peer_stream,
const char *peer_addr,
const char *peer_port,
- int timeout)
+ int 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 +173,42 @@ 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, 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 +220,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 +242,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 +258,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..ecfb149df 100644
--- a/postfix/src/tlsproxy/tlsproxy.c
+++ b/postfix/src/tlsproxy/tlsproxy.c
@@ -309,14 +309,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 +362,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
+ * session 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 +430,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 +446,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 +462,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,10 +474,63 @@ 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)
+{
+
+ /*
+ * Currently, tls_server_post_accept() and tls_client_post_connect()
+ * 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. */
static void tlsp_strategy(TLSP_STATE *state)
@@ -466,33 +543,25 @@ 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);
+ 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;
+ if (tlsp_post_handshake(state) != TLSP_STAT_OK) {
return;
}
- state->flags &= ~TLSP_FLAG_DO_HANDSHAKE;
}
/*
@@ -585,17 +654,19 @@ static void tlsp_strategy(TLSP_STATE *state)
/*
* 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)
@@ -661,9 +732,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 +761,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 +801,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,59 +810,75 @@ 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);
}
-/* tlsp_get_fd_event - receive final postscreen(8) hand-off information */
+ /*
+ * 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 I/O events. */
+}
+
+/* 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.
+ * Disable I/O events on the plaintext stream until the TLS handshake is
+ * completed.
*/
+ 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)
return;
/*
@@ -786,7 +887,46 @@ static void tlsp_get_fd_event(int event, void *context)
tlsp_strategy(state);
}
-/* tlsp_get_request_event - receive initial postscreen(8) hand-off info */
+ /*
+ * 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);
+ 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 hand-off info */
static void tlsp_get_request_event(int event, void *context)
{
@@ -798,7 +938,13 @@ static void tlsp_get_request_event(int event, void *context)
static VSTRING *server_id;
int req_flags;
int timeout;
- int ready;
+ int ready = 0;
+
+ /*
+ * At this point we still manually manage plaintext read/write/timeout
+ * events.
+ */
+ tlsp_accept_event(event, tlsp_get_request_event, (void *) state);
/*
* One-time initialization.
@@ -808,16 +954,6 @@ static void tlsp_get_request_event(int event, void *context)
server_id = vstring_alloc(10);
}
- /*
- * 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
@@ -825,52 +961,77 @@ static void tlsp_get_request_event(int event, void *context)
*/
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),
+ RECV_ATTR_STR(TLS_ATTR_REMOTE_ENDPT, remote_endpt),
+ RECV_ATTR_INT(TLS_ATTR_FLAGS, &req_flags),
+ RECV_ATTR_INT(TLS_ATTR_TIMEOUT, &timeout),
+ RECV_ATTR_STR(TLS_ATTR_SERVERID, server_id),
ATTR_TYPE_END) != 4) {
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.
+ */
+ 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->timeout = timeout + 10; /* XXX */
+
+ /*
+ * Receive the TLS preferences now, to reduce the number of protocol
+ * roundtrips. To call the pre-handshake tls_*_start() before receiving
+ * the ciphertext FD, pass in the FD through some other interface.
+ */
+ 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;
+ }
+
/*
* 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 +1067,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 +1193,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;
diff --git a/postfix/src/tlsproxy/tlsproxy.h b/postfix/src/tlsproxy/tlsproxy.h
index e3e1d11fa..336c58c69 100644
--- a/postfix/src/tlsproxy/tlsproxy.h
+++ b/postfix/src/tlsproxy/tlsproxy.h
@@ -25,6 +25,7 @@
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 */
@@ -33,8 +34,13 @@ typedef struct {
int timeout; /* read/write 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 +57,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..91dcfca7c 100644
--- a/postfix/src/tlsproxy/tlsproxy_state.c
+++ b/postfix/src/tlsproxy/tlsproxy_state.c
@@ -18,7 +18,9 @@
/*
/* 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 sends a 'handshake failed' message to the
+/* plaintext peer.
/*
/* Arguments:
/* .IP service
@@ -60,6 +62,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 +92,7 @@
#ifdef USE_TLS
#define TLS_INTERNAL /* XXX */
#include
+#include
/*
* Application-specific.
@@ -108,6 +116,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 +128,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 +153,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);
+}