]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.4-20180603-nonprod
authorWietse Venema <wietse@porcupine.org>
Sun, 3 Jun 2018 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Tue, 19 Jun 2018 16:18:06 +0000 (12:18 -0400)
44 files changed:
postfix/HISTORY
postfix/WISHLIST
postfix/html/postconf.5.html
postfix/html/posttls-finger.1.html
postfix/makedefs
postfix/man/man1/posttls-finger.1
postfix/man/man5/postconf.5
postfix/mantools/postlink
postfix/proto/postconf.proto
postfix/src/global/mail_params.h
postfix/src/global/mail_proto.h
postfix/src/postscreen/postscreen_starttls.c
postfix/src/posttls-finger/posttls-finger.c
postfix/src/smtp/Makefile.in
postfix/src/smtp/lmtp_params.c
postfix/src/smtp/smtp.c
postfix/src/smtp/smtp.h
postfix/src/smtp/smtp_connect.c
postfix/src/smtp/smtp_key.c
postfix/src/smtp/smtp_params.c
postfix/src/smtp/smtp_proto.c
postfix/src/smtp/smtp_reuse.c
postfix/src/smtp/smtp_session.c
postfix/src/smtpd/smtpd.c
postfix/src/tls/Makefile.in
postfix/src/tls/tls.h
postfix/src/tls/tls_client.c
postfix/src/tls/tls_proxy.h
postfix/src/tls/tls_proxy_client_print.c [new file with mode: 0644]
postfix/src/tls/tls_proxy_client_scan.c [new file with mode: 0644]
postfix/src/tls/tls_proxy_clnt.c
postfix/src/tls/tls_proxy_context_print.c [moved from postfix/src/tls/tls_proxy_print.c with 63% similarity]
postfix/src/tls/tls_proxy_context_scan.c [new file with mode: 0644]
postfix/src/tls/tls_proxy_scan.c [deleted file]
postfix/src/tls/tls_proxy_server_print.c [new file with mode: 0644]
postfix/src/tls/tls_proxy_server_scan.c [new file with mode: 0644]
postfix/src/tlsproxy/Makefile.in
postfix/src/tlsproxy/tlsproxy.c
postfix/src/tlsproxy/tlsproxy.h
postfix/src/tlsproxy/tlsproxy_state.c
postfix/src/util/Makefile.in
postfix/src/util/argv_attr.h [new file with mode: 0644]
postfix/src/util/argv_attr_print.c [new file with mode: 0644]
postfix/src/util/argv_attr_scan.c [new file with mode: 0644]

index c8412f1dde3c9f7e51fb8b807b2aedef5792a17c..b856888d325429e5b7fdd1f63b91fc0059368595 100644 (file)
@@ -23399,6 +23399,38 @@ Apologies for any names omitted.
        TLS handshake error. Found during code maintenance. File:
        tlsproxy/tlsproxy.c.
 
+       Connection reuse for TLS-encrypted SMTP sessions. This is
+       work-in-progress, #ifdef USE_TLSPROXY, to avoid contamination
+       of existing code.
+
+       The idea is to have smtp(8) talk plaintext while tlsproxy(8)
+       converts between local plaintext and remote ciphertext.
+       Then, smtp(8) can save plaintext connections to the cache,
+       and scache(8) holds the handles to the tlsproxy(8) processes.
+
+       This preliminary implementation does not yet support proxying
+       of DANE attributes from smtp(8) to tlsproxy(8). tlsproxy(8)
+       does not have permissions to read private key files that
+       smtp(8) can read. And the name of a connection cache entry
+       does not yet depend on whether the cached connection uses
+       TLS, nor does it depend on DANE information.
+
+       Files: global/mail_proto.h, postscreen/postscreen_starttls.c,
+       posttls-finger/posttls-finger.c, smtp/smtp.c, smtp/smtp.h,
+       smtp/smtp_params.c, smtp/smtp_proto.c, smtp/smtp_session.c,
+       smtpd/smtpd.c, tls/tls.h, tls/tls_client.c, tls/tls_proxy.h,
+       tls/tls_proxy_client_init_print.c,
+       tls/tls_proxy_client_init_scan.c,
+       tls/tls_proxy_client_start_print.c,
+       tls/tls_proxy_client_start_scan.c, tls/tls_proxy_clnt.c,
+       tls/tls_proxy_context_print.c, tls/tls_proxy_context_scan.c,
+       tls/tls_proxy_server_init_print.c,
+       tls/tls_proxy_server_init_scan.c,
+       tls/tls_proxy_server_start_print.c,
+       tls/tls_proxy_server_start_scan.c, tlsproxy/tlsproxy.c,
+       tlsproxy/tlsproxy.h, tlsproxy/tlsproxy_state.c, util/argv_attr.h,
+       util/argv_attr_print.c, util/argv_attr_scan.c.
+
 20180425
 
        Cleanup: dnsblog proccesses now retire voluntarily after 
@@ -23445,3 +23477,26 @@ Apologies for any names omitted.
        Documentation: bash syntax to eliminate or view default
        settings in "postconf -n" output. File: postconf/postconf.c.
        Contributed by various postfix-users list members.
+
+20180603
+
+       TLS reuse: serializer/deserializer support for TLS_DANE and
+       related data structures. Files: tls/tls_proxy_client_print.c,
+       tls/tls_proxy_client_scan.c, tls/tls_proxy.h, util/argv_attr.h,
+       util/argv_attr_print.c, util/argv_attr_scan.c.
+
+       TLS reuse: posttls-finger -X test flag for quick tests.
+       File: posttls-finger/posttls-finger.c.
+
+       TLS reuse: smtp_use_tlsproxy boolean parameter. This is a
+       preliminary implementation that should support override via
+       smtp_tls_policy_maps. Files: smtp.c, smtp_connect.c,
+       smtp_params.c, smtp_proto.c, smtp_session.c.
+
+       TLS reuse: the SMTP client now includes the requested TLS
+       security level in the scache(8) key.
+
+       TLS reuse: address-based reuse is allowed only for TLS
+       levels that require no certificate checks. Perhaps it still
+       makes sense to save such sessions for reuse by less sensitive
+       deliveries. Files: smtp/smtp.h smtp/smtp_reuse.c.
index 42106f2c395e7f7f5f60657a91ed9e61f7f40ccd..440aea6550cc91b8662dcd646668cbaa79b8bcaa 100644 (file)
@@ -1,5 +1,9 @@
 Wish list:
 
+       With smtpd_reject_footer=$foo in master.cf, and foo defined
+       in main.cf, postconf complains about an unuse setting in main.cf.
+       Note that "postconf -Px" will expand the macro.
+
        Things to do before the stable release:
 
        Spell-check, double-word check, HTML validator check,
index 093a3c6b5a2524ab217f4fbe37a0b2624de4f223..a631ad157c4ea31aff04a77a41c46bc2cc9fe4ec 100644 (file)
@@ -13337,6 +13337,17 @@ is a concern for you, use the <a href="postconf.5.html#smtp_tls_per_site">smtp_t
 Postfix 2.3 and later use <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> instead. </p>
 
 
+</DD>
+
+<DT><b><a name="smtp_use_tlsproxy">smtp_use_tlsproxy</a>
+(default: no)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_use_tlsproxy">smtp_use_tlsproxy</a> configuration
+parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 3.4 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="smtp_xforward_timeout">smtp_xforward_timeout</a>
index 44dbd9eaa5dd9fac6c01ce4bf28d1f11651a81a6..db83e928de33dfe3f84e18a6a195014d0739b484 100644 (file)
@@ -280,17 +280,20 @@ POSTTLS-FINGER(1)                                            POSTTLS-FINGER(1)
               STARTTLS protocol.  The destination <i>domain</i>:<i>port</i> should of course
               provide such a service.
 
+       <b>-X</b>     Enable  <a href="tlsproxy.8.html"><b>tlsproxy</b>(8)</a>  mode. This is an unsupported mode, for pro-
+              gram development only.
+
        [<b>inet:</b>]<i>domain</i>[:<i>port</i>]
               Connect via TCP to domain <i>domain</i>, port <i>port</i>. The default port is
-              <b>smtp</b> (or 24 with LMTP).  With SMTP an MX lookup is performed  to
-              resolve  the  domain to a host, unless the domain is enclosed in
-              <b>[]</b>.  If you want to connect to a specific MX host, for  instance
-              <i>mx1.example.com</i>,  specify  [<i>mx1.example.com</i>]  as the destination
+              <b>smtp</b>  (or 24 with LMTP).  With SMTP an MX lookup is performed to
+              resolve the domain to a host, unless the domain is  enclosed  in
+              <b>[]</b>.   If you want to connect to a specific MX host, for instance
+              <i>mx1.example.com</i>, specify [<i>mx1.example.com</i>]  as  the  destination
               and <i>example.com</i> as a <b>match</b> argument.  When using DNS, the desti-
-              nation  domain  is assumed fully qualified and no <a href="ADDRESS_CLASS_README.html#default_domain_class">default domain</a>
-              or search suffixes are applied;  you  must  use  fully-qualified
-              names  or  also  enable <b>native</b> host lookups (these don't support
-              <b>dane</b> or <b>dane-only</b> as no DNSSEC validation information is  avail-
+              nation domain is assumed fully qualified and no  default  domain
+              or  search  suffixes  are  applied; you must use fully-qualified
+              names or also enable <b>native</b> host lookups  (these  don't  support
+              <b>dane</b>  or <b>dane-only</b> as no DNSSEC validation information is avail-
               able via <b>native</b> lookups).
 
        <b>unix:</b><i>pathname</i>
@@ -299,8 +302,8 @@ POSTTLS-FINGER(1)                                            POSTTLS-FINGER(1)
        <b>match ...</b>
               With no match arguments specified, certificate peername matching
               uses the compiled-in default strategies for each security level.
-              If  you specify one or more arguments, these will be used as the
-              list of certificate or public-key digests to match for the  <b>fin-</b>
+              If you specify one or more arguments, these will be used as  the
+              list  of certificate or public-key digests to match for the <b>fin-</b>
               <b>gerprint</b> level, or as the list of DNS names to match in the cer-
               tificate at the <b>verify</b> and <b>secure</b> levels.  If the security level
               is <b>dane</b>, or <b>dane-only</b> the match names are ignored, and <b>hostname,</b>
index 5449732b09a57c45c9a027bc08208cd05f39f26f..26e950d4b72eadc4d643c727e79a2aabe813616d 100644 (file)
@@ -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"
index ca773102a01916fb8feeb66e8df5a9b875828251..2515089b76f0ae1631c4ef444900e15385fcd6e2 100644 (file)
@@ -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
index b2dff862ea9d67a09ed10975916d79f756b40581..3ca7bcfc5d951232a10c8f194ab9d60985809cd7 100644 (file)
@@ -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.
index 4f07119a1098541422338e84e1579248a5cace40..4f7b6adafae2f03b833dcd0dde39e35cbfe63875 100755 (executable)
@@ -690,6 +690,8 @@ while (<>) {
     s;\bsmtp_per_record_deadline\b;<a href="postconf.5.html#smtp_per_record_deadline">$&</a>;g;
     s;\bsmtp_send_dummy_mail_auth\b;<a href="postconf.5.html#smtp_send_dummy_mail_auth">$&</a>;g;
     s;\bsmtp_balance_inet_protocols\b;<a href="postconf.5.html#smtp_balance_inet_protocols">$&</a>;g;
+    s;\bsmtp_use_tlsproxy\b;<a href="postconf.5.html#smtp_use_tlsproxy">$&</a>;g;
+    s;\blmtp_use_tlsproxy\b;<a href="postconf.5.html#lmtp_use_tlsproxy">$&</a>;g;
     s;\bsmtpd_enforce_tls\b;<a href="postconf.5.html#smtpd_enforce_tls">$&</a>;g;
     s;\bsmtpd_sasl_tls_security_options\b;<a href="postconf.5.html#smtpd_sasl_tls_security_options">$&</a>;g;
     s;\bsmtpd_sasl_type\b;<a href="postconf.5.html#smtpd_sasl_type">$&</a>;g;
index 23f4f63e6a91af80fe3da5ec7b9782481ca677f3..76670568e0a9b3ec912c2263cc65c9fe98907276 100644 (file)
@@ -16582,6 +16582,22 @@ Postfix versions. </p>
 
 <p> This feature is available in Postfix 3.0 and later.  </p>
 
+%PARAM smtp_use_tlsproxy no
+
+<p> 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.
+</p>
+
+<p> This feature is available in Postfix 3.4 and later.  </p>
+
+%PARAM smtp_use_tlsproxy no
+
+<p> The LMTP-specific version of the smtp_use_tlsproxy configuration
+parameter. See there for details. </p>
+
+<p> This feature is available in Postfix 3.4 and later. </p>
+
 %PARAM virtual_alias_address_length_limit 1000
 
 <p>
index dd2589fe0aecdd9e2906b67ef0a2ad756410fe5b..31843dfca67e1d58acb7219be9135bcc8146d3ac 100644 (file)
@@ -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.
   * 
index 363bc71ec84d0510ada614ea91217846ead322cc..9fc9ab68eaea59784d9db152439858ed68979961 100644 (file)
@@ -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.
   */
index a5d6906e695ca42049c524200cb67e15a9cce627..863ba8c23391881a37eb175ddd60b93d2119ea0e 100644 (file)
@@ -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);
index 5f559b4fd06d8bdc6510077b5b16864315d1e723..d9d128ff127ead7fcd3342b02b66f663e336bcfd 100644 (file)
 /*     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
 #include <smtp_stream.h>
 #include <dsn_buf.h>
 #include <mail_parm_split.h>
+#include <mail_proto.h>
 
 /* DNS library. */
 
 #include <tls.h>
 
 #ifdef USE_TLS
+#include <tls_proxy.h>
 #include <openssl/engine.h>
 #endif
 
@@ -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
        }
index acd830264dc94290d858c1f2e49e7693b0ebcafb..7d455649699ff5f65d1be592e25524522874dda0 100644 (file)
@@ -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
index 68d611e265b77c2628b2cc8138bf077e2cbc63fa..d057195d46c5add6fb8aa5c84df744d784a2cf50 100644 (file)
@@ -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[] = {
        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,
index ed27be96ae3771ef97cbbd6cf98bfda6647fc8c8..bbd0399723194b40b7ec0e0c960089093793a5da 100644 (file)
 /* Global library. */
 
 #include <deliver_request.h>
+#include <mail_proto.h>
 #include <mail_params.h>
 #include <mail_version.h>
 #include <mail_conf.h>
@@ -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,
index 11beb440683c97dc736d2f904e5413d4dc5f2e2b..27e7a8612340c990a303c41f3bec80b9a9eebc88 100644 (file)
   */
 #include <tls.h>
 
+ /*
+  * tlsproxy client.
+  */
+#include <tls_proxy.h>
+
  /*
   * Global iterator support. This is updated by the connection-management
   * loop, and contains dynamic context that appears in lookup keys for SASL
@@ -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
index 2bf209d92596acb3eacab6264685974a4b8a9efa..7fb8e168afd0ad7f133b3a1b1ad482ca80d5fdc2 100644 (file)
@@ -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
index 78d7959234cd1f23b9c0f9bb7c8cc0c5031e9483..56cff253b4685fb8310080f7e7e69997942ec32d 100644 (file)
 /*     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);
 
index ed5ea455ba7079e3c0e8b3aa498ada33dc7ee353..7fef9e70780f2db6b68099c07c327f168540e637 100644 (file)
@@ -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[] = {
        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,
index 15a15db171e0c7311d029f54f75f0acc20bb6f39..44c9a4d2e765538fdc7e0e0ab718945a3fe18fbc 100644 (file)
@@ -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) {
index da23692fd1f616e41a5af8f4eb68fdb4791cca20..2bc5c5ed0ec07c98ce6f33c74f1eb7329aae2452 100644 (file)
@@ -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:
 /*     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
 
index 806d8de060bd38214268c0d57d0728802fdf9b26..594567f78350a8ee768b7276d208abd5d6a88a7d 100644 (file)
 /*     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),
index 20598992b7db35f94be471f65066fd21b61b1f72..5006e7449197770427ff0e98c80e282f32edec70 100644 (file)
@@ -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"
index 55dfd1ecce917733b3d2534f7f8d7d9be215c006..16efff4dceadf0de8c9ee19f17c35688204e6d3f 100644 (file)
@@ -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
index 79b8d73a3b10903f7ed422e16898b12af1bb5955..01419cb56253d3b3b2533db7fec6eb33833c34d1 100644 (file)
@@ -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
index bbe18f9050fba1cd5aff8ce8842914567b6f919e..09e523364eaf77be648cf29b07921001e77e15d6 100644 (file)
 /*     TLS_SESS_STATE *tls_client_start(start_props)
 /*     const TLS_CLIENT_START_PROPS *start_props;
 /*
+/*     TLS_SESS_STATE *tls_client_post_connect(TLScontext, start_props)
+/*     TLS_SESS_STATE *TLScontext;
+/*     const TLS_CLIENT_START_PROPS *start_props;
+/*
 /*     void    tls_client_stop(app_ctx, stream, failure, TLScontext)
 /*     TLS_APPL_STATE *app_ctx;
 /*     VSTREAM *stream;
 /*     the fingerprint of the certificate.
 /* .PP
 /*     If no peer certificate is presented the peer_status is set to 0.
+/* EVENT_DRIVEN APPLICATIONS
+/* .ad
+/* .fi
+/*     Event-driven programs manage multiple I/O channels.  Such
+/*     programs cannot use the synchronous VSTREAM-over-TLS
+/*     implementation that the 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" :
index e677c67aba9d38e562198afb8832cd13c373a24d..99f1afe1ab4aeab8edb9bb7f6485c47fb9633d73 100644 (file)
 
 #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 (file)
index 0000000..7ed66b9
--- /dev/null
@@ -0,0 +1,324 @@
+/*++
+/* NAME
+/*     tls_proxy_client_print 3
+/* SUMMARY
+/*     write TLS_CLIENT_XXX structures to stream
+/* SYNOPSIS
+/*     #include <tls_proxy.h>
+/*
+/*     int     tls_proxy_client_init_print(print_fn, stream, flags, ptr)
+/*     ATTR_PRINT_MASTER_FN print_fn;
+/*     VSTREAM *stream;
+/*     int     flags;
+/*     void    *ptr;
+/*
+/*     int     tls_proxy_client_start_print(print_fn, stream, flags, ptr)
+/*     ATTR_PRINT_MASTER_FN print_fn;
+/*     VSTREAM *stream;
+/*     int     flags;
+/*     void    *ptr;
+/* DESCRIPTION
+/*     tls_proxy_client_init_print() writes a full TLS_CLIENT_INIT_PROPS
+/*     structure to the named stream using the specified attribute
+/*     print routine. tls_proxy_client_init_print() is meant to
+/*     be passed as a call-back to attr_print(), thusly:
+/*
+/*     SEND_ATTR_FUNC(tls_proxy_client_init_print, (void *) init_props), ...
+/*
+/*     tls_proxy_client_start_print() writes a TLS_CLIENT_START_PROPS
+/*     structure, without stream or file descriptor members, to
+/*     the named stream using the specified attribute print routine.
+/*     tls_proxy_client_start_print() is meant to be passed as a
+/*     call-back to attr_print(), thusly:
+/*
+/*     SEND_ATTR_FUNC(tls_proxy_client_start_print, (void *) start_props), ...
+/* DIAGNOSTICS
+/*     Fatal: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
+/*--*/
+
+#ifdef USE_TLS
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library */
+
+#include <argv_attr.h>
+#include <attr.h>
+#include <msg.h>
+
+/* TLS library. */
+
+#include <tls.h>
+#include <tls_proxy.h>
+
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* tls_proxy_client_init_print - send TLS_CLIENT_INIT_PROPS over stream */
+
+int     tls_proxy_client_init_print(ATTR_PRINT_MASTER_FN print_fn, VSTREAM *fp,
+                                           int flags, void *ptr)
+{
+    TLS_CLIENT_INIT_PROPS *props = (TLS_CLIENT_INIT_PROPS *) ptr;
+    int     ret;
+
+    if (msg_verbose)
+       msg_info("begin tls_proxy_client_init_print");
+
+#define STRING_OR_EMPTY(s) ((s) ? (s) : "")
+
+    ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+                  SEND_ATTR_STR(TLS_ATTR_LOG_PARAM,
+                                STRING_OR_EMPTY(props->log_param)),
+                  SEND_ATTR_STR(TLS_ATTR_LOG_LEVEL,
+                                STRING_OR_EMPTY(props->log_level)),
+                  SEND_ATTR_INT(TLS_ATTR_VERIFYDEPTH, props->verifydepth),
+                  SEND_ATTR_STR(TLS_ATTR_CACHE_TYPE,
+                                STRING_OR_EMPTY(props->cache_type)),
+                  SEND_ATTR_STR(TLS_ATTR_CERT_FILE,
+                                STRING_OR_EMPTY(props->cert_file)),
+                  SEND_ATTR_STR(TLS_ATTR_KEY_FILE,
+                                STRING_OR_EMPTY(props->key_file)),
+                  SEND_ATTR_STR(TLS_ATTR_DCERT_FILE,
+                                STRING_OR_EMPTY(props->dcert_file)),
+                  SEND_ATTR_STR(TLS_ATTR_DKEY_FILE,
+                                STRING_OR_EMPTY(props->dkey_file)),
+                  SEND_ATTR_STR(TLS_ATTR_ECCERT_FILE,
+                                STRING_OR_EMPTY(props->eccert_file)),
+                  SEND_ATTR_STR(TLS_ATTR_ECKEY_FILE,
+                                STRING_OR_EMPTY(props->eckey_file)),
+                  SEND_ATTR_STR(TLS_ATTR_CAFILE,
+                                STRING_OR_EMPTY(props->CAfile)),
+                  SEND_ATTR_STR(TLS_ATTR_CAPATH,
+                                STRING_OR_EMPTY(props->CApath)),
+                  SEND_ATTR_STR(TLS_ATTR_MDALG,
+                                STRING_OR_EMPTY(props->mdalg)),
+                  ATTR_TYPE_END);
+    /* Do not flush the stream. */
+    if (msg_verbose)
+       msg_info("tls_proxy_client_init_print ret=%d", ret);
+    return (ret);
+}
+
+/* tls_proxy_client_certs_print - send x509 certificates over stream */
+
+static int tls_proxy_client_certs_print(ATTR_PRINT_MASTER_FN print_fn,
+                                         VSTREAM *fp, int flags, void *ptr)
+{
+    TLS_CERTS *tls_certs = (TLS_CERTS *) ptr;
+    TLS_CERTS *tp;
+    int     count;
+    int     ret;
+
+    for (tp = tls_certs, count = 0; tp != 0; tp = tp->next, count++)
+        /* void */ ;
+    if (msg_verbose)
+       msg_info("tls_proxy_client_certs_print count=%d", count);
+
+    ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+                  SEND_ATTR_INT(TLS_ATTR_COUNT, count),
+                  ATTR_TYPE_END);
+
+    if (ret == 0 && count > 0) {
+       VSTRING *buf = vstring_alloc(100);
+       int     n;
+
+       for (tp = tls_certs, n = 0; ret == 0 && n < count; tp = tp->next, n++) {
+           size_t  len = i2d_X509(tp->cert, (unsigned char **) 0);
+           unsigned char *bp;
+
+           VSTRING_RESET(buf);
+           VSTRING_SPACE(buf, len);
+           bp = (unsigned char *) STR(buf);
+           i2d_X509(tp->cert, &bp);
+           if ((char *) bp - STR(buf) != len)
+               msg_panic("i2d_X509 failed to encode certificate");
+           ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+                          SEND_ATTR_DATA(TLS_ATTR_CERT, LEN(buf), STR(buf)),
+                          ATTR_TYPE_END);
+       }
+       vstring_free(buf);
+    }
+    /* Do not flush the stream. */
+    if (msg_verbose)
+       msg_info("tls_proxy_client_certs_print ret=%d", count);
+    return (ret);
+}
+
+/* tls_proxy_client_pkeys_print - send public keys over stream */
+
+static int tls_proxy_client_pkeys_print(ATTR_PRINT_MASTER_FN print_fn,
+                                         VSTREAM *fp, int flags, void *ptr)
+{
+    TLS_PKEYS *tls_pkeys = (TLS_PKEYS *) ptr;
+    TLS_PKEYS *tp;
+    int     count;
+    int     ret;
+
+    for (tp = tls_pkeys, count = 0; tp != 0; tp = tp->next, count++)
+        /* void */ ;
+    if (msg_verbose)
+       msg_info("tls_proxy_client_pkeys_print count=%d", count);
+
+    ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+                  SEND_ATTR_INT(TLS_ATTR_COUNT, count),
+                  ATTR_TYPE_END);
+
+    if (ret == 0 && count > 0) {
+       VSTRING *buf = vstring_alloc(100);
+       int     n;
+
+       for (tp = tls_pkeys, n = 0; ret == 0 && n < count; tp = tp->next, n++) {
+           size_t  len = i2d_PUBKEY(tp->pkey, (unsigned char **) 0);
+           unsigned char *bp;
+
+           VSTRING_RESET(buf);
+           VSTRING_SPACE(buf, len);
+           bp = (unsigned char *) STR(buf);
+           i2d_PUBKEY(tp->pkey, &bp);
+           if ((char *) bp - STR(buf) != len)
+               msg_panic("i2d_PUBKEY failed to encode public key");
+           ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+                          SEND_ATTR_DATA(TLS_ATTR_PKEY, LEN(buf), STR(buf)),
+                          ATTR_TYPE_END);
+       }
+       vstring_free(buf);
+    }
+    /* Do not flush the stream. */
+    if (msg_verbose)
+       msg_info("tls_proxy_client_pkeys_print ret=%d", count);
+    return (ret);
+}
+
+/* tls_proxy_client_tlsa_print - send TLS_TLSA over stream */
+
+static int tls_proxy_client_tlsa_print(ATTR_PRINT_MASTER_FN print_fn,
+                                         VSTREAM *fp, int flags, void *ptr)
+{
+    TLS_TLSA *tls_tlsa = (TLS_TLSA *) ptr;
+    TLS_TLSA *tp;
+    int     count;
+    int     ret;
+
+    for (tp = tls_tlsa, count = 0; tp != 0; tp = tp->next, count++)
+        /* void */ ;
+    if (msg_verbose)
+       msg_info("tls_proxy_client_tlsa_print count=%d", count);
+
+    ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+                  SEND_ATTR_INT(TLS_ATTR_COUNT, count),
+                  ATTR_TYPE_END);
+
+    if (ret == 0 && count > 0) {
+       int     n;
+
+       for (tp = tls_tlsa, n = 0; ret == 0 && n < count; tp = tp->next, n++) {
+           ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+                          SEND_ATTR_STR(TLS_ATTR_MDALG, tp->mdalg),
+                          SEND_ATTR_FUNC(argv_attr_print,
+                                         (void *) tp->certs),
+                          SEND_ATTR_FUNC(argv_attr_print,
+                                         (void *) tp->pkeys),
+                          ATTR_TYPE_END);
+       }
+    }
+    /* Do not flush the stream. */
+    if (msg_verbose)
+       msg_info("tls_proxy_client_tlsa_print ret=%d", count);
+    return (ret);
+}
+
+/* tls_proxy_client_dane_print - send TLS_DANE over stream */
+
+static int tls_proxy_client_dane_print(ATTR_PRINT_MASTER_FN print_fn,
+                                         VSTREAM *fp, int flags, void *ptr)
+{
+    TLS_DANE *dane = (TLS_DANE *) ptr;
+    int     ret;
+
+    ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+                  SEND_ATTR_INT(TLS_ATTR_DANE, dane != 0),
+                  ATTR_TYPE_END);
+    if (msg_verbose)
+       msg_info("tls_proxy_client_dane_print dane=%d", dane != 0);
+
+    if (ret == 0 && dane != 0) {
+       ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+                      SEND_ATTR_FUNC(tls_proxy_client_tlsa_print,
+                                     (void *) dane->ta),
+                      SEND_ATTR_FUNC(tls_proxy_client_tlsa_print,
+                                     (void *) dane->ee),
+                      SEND_ATTR_FUNC(tls_proxy_client_certs_print,
+                                     (void *) dane->certs),
+                      SEND_ATTR_FUNC(tls_proxy_client_pkeys_print,
+                                     (void *) dane->pkeys),
+                      SEND_ATTR_STR(TLS_ATTR_DOMAIN,
+                                    STRING_OR_EMPTY(dane->base_domain)),
+                      SEND_ATTR_INT(TLS_ATTR_FLAGS, dane->flags),
+                      SEND_ATTR_LONG(TLS_ATTR_EXP, dane->expires),
+                      ATTR_TYPE_END);
+    }
+    /* Do not flush the stream. */
+    if (msg_verbose)
+       msg_info("tls_proxy_client_dane_print ret=%d", ret);
+    return (ret);
+}
+
+/* tls_proxy_client_start_print - send TLS_CLIENT_START_PROPS over stream */
+
+int     tls_proxy_client_start_print(ATTR_PRINT_MASTER_FN print_fn,
+                                         VSTREAM *fp, int flags, void *ptr)
+{
+    TLS_CLIENT_START_PROPS *props = (TLS_CLIENT_START_PROPS *) ptr;
+    int     ret;
+
+    if (msg_verbose)
+       msg_info("begin tls_proxy_client_start_print");
+
+#define STRING_OR_EMPTY(s) ((s) ? (s) : "")
+
+    ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+                  SEND_ATTR_INT(TLS_ATTR_TIMEOUT, props->timeout),
+                  SEND_ATTR_INT(TLS_ATTR_TLS_LEVEL, props->tls_level),
+                  SEND_ATTR_STR(TLS_ATTR_NEXTHOP,
+                                STRING_OR_EMPTY(props->nexthop)),
+                  SEND_ATTR_STR(TLS_ATTR_HOST,
+                                STRING_OR_EMPTY(props->host)),
+                  SEND_ATTR_STR(TLS_ATTR_NAMADDR,
+                                STRING_OR_EMPTY(props->namaddr)),
+                  SEND_ATTR_STR(TLS_ATTR_SERVERID,
+                                STRING_OR_EMPTY(props->serverid)),
+                  SEND_ATTR_STR(TLS_ATTR_HELO,
+                                STRING_OR_EMPTY(props->helo)),
+                  SEND_ATTR_STR(TLS_ATTR_PROTOCOLS,
+                                STRING_OR_EMPTY(props->protocols)),
+                  SEND_ATTR_STR(TLS_ATTR_CIPHER_GRADE,
+                                STRING_OR_EMPTY(props->cipher_grade)),
+                  SEND_ATTR_STR(TLS_ATTR_CIPHER_EXCLUSIONS,
+                                STRING_OR_EMPTY(props->cipher_exclusions)),
+                  SEND_ATTR_FUNC(argv_attr_print,
+                                 (void *) props->matchargv),
+                  SEND_ATTR_STR(TLS_ATTR_MDALG,
+                                STRING_OR_EMPTY(props->mdalg)),
+                  SEND_ATTR_FUNC(tls_proxy_client_dane_print,
+                                 (void *) props->dane),
+                  ATTR_TYPE_END);
+    /* Do not flush the stream. */
+    if (msg_verbose)
+       msg_info("tls_proxy_client_start_print ret=%d", ret);
+    return (ret);
+}
+
+#endif
diff --git a/postfix/src/tls/tls_proxy_client_scan.c b/postfix/src/tls/tls_proxy_client_scan.c
new file mode 100644 (file)
index 0000000..19ca7c2
--- /dev/null
@@ -0,0 +1,566 @@
+/*++
+/* NAME
+/*     tls_proxy_client_scan 3
+/* SUMMARY
+/*     read TLS_CLIENT_XXX structures from stream
+/* SYNOPSIS
+/*     #include <tls_proxy.h>
+/*
+/*     int     tls_proxy_client_init_scan(scan_fn, stream, flags, ptr)
+/*     ATTR_SCAN_MASTER_FN scan_fn;
+/*     VSTREAM *stream;
+/*     int     flags;
+/*     void    *ptr;
+/*
+/*     char    *tls_proxy_client_init_to_string(buf, init_props)
+/*     VSTRING *buf;
+/*     TLS_CLIENT_INIT_PROPS *init_props;
+/*
+/*     void    tls_proxy_client_init_free(init_props)
+/*     TLS_CLIENT_INIT_PROPS *init_props;
+/*
+/*     int     tls_proxy_client_start_scan(scan_fn, stream, flags, ptr)
+/*     ATTR_SCAN_MASTER_FN scan_fn;
+/*     VSTREAM *stream;
+/*     int     flags;
+/*     void    *ptr;
+/*
+/*     void    tls_proxy_client_start_free(start_props)
+/*     TLS_CLIENT_START_PROPS *start_props;
+/* DESCRIPTION
+/*     tls_proxy_client_init_scan() reads a full TLS_CLIENT_INIT_PROPS
+/*     structure from the named stream using the specified attribute
+/*     scan routine. tls_proxy_client_init_scan() is meant to be passed
+/*     as a call-back function to attr_scan(), as shown below.
+/*
+/*     tls_proxy_client_init_to_string() produces a lookup key
+/*     that is unique for the properties received by
+/*     tls_proxy_client_init_scan().
+/*
+/*     tls_proxy_client_init_free() destroys a TLS_CLIENT_INIT_PROPS
+/*     structure that was created by tls_proxy_client_init_scan().
+/*
+/*     TLS_CLIENT_INIT_PROPS *init_props = 0;
+/*     ...
+/*     ... RECV_ATTR_FUNC(tls_proxy_client_init_scan, (void *) &init_props)
+/*     ...
+/*     if (init_props != 0)
+/*         tls_proxy_client_init_free(init_props);
+/*
+/*     tls_proxy_client_start_scan() reads a TLS_CLIENT_START_PROPS
+/*     structure, without the stream of file descriptor members,
+/*     from the named stream using the specified attribute scan
+/*     routine. tls_proxy_client_start_scan() is meant to be passed
+/*     as a call-back function to attr_scan(), as shown below.
+/*
+/*     tls_proxy_client_start_free() destroys a TLS_CLIENT_START_PROPS
+/*     structure that was created by tls_proxy_client_start_scan().
+/*
+/*     TLS_CLIENT_START_PROPS *start_props = 0;
+/*     ...
+/*     ... RECV_ATTR_FUNC(tls_proxy_client_start_scan, (void *) &start_props)
+/*     ...
+/*     if (start_props != 0)
+/*         tls_proxy_client_start_free(start_props);
+/* DIAGNOSTICS
+/*     Fatal: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
+/*--*/
+
+#ifdef USE_TLS
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library */
+
+#include <argv_attr.h>
+#include <attr.h>
+#include <msg.h>
+#include <vstring.h>
+
+/* TLS library. */
+
+#include <tls.h>
+#include <tls_proxy.h>
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* tls_proxy_client_init_free - destroy TLS_CLIENT_INIT_PROPS structure */
+
+void    tls_proxy_client_init_free(TLS_CLIENT_INIT_PROPS *props)
+{
+    myfree((void *) props->log_param);
+    myfree((void *) props->log_level);
+    myfree((void *) props->cache_type);
+    myfree((void *) props->cert_file);
+    myfree((void *) props->key_file);
+    myfree((void *) props->dcert_file);
+    myfree((void *) props->dkey_file);
+    myfree((void *) props->eccert_file);
+    myfree((void *) props->eckey_file);
+    myfree((void *) props->CAfile);
+    myfree((void *) props->CApath);
+    myfree((void *) props->mdalg);
+    myfree((void *) props);
+}
+
+/* tls_proxy_client_init_scan - receive TLS_CLIENT_INIT_PROPS from stream */
+
+int     tls_proxy_client_init_scan(ATTR_SCAN_MASTER_FN scan_fn, VSTREAM *fp,
+                                          int flags, void *ptr)
+{
+    TLS_CLIENT_INIT_PROPS *props
+    = (TLS_CLIENT_INIT_PROPS *) mymalloc(sizeof(*props));
+    int     ret;
+    VSTRING *log_param = vstring_alloc(25);
+    VSTRING *log_level = vstring_alloc(25);
+    VSTRING *cache_type = vstring_alloc(25);
+    VSTRING *cert_file = vstring_alloc(25);
+    VSTRING *key_file = vstring_alloc(25);
+    VSTRING *dcert_file = vstring_alloc(25);
+    VSTRING *dkey_file = vstring_alloc(25);
+    VSTRING *eccert_file = vstring_alloc(25);
+    VSTRING *eckey_file = vstring_alloc(25);
+    VSTRING *CAfile = vstring_alloc(25);
+    VSTRING *CApath = vstring_alloc(25);
+    VSTRING *mdalg = vstring_alloc(25);
+
+    if (msg_verbose)
+       msg_info("begin tls_proxy_client_init_scan");
+
+    /*
+     * Note: memset() is not a portable way to initialize non-integer types.
+     */
+    memset(props, 0, sizeof(*props));
+    ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+                 RECV_ATTR_STR(TLS_ATTR_LOG_PARAM, log_param),
+                 RECV_ATTR_STR(TLS_ATTR_LOG_LEVEL, log_level),
+                 RECV_ATTR_INT(TLS_ATTR_VERIFYDEPTH, &props->verifydepth),
+                 RECV_ATTR_STR(TLS_ATTR_CACHE_TYPE, cache_type),
+                 RECV_ATTR_STR(TLS_ATTR_CERT_FILE, cert_file),
+                 RECV_ATTR_STR(TLS_ATTR_KEY_FILE, key_file),
+                 RECV_ATTR_STR(TLS_ATTR_DCERT_FILE, dcert_file),
+                 RECV_ATTR_STR(TLS_ATTR_DKEY_FILE, dkey_file),
+                 RECV_ATTR_STR(TLS_ATTR_ECCERT_FILE, eccert_file),
+                 RECV_ATTR_STR(TLS_ATTR_ECKEY_FILE, eckey_file),
+                 RECV_ATTR_STR(TLS_ATTR_CAFILE, CAfile),
+                 RECV_ATTR_STR(TLS_ATTR_CAPATH, CApath),
+                 RECV_ATTR_STR(TLS_ATTR_MDALG, mdalg),
+                 ATTR_TYPE_END);
+    /* Always construct a well-formed structure. */
+    props->log_param = vstring_export(log_param);
+    props->log_level = vstring_export(log_level);
+    props->cache_type = vstring_export(cache_type);
+    props->cert_file = vstring_export(cert_file);
+    props->key_file = vstring_export(key_file);
+    props->dcert_file = vstring_export(dcert_file);
+    props->dkey_file = vstring_export(dkey_file);
+    props->eccert_file = vstring_export(eccert_file);
+    props->eckey_file = vstring_export(eckey_file);
+    props->CAfile = vstring_export(CAfile);
+    props->CApath = vstring_export(CApath);
+    props->mdalg = vstring_export(mdalg);
+    ret = (ret == 13 ? 1 : -1);
+    if (ret != 1) {
+       tls_proxy_client_init_free(props);
+       props = 0;
+    }
+    *(TLS_CLIENT_INIT_PROPS **) ptr = props;
+    if (msg_verbose)
+       msg_info("tls_proxy_client_init_scan ret=%d", ret);
+    return (ret);
+}
+
+/* tls_proxy_client_init_to_string - serialize to string */
+
+char   *tls_proxy_client_init_to_string(VSTRING *buf,
+                                               TLS_CLIENT_INIT_PROPS *props)
+{
+    vstring_sprintf(buf, "%s\n%s\n%d\n%s\n%s\n%s\n%s\n%s\n"
+                   "%s\n%s\n%s\n%s\n%s\n", props->log_param,
+                   props->log_level, props->verifydepth,
+                   props->cache_type, props->cert_file, props->key_file,
+                   props->dcert_file, props->dkey_file,
+                   props->eccert_file, props->eckey_file,
+                   props->CAfile, props->CApath, props->mdalg);
+    return (vstring_str(buf));
+}
+
+/* tls_proxy_client_certs_free - destroy TLS_PKEYS from stream */
+
+static void tls_proxy_client_certs_free(TLS_CERTS *tp)
+{
+    if (tp->next)
+       tls_proxy_client_certs_free(tp->next);
+    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
index 97d096264c04b7deec4385d1fc129cac0ad9632f..90b21cd506577c564c2ee011f7df6712f1031cbf 100644 (file)
@@ -2,24 +2,36 @@
 /* NAME
 /*     tlsproxy_clnt 3
 /* SUMMARY
-/*     postscreen TLS proxy support
+/*     tlsproxy(8) client support
 /* SYNOPSIS
 /*     #include <tlsproxy_clnt.h>
 /*
 /*     VSTREAM *tls_proxy_open(service, flags, peer_stream, peer_addr,
-/*                               peer_port, timeout)
+/*                             peer_port, 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
 /*     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
 /*     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
similarity index 63%
rename from postfix/src/tls/tls_proxy_print.c
rename to postfix/src/tls/tls_proxy_context_print.c
index e30e8be88beea626dd26ef4e108f98ebed450354..a597024b2bbbf8440e2a0974ac0cb73c9ec7e626 100644 (file)
@@ -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 <tls_proxy.h>
 /*
 /*     int     flags;
 /*     void    *ptr;
 /* DESCRIPTION
-/*     tls_proxy_context_print() writes a TLS_SESS_STATE structure
-/*     to the named stream using the specified attribute print
-/*     routine. TLS_SESS_STATE() is meant to be passed as a call-back
-/*     to attr_print(), thusly:
+/*     tls_proxy_context_print() writes the public members of a
+/*     TLS_ATTR_STATE structure to the named stream using the
+/*     specified attribute print routine. tls_proxy_context_print()
+/*     is meant to be passed as a call-back to attr_print(), thusly:
 /*
 /*     ... SEND_ATTR_FUNC(tls_proxy_context_print, (void *) tls_context), ...
 /* DIAGNOSTICS
 /*     IBM T.J. Watson Research
 /*     P.O. Box 704
 /*     Yorktown Heights, NY 10598, USA
+/*
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
 /*--*/
 
 #ifdef USE_TLS
 
 #include <attr.h>
 
-/* Global library. */
-
-#include <mail_proto.h>
-
 /* TLS library. */
 
 #include <tls.h>
@@ -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 (file)
index 0000000..74f109b
--- /dev/null
@@ -0,0 +1,140 @@
+/*++
+/* NAME
+/*     tls_proxy_context_scan
+/* SUMMARY
+/*     read TLS session state from stream
+/* SYNOPSIS
+/*     #include <tls_proxy.h>
+/*
+/*     int     tls_proxy_context_scan(scan_fn, stream, flags, ptr)
+/*     ATTR_SCAN_MASTER_FN scan_fn;
+/*     VSTREAM *stream;
+/*     int     flags;
+/*     void    *ptr;
+/*
+/*     void    tls_proxy_context_free(tls_context)
+/*     TLS_SESS_STATE *tls_context;
+/* DESCRIPTION
+/*     tls_proxy_context_scan() reads the public members of a
+/*     TLS_ATTR_STATE structure from the named stream using the
+/*     specified attribute scan routine.  tls_proxy_context_scan()
+/*     is meant to be passed as a call-back to attr_scan() as shown
+/*     below.
+/*
+/*     tls_proxy_context_free() destroys a TLS context object that
+/*     was received with tls_proxy_context_scan().
+/*
+/*     TLS_ATTR_STATE *tls_context = 0;
+/*     ...
+/*     ... RECV_ATTR_FUNC(tls_proxy_context_scan, (void *) &tls_context), ...
+/*     ...
+/*     if (tls_context)
+/*         tls_proxy_context_free(tls_context);
+/* DIAGNOSTICS
+/*     Fatal: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
+/*--*/
+
+#ifdef USE_TLS
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library */
+
+#include <attr.h>
+#include <msg.h>
+
+/* TLS library. */
+
+#include <tls.h>
+#include <tls_proxy.h>
+
+/* tls_proxy_context_scan - receive TLS session state from stream */
+
+int     tls_proxy_context_scan(ATTR_SCAN_MASTER_FN scan_fn, VSTREAM *fp,
+                                      int flags, void *ptr)
+{
+    TLS_SESS_STATE *tls_context
+    = (TLS_SESS_STATE *) mymalloc(sizeof(*tls_context));;
+    int     ret;
+    VSTRING *peer_CN = vstring_alloc(25);
+    VSTRING *issuer_CN = vstring_alloc(25);
+    VSTRING *peer_cert_fprint = vstring_alloc(60);     /* 60 for SHA-1 */
+    VSTRING *peer_pkey_fprint = vstring_alloc(60);     /* 60 for SHA-1 */
+    VSTRING *protocol = vstring_alloc(25);
+    VSTRING *cipher_name = vstring_alloc(25);
+
+    if (msg_verbose)
+       msg_info("begin tls_proxy_context_scan");
+
+    /*
+     * Note: memset() is not a portable way to initialize non-integer types.
+     */
+    memset(tls_context, 0, sizeof(*tls_context));
+    ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+                 RECV_ATTR_STR(TLS_ATTR_PEER_CN, peer_CN),
+                 RECV_ATTR_STR(TLS_ATTR_ISSUER_CN, issuer_CN),
+                 RECV_ATTR_STR(TLS_ATTR_PEER_CERT_FPT, peer_cert_fprint),
+                 RECV_ATTR_STR(TLS_ATTR_PEER_PKEY_FPT, peer_pkey_fprint),
+                 RECV_ATTR_INT(TLS_ATTR_PEER_STATUS,
+                               &tls_context->peer_status),
+                 RECV_ATTR_STR(TLS_ATTR_CIPHER_PROTOCOL, protocol),
+                 RECV_ATTR_STR(TLS_ATTR_CIPHER_NAME, cipher_name),
+                 RECV_ATTR_INT(TLS_ATTR_CIPHER_USEBITS,
+                               &tls_context->cipher_usebits),
+                 RECV_ATTR_INT(TLS_ATTR_CIPHER_ALGBITS,
+                               &tls_context->cipher_algbits),
+                 ATTR_TYPE_END);
+    /* Always construct a well-formed structure. */
+    tls_context->peer_CN = vstring_export(peer_CN);
+    tls_context->issuer_CN = vstring_export(issuer_CN);
+    tls_context->peer_cert_fprint = vstring_export(peer_cert_fprint);
+    tls_context->peer_pkey_fprint = vstring_export(peer_pkey_fprint);
+    tls_context->protocol = vstring_export(protocol);
+    tls_context->cipher_name = vstring_export(cipher_name);
+    ret = (ret == 9 ? 1 : -1);
+    if (ret != 1) {
+       tls_proxy_context_free(tls_context);
+       tls_context = 0;
+    }
+    *(TLS_SESS_STATE **) ptr = tls_context;
+    if (msg_verbose)
+       msg_info("tls_proxy_context_scan ret=%d", ret);
+    return (ret);
+}
+
+/* tls_proxy_context_free - destroy object from tls_proxy_context_receive() */
+
+void    tls_proxy_context_free(TLS_SESS_STATE *tls_context)
+{
+    if (tls_context->peer_CN)
+       myfree(tls_context->peer_CN);
+    if (tls_context->issuer_CN)
+       myfree(tls_context->issuer_CN);
+    if (tls_context->peer_cert_fprint)
+       myfree(tls_context->peer_cert_fprint);
+    if (tls_context->peer_pkey_fprint)
+       myfree(tls_context->peer_pkey_fprint);
+    if (tls_context->protocol)
+       myfree((void *) tls_context->protocol);
+    if (tls_context->cipher_name)
+       myfree((void *) tls_context->cipher_name);
+    myfree((void *) tls_context);
+}
+
+#endif
diff --git a/postfix/src/tls/tls_proxy_scan.c b/postfix/src/tls/tls_proxy_scan.c
deleted file mode 100644 (file)
index 29a0cd9..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*++
-/* NAME
-/*     tls_proxy_scan
-/* SUMMARY
-/*     read TLS session state from stream
-/* SYNOPSIS
-/*     #include <tls_proxy.h>
-/*
-/*     int     tls_proxy_context_scan(scan_fn, stream, flags, ptr)
-/*     ATTR_SCAN_MASTER_FN scan_fn;
-/*     VSTREAM *stream;
-/*     int     flags;
-/*     void    *ptr;
-/* DESCRIPTION
-/*     tls_proxy_context_scan() reads a TLS_SESS_STATE structure
-/*     from the named stream using the specified attribute scan
-/*     routine.  tls_proxy_context_scan() is meant to be passed as
-/*     a call-back to attr_scan(), thusly:
-/*
-/*     ... RECV_ATTR_FUNC(tls_proxy_context_scan, (void *) tls_context), ...
-/* DIAGNOSTICS
-/*     Fatal: out of memory.
-/* LICENSE
-/* .ad
-/* .fi
-/*     The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/*     Wietse Venema
-/*     IBM T.J. Watson Research
-/*     P.O. Box 704
-/*     Yorktown Heights, NY 10598, USA
-/*--*/
-
-#ifdef USE_TLS
-
-/* System library. */
-
-#include <sys_defs.h>
-
-/* Utility library */
-
-#include <attr.h>
-
-/* Global library. */
-
-#include <mail_proto.h>
-
-/* TLS library. */
-
-#include <tls.h>
-#include <tls_proxy.h>
-
-/* tls_proxy_context_scan - receive TLS session state from stream */
-
-int     tls_proxy_context_scan(ATTR_SCAN_MASTER_FN scan_fn, VSTREAM *fp,
-                                      int flags, void *ptr)
-{
-    TLS_SESS_STATE *tls_context = (TLS_SESS_STATE *) ptr;
-    int     ret;
-    VSTRING *peer_CN = vstring_alloc(25);
-    VSTRING *issuer_CN = vstring_alloc(25);
-    VSTRING *peer_cert_fprint = vstring_alloc(60);     /* 60 for SHA-1 */
-    VSTRING *peer_pkey_fprint = vstring_alloc(60);     /* 60 for SHA-1 */
-    VSTRING *protocol = vstring_alloc(25);
-    VSTRING *cipher_name = vstring_alloc(25);
-
-    /*
-     * Note: memset() is not a portable way to initialize non-integer types.
-     */
-    memset(ptr, 0, sizeof(TLS_SESS_STATE));
-    ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
-                 RECV_ATTR_STR(MAIL_ATTR_PEER_CN, peer_CN),
-                 RECV_ATTR_STR(MAIL_ATTR_ISSUER_CN, issuer_CN),
-                 RECV_ATTR_STR(MAIL_ATTR_PEER_CERT_FPT, peer_cert_fprint),
-                 RECV_ATTR_STR(MAIL_ATTR_PEER_PKEY_FPT, peer_pkey_fprint),
-                 RECV_ATTR_INT(MAIL_ATTR_PEER_STATUS,
-                               &tls_context->peer_status),
-                 RECV_ATTR_STR(MAIL_ATTR_CIPHER_PROTOCOL, protocol),
-                 RECV_ATTR_STR(MAIL_ATTR_CIPHER_NAME, cipher_name),
-                 RECV_ATTR_INT(MAIL_ATTR_CIPHER_USEBITS,
-                               &tls_context->cipher_usebits),
-                 RECV_ATTR_INT(MAIL_ATTR_CIPHER_ALGBITS,
-                               &tls_context->cipher_algbits),
-                 ATTR_TYPE_END);
-    tls_context->peer_CN = vstring_export(peer_CN);
-    tls_context->issuer_CN = vstring_export(issuer_CN);
-    tls_context->peer_cert_fprint = vstring_export(peer_cert_fprint);
-    tls_context->peer_pkey_fprint = vstring_export(peer_pkey_fprint);
-    tls_context->protocol = vstring_export(protocol);
-    tls_context->cipher_name = vstring_export(cipher_name);
-    return (ret == 9 ? 1 : -1);
-}
-
-#endif
diff --git a/postfix/src/tls/tls_proxy_server_print.c b/postfix/src/tls/tls_proxy_server_print.c
new file mode 100644 (file)
index 0000000..670f841
--- /dev/null
@@ -0,0 +1,141 @@
+/*++
+/* NAME
+/*     tls_proxy_server_print 3
+/* SUMMARY
+/*     write TLS_SERVER_XXX structures to stream
+/* SYNOPSIS
+/*     #include <tls_proxy.h>
+/*
+/*     int     tls_proxy_server_init_print(print_fn, stream, flags, ptr)
+/*     ATTR_PRINT_MASTER_FN print_fn;
+/*     VSTREAM *stream;
+/*     int     flags;
+/*     void    *ptr;
+/*
+/*     int     tls_proxy_server_start_print(print_fn, stream, flags, ptr)
+/*     ATTR_PRINT_MASTER_FN print_fn;
+/*     VSTREAM *stream;
+/*     int     flags;
+/*     void    *ptr;
+/* DESCRIPTION
+/*     tls_proxy_server_init_print() writes a TLS_SERVER_INIT_PROPS
+/*     structure to the named stream using the specified attribute print
+/*     routine. tls_proxy_server_init_print() is meant to be passed as
+/*     a call-back to attr_print(), thusly:
+/*
+/*     ... SEND_ATTR_FUNC(tls_proxy_server_init_print, (void *) init_props), ...
+/*
+/*     tls_proxy_server_start_print() writes a TLS_SERVER_START_PROPS
+/*     structure to the named stream using the specified attribute print
+/*     routine. tls_proxy_server_start_print() is meant to be passed as
+/*     a call-back to attr_print(), thusly:
+/*
+/*     ... SEND_ATTR_FUNC(tls_proxy_server_start_print, (void *) start_props), ...
+/* DIAGNOSTICS
+/*     Fatal: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
+/*--*/
+
+#ifdef USE_TLS
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library */
+
+#include <attr.h>
+
+/* TLS library. */
+
+#include <tls.h>
+#include <tls_proxy.h>
+
+/* tls_proxy_server_init_print - send TLS_SERVER_INIT_PROPS over stream */
+
+int     tls_proxy_server_init_print(ATTR_PRINT_MASTER_FN print_fn, VSTREAM *fp,
+                                           int flags, void *ptr)
+{
+    TLS_SERVER_INIT_PROPS *props = (TLS_SERVER_INIT_PROPS *) ptr;
+    int     ret;
+
+#define STRING_OR_EMPTY(s) ((s) ? (s) : "")
+
+    ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+                  SEND_ATTR_STR(TLS_ATTR_LOG_PARAM,
+                                STRING_OR_EMPTY(props->log_param)),
+                  SEND_ATTR_STR(TLS_ATTR_LOG_LEVEL,
+                                STRING_OR_EMPTY(props->log_level)),
+                  SEND_ATTR_INT(TLS_ATTR_VERIFYDEPTH, props->verifydepth),
+                  SEND_ATTR_STR(TLS_ATTR_CACHE_TYPE,
+                                STRING_OR_EMPTY(props->cache_type)),
+                  SEND_ATTR_INT(TLS_ATTR_SET_SESSID, props->set_sessid),
+                  SEND_ATTR_STR(TLS_ATTR_CERT_FILE,
+                                STRING_OR_EMPTY(props->cert_file)),
+                  SEND_ATTR_STR(TLS_ATTR_KEY_FILE,
+                                STRING_OR_EMPTY(props->key_file)),
+                  SEND_ATTR_STR(TLS_ATTR_DCERT_FILE,
+                                STRING_OR_EMPTY(props->dcert_file)),
+                  SEND_ATTR_STR(TLS_ATTR_DKEY_FILE,
+                                STRING_OR_EMPTY(props->dkey_file)),
+                  SEND_ATTR_STR(TLS_ATTR_ECCERT_FILE,
+                                STRING_OR_EMPTY(props->eccert_file)),
+                  SEND_ATTR_STR(TLS_ATTR_ECKEY_FILE,
+                                STRING_OR_EMPTY(props->eckey_file)),
+                  SEND_ATTR_STR(TLS_ATTR_CAFILE,
+                                STRING_OR_EMPTY(props->CAfile)),
+                  SEND_ATTR_STR(TLS_ATTR_CAPATH,
+                                STRING_OR_EMPTY(props->CApath)),
+                  SEND_ATTR_STR(TLS_ATTR_PROTOCOLS,
+                                STRING_OR_EMPTY(props->protocols)),
+                  SEND_ATTR_STR(TLS_ATTR_EECDH_GRADE,
+                                STRING_OR_EMPTY(props->eecdh_grade)),
+                  SEND_ATTR_STR(TLS_ATTR_DH1K_PARAM_FILE,
+                                STRING_OR_EMPTY(props->dh1024_param_file)),
+                  SEND_ATTR_STR(TLS_ATTR_DH512_PARAM_FILE,
+                                STRING_OR_EMPTY(props->dh512_param_file)),
+                  SEND_ATTR_INT(TLS_ATTR_ASK_CCERT, props->ask_ccert),
+                  SEND_ATTR_STR(TLS_ATTR_MDALG,
+                                STRING_OR_EMPTY(props->mdalg)),
+                  ATTR_TYPE_END);
+    /* Do not flush the stream. */
+    return (ret);
+}
+
+/* tls_proxy_server_start_print - send TLS_SERVER_START_PROPS over stream */
+
+int     tls_proxy_server_start_print(ATTR_PRINT_MASTER_FN print_fn, VSTREAM *fp,
+                                            int flags, void *ptr)
+{
+    TLS_SERVER_START_PROPS *props = (TLS_SERVER_START_PROPS *) ptr;
+    int     ret;
+
+#define STRING_OR_EMPTY(s) ((s) ? (s) : "")
+
+    ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+                  SEND_ATTR_INT(TLS_ATTR_TIMEOUT, props->timeout),
+                  SEND_ATTR_INT(TLS_ATTR_REQUIRECERT, props->requirecert),
+                  SEND_ATTR_STR(TLS_ATTR_SERVERID,
+                                STRING_OR_EMPTY(props->serverid)),
+                  SEND_ATTR_STR(TLS_ATTR_NAMADDR,
+                                STRING_OR_EMPTY(props->namaddr)),
+                  SEND_ATTR_STR(TLS_ATTR_CIPHER_GRADE,
+                                STRING_OR_EMPTY(props->cipher_grade)),
+                  SEND_ATTR_STR(TLS_ATTR_CIPHER_EXCLUSIONS,
+                                STRING_OR_EMPTY(props->cipher_exclusions)),
+                  SEND_ATTR_STR(TLS_ATTR_MDALG,
+                                STRING_OR_EMPTY(props->mdalg)),
+                  ATTR_TYPE_END);
+    /* Do not flush the stream. */
+    return (ret);
+}
+
+#endif
diff --git a/postfix/src/tls/tls_proxy_server_scan.c b/postfix/src/tls/tls_proxy_server_scan.c
new file mode 100644 (file)
index 0000000..62487d8
--- /dev/null
@@ -0,0 +1,241 @@
+/*++
+/* NAME
+/*     tls_proxy_server_scan 3
+/* SUMMARY
+/*     read TLS_SERVER_XXX structures from stream
+/* SYNOPSIS
+/*     #include <tls_proxy.h>
+/*
+/*     int     tls_proxy_server_init_scan(scan_fn, stream, flags, ptr)
+/*     ATTR_SCAN_MASTER_FN scan_fn;
+/*     VSTREAM *stream;
+/*     int     flags;
+/*     void    *ptr;
+/*
+/*     tls_proxy_server_init_free(init_props)
+/*     TLS_SERVER_INIT_PROPS *init_props;
+/*
+/*     int     tls_proxy_server_start_scan(scan_fn, stream, flags, ptr)
+/*     ATTR_SCAN_MASTER_FN scan_fn;
+/*     VSTREAM *stream;
+/*     int     flags;
+/*     void    *ptr;
+/*
+/*     void    tls_proxy_server_start_free(start_props)
+/*     TLS_SERVER_START_PROPS *start_props;
+/* DESCRIPTION
+/*     tls_proxy_server_init_scan() reads a TLS_SERVER_INIT_PROPS
+/*     structure from the named stream using the specified attribute
+/*     scan routine. tls_proxy_server_init_scan() is meant to be passed
+/*     as a call-back function to attr_scan(), as shown below.
+/*
+/*     tls_proxy_server_init_free() destroys a TLS_SERVER_INIT_PROPS
+/*     structure that was created by tls_proxy_server_init_scan().
+/*
+/*     TLS_SERVER_INIT_PROPS *init_props = 0;
+/*     ...
+/*     ... RECV_ATTR_FUNC(tls_proxy_server_init_scan, (void *) &init_props)
+/*     ...
+/*     if (init_props)
+/*         tls_proxy_client_init_free(init_props);
+/*
+/*     tls_proxy_server_start_scan() reads a TLS_SERVER_START_PROPS
+/*     structure from the named stream using the specified attribute
+/*     scan routine. tls_proxy_server_start_scan() is meant to be passed
+/*     as a call-back function to attr_scan(), as shown below.
+/*
+/*     tls_proxy_server_start_free() destroys a TLS_SERVER_START_PROPS
+/*     structure that was created by tls_proxy_server_start_scan().
+/*
+/*     TLS_SERVER_START_PROPS *start_props = 0;
+/*     ...
+/*     ... RECV_ATTR_FUNC(tls_proxy_server_start_scan, (void *) &start_props)
+/*     ...
+/*     if (start_props)
+/*         tls_proxy_server_start_free(start_props);
+/* DIAGNOSTICS
+/*     Fatal: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
+/*--*/
+
+#ifdef USE_TLS
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library */
+
+#include <attr.h>
+
+/* TLS library. */
+
+#include <tls.h>
+#include <tls_proxy.h>
+
+/* tls_proxy_server_init_scan - receive TLS_SERVER_INIT_PROPS from stream */
+
+int     tls_proxy_server_init_scan(ATTR_SCAN_MASTER_FN scan_fn, VSTREAM *fp,
+                                          int flags, void *ptr)
+{
+    TLS_SERVER_INIT_PROPS *props
+    = (TLS_SERVER_INIT_PROPS *) mymalloc(sizeof(*props));
+    int     ret;
+    VSTRING *log_param = vstring_alloc(25);
+    VSTRING *log_level = vstring_alloc(25);
+    VSTRING *cache_type = vstring_alloc(25);
+    VSTRING *cert_file = vstring_alloc(25);
+    VSTRING *key_file = vstring_alloc(25);
+    VSTRING *dcert_file = vstring_alloc(25);
+    VSTRING *dkey_file = vstring_alloc(25);
+    VSTRING *eccert_file = vstring_alloc(25);
+    VSTRING *eckey_file = vstring_alloc(25);
+    VSTRING *CAfile = vstring_alloc(25);
+    VSTRING *CApath = vstring_alloc(25);
+    VSTRING *protocols = vstring_alloc(25);
+    VSTRING *eecdh_grade = vstring_alloc(25);
+    VSTRING *dh1024_param_file = vstring_alloc(25);
+    VSTRING *dh512_param_file = vstring_alloc(25);
+    VSTRING *mdalg = vstring_alloc(25);
+
+    /*
+     * Note: memset() is not a portable way to initialize non-integer types.
+     */
+    memset(props, 0, sizeof(*props));
+    ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+                 RECV_ATTR_STR(TLS_ATTR_LOG_PARAM, log_param),
+                 RECV_ATTR_STR(TLS_ATTR_LOG_LEVEL, log_level),
+                 RECV_ATTR_INT(TLS_ATTR_VERIFYDEPTH, &props->verifydepth),
+                 RECV_ATTR_STR(TLS_ATTR_CACHE_TYPE, cache_type),
+                 RECV_ATTR_INT(TLS_ATTR_SET_SESSID, &props->set_sessid),
+                 RECV_ATTR_STR(TLS_ATTR_CERT_FILE, cert_file),
+                 RECV_ATTR_STR(TLS_ATTR_KEY_FILE, key_file),
+                 RECV_ATTR_STR(TLS_ATTR_DCERT_FILE, dcert_file),
+                 RECV_ATTR_STR(TLS_ATTR_DKEY_FILE, dkey_file),
+                 RECV_ATTR_STR(TLS_ATTR_ECCERT_FILE, eccert_file),
+                 RECV_ATTR_STR(TLS_ATTR_ECKEY_FILE, eckey_file),
+                 RECV_ATTR_STR(TLS_ATTR_CAFILE, CAfile),
+                 RECV_ATTR_STR(TLS_ATTR_CAPATH, CApath),
+                 RECV_ATTR_STR(TLS_ATTR_PROTOCOLS, protocols),
+                 RECV_ATTR_STR(TLS_ATTR_EECDH_GRADE, eecdh_grade),
+                 RECV_ATTR_STR(TLS_ATTR_DH1K_PARAM_FILE, dh1024_param_file),
+                 RECV_ATTR_STR(TLS_ATTR_DH512_PARAM_FILE, dh512_param_file),
+                 RECV_ATTR_INT(TLS_ATTR_ASK_CCERT, &props->ask_ccert),
+                 RECV_ATTR_STR(TLS_ATTR_MDALG, mdalg),
+                 ATTR_TYPE_END);
+    /* Always construct a well-formed structure. */
+    props->log_param = vstring_export(log_param);
+    props->log_level = vstring_export(log_level);
+    props->cache_type = vstring_export(cache_type);
+    props->cert_file = vstring_export(cert_file);
+    props->key_file = vstring_export(key_file);
+    props->dcert_file = vstring_export(dcert_file);
+    props->dkey_file = vstring_export(dkey_file);
+    props->eccert_file = vstring_export(eccert_file);
+    props->eckey_file = vstring_export(eckey_file);
+    props->CAfile = vstring_export(CAfile);
+    props->CApath = vstring_export(CApath);
+    props->protocols = vstring_export(protocols);
+    props->eecdh_grade = vstring_export(eecdh_grade);
+    props->dh1024_param_file = vstring_export(dh1024_param_file);
+    props->dh512_param_file = vstring_export(dh512_param_file);
+    props->mdalg = vstring_export(mdalg);
+    ret = (ret == 19 ? 1 : -1);
+    if (ret != 1) {
+       tls_proxy_server_init_free(props);
+       props = 0;
+    }
+    *(TLS_SERVER_INIT_PROPS **) ptr = props;
+    return (ret);
+}
+
+/* tls_proxy_server_init_free - destroy TLS_SERVER_INIT_PROPS structure */
+
+void    tls_proxy_server_init_free(TLS_SERVER_INIT_PROPS *props)
+{
+    myfree((void *) props->log_param);
+    myfree((void *) props->log_level);
+    myfree((void *) props->cache_type);
+    myfree((void *) props->cert_file);
+    myfree((void *) props->key_file);
+    myfree((void *) props->dcert_file);
+    myfree((void *) props->dkey_file);
+    myfree((void *) props->eccert_file);
+    myfree((void *) props->eckey_file);
+    myfree((void *) props->CAfile);
+    myfree((void *) props->CApath);
+    myfree((void *) props->protocols);
+    myfree((void *) props->eecdh_grade);
+    myfree((void *) props->dh1024_param_file);
+    myfree((void *) props->dh512_param_file);
+    myfree((void *) props->mdalg);
+    myfree((void *) props);
+}
+
+/* tls_proxy_server_start_scan - receive TLS_SERVER_START_PROPS from stream */
+
+int     tls_proxy_server_start_scan(ATTR_SCAN_MASTER_FN scan_fn, VSTREAM *fp,
+                                           int flags, void *ptr)
+{
+    TLS_SERVER_START_PROPS *props
+    = (TLS_SERVER_START_PROPS *) mymalloc(sizeof(*props));
+    int     ret;
+    VSTRING *serverid = vstring_alloc(25);
+    VSTRING *namaddr = vstring_alloc(25);
+    VSTRING *cipher_grade = vstring_alloc(25);
+    VSTRING *cipher_exclusions = vstring_alloc(25);
+    VSTRING *mdalg = vstring_alloc(25);
+
+    /*
+     * Note: memset() is not a portable way to initialize non-integer types.
+     */
+    memset(props, 0, sizeof(*props));
+    props->ctx = 0;
+    props->stream = 0;
+    /* XXX Caller sets fd. */
+    ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+                 RECV_ATTR_INT(TLS_ATTR_TIMEOUT, &props->timeout),
+                 RECV_ATTR_INT(TLS_ATTR_REQUIRECERT, &props->requirecert),
+                 RECV_ATTR_STR(TLS_ATTR_SERVERID, serverid),
+                 RECV_ATTR_STR(TLS_ATTR_NAMADDR, namaddr),
+                 RECV_ATTR_STR(TLS_ATTR_CIPHER_GRADE, cipher_grade),
+                 RECV_ATTR_STR(TLS_ATTR_CIPHER_EXCLUSIONS,
+                               cipher_exclusions),
+                 RECV_ATTR_STR(TLS_ATTR_MDALG, mdalg),
+                 ATTR_TYPE_END);
+    props->serverid = vstring_export(serverid);
+    props->namaddr = vstring_export(namaddr);
+    props->cipher_grade = vstring_export(cipher_grade);
+    props->cipher_exclusions = vstring_export(cipher_exclusions);
+    props->mdalg = vstring_export(mdalg);
+    ret = (ret == 7 ? 1 : -1);
+    if (ret != 1) {
+       tls_proxy_server_start_free(props);
+       props = 0;
+    }
+    *(TLS_SERVER_START_PROPS **) ptr = props;
+    return (ret);
+}
+
+/* tls_proxy_server_start_free - destroy TLS_SERVER_START_PROPS structure */
+
+void    tls_proxy_server_start_free(TLS_SERVER_START_PROPS *props)
+{
+    /* XXX Caller closes fd. */
+    myfree((void *) props->serverid);
+    myfree((void *) props->namaddr);
+    myfree((void *) props->cipher_grade);
+    myfree((void *) props->cipher_exclusions);
+    myfree((void *) props->mdalg);
+    myfree((void *) props);
+}
+
+#endif
index 087721bf7597e127bede771c857af5043ab085b2..e248f8501c22e6eba99126b0acf18ea3209b7d6c 100644 (file)
@@ -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
index 2c855ffcc72077fb38a924f68c4de886d23c24ab..ecfb149df02b36b08d733ce5dad2381013d4d89c 100644 (file)
@@ -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;
index e3e1d11fa38df2a8ed2b84c800799d369642eb8f..336c58c69aee1af41a691293128bc717a728cedb 100644 (file)
@@ -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
 /*--*/
index e327649207fe1d189c65a7c34859c4dc2b688c5b..91dcfca7cdf93def1fffc7a02b1427f7c3d91207 100644 (file)
@@ -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
 /*     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 <tls.h>
+#include <tls_proxy.h>
 
  /*
   * 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);
 }
 
index 32cefbc0773ec74a02ff2fcd47b58ae2903593a6..d419cb3975562a4fe7a2618be37ef61fe5d0eb37 100644 (file)
@@ -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 (file)
index 0000000..65cfa55
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef _ARGV_ATTR_H_INCLUDED_
+#define _ARGV_ATTR_H_INCLUDED_
+
+/*++
+/* NAME
+/*     argv_attr 3h
+/* SUMMARY
+/*     argv serialization/deserialization
+/* SYNOPSIS
+/*     #include <argv_attr.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <argv.h>
+#include <attr.h>
+#include <check_arg.h>
+#include <vstream.h>
+
+ /*
+  * External API.
+  */
+#define ARGV_ATTR_SIZE "argv_size"
+#define ARGV_ATTR_VALUE        "argv_value"
+#define ARGV_ATTR_MAX  1024
+
+extern int argv_attr_print(ATTR_PRINT_MASTER_FN, VSTREAM *, int, void *);
+extern int argv_attr_scan(ATTR_SCAN_MASTER_FN, VSTREAM *, int, void *);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
+/*--*/
+
+#endif
diff --git a/postfix/src/util/argv_attr_print.c b/postfix/src/util/argv_attr_print.c
new file mode 100644 (file)
index 0000000..87b086a
--- /dev/null
@@ -0,0 +1,73 @@
+/*++
+/* NAME
+/*     argv_attr_print
+/* SUMMARY
+/*     write ARGV to stream
+/* SYNOPSIS
+/*     #include <argv_attr.h>
+/*
+/*     int     argv_attr_print(print_fn, stream, flags, ptr)
+/*     ATTR_PRINT_MASTER_FN print_fn;
+/*     VSTREAM *stream;
+/*     int     flags;
+/*     void    *ptr;
+/* DESCRIPTION
+/*     argv_attr_print() writes an ARGV to the named stream using
+/*     the specified attribute print routine. argv_attr_print() is meant
+/*     to be passed as a call-back to attr_print(), thusly:
+/*
+/*     ... SEND_ATTR_FUNC(argv_attr_print, (void *) argv), ...
+/* DIAGNOSTICS
+/*     Fatal: out of memory.
+/*
+/*     The result value is zero in case of success, non-zero
+/*     otherwise.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
+/*--*/
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+
+ /*
+  * Utility library.
+  */
+#include <argv.h>
+#include <argv_attr.h>
+#include <attr.h>
+#include <vstream.h>
+#include <msg.h>
+
+/* argv_attr_print - write ARGV to stream */
+
+int     argv_attr_print(ATTR_PRINT_MASTER_FN print_fn, VSTREAM *fp,
+                               int flags, void *ptr)
+{
+    ARGV   *argv = (ARGV *) ptr;
+    int     n;
+    int     ret;
+    int     argc = argv ? argv->argc : 0;
+
+    ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+                  SEND_ATTR_INT(ARGV_ATTR_SIZE, argc),
+                  ATTR_TYPE_END);
+    if (msg_verbose)
+       msg_info("argv_attr_print count=%d", argc);
+    for (n = 0; ret == 0 && n < argc; n++)
+       ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+                      SEND_ATTR_STR(ARGV_ATTR_VALUE, argv->argv[n]),
+                      ATTR_TYPE_END);
+    if (msg_verbose)
+       msg_info("argv_attr_print ret=%d", ret);
+    /* Do not flush the stream. */
+    return (ret);
+}
diff --git a/postfix/src/util/argv_attr_scan.c b/postfix/src/util/argv_attr_scan.c
new file mode 100644 (file)
index 0000000..3005cb2
--- /dev/null
@@ -0,0 +1,93 @@
+/*++
+/* NAME
+/*     argv_attr_scan
+/* SUMMARY
+/*     read ARGV from stream
+/* SYNOPSIS
+/*     #include <argv_attr.h>
+/*
+/*     int     argv_attr_scan(scan_fn, stream, flags, ptr)
+/*     ATTR_SCAN_MASTER_FN scan_fn;
+/*     VSTREAM *stream;
+/*     int     flags;
+/*     void    *ptr;
+/* DESCRIPTION
+/*     argv_attr_scan() creates an ARGV and reads its contents
+/*     from the named stream using the specified attribute scan
+/*     routine. argv_attr_scan() is meant to be passed as a call-back
+/*     to attr_scan(), thusly:
+/*
+/*     ARGV *argv = 0;
+/*     ...
+/*     ... RECV_ATTR_FUNC(argv_attr_scan, (void *) &argv), ...
+/*     ...
+/*     if (argv)
+/*         argv_free(argv);
+/* DIAGNOSTICS
+/*     Fatal: out of memory.
+/*
+/*     In case of error, this function returns non-zero and creates
+/*     an ARGV null pointer.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
+/*--*/
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+
+ /*
+  * Utility library.
+  */
+#include <argv.h>
+#include <argv_attr.h>
+#include <attr.h>
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+
+/* argv_attr_scan - write ARGV to stream */
+
+int     argv_attr_scan(ATTR_PRINT_MASTER_FN scan_fn, VSTREAM *fp,
+                              int flags, void *ptr)
+{
+    ARGV   *argv = 0;
+    int     size;
+    int     ret;
+
+    if ((ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+                      RECV_ATTR_INT(ARGV_ATTR_SIZE, &size),
+                      ATTR_TYPE_END)) == 1) {
+       if (msg_verbose)
+           msg_info("argv_attr_scan count=%d", size);
+       if (size < 0 || size > ARGV_ATTR_MAX) {
+           msg_warn("invalid size %d from %s while reading ARGV",
+                    size, VSTREAM_PATH(fp));
+           ret = -1;
+       } else if (size > 0) {
+           VSTRING *buffer = vstring_alloc(100);
+
+           argv = argv_alloc(size);
+           while (ret == 1 && size-- > 0) {
+               if ((ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+                                  RECV_ATTR_STR(ARGV_ATTR_VALUE, buffer),
+                                  ATTR_TYPE_END)) == 1)
+                   argv_add(argv, vstring_str(buffer), ARGV_END);
+           }
+           argv_terminate(argv);
+           vstring_free(buffer);
+       }
+    }
+    *(ARGV **) ptr = argv;
+    if (msg_verbose)
+       msg_info("argv_attr_scan ret=%d", ret);
+    return (ret);
+}