]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.11-20130405-nonprod
authorWietse Venema <wietse@porcupine.org>
Sat, 6 Apr 2013 02:58:59 +0000 (22:58 -0400)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Sat, 6 Apr 2013 03:46:27 +0000 (23:46 -0400)
35 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/README_FILES/TLS_README
postfix/WISHLIST
postfix/html/TLS_README.html
postfix/html/lmtp.8.html
postfix/html/postconf.5.html
postfix/html/smtp.8.html
postfix/makedefs
postfix/man/man5/postconf.5
postfix/man/man8/smtp.8
postfix/mantools/postlink
postfix/proto/TLS_README.html
postfix/proto/postconf.proto
postfix/proto/stop
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
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_params.c
postfix/src/smtp/smtp_proto.c
postfix/src/smtp/smtp_session.c
postfix/src/smtp/smtp_state.c
postfix/src/smtp/smtp_tls_sess.c
postfix/src/tls/Makefile.in
postfix/src/tls/tls.h
postfix/src/tls/tls_client.c
postfix/src/tls/tls_dane.c [new file with mode: 0644]
postfix/src/tls/tls_fprint.c
postfix/src/tls/tls_misc.c
postfix/src/tls/tls_verify.c
postfix/src/util/argv.c
postfix/src/util/argv.h

index 8979235c9f5bc8ddbacf87d8a64554b3b7f01a52..5774f67f2bb1eebe8269e2693067a9cfbe2684d4 100644 (file)
 -TTLS_APPL_STATE
 -TTLS_CLIENT_INIT_PROPS
 -TTLS_CLIENT_START_PROPS
+-TTLS_DANE
 -TTLS_PRNG_SEED_INFO
 -TTLS_PRNG_SRC
 -TTLS_SCACHE
 -TTLS_SERVER_INIT_PROPS
 -TTLS_SERVER_START_PROPS
 -TTLS_SESS_STATE
+-TTLS_TLSA
 -TTLS_VINFO
 -TTLScontext_t
 -TTOK822
index b2e474cd6c25d20b55dd0d77b90570d8a274b878..d40c98664a482361b5b97bca3a7cf9caf131ab05 100644 (file)
@@ -18410,3 +18410,19 @@ Apologies for any names omitted.
 
        Documentation: in smtpd.c, the comment that justifies the
        454 reply for "TLS unavailable" cited the wrong RFC.
+
+20130405
+
+       Feature: support for trust anchors, i.e. CA certificates
+       or public keys that will be used instead of conventional
+       root certificates, and revised fingerprint support.  This
+       can be used by itself, and this provides support for an
+       upcoming DANE implementation.  Victor Duchovni.  Files:
+       mantools/postlink, proto/TLS_README.html, proto/postconf.proto,
+       src/global/mail_params.h, src/smtp/lmtp_params.c,
+       src/smtp/smtp.c, src/smtp/smtp.h, src/smtp/smtp_params.c,
+       src/smtp/smtp_proto.c, src/smtp/smtp_session.c,
+       src/smtp/smtp_state.c, src/smtp/smtp_tls_sess.c,
+       src/tls/Makefile.in, src/tls/tls.h, src/tls/tls_client.c,
+       src/tls/tls_dane.c, src/tls/tls_fprint.c, src/tls/tls_misc.c,
+       src/tls/tls_verify.c, src/util/argv.c, src/util/argv.h.
index 893b2c95bb1fcef17158a10db975263e61d4b040..377c061ae91ffed72190408a8f7ff4f199be66e3 100644 (file)
@@ -908,6 +908,17 @@ extension are used to verify the remote SMTP server name. If no DNS names are
 specified, the certificate CommonName is checked. If you want mandatory
 encryption without server certificate verification, see above.
 
+With Postfix >= 2.11 the "smtp_tls_trust_anchor_file" parameter or more
+typically the corresponding per-destination "tafile" attribute optionally
+modifies trust chain verification. If the parameter is not empty the root CAs
+in CAfile and CApath are no longer trusted. Rather, the Postfix SMTP client
+will only trust certificate-chains signed by one of the trust-anchors contained
+in the chosen files. The specified trust-anchor certificates and public keys
+are not subject to expiration, and need not be (self-signed) root CAs. They
+may, if desired, be intermediate certificates. Therefore, these certificates
+also may be found "in the middle" of the trust chain presented by the remote
+SMTP server, and any untrusted issuing parent certificates will be ignored.
+
 Despite the potential for eliminating "man-in-the-middle" and other attacks,
 mandatory certificate trust chain and subject name verification is not viable
 as a default Internet mail delivery policy. Most MX hosts do not support TLS at
@@ -958,6 +969,17 @@ extension are used to verify the remote SMTP server name. If no DNS names are
 specified, the CommonName is checked. If you want mandatory encryption without
 server certificate verification, see above.
 
+With Postfix >= 2.11 the "smtp_tls_trust_anchor_file" parameter or more
+typically the corresponding per-destination "tafile" attribute optionally
+modifies trust chain verification. If the parameter is not empty the root CAs
+in CAfile and CApath are no longer trusted. Rather, the Postfix SMTP client
+will only trust certificate-chains signed by one of the trust-anchors contained
+in the chosen files. The specified trust-anchor certificates and public keys
+are not subject to expiration, and need not be (self-signed) root CAs. They
+may, if desired, be intermediate certificates. Therefore, these certificates
+also may be found "in the middle" of the trust chain presented by the remote
+SMTP server, and any untrusted issuing parent certificates will be ignored.
+
 Despite the potential for eliminating "man-in-the-middle" and other attacks,
 mandatory secure server certificate verification is not viable as a default
 Internet mail delivery policy. Most MX hosts do not support TLS at all, and a
@@ -1360,14 +1382,21 @@ v\bve\ber\bri\bif\bfy\by
     validated (not expired or revoked, and signed by a trusted certificate
     authority), and if the server certificate name matches the optional "match"
     attribute (or the main.cf smtp_tls_verify_cert_match parameter value when
-    no optional "match" attribute is specified).
+    no optional "match" attribute is specified). With Postfix >= 2.11 the
+    "tafile" attribute optionally modifies trust chain verification in the same
+    manner as the "smtp_tls_trust_anchor_file" parameter. The "tafile"
+    attribute may be specified multiple times to load multiple trust-anchor
+    files.
 s\bse\bec\bcu\bur\bre\be
     Secure certificate verification. Mail is delivered only if the TLS
     handshake succeeds, if the remote SMTP server certificate can be validated
     (not expired or revoked, and signed by a trusted certificate authority),
     and if the server certificate name matches the optional "match" attribute
     (or the main.cf smtp_tls_secure_cert_match parameter value when no optional
-    "match" attribute is specified).
+    "match" attribute is specified). With Postfix >= 2.11 the "tafile"
+    attribute optionally modifies trust chain verification in the same manner
+    as the "smtp_tls_trust_anchor_file" parameter. The "tafile" attribute may
+    be specified multiple times to load multiple trust-anchor files.
 Notes:
 
   * The "match" attribute is especially useful to verify TLS certificates for
index b61e913e76cc60f9eed5dc43136552a87359400c..7170996e43329f13ebeae633d9a5e9836059f7aa 100644 (file)
@@ -11,6 +11,32 @@ Wish list:
        Begin code revision, after DANE support stabilizes.  This
        should be one pass that changes only names and no code.
 
+       Run new source files through ccformat. If I do it now,
+       almost every block of code or comments is changed. Having
+       different formatting styles in the same project is PROBLEMATIC.
+       There is a reason why ccformat is included with source code.
+
+       Embed all statement-like macros in do { ... } while (0).
+       This is especially necessary with macros that contain an
+       "if" statement, or that contain multiple statements.
+
+       Spell-check, double-word check, and HTML validator check.
+
+       Use make(1) and cc(1) to convert the C++ like templates
+       into debuggable source code, such that each statement has
+       its own distinct line number (what a revolutionary idea).
+
+       All source code must specify its original author and
+       license statement. Some code modules specify Lutz Jaenicke
+       as the original author and fall under his liberal license.
+       Code that is added to such a module has the same license
+       (or at least something that is not more restrictive). Code
+       modules without input from Lutz Jaenicke must state its
+       original author and license (preferably no more restrictive
+       than Postfix's own license). Currently, too many files list
+       Wietse as the original author, and Lutz Jaenicke's license,
+       which is wrong.
+
        Generally, macro and function names should make a program
        more clear, not merely reduce the number of programmer
        keystrokes; similar considerations hold for variable names
index aaeb6296f5069c1fd7978c6d7cba556ef4eda698..a8a1998accb815d3d6648cc8758b43122b16ffb9 100644 (file)
@@ -1239,6 +1239,19 @@ DNS names are specified, the certificate CommonName is checked.
 If you want mandatory encryption without server certificate
 verification, see <a href="#client_tls_encrypt">above</a>. </p>
 
+<p> With Postfix &ge; 2.11 the "<a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a>" parameter
+or more typically the corresponding per-destination "tafile" attribute
+optionally modifies trust chain verification.  If the parameter is
+not empty the root CAs in CAfile and CApath are no longer trusted.
+Rather, the Postfix SMTP client will only trust certificate-chains
+signed by one of the trust-anchors contained in the chosen files.
+The specified trust-anchor certificates and public keys are not
+subject to expiration, and need not be (self-signed) root CAs.  They
+may, if desired, be intermediate certificates. Therefore, these
+certificates also may be found "in the middle" of the trust chain
+presented by the remote SMTP server, and any untrusted issuing
+parent certificates will be ignored.  </p>
+
 <p> Despite the potential for eliminating "man-in-the-middle" and other
 attacks, mandatory certificate trust chain and subject name verification
 is not viable as a default Internet mail delivery policy.  Most MX hosts
@@ -1300,6 +1313,19 @@ specified, the CommonName is checked. If you want mandatory encryption
 without server certificate verification, see <a
 href="#client_tls_encrypt">above</a>. </p>
 
+<p> With Postfix &ge; 2.11 the "<a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a>" parameter
+or more typically the corresponding per-destination "tafile" attribute
+optionally modifies trust chain verification.  If the parameter is
+not empty the root CAs in CAfile and CApath are no longer trusted.
+Rather, the Postfix SMTP client will only trust certificate-chains
+signed by one of the trust-anchors contained in the chosen files.
+The specified trust-anchor certificates and public keys are not
+subject to expiration, and need not be (self-signed) root CAs.  They
+may, if desired, be intermediate certificates. Therefore, these
+certificates also may be found "in the middle" of the trust chain
+presented by the remote SMTP server, and any untrusted issuing
+parent certificates will be ignored.  </p>
+
 <p> Despite the potential for eliminating "man-in-the-middle" and other
 attacks, mandatory secure server certificate verification is not
 viable as a default Internet mail delivery policy.  Most MX hosts
@@ -1806,12 +1832,16 @@ digits. </dd>
 
 <dt><b>verify</b></dt> <dd><a href="#client_tls_verify">Mandatory
 server certificate verification</a>.  Mail is delivered only if the
-TLS handshake
-succeeds, if the remote SMTP server certificate can be validated (not
-expired or revoked, and signed by a trusted certificate authority), and
-if the server certificate name matches the optional "match" attribute (or
-the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a> parameter value when no optional
-"match" attribute is specified).  </dd>
+TLS handshake succeeds, if the remote SMTP server certificate can
+be validated (not expired or revoked, and signed by a trusted
+certificate authority), and if the server certificate name matches
+the optional "match" attribute (or the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a>
+parameter value when no optional "match" attribute is specified).
+With Postfix &ge; 2.11 the "tafile" attribute optionally modifies
+trust chain verification in the same manner as the
+"<a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a>" parameter.  The "tafile" attribute
+may be specified multiple times to load multiple trust-anchor
+files.  </dd>
 
 <dt><b>secure</b></dt> <dd><a href="#client_tls_secure">Secure certificate
 verification.</a> Mail is delivered only if the TLS handshake succeeds,
@@ -1819,7 +1849,11 @@ if the remote SMTP server certificate can be validated (not expired
 or revoked, and signed by a trusted certificate authority), and if the
 server certificate name matches the optional "match" attribute (or the
 <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtp_tls_secure_cert_match">smtp_tls_secure_cert_match</a> parameter value when no optional
-"match" attribute is specified).  </dd>
+"match" attribute is specified).  With Postfix &ge; 2.11 the "tafile"
+attribute optionally modifies trust chain verification in the same manner
+as the "<a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a>" parameter.  The "tafile" attribute
+may be specified multiple times to load multiple trust-anchor
+files.  </dd>
 
 </dl>
 
index 20bed174b8025922ef4381986cb8406163d8a2e3..7db52e027efb060d8f5f8c844f6f20ef7e9979c2 100644 (file)
@@ -587,29 +587,35 @@ SMTP(8)                                                                SMTP(8)
               List or bit-mask of  OpenSSL  bug  work-arounds  to
               disable.
 
+       Available in Postfix version 2.11 and later:
+
+       <b><a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a> (empty)</b>
+              Zero  or  more  PEM-format  files with trust-anchor
+              certificates and/or public keys.
+
 <b>OBSOLETE STARTTLS CONTROLS</b>
-       The  following configuration parameters exist for compati-
+       The following configuration parameters exist for  compati-
        bility with Postfix versions before 2.3. Support for these
        will be removed in a future release.
 
        <b><a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a> (no)</b>
-              Opportunistic  mode:  use  TLS  when  a remote SMTP
-              server announces STARTTLS support,  otherwise  send
+              Opportunistic mode: use  TLS  when  a  remote  SMTP
+              server  announces  STARTTLS support, otherwise send
               the mail in the clear.
 
        <b><a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> (no)</b>
-              Enforcement  mode: require that remote SMTP servers
-              use TLS encryption, and  never  send  mail  in  the
+              Enforcement mode: require that remote SMTP  servers
+              use  TLS  encryption,  and  never  send mail in the
               clear.
 
        <b><a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> (yes)</b>
-              With  mandatory  TLS  encryption,  require that the
+              With mandatory TLS  encryption,  require  that  the
               remote SMTP server hostname matches the information
               in the remote SMTP server certificate.
 
        <b><a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> (empty)</b>
               Optional lookup tables with the Postfix SMTP client
-              TLS usage policy by  next-hop  destination  and  by
+              TLS  usage  policy  by  next-hop destination and by
               remote SMTP server hostname.
 
        <b><a href="postconf.5.html#smtp_tls_cipherlist">smtp_tls_cipherlist</a> (empty)</b>
@@ -619,80 +625,80 @@ SMTP(8)                                                                SMTP(8)
 <b>RESOURCE AND RATE CONTROLS</b>
        <b><a href="postconf.5.html#smtp_destination_concurrency_limit">smtp_destination_concurrency_limit</a>      ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destina</a>-</b>
        <b><a href="postconf.5.html#default_destination_concurrency_limit">tion_concurrency_limit</a>)</b>
-              The maximal number of parallel  deliveries  to  the
-              same  destination  via  the  smtp  message delivery
+              The  maximal  number  of parallel deliveries to the
+              same destination  via  the  smtp  message  delivery
               transport.
 
        <b><a href="postconf.5.html#smtp_destination_recipient_limit">smtp_destination_recipient_limit</a>        ($<a href="postconf.5.html#default_destination_recipient_limit">default_destina</a>-</b>
        <b><a href="postconf.5.html#default_destination_recipient_limit">tion_recipient_limit</a>)</b>
-              The maximal number of recipients  per  message  for
+              The  maximal  number  of recipients per message for
               the smtp message delivery transport.
 
        <b><a href="postconf.5.html#smtp_connect_timeout">smtp_connect_timeout</a> (30s)</b>
               The Postfix SMTP client time limit for completing a
-              TCP connection, or zero (use the  operating  system
+              TCP  connection,  or zero (use the operating system
               built-in time limit).
 
        <b><a href="postconf.5.html#smtp_helo_timeout">smtp_helo_timeout</a> (300s)</b>
-              The  Postfix SMTP client time limit for sending the
+              The Postfix SMTP client time limit for sending  the
               HELO or EHLO command, and for receiving the initial
               remote SMTP server response.
 
        <b><a href="postconf.5.html#lmtp_lhlo_timeout">lmtp_lhlo_timeout</a> (300s)</b>
-              The  Postfix LMTP client time limit for sending the
-              LHLO command, and for receiving the initial  remote
+              The Postfix LMTP client time limit for sending  the
+              LHLO  command, and for receiving the initial remote
               LMTP server response.
 
        <b><a href="postconf.5.html#smtp_xforward_timeout">smtp_xforward_timeout</a> (300s)</b>
-              The  Postfix SMTP client time limit for sending the
+              The Postfix SMTP client time limit for sending  the
               XFORWARD command, and for receiving the remote SMTP
               server response.
 
        <b><a href="postconf.5.html#smtp_mail_timeout">smtp_mail_timeout</a> (300s)</b>
-              The  Postfix SMTP client time limit for sending the
-              MAIL FROM command, and  for  receiving  the  remote
+              The Postfix SMTP client time limit for sending  the
+              MAIL  FROM  command,  and  for receiving the remote
               SMTP server response.
 
        <b><a href="postconf.5.html#smtp_rcpt_timeout">smtp_rcpt_timeout</a> (300s)</b>
-              The  Postfix SMTP client time limit for sending the
-              SMTP RCPT TO command, and for receiving the  remote
+              The Postfix SMTP client time limit for sending  the
+              SMTP  RCPT TO command, and for receiving the remote
               SMTP server response.
 
        <b><a href="postconf.5.html#smtp_data_init_timeout">smtp_data_init_timeout</a> (120s)</b>
-              The  Postfix SMTP client time limit for sending the
-              SMTP DATA command, and  for  receiving  the  remote
+              The Postfix SMTP client time limit for sending  the
+              SMTP  DATA  command,  and  for receiving the remote
               SMTP server response.
 
        <b><a href="postconf.5.html#smtp_data_xfer_timeout">smtp_data_xfer_timeout</a> (180s)</b>
-              The  Postfix SMTP client time limit for sending the
+              The Postfix SMTP client time limit for sending  the
               SMTP message content.
 
        <b><a href="postconf.5.html#smtp_data_done_timeout">smtp_data_done_timeout</a> (600s)</b>
-              The Postfix SMTP client time limit for sending  the
-              SMTP  ".", and for receiving the remote SMTP server
+              The  Postfix SMTP client time limit for sending the
+              SMTP ".", and for receiving the remote SMTP  server
               response.
 
        <b><a href="postconf.5.html#smtp_quit_timeout">smtp_quit_timeout</a> (300s)</b>
-              The Postfix SMTP client time limit for sending  the
-              QUIT  command,  and  for  receiving the remote SMTP
+              The  Postfix SMTP client time limit for sending the
+              QUIT command, and for  receiving  the  remote  SMTP
               server response.
 
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#smtp_mx_address_limit">smtp_mx_address_limit</a> (5)</b>
               The  maximal  number  of  MX  (mail  exchanger)  IP
-              addresses  that can result from Postfix SMTP client
+              addresses that can result from Postfix SMTP  client
               mail exchanger lookups, or zero (no limit).
 
        <b><a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> (2)</b>
-              The maximal number of SMTP  sessions  per  delivery
-              request  before the Postfix SMTP client gives up or
-              delivers to a fall-back <a href="postconf.5.html#relayhost">relay  host</a>,  or  zero  (no
+              The  maximal  number  of SMTP sessions per delivery
+              request before the Postfix SMTP client gives up  or
+              delivers  to  a  fall-back  <a href="postconf.5.html#relayhost">relay host</a>, or zero (no
               limit).
 
        <b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (20s)</b>
-              The  Postfix SMTP client time limit for sending the
-              RSET command, and for  receiving  the  remote  SMTP
+              The Postfix SMTP client time limit for sending  the
+              RSET  command,  and  for  receiving the remote SMTP
               server response.
 
        Available in Postfix version 2.2 and earlier:
@@ -704,11 +710,11 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix version 2.2 and later:
 
        <b><a href="postconf.5.html#smtp_connection_cache_destinations">smtp_connection_cache_destinations</a> (empty)</b>
-              Permanently enable SMTP connection caching for  the
+              Permanently  enable SMTP connection caching for the
               specified destinations.
 
        <b><a href="postconf.5.html#smtp_connection_cache_on_demand">smtp_connection_cache_on_demand</a> (yes)</b>
-              Temporarily  enable SMTP connection caching while a
+              Temporarily enable SMTP connection caching while  a
               destination has a high volume of mail in the active
               queue.
 
@@ -718,72 +724,72 @@ SMTP(8)                                                                SMTP(8)
 
        <b><a href="postconf.5.html#smtp_connection_cache_time_limit">smtp_connection_cache_time_limit</a> (2s)</b>
               When SMTP connection caching is enabled, the amount
-              of  time  that an unused SMTP client socket is kept
+              of time that an unused SMTP client socket  is  kept
               open before it is closed.
 
        Available in Postfix version 2.3 and later:
 
        <b><a href="postconf.5.html#connection_cache_protocol_timeout">connection_cache_protocol_timeout</a> (5s)</b>
-              Time limit for connection cache  connect,  send  or
+              Time  limit  for  connection cache connect, send or
               receive operations.
 
        Available in Postfix version 2.9 and later:
 
        <b><a href="postconf.5.html#smtp_per_record_deadline">smtp_per_record_deadline</a> (no)</b>
               Change the behavior of the smtp_*_timeout time lim-
-              its, from a time limit per  read  or  write  system
+              its,  from  a  time  limit per read or write system
               call, to a time limit to send or receive a complete
-              record (an SMTP command line, SMTP  response  line,
-              SMTP  message  content  line,  or TLS protocol mes-
+              record  (an  SMTP command line, SMTP response line,
+              SMTP message content line,  or  TLS  protocol  mes-
               sage).
 
 <b>TROUBLE SHOOTING CONTROLS</b>
        <b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
-              The increment  in  verbose  logging  level  when  a
-              remote  client  or  server matches a pattern in the
+              The  increment  in  verbose  logging  level  when a
+              remote client or server matches a  pattern  in  the
               <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
 
        <b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
-              Optional list of remote client or  server  hostname
-              or  network address patterns that cause the verbose
-              logging level to increase by the  amount  specified
+              Optional  list  of remote client or server hostname
+              or network address patterns that cause the  verbose
+              logging  level  to increase by the amount specified
               in $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
 
        <b><a href="postconf.5.html#error_notice_recipient">error_notice_recipient</a> (postmaster)</b>
-              The  recipient  of  postmaster  notifications about
-              mail delivery problems that are caused  by  policy,
+              The recipient  of  postmaster  notifications  about
+              mail  delivery  problems that are caused by policy,
               resource, software or protocol errors.
 
        <b><a href="postconf.5.html#internal_mail_filter_classes">internal_mail_filter_classes</a> (empty)</b>
-              What  categories of Postfix-generated mail are sub-
-              ject  to   before-queue   content   inspection   by
+              What categories of Postfix-generated mail are  sub-
+              ject   to   before-queue   content   inspection  by
               <a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a>, <a href="postconf.5.html#header_checks">header_checks</a> and <a href="postconf.5.html#body_checks">body_checks</a>.
 
        <b><a href="postconf.5.html#notify_classes">notify_classes</a> (resource, software)</b>
-              The  list of error classes that are reported to the
+              The list of error classes that are reported to  the
               postmaster.
 
 <b>MISCELLANEOUS CONTROLS</b>
        <b><a href="postconf.5.html#best_mx_transport">best_mx_transport</a> (empty)</b>
-              Where the Postfix SMTP client should  deliver  mail
+              Where  the  Postfix SMTP client should deliver mail
               when it detects a "mail loops back to myself" error
               condition.
 
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The default location of  the  Postfix  <a href="postconf.5.html">main.cf</a>  and
+              The  default  location  of  the Postfix <a href="postconf.5.html">main.cf</a> and
               <a href="master.5.html">master.cf</a> configuration files.
 
        <b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
-              How  much time a Postfix daemon process may take to
-              handle a request  before  it  is  terminated  by  a
+              How much time a Postfix daemon process may take  to
+              handle  a  request  before  it  is  terminated by a
               built-in watchdog timer.
 
        <b><a href="postconf.5.html#delay_logging_resolution_limit">delay_logging_resolution_limit</a> (2)</b>
-              The  maximal  number  of  digits  after the decimal
+              The maximal number  of  digits  after  the  decimal
               point when logging sub-second delay values.
 
        <b><a href="postconf.5.html#disable_dns_lookups">disable_dns_lookups</a> (no)</b>
-              Disable DNS lookups in the Postfix  SMTP  and  LMTP
+              Disable  DNS  lookups  in the Postfix SMTP and LMTP
               clients.
 
        <b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> (all)</b>
@@ -791,7 +797,7 @@ SMTP(8)                                                                SMTP(8)
               tem receives mail on.
 
        <b><a href="postconf.5.html#inet_protocols">inet_protocols</a> (all)</b>
-              The Internet protocols Postfix will attempt to  use
+              The  Internet protocols Postfix will attempt to use
               when making or accepting connections.
 
        <b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
@@ -801,85 +807,85 @@ SMTP(8)                                                                SMTP(8)
        <b><a href="postconf.5.html#lmtp_assume_final">lmtp_assume_final</a> (no)</b>
               When a remote LMTP server announces no DSN support,
               assume that the server performs final delivery, and
-              send  "delivered"  delivery  status   notifications
+              send   "delivered"  delivery  status  notifications
               instead of "relayed".
 
        <b><a href="postconf.5.html#lmtp_tcp_port">lmtp_tcp_port</a> (24)</b>
-              The  default  TCP port that the Postfix LMTP client
+              The default TCP port that the Postfix  LMTP  client
               connects to.
 
        <b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
-              The maximum amount of time  that  an  idle  Postfix
-              daemon  process  waits  for  an incoming connection
+              The  maximum  amount  of  time that an idle Postfix
+              daemon process waits  for  an  incoming  connection
               before terminating voluntarily.
 
        <b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
-              The maximal number of incoming connections  that  a
-              Postfix  daemon  process will service before termi-
+              The  maximal  number of incoming connections that a
+              Postfix daemon process will service  before  termi-
               nating voluntarily.
 
        <b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
-              The process ID  of  a  Postfix  command  or  daemon
+              The  process  ID  of  a  Postfix  command or daemon
               process.
 
        <b><a href="postconf.5.html#process_name">process_name</a> (read-only)</b>
-              The  process  name  of  a Postfix command or daemon
+              The process name of a  Postfix  command  or  daemon
               process.
 
        <b><a href="postconf.5.html#proxy_interfaces">proxy_interfaces</a> (empty)</b>
               The network interface addresses that this mail sys-
-              tem  receives  mail on by way of a proxy or network
+              tem receives mail on by way of a proxy  or  network
               address translation unit.
 
        <b><a href="postconf.5.html#smtp_address_preference">smtp_address_preference</a> (any)</b>
               The address type ("ipv6", "ipv4" or "any") that the
               Postfix SMTP client will try first, when a destina-
-              tion has IPv6 and  IPv4  addresses  with  equal  MX
+              tion  has  IPv6  and  IPv4  addresses with equal MX
               preference.
 
        <b><a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> (empty)</b>
-              An  optional  numerical  network  address  that the
-              Postfix SMTP client should bind to when  making  an
+              An optional  numerical  network  address  that  the
+              Postfix  SMTP  client should bind to when making an
               IPv4 connection.
 
        <b><a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> (empty)</b>
-              An  optional  numerical  network  address  that the
-              Postfix SMTP client should bind to when  making  an
+              An optional  numerical  network  address  that  the
+              Postfix  SMTP  client should bind to when making an
               IPv6 connection.
 
        <b><a href="postconf.5.html#smtp_helo_name">smtp_helo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
-              The  hostname to send in the SMTP EHLO or HELO com-
+              The hostname to send in the SMTP EHLO or HELO  com-
               mand.
 
        <b><a href="postconf.5.html#lmtp_lhlo_name">lmtp_lhlo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
               The hostname to send in the LMTP LHLO command.
 
        <b><a href="postconf.5.html#smtp_host_lookup">smtp_host_lookup</a> (dns)</b>
-              What mechanisms the Postfix  SMTP  client  uses  to
+              What  mechanisms  the  Postfix  SMTP client uses to
               look up a host's IP address.
 
        <b><a href="postconf.5.html#smtp_randomize_addresses">smtp_randomize_addresses</a> (yes)</b>
-              Randomize  the  order  of  equal-preference MX host
+              Randomize the order  of  equal-preference  MX  host
               addresses.
 
        <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
-              The mail system  name  that  is  prepended  to  the
-              process  name  in  syslog  records, so that "smtpd"
+              The  mail  system  name  that  is  prepended to the
+              process name in syslog  records,  so  that  "smtpd"
               becomes, for example, "postfix/smtpd".
 
        Available with Postfix 2.2 and earlier:
 
        <b><a href="postconf.5.html#fallback_relay">fallback_relay</a> (empty)</b>
-              Optional list of relay hosts for SMTP  destinations
+              Optional  list of relay hosts for SMTP destinations
               that can't be found or that are unreachable.
 
        Available with Postfix 2.3 and later:
 
        <b><a href="postconf.5.html#smtp_fallback_relay">smtp_fallback_relay</a> ($<a href="postconf.5.html#fallback_relay">fallback_relay</a>)</b>
-              Optional  list of relay hosts for SMTP destinations
+              Optional list of relay hosts for SMTP  destinations
               that can't be found or that are unreachable.
 
 <b>SEE ALSO</b>
@@ -900,7 +906,7 @@ SMTP(8)                                                                SMTP(8)
        <a href="TLS_README.html">TLS_README</a>, Postfix STARTTLS howto
 
 <b>LICENSE</b>
-       The  Secure  Mailer  license must be distributed with this
+       The Secure Mailer license must be  distributed  with  this
        software.
 
 <b>AUTHOR(S)</b>
index 1c7b27b8e178f4d422ca54e36805f41d12d2ed61..096e97bcda3984b9097192ee0d43b7c9cc6bb4aa 100644 (file)
@@ -4963,6 +4963,17 @@ configuration parameter.  See there for details. </p>
 <p> This feature is available in Postfix 2.3 and later. </p>
 
 
+</DD>
+
+<DT><b><a name="lmtp_tls_trust_anchor_file">lmtp_tls_trust_anchor_file</a>
+(default: empty)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a>
+configuration parameter.  See there for details.  </p>
+
+<p> This feature is available in Postfix 2.11 and later.  </p>
+
+
 </DD>
 
 <DT><b><a name="lmtp_tls_verify_cert_match">lmtp_tls_verify_cert_match</a>
@@ -11918,6 +11929,55 @@ are not possible. </p>
 <p> This feature is available in Postfix 2.2 and later.  </p>
 
 
+</DD>
+
+<DT><b><a name="smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a>
+(default: empty)</b></DT><DD>
+
+<p> Zero or more PEM-format files with trust-anchor certificates
+and/or public keys.  If the parameter is not empty the root CAs in
+CAfile and CApath are no longer trusted.  Rather, the Postfix SMTP
+client will only trust certificate-chains signed by one of the
+trust-anchors contained in the chosen files.  The specified
+trust-anchor certificates and public keys are not subject to
+expiration, and need not be (self-signed) root CAs.  They may, if
+desired, be intermediate certificates. Therefore, these certificates
+also may be found "in the middle" of the trust chain presented by
+the remote SMTP server, and any untrusted issuing parent certificates
+will be ignored.  Specify a list of pathnames separated by comma
+or whitespace.  </p>
+
+<p> This feature is implemented for completeness, to allow installations
+with a small set of SMTP peers to set global policy in <a href="postconf.5.html">main.cf</a>,
+that at most sites would be set via <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>.  In almost
+all cases it is better to use it on a per-destination basis via the
+"tafile" policy attribute of the "verify" and "secure" levels leaving
+the global <a href="postconf.5.html">main.cf</a> setting empty.  </p>
+
+<p>  When used on a per-destination basis, each "tafile" PEM file
+must be accessible to the Postfix SMTP client in the chroot jail
+if applicable.  The files should not contain any sensitive data,
+and must be readable by the non-privileged $<a href="postconf.5.html#mail_owner">mail_owner</a> user.  This
+allows destinations to be bound to a set of specific CAs or public
+keys without trusting the same CAs for all destinations.  </p>
+
+<p> The underlying mechanism is in support of <a href="http://tools.ietf.org/html/rfc6698">RFC 6698</a> (DANE TLSA),
+which defines mechanisms for a client to securely determine server
+TLS certificates via DNS.  </p>
+
+<p> If you want your trust anchors to be public keys, with OpenSSL
+you can extract a single PEM public key from a PEM X.509 file
+containing a single certificate, as follows: </p>
+
+<blockquote>
+<pre>
+$ openssl x509 -in cert.pem -out ta-key.pem -noout -pubkey
+</pre>
+</blockquote>
+
+<p> This feature is available in Postfix 2.11 and later.  </p>
+
+
 </DD>
 
 <DT><b><a name="smtp_tls_verify_cert_match">smtp_tls_verify_cert_match</a>
index 20bed174b8025922ef4381986cb8406163d8a2e3..7db52e027efb060d8f5f8c844f6f20ef7e9979c2 100644 (file)
@@ -587,29 +587,35 @@ SMTP(8)                                                                SMTP(8)
               List or bit-mask of  OpenSSL  bug  work-arounds  to
               disable.
 
+       Available in Postfix version 2.11 and later:
+
+       <b><a href="postconf.5.html#smtp_tls_trust_anchor_file">smtp_tls_trust_anchor_file</a> (empty)</b>
+              Zero  or  more  PEM-format  files with trust-anchor
+              certificates and/or public keys.
+
 <b>OBSOLETE STARTTLS CONTROLS</b>
-       The  following configuration parameters exist for compati-
+       The following configuration parameters exist for  compati-
        bility with Postfix versions before 2.3. Support for these
        will be removed in a future release.
 
        <b><a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a> (no)</b>
-              Opportunistic  mode:  use  TLS  when  a remote SMTP
-              server announces STARTTLS support,  otherwise  send
+              Opportunistic mode: use  TLS  when  a  remote  SMTP
+              server  announces  STARTTLS support, otherwise send
               the mail in the clear.
 
        <b><a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> (no)</b>
-              Enforcement  mode: require that remote SMTP servers
-              use TLS encryption, and  never  send  mail  in  the
+              Enforcement mode: require that remote SMTP  servers
+              use  TLS  encryption,  and  never  send mail in the
               clear.
 
        <b><a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> (yes)</b>
-              With  mandatory  TLS  encryption,  require that the
+              With mandatory TLS  encryption,  require  that  the
               remote SMTP server hostname matches the information
               in the remote SMTP server certificate.
 
        <b><a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> (empty)</b>
               Optional lookup tables with the Postfix SMTP client
-              TLS usage policy by  next-hop  destination  and  by
+              TLS  usage  policy  by  next-hop destination and by
               remote SMTP server hostname.
 
        <b><a href="postconf.5.html#smtp_tls_cipherlist">smtp_tls_cipherlist</a> (empty)</b>
@@ -619,80 +625,80 @@ SMTP(8)                                                                SMTP(8)
 <b>RESOURCE AND RATE CONTROLS</b>
        <b><a href="postconf.5.html#smtp_destination_concurrency_limit">smtp_destination_concurrency_limit</a>      ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destina</a>-</b>
        <b><a href="postconf.5.html#default_destination_concurrency_limit">tion_concurrency_limit</a>)</b>
-              The maximal number of parallel  deliveries  to  the
-              same  destination  via  the  smtp  message delivery
+              The  maximal  number  of parallel deliveries to the
+              same destination  via  the  smtp  message  delivery
               transport.
 
        <b><a href="postconf.5.html#smtp_destination_recipient_limit">smtp_destination_recipient_limit</a>        ($<a href="postconf.5.html#default_destination_recipient_limit">default_destina</a>-</b>
        <b><a href="postconf.5.html#default_destination_recipient_limit">tion_recipient_limit</a>)</b>
-              The maximal number of recipients  per  message  for
+              The  maximal  number  of recipients per message for
               the smtp message delivery transport.
 
        <b><a href="postconf.5.html#smtp_connect_timeout">smtp_connect_timeout</a> (30s)</b>
               The Postfix SMTP client time limit for completing a
-              TCP connection, or zero (use the  operating  system
+              TCP  connection,  or zero (use the operating system
               built-in time limit).
 
        <b><a href="postconf.5.html#smtp_helo_timeout">smtp_helo_timeout</a> (300s)</b>
-              The  Postfix SMTP client time limit for sending the
+              The Postfix SMTP client time limit for sending  the
               HELO or EHLO command, and for receiving the initial
               remote SMTP server response.
 
        <b><a href="postconf.5.html#lmtp_lhlo_timeout">lmtp_lhlo_timeout</a> (300s)</b>
-              The  Postfix LMTP client time limit for sending the
-              LHLO command, and for receiving the initial  remote
+              The Postfix LMTP client time limit for sending  the
+              LHLO  command, and for receiving the initial remote
               LMTP server response.
 
        <b><a href="postconf.5.html#smtp_xforward_timeout">smtp_xforward_timeout</a> (300s)</b>
-              The  Postfix SMTP client time limit for sending the
+              The Postfix SMTP client time limit for sending  the
               XFORWARD command, and for receiving the remote SMTP
               server response.
 
        <b><a href="postconf.5.html#smtp_mail_timeout">smtp_mail_timeout</a> (300s)</b>
-              The  Postfix SMTP client time limit for sending the
-              MAIL FROM command, and  for  receiving  the  remote
+              The Postfix SMTP client time limit for sending  the
+              MAIL  FROM  command,  and  for receiving the remote
               SMTP server response.
 
        <b><a href="postconf.5.html#smtp_rcpt_timeout">smtp_rcpt_timeout</a> (300s)</b>
-              The  Postfix SMTP client time limit for sending the
-              SMTP RCPT TO command, and for receiving the  remote
+              The Postfix SMTP client time limit for sending  the
+              SMTP  RCPT TO command, and for receiving the remote
               SMTP server response.
 
        <b><a href="postconf.5.html#smtp_data_init_timeout">smtp_data_init_timeout</a> (120s)</b>
-              The  Postfix SMTP client time limit for sending the
-              SMTP DATA command, and  for  receiving  the  remote
+              The Postfix SMTP client time limit for sending  the
+              SMTP  DATA  command,  and  for receiving the remote
               SMTP server response.
 
        <b><a href="postconf.5.html#smtp_data_xfer_timeout">smtp_data_xfer_timeout</a> (180s)</b>
-              The  Postfix SMTP client time limit for sending the
+              The Postfix SMTP client time limit for sending  the
               SMTP message content.
 
        <b><a href="postconf.5.html#smtp_data_done_timeout">smtp_data_done_timeout</a> (600s)</b>
-              The Postfix SMTP client time limit for sending  the
-              SMTP  ".", and for receiving the remote SMTP server
+              The  Postfix SMTP client time limit for sending the
+              SMTP ".", and for receiving the remote SMTP  server
               response.
 
        <b><a href="postconf.5.html#smtp_quit_timeout">smtp_quit_timeout</a> (300s)</b>
-              The Postfix SMTP client time limit for sending  the
-              QUIT  command,  and  for  receiving the remote SMTP
+              The  Postfix SMTP client time limit for sending the
+              QUIT command, and for  receiving  the  remote  SMTP
               server response.
 
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#smtp_mx_address_limit">smtp_mx_address_limit</a> (5)</b>
               The  maximal  number  of  MX  (mail  exchanger)  IP
-              addresses  that can result from Postfix SMTP client
+              addresses that can result from Postfix SMTP  client
               mail exchanger lookups, or zero (no limit).
 
        <b><a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> (2)</b>
-              The maximal number of SMTP  sessions  per  delivery
-              request  before the Postfix SMTP client gives up or
-              delivers to a fall-back <a href="postconf.5.html#relayhost">relay  host</a>,  or  zero  (no
+              The  maximal  number  of SMTP sessions per delivery
+              request before the Postfix SMTP client gives up  or
+              delivers  to  a  fall-back  <a href="postconf.5.html#relayhost">relay host</a>, or zero (no
               limit).
 
        <b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (20s)</b>
-              The  Postfix SMTP client time limit for sending the
-              RSET command, and for  receiving  the  remote  SMTP
+              The Postfix SMTP client time limit for sending  the
+              RSET  command,  and  for  receiving the remote SMTP
               server response.
 
        Available in Postfix version 2.2 and earlier:
@@ -704,11 +710,11 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix version 2.2 and later:
 
        <b><a href="postconf.5.html#smtp_connection_cache_destinations">smtp_connection_cache_destinations</a> (empty)</b>
-              Permanently enable SMTP connection caching for  the
+              Permanently  enable SMTP connection caching for the
               specified destinations.
 
        <b><a href="postconf.5.html#smtp_connection_cache_on_demand">smtp_connection_cache_on_demand</a> (yes)</b>
-              Temporarily  enable SMTP connection caching while a
+              Temporarily enable SMTP connection caching while  a
               destination has a high volume of mail in the active
               queue.
 
@@ -718,72 +724,72 @@ SMTP(8)                                                                SMTP(8)
 
        <b><a href="postconf.5.html#smtp_connection_cache_time_limit">smtp_connection_cache_time_limit</a> (2s)</b>
               When SMTP connection caching is enabled, the amount
-              of  time  that an unused SMTP client socket is kept
+              of time that an unused SMTP client socket  is  kept
               open before it is closed.
 
        Available in Postfix version 2.3 and later:
 
        <b><a href="postconf.5.html#connection_cache_protocol_timeout">connection_cache_protocol_timeout</a> (5s)</b>
-              Time limit for connection cache  connect,  send  or
+              Time  limit  for  connection cache connect, send or
               receive operations.
 
        Available in Postfix version 2.9 and later:
 
        <b><a href="postconf.5.html#smtp_per_record_deadline">smtp_per_record_deadline</a> (no)</b>
               Change the behavior of the smtp_*_timeout time lim-
-              its, from a time limit per  read  or  write  system
+              its,  from  a  time  limit per read or write system
               call, to a time limit to send or receive a complete
-              record (an SMTP command line, SMTP  response  line,
-              SMTP  message  content  line,  or TLS protocol mes-
+              record  (an  SMTP command line, SMTP response line,
+              SMTP message content line,  or  TLS  protocol  mes-
               sage).
 
 <b>TROUBLE SHOOTING CONTROLS</b>
        <b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
-              The increment  in  verbose  logging  level  when  a
-              remote  client  or  server matches a pattern in the
+              The  increment  in  verbose  logging  level  when a
+              remote client or server matches a  pattern  in  the
               <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
 
        <b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
-              Optional list of remote client or  server  hostname
-              or  network address patterns that cause the verbose
-              logging level to increase by the  amount  specified
+              Optional  list  of remote client or server hostname
+              or network address patterns that cause the  verbose
+              logging  level  to increase by the amount specified
               in $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
 
        <b><a href="postconf.5.html#error_notice_recipient">error_notice_recipient</a> (postmaster)</b>
-              The  recipient  of  postmaster  notifications about
-              mail delivery problems that are caused  by  policy,
+              The recipient  of  postmaster  notifications  about
+              mail  delivery  problems that are caused by policy,
               resource, software or protocol errors.
 
        <b><a href="postconf.5.html#internal_mail_filter_classes">internal_mail_filter_classes</a> (empty)</b>
-              What  categories of Postfix-generated mail are sub-
-              ject  to   before-queue   content   inspection   by
+              What categories of Postfix-generated mail are  sub-
+              ject   to   before-queue   content   inspection  by
               <a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a>, <a href="postconf.5.html#header_checks">header_checks</a> and <a href="postconf.5.html#body_checks">body_checks</a>.
 
        <b><a href="postconf.5.html#notify_classes">notify_classes</a> (resource, software)</b>
-              The  list of error classes that are reported to the
+              The list of error classes that are reported to  the
               postmaster.
 
 <b>MISCELLANEOUS CONTROLS</b>
        <b><a href="postconf.5.html#best_mx_transport">best_mx_transport</a> (empty)</b>
-              Where the Postfix SMTP client should  deliver  mail
+              Where  the  Postfix SMTP client should deliver mail
               when it detects a "mail loops back to myself" error
               condition.
 
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The default location of  the  Postfix  <a href="postconf.5.html">main.cf</a>  and
+              The  default  location  of  the Postfix <a href="postconf.5.html">main.cf</a> and
               <a href="master.5.html">master.cf</a> configuration files.
 
        <b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
-              How  much time a Postfix daemon process may take to
-              handle a request  before  it  is  terminated  by  a
+              How much time a Postfix daemon process may take  to
+              handle  a  request  before  it  is  terminated by a
               built-in watchdog timer.
 
        <b><a href="postconf.5.html#delay_logging_resolution_limit">delay_logging_resolution_limit</a> (2)</b>
-              The  maximal  number  of  digits  after the decimal
+              The maximal number  of  digits  after  the  decimal
               point when logging sub-second delay values.
 
        <b><a href="postconf.5.html#disable_dns_lookups">disable_dns_lookups</a> (no)</b>
-              Disable DNS lookups in the Postfix  SMTP  and  LMTP
+              Disable  DNS  lookups  in the Postfix SMTP and LMTP
               clients.
 
        <b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> (all)</b>
@@ -791,7 +797,7 @@ SMTP(8)                                                                SMTP(8)
               tem receives mail on.
 
        <b><a href="postconf.5.html#inet_protocols">inet_protocols</a> (all)</b>
-              The Internet protocols Postfix will attempt to  use
+              The  Internet protocols Postfix will attempt to use
               when making or accepting connections.
 
        <b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
@@ -801,85 +807,85 @@ SMTP(8)                                                                SMTP(8)
        <b><a href="postconf.5.html#lmtp_assume_final">lmtp_assume_final</a> (no)</b>
               When a remote LMTP server announces no DSN support,
               assume that the server performs final delivery, and
-              send  "delivered"  delivery  status   notifications
+              send   "delivered"  delivery  status  notifications
               instead of "relayed".
 
        <b><a href="postconf.5.html#lmtp_tcp_port">lmtp_tcp_port</a> (24)</b>
-              The  default  TCP port that the Postfix LMTP client
+              The default TCP port that the Postfix  LMTP  client
               connects to.
 
        <b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
-              The maximum amount of time  that  an  idle  Postfix
-              daemon  process  waits  for  an incoming connection
+              The  maximum  amount  of  time that an idle Postfix
+              daemon process waits  for  an  incoming  connection
               before terminating voluntarily.
 
        <b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
-              The maximal number of incoming connections  that  a
-              Postfix  daemon  process will service before termi-
+              The  maximal  number of incoming connections that a
+              Postfix daemon process will service  before  termi-
               nating voluntarily.
 
        <b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
-              The process ID  of  a  Postfix  command  or  daemon
+              The  process  ID  of  a  Postfix  command or daemon
               process.
 
        <b><a href="postconf.5.html#process_name">process_name</a> (read-only)</b>
-              The  process  name  of  a Postfix command or daemon
+              The process name of a  Postfix  command  or  daemon
               process.
 
        <b><a href="postconf.5.html#proxy_interfaces">proxy_interfaces</a> (empty)</b>
               The network interface addresses that this mail sys-
-              tem  receives  mail on by way of a proxy or network
+              tem receives mail on by way of a proxy  or  network
               address translation unit.
 
        <b><a href="postconf.5.html#smtp_address_preference">smtp_address_preference</a> (any)</b>
               The address type ("ipv6", "ipv4" or "any") that the
               Postfix SMTP client will try first, when a destina-
-              tion has IPv6 and  IPv4  addresses  with  equal  MX
+              tion  has  IPv6  and  IPv4  addresses with equal MX
               preference.
 
        <b><a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> (empty)</b>
-              An  optional  numerical  network  address  that the
-              Postfix SMTP client should bind to when  making  an
+              An optional  numerical  network  address  that  the
+              Postfix  SMTP  client should bind to when making an
               IPv4 connection.
 
        <b><a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> (empty)</b>
-              An  optional  numerical  network  address  that the
-              Postfix SMTP client should bind to when  making  an
+              An optional  numerical  network  address  that  the
+              Postfix  SMTP  client should bind to when making an
               IPv6 connection.
 
        <b><a href="postconf.5.html#smtp_helo_name">smtp_helo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
-              The  hostname to send in the SMTP EHLO or HELO com-
+              The hostname to send in the SMTP EHLO or HELO  com-
               mand.
 
        <b><a href="postconf.5.html#lmtp_lhlo_name">lmtp_lhlo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
               The hostname to send in the LMTP LHLO command.
 
        <b><a href="postconf.5.html#smtp_host_lookup">smtp_host_lookup</a> (dns)</b>
-              What mechanisms the Postfix  SMTP  client  uses  to
+              What  mechanisms  the  Postfix  SMTP client uses to
               look up a host's IP address.
 
        <b><a href="postconf.5.html#smtp_randomize_addresses">smtp_randomize_addresses</a> (yes)</b>
-              Randomize  the  order  of  equal-preference MX host
+              Randomize the order  of  equal-preference  MX  host
               addresses.
 
        <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
-              The mail system  name  that  is  prepended  to  the
-              process  name  in  syslog  records, so that "smtpd"
+              The  mail  system  name  that  is  prepended to the
+              process name in syslog  records,  so  that  "smtpd"
               becomes, for example, "postfix/smtpd".
 
        Available with Postfix 2.2 and earlier:
 
        <b><a href="postconf.5.html#fallback_relay">fallback_relay</a> (empty)</b>
-              Optional list of relay hosts for SMTP  destinations
+              Optional  list of relay hosts for SMTP destinations
               that can't be found or that are unreachable.
 
        Available with Postfix 2.3 and later:
 
        <b><a href="postconf.5.html#smtp_fallback_relay">smtp_fallback_relay</a> ($<a href="postconf.5.html#fallback_relay">fallback_relay</a>)</b>
-              Optional  list of relay hosts for SMTP destinations
+              Optional list of relay hosts for SMTP  destinations
               that can't be found or that are unreachable.
 
 <b>SEE ALSO</b>
@@ -900,7 +906,7 @@ SMTP(8)                                                                SMTP(8)
        <a href="TLS_README.html">TLS_README</a>, Postfix STARTTLS howto
 
 <b>LICENSE</b>
-       The  Secure  Mailer  license must be distributed with this
+       The Secure Mailer license must be  distributed  with  this
        software.
 
 <b>AUTHOR(S)</b>
index 2ceff2e4c488a3f5da1b853e62bf8554de501928..0ac57f4234c598adb2bae761ae3b28731b67cb78 100644 (file)
@@ -626,7 +626,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"
 
 sed 's/  / /g' <<EOF
 SYSTYPE        = $SYSTYPE
index 3f981492718a4a5704d784eff14173f0a047c6e0..979406aeebab4aa51957a8bfb2e8a4dfe3b9707c 100644 (file)
@@ -2818,6 +2818,11 @@ The LMTP-specific version of the smtp_tls_session_cache_timeout
 configuration parameter.  See there for details.
 .PP
 This feature is available in Postfix 2.3 and later.
+.SH lmtp_tls_trust_anchor_file (default: empty)
+The LMTP-specific version of the smtp_tls_trust_anchor_file
+configuration parameter.  See there for details.
+.PP
+This feature is available in Postfix 2.11 and later.
 .SH lmtp_tls_verify_cert_match (default: hostname)
 The LMTP-specific version of the smtp_tls_verify_cert_match
 configuration parameter. See there for details.
@@ -7673,6 +7678,53 @@ $smtp_tls_session_cache_database, this parameter is implemented in the
 are not possible.
 .PP
 This feature is available in Postfix 2.2 and later.
+.SH smtp_tls_trust_anchor_file (default: empty)
+Zero or more PEM-format files with trust-anchor certificates
+and/or public keys.  If the parameter is not empty the root CAs in
+CAfile and CApath are no longer trusted.  Rather, the Postfix SMTP
+client will only trust certificate-chains signed by one of the
+trust-anchors contained in the chosen files.  The specified
+trust-anchor certificates and public keys are not subject to
+expiration, and need not be (self-signed) root CAs.  They may, if
+desired, be intermediate certificates. Therefore, these certificates
+also may be found "in the middle" of the trust chain presented by
+the remote SMTP server, and any untrusted issuing parent certificates
+will be ignored.  Specify a list of pathnames separated by comma
+or whitespace.
+.PP
+This feature is implemented for completeness, to allow installations
+with a small set of SMTP peers to set global policy in main.cf,
+that at most sites would be set via smtp_tls_policy_maps.  In almost
+all cases it is better to use it on a per-destination basis via the
+"tafile" policy attribute of the "verify" and "secure" levels leaving
+the global main.cf setting empty.
+.PP
+When used on a per-destination basis, each "tafile" PEM file
+must be accessible to the Postfix SMTP client in the chroot jail
+if applicable.  The files should not contain any sensitive data,
+and must be readable by the non-privileged $mail_owner user.  This
+allows destinations to be bound to a set of specific CAs or public
+keys without trusting the same CAs for all destinations.
+.PP
+The underlying mechanism is in support of RFC 6698 (DANE TLSA),
+which defines mechanisms for a client to securely determine server
+TLS certificates via DNS.
+.PP
+If you want your trust anchors to be public keys, with OpenSSL
+you can extract a single PEM public key from a PEM X.509 file
+containing a single certificate, as follows:
+.sp
+.in +4
+.nf
+.na
+.ft C
+$ openssl x509 -in cert.pem -out ta-key.pem -noout -pubkey
+.fi
+.ad
+.ft R
+.in -4
+.PP
+This feature is available in Postfix 2.11 and later.
 .SH smtp_tls_verify_cert_match (default: hostname)
 How the Postfix SMTP client verifies the server certificate
 peername for the
index 57c0c915971935494b70e38a2d30d95788747a51..66dedf63e3ddbd700537c2d371e3cd1a4550e989 100644 (file)
@@ -471,6 +471,11 @@ HELO, MAIL, RCPT, DATA commands to a Postfix SMTP client TLS session.
 Available in Postfix version 2.8 and later:
 .IP "\fBtls_disable_workarounds (see 'postconf -d' output)\fR"
 List or bit-mask of OpenSSL bug work-arounds to disable.
+.PP
+Available in Postfix version 2.11 and later:
+.IP "\fBsmtp_tls_trust_anchor_file (empty)\fR"
+Zero or more PEM-format files with trust-anchor certificates
+and/or public keys.
 .SH "OBSOLETE STARTTLS CONTROLS"
 .na
 .nf
index 4c12073ae579d7a6bd680eb5bd1975195721f72d..6018e352230ae87378b8b446b599672d1838a25c 100755 (executable)
@@ -242,6 +242,7 @@ while (<>) {
     s;\blmtp_tls_security_level\b;<a href="postconf.5.html#lmtp_tls_security_level">$&</a>;g;
     s;\blmtp_tls_fingerprint_cert_match\b;<a href="postconf.5.html#lmtp_tls_fingerprint_cert_match">$&</a>;g;
     s;\blmtp_tls_verify_cert_match\b;<a href="postconf.5.html#lmtp_tls_verify_cert_match">$&</a>;g;
+    s;\blmtp_tls_trust_anchor_file\b;<a href="postconf.5.html#lmtp_tls_trust_anchor_file">$&</a>;g;
     s;\blmtp_tls_per_site\b;<a href="postconf.5.html#lmtp_tls_per_site">$&</a>;g;
     s;\blmtp_tls_cert_file\b;<a href="postconf.5.html#lmtp_tls_cert_file">$&</a>;g;
     s;\blmtp_tls_key_file\b;<a href="postconf.5.html#lmtp_tls_key_file">$&</a>;g;
@@ -638,6 +639,7 @@ while (<>) {
     s;\bsmtp_tls_fingerprint_cert_match\b;<a href="postconf.5.html#smtp_tls_fingerprint_cert_match">$&</a>;g;
     s;\bsmtp_tls_verify_cert_match\b;<a href="postconf.5.html#smtp_tls_verify_cert_match">$&</a>;g;
     s;\bsmtp_tls_secure_cert_match\b;<a href="postconf.5.html#smtp_tls_secure_cert_match">$&</a>;g;
+    s;\bsmtp_tls_trust_anchor_file\b;<a href="postconf.5.html#smtp_tls_trust_anchor_file">$&</a>;g;
     s;\bsmtp_tls_scert_verifydepth\b;<a href="postconf.5.html#smtp_tls_scert_verifydepth">$&</a>;g;
     s;\bsmtp_tls_security_level\b;<a href="postconf.5.html#smtp_tls_security_level">$&</a>;g;
     s;\bsmtp_tls_session_cache_database\b;<a href="postconf.5.html#smtp_tls_session_cache_database">$&</a>;g;
index 833e445d6fab72f1a8f890545f4d587e55d6aad3..08ee5d474ccf1a402938e262e7e5172c05409d5e 100644 (file)
@@ -1239,6 +1239,19 @@ DNS names are specified, the certificate CommonName is checked.
 If you want mandatory encryption without server certificate
 verification, see <a href="#client_tls_encrypt">above</a>. </p>
 
+<p> With Postfix &ge; 2.11 the "smtp_tls_trust_anchor_file" parameter
+or more typically the corresponding per-destination "tafile" attribute
+optionally modifies trust chain verification.  If the parameter is
+not empty the root CAs in CAfile and CApath are no longer trusted.
+Rather, the Postfix SMTP client will only trust certificate-chains
+signed by one of the trust-anchors contained in the chosen files.
+The specified trust-anchor certificates and public keys are not
+subject to expiration, and need not be (self-signed) root CAs.  They
+may, if desired, be intermediate certificates. Therefore, these
+certificates also may be found "in the middle" of the trust chain
+presented by the remote SMTP server, and any untrusted issuing
+parent certificates will be ignored.  </p>
+
 <p> Despite the potential for eliminating "man-in-the-middle" and other
 attacks, mandatory certificate trust chain and subject name verification
 is not viable as a default Internet mail delivery policy.  Most MX hosts
@@ -1300,6 +1313,19 @@ specified, the CommonName is checked. If you want mandatory encryption
 without server certificate verification, see <a
 href="#client_tls_encrypt">above</a>. </p>
 
+<p> With Postfix &ge; 2.11 the "smtp_tls_trust_anchor_file" parameter
+or more typically the corresponding per-destination "tafile" attribute
+optionally modifies trust chain verification.  If the parameter is
+not empty the root CAs in CAfile and CApath are no longer trusted.
+Rather, the Postfix SMTP client will only trust certificate-chains
+signed by one of the trust-anchors contained in the chosen files.
+The specified trust-anchor certificates and public keys are not
+subject to expiration, and need not be (self-signed) root CAs.  They
+may, if desired, be intermediate certificates. Therefore, these
+certificates also may be found "in the middle" of the trust chain
+presented by the remote SMTP server, and any untrusted issuing
+parent certificates will be ignored.  </p>
+
 <p> Despite the potential for eliminating "man-in-the-middle" and other
 attacks, mandatory secure server certificate verification is not
 viable as a default Internet mail delivery policy.  Most MX hosts
@@ -1806,12 +1832,16 @@ digits. </dd>
 
 <dt><b>verify</b></dt> <dd><a href="#client_tls_verify">Mandatory
 server certificate verification</a>.  Mail is delivered only if the
-TLS handshake
-succeeds, if the remote SMTP server certificate can be validated (not
-expired or revoked, and signed by a trusted certificate authority), and
-if the server certificate name matches the optional "match" attribute (or
-the main.cf smtp_tls_verify_cert_match parameter value when no optional
-"match" attribute is specified).  </dd>
+TLS handshake succeeds, if the remote SMTP server certificate can
+be validated (not expired or revoked, and signed by a trusted
+certificate authority), and if the server certificate name matches
+the optional "match" attribute (or the main.cf smtp_tls_verify_cert_match
+parameter value when no optional "match" attribute is specified).
+With Postfix &ge; 2.11 the "tafile" attribute optionally modifies
+trust chain verification in the same manner as the
+"smtp_tls_trust_anchor_file" parameter.  The "tafile" attribute
+may be specified multiple times to load multiple trust-anchor
+files.  </dd>
 
 <dt><b>secure</b></dt> <dd><a href="#client_tls_secure">Secure certificate
 verification.</a> Mail is delivered only if the TLS handshake succeeds,
@@ -1819,7 +1849,11 @@ if the remote SMTP server certificate can be validated (not expired
 or revoked, and signed by a trusted certificate authority), and if the
 server certificate name matches the optional "match" attribute (or the
 main.cf smtp_tls_secure_cert_match parameter value when no optional
-"match" attribute is specified).  </dd>
+"match" attribute is specified).  With Postfix &ge; 2.11 the "tafile"
+attribute optionally modifies trust chain verification in the same manner
+as the "smtp_tls_trust_anchor_file" parameter.  The "tafile" attribute
+may be specified multiple times to load multiple trust-anchor
+files.  </dd>
 
 </dl>
 
index c42856f1bd9ab0b1f9591e20b415d5bd4ad16846..cb9ad8ef1c96e999f25ba307872fac1f0c80aec3 100644 (file)
@@ -14919,7 +14919,7 @@ limited to 13 over the lifetime of a daemon process. </p>
 
 <p> This feature is available in Postfix 2.9 and later.  </p>
 
-%PARAM smtpd_log_access_permit_actions empty
+%PARAM smtpd_log_access_permit_actions
 
 <p> Enable logging of the named "permit" actions in SMTP server
 access lists (by default, the SMTP server logs "reject" actions but
@@ -15042,3 +15042,55 @@ RES_USE_DNSSEC and RES_USE_EDNS0 resolver options. </p>
 configuration parameter.  See there for details.  </p>
 
 <p> This feature is available in Postfix 2.11 and later.  </p>
+
+%PARAM smtp_tls_trust_anchor_file
+
+<p> Zero or more PEM-format files with trust-anchor certificates
+and/or public keys.  If the parameter is not empty the root CAs in
+CAfile and CApath are no longer trusted.  Rather, the Postfix SMTP
+client will only trust certificate-chains signed by one of the
+trust-anchors contained in the chosen files.  The specified
+trust-anchor certificates and public keys are not subject to
+expiration, and need not be (self-signed) root CAs.  They may, if
+desired, be intermediate certificates. Therefore, these certificates
+also may be found "in the middle" of the trust chain presented by
+the remote SMTP server, and any untrusted issuing parent certificates
+will be ignored.  Specify a list of pathnames separated by comma
+or whitespace.  </p>
+
+<p> This feature is implemented for completeness, to allow installations
+with a small set of SMTP peers to set global policy in main.cf,
+that at most sites would be set via smtp_tls_policy_maps.  In almost
+all cases it is better to use it on a per-destination basis via the
+"tafile" policy attribute of the "verify" and "secure" levels leaving
+the global main.cf setting empty.  </p>
+
+<p>  When used on a per-destination basis, each "tafile" PEM file
+must be accessible to the Postfix SMTP client in the chroot jail
+if applicable.  The files should not contain any sensitive data,
+and must be readable by the non-privileged $mail_owner user.  This
+allows destinations to be bound to a set of specific CAs or public
+keys without trusting the same CAs for all destinations.  </p>
+
+<p> The underlying mechanism is in support of RFC 6698 (DANE TLSA),
+which defines mechanisms for a client to securely determine server
+TLS certificates via DNS.  </p>
+
+<p> If you want your trust anchors to be public keys, with OpenSSL
+you can extract a single PEM public key from a PEM X.509 file
+containing a single certificate, as follows: </p>
+
+<blockquote>
+<pre>
+$ openssl x509 -in cert.pem -out ta-key.pem -noout -pubkey
+</pre>
+</blockquote>
+
+<p> This feature is available in Postfix 2.11 and later.  </p>
+
+%PARAM lmtp_tls_trust_anchor_file
+
+<p> The LMTP-specific version of the smtp_tls_trust_anchor_file
+configuration parameter.  See there for details.  </p>
+
+<p> This feature is available in Postfix 2.11 and later.  </p>
index 396e2ff021a1f754c3b083a3805f8d795806a6aa..2d1867267e256471b0c8f8f7c3330d5641112449 100644 (file)
@@ -1256,3 +1256,6 @@ uncached
 unzipping
 windowsize
 xpostconf
+TLSA
+tafile
+VPN
index 1e368f8a50a817e96169f97215b6b63396fad43f..6b1aef850dea76244cf16c02ef4a724f947c032e 100644 (file)
@@ -1440,6 +1440,12 @@ extern char *var_smtp_tls_mand_excl;
 #define DEF_LMTP_TLS_FPT_DGST  "md5"
 extern char *var_smtp_tls_fpt_dgst;
 
+#define VAR_SMTP_TLS_TAFILE    "smtp_tls_trust_anchor_file"
+#define DEF_SMTP_TLS_TAFILE    ""
+#define VAR_LMTP_TLS_TAFILE    "lmtp_tls_trust_anchor_file"
+#define DEF_LMTP_TLS_TAFILE    ""
+extern char *var_smtp_tls_tafile;
+
 #define VAR_SMTP_TLS_LOGLEVEL  "smtp_tls_loglevel"
 #define DEF_SMTP_TLS_LOGLEVEL  "0"
 #define VAR_LMTP_TLS_LOGLEVEL  "lmtp_tls_loglevel"
index a869f257d30069be25d76c11a4e14b09338fe77e..434e516e84da5d54c7459865ddd017bdfffbb9e5 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20130403"
+#define MAIL_RELEASE_DATE      "20130405"
 #define MAIL_VERSION_NUMBER    "2.11"
 
 #ifdef SNAPSHOT
index 98ba32cbfceb1e8159763a4961e0726b9ea9ebbb..f30cdfa6d7a31a3422bb2e02c1f2b036596b707e 100644 (file)
@@ -593,6 +593,7 @@ smtp_state.o: smtp_sasl.h
 smtp_state.o: smtp_state.c
 smtp_tls_sess.o: ../../include/argv.h
 smtp_tls_sess.o: ../../include/attr.h
+smtp_tls_sess.o: ../../include/ctable.h
 smtp_tls_sess.o: ../../include/deliver_request.h
 smtp_tls_sess.o: ../../include/dict.h
 smtp_tls_sess.o: ../../include/dsn.h
index cb645d5679cd65c2e7f4d2bbc2ebe4080f0814db..40b7b70bf2dcfc7c45f72a56e186a5024b0f0530 100644 (file)
@@ -22,6 +22,7 @@
        VAR_LMTP_TLS_SEC_CMATCH, DEF_LMTP_TLS_SEC_CMATCH, &var_smtp_tls_sec_cmatch, 1, 0,
        VAR_LMTP_TLS_FPT_CMATCH, DEF_LMTP_TLS_FPT_CMATCH, &var_smtp_tls_fpt_cmatch, 0, 0,
        VAR_LMTP_TLS_FPT_DGST, DEF_LMTP_TLS_FPT_DGST, &var_smtp_tls_fpt_dgst, 1, 0,
+       VAR_LMTP_TLS_TAFILE, DEF_LMTP_TLS_TAFILE, &var_smtp_tls_tafile, 0, 0,
        VAR_LMTP_TLS_PROTO, DEF_LMTP_TLS_PROTO, &var_smtp_tls_proto, 0, 0,
        VAR_LMTP_TLS_CIPH, DEF_LMTP_TLS_CIPH, &var_smtp_tls_ciph, 1, 0,
        VAR_LMTP_TLS_ECCERT_FILE, DEF_LMTP_TLS_ECCERT_FILE, &var_smtp_tls_eccert_file, 0, 0,
index 841910b0b949cea5185ce01ad4ff8550cbd8914a..42e24070ccf98e242d67289f753100c68c300fe5 100644 (file)
 /*     Available in Postfix version 2.8 and later:
 /* .IP "\fBtls_disable_workarounds (see 'postconf -d' output)\fR"
 /*     List or bit-mask of OpenSSL bug work-arounds to disable.
+/* .PP
+/*     Available in Postfix version 2.11 and later:
+/* .IP "\fBsmtp_tls_trust_anchor_file (empty)\fR"
+/*     Zero or more PEM-format files with trust-anchor certificates
+/*     and/or public keys.
 /* OBSOLETE STARTTLS CONTROLS
 /* .ad
 /* .fi
@@ -825,6 +830,7 @@ int     var_smtp_tls_scert_vd;
 char   *var_smtp_tls_vfy_cmatch;
 char   *var_smtp_tls_fpt_cmatch;
 char   *var_smtp_tls_fpt_dgst;
+char   *var_smtp_tls_tafile;
 char   *var_smtp_tls_proto;
 char   *var_smtp_tls_ciph;
 char   *var_smtp_tls_eccert_file;
index 7ccd29bfb8f9618044c36204bff141f51cbc43ff..1dbbf516a486400c646dc574ea208320dba7e4a8 100644 (file)
@@ -202,13 +202,16 @@ extern HBC_CHECKS *smtp_body_checks;      /* limited body checks */
   * smtp_session.c
   */
 #ifdef USE_TLS
-typedef struct SMTP_TLS_SESS {
+typedef struct SMTP_TLS_POLICY {
+    int     refs;                      /* Reference count */
     int     level;                     /* TLS enforcement level */
     char   *protocols;                 /* Acceptable SSL protocols */
     char   *grade;                     /* Cipher grade: "export", ... */
     VSTRING *exclusions;               /* Excluded SSL ciphers */
     ARGV   *matchargv;                 /* Cert match patterns */
-} SMTP_TLS_SESS;
+    DSN_BUF *why;                      /* Lookup error status */
+    TLS_DANE *dane;                    /* DANE TLSA digests */
+} SMTP_TLS_POLICY;
 
 #endif
 
@@ -254,7 +257,7 @@ typedef struct SMTP_SESSION {
     TLS_SESS_STATE *tls_context;       /* TLS library session state */
     char   *tls_nexthop;               /* Nexthop domain for cert checks */
     int     tls_retry_plain;           /* Try plain when TLS handshake fails */
-    SMTP_TLS_SESS *tls;                        /* SMTP session TLS policy */
+    SMTP_TLS_POLICY *tls;              /* SMTP session TLS policy */
 #endif
 
     SMTP_STATE *state;                 /* back link */
@@ -274,10 +277,10 @@ extern SMTP_SESSION *smtp_session_activate(int, VSTRING *, VSTRING *);
   * smtp_tls_sess.c
   */
 extern void smtp_tls_list_init(void);
-extern SMTP_TLS_SESS *smtp_tls_sess_alloc(DSN_BUF *, const char *, const char *,
-                                                 unsigned, int);
-extern SMTP_TLS_SESS *smtp_tls_sess_free(SMTP_TLS_SESS *);
-
+extern SMTP_TLS_POLICY *smtp_tls_policy(DSN_BUF *, const char *, const char *,
+                                       unsigned, int);
+extern void smtp_tls_policy_free(SMTP_TLS_POLICY *);
+extern void smtp_tls_policy_flush(void);
 #endif
 
  /*
index cd25fdc4faef354a65f79e1fd9fa9ea3baec85b8..13f8723fa5f6d73501898ed2cf45f0926d2989a9 100644 (file)
@@ -23,6 +23,7 @@
        VAR_SMTP_TLS_SEC_CMATCH, DEF_SMTP_TLS_SEC_CMATCH, &var_smtp_tls_sec_cmatch, 1, 0,
        VAR_SMTP_TLS_FPT_CMATCH, DEF_SMTP_TLS_FPT_CMATCH, &var_smtp_tls_fpt_cmatch, 0, 0,
        VAR_SMTP_TLS_FPT_DGST, DEF_SMTP_TLS_FPT_DGST, &var_smtp_tls_fpt_dgst, 1, 0,
+       VAR_SMTP_TLS_TAFILE, DEF_SMTP_TLS_TAFILE, &var_smtp_tls_tafile, 0, 0,
        VAR_SMTP_TLS_PROTO, DEF_SMTP_TLS_PROTO, &var_smtp_tls_proto, 0, 0,
        VAR_SMTP_TLS_CIPH, DEF_SMTP_TLS_CIPH, &var_smtp_tls_ciph, 1, 0,
        VAR_SMTP_TLS_ECCERT_FILE, DEF_SMTP_TLS_ECCERT_FILE, &var_smtp_tls_eccert_file, 0, 0,
index 929c74a670871102221d7a653699329ffd8a9e6e..ec3559d37e277d3656cae785af6c80173815c09b 100644 (file)
@@ -808,7 +808,8 @@ static int smtp_start_tls(SMTP_STATE *state)
                         cipher_exclusions
                         = vstring_str(session->tls->exclusions),
                         matchargv = session->tls->matchargv,
-                        mdalg = var_smtp_tls_fpt_dgst);
+                        mdalg = var_smtp_tls_fpt_dgst,
+                        dane = session->tls->dane);
     vstring_free(serverid);
 
     if (session->tls_context == 0) {
@@ -848,8 +849,23 @@ static int smtp_start_tls(SMTP_STATE *state)
      * server, so no need to disable I/O, ... we can even be polite and send
      * "QUIT".
      * 
-     * See src/tls/tls_level.c. Levels above encrypt require matching. Levels >=
-     * verify require CA trust.
+     * See src/tls/tls_level.c. Levels above encrypt require matching.
+     * Levels >= verify require CA trust.
+     *
+     * The DANE level is a hybrid of verify and fingerprint, if we have
+     * trust-anchors, we must do name checking, so we treat like verify.
+     * We also do fingerprint verification against any end-entity certs,
+     * so it'll work for that too.
+     *
+     * If we only have end-entity certs, then it is just like fingerprint.
+     *
+     * If we have no usable certs at all, but TLSA records were found,
+     * we do "encrypt". Contrary to draft-ietf-dane-srv, we can't
+     * do standard PKIX as a fallback, it will almost always fail,
+     * with no human present to "click ok".
+     *
+     * The DANE security level is for now still disabled in tls_level.c
+     * When it is enabled, we're ready to enforce its constraints. 
      */
     if (session->tls->level >= TLS_LEV_VERIFY)
        if (!TLS_CERT_IS_TRUSTED(session->tls_context))
index 62937926ac62485346ee30594c8e3e66a1a07ba1..443b4d21620a65d3ce8b70e7842c845330dd2e36 100644 (file)
@@ -203,9 +203,8 @@ SMTP_SESSION *smtp_session_alloc(DSN_BUF *why, const char *dest,
     /*
      * When the destination argument of smtp_tls_sess_alloc() is null, a
      * trivial TLS policy (level = "none") is returned unconditionally and
-     * the other arguments are not used.  Soon the DSN_BUF "why" argument
-     * will be optional when the caller is not interested in the error
-     * status.
+     * the other arguments are not used.  The DSN_BUF "why" argument is
+     * optional when the caller is not interested in the error status.
      */
 #define NO_DSN_BUF     (DSN_BUF *) 0
 #define NO_DEST                (char *) 0
@@ -218,12 +217,12 @@ SMTP_SESSION *smtp_session_alloc(DSN_BUF *why, const char *dest,
     session->tls_retry_plain = 0;
     session->tls_nexthop = 0;
     if (flags & SMTP_MISC_FLAG_NO_TLS)
-       session->tls = smtp_tls_sess_alloc(NO_DSN_BUF, NO_DEST, NO_HOST,
-                                          NO_PORT, NO_FLAGS);
+       session->tls = smtp_tls_policy(NO_DSN_BUF, NO_DEST, NO_HOST,
+                                      NO_PORT, NO_FLAGS);
     else {
        if (why == 0)
            msg_panic("%s: null error buffer", myname);
-       session->tls = smtp_tls_sess_alloc(why, dest, host, port, valid);
+       session->tls = smtp_tls_policy(why, dest, host, port, valid);
     }
     if (!session->tls) {
        smtp_session_free(session);
@@ -275,7 +274,7 @@ void    smtp_session_free(SMTP_SESSION *session)
                          var_smtp_starttls_tmout, 0, session->tls_context);
     }
     if (session->tls)
-       (void) smtp_tls_sess_free(session->tls);
+       smtp_tls_policy_free(session->tls);
 #endif
     if (session->stream)
        vstream_fclose(session->stream);
@@ -310,20 +309,15 @@ int     smtp_sess_tls_check(const char *dest, const char *host, unsigned port,
                                    int valid)
 {
 #ifdef USE_TLS
-    static DSN_BUF *why;
-    SMTP_TLS_SESS *tls;
+    int     needed = 1;
+    SMTP_TLS_POLICY *tls;
 
-    if (!why)
-       why = dsb_create();
-
-    tls = smtp_tls_sess_alloc(why, dest, host, ntohs(port), valid);
-    dsb_reset(why);
-
-    if (tls && tls->level >= TLS_LEV_NONE && tls->level <= TLS_LEV_MAY)
-       return (0);
-    if (tls)
-       smtp_tls_sess_free(tls);
-    return (1);
+    /* Say "no" only when we're sure. Otherwise, "yes". */
+    if ((tls = smtp_tls_policy(0, dest, host, ntohs(port), valid)) != 0) {
+       needed = tls->level >= TLS_LEV_NONE && tls->level <= TLS_LEV_MAY;
+       smtp_tls_policy_free(tls);
+    }
+    return (needed);
 #else
     return (0);
 #endif
index 9699a053b91a162cc8f38681d33b8604948085f7..995af3496883e99896e2c65645d204d27deedc71 100644 (file)
@@ -84,6 +84,10 @@ SMTP_STATE *smtp_state_alloc(void)
 
 void    smtp_state_free(SMTP_STATE *state)
 {
+#ifdef USE_TLS
+    /* The TLS policy cache lifetime is one delivery. */
+    smtp_tls_policy_flush();
+#endif
     if (state->dest_label)
        vstring_free(state->dest_label);
     if (state->dest_prop)
index 74467da1dcd56cb6334625de634c3427260761c3..bd514cc9f0a0853dd36d28d96fd44cd9924e04f1 100644 (file)
@@ -1,38 +1,49 @@
 /*++
 /* NAME
-/*     smtp_tls_sess 3
+/*     smtp_tls_policy 3
 /* SUMMARY
-/*     SMTP_TLS_SESS structure management
+/*     SMTP_TLS_POLICY structure management
 /* SYNOPSIS
 /*     #include "smtp.h"
 /*
 /*     void    smtp_tls_list_init()
 /*
-/*     SMTP_TLS_SESS *smtp_tls_sess_alloc(why, dest, host, port, valid)
+/*     SMTP_TLS_POLICY *smtp_tls_policy(why, dest, host, port, valid)
 /*     DSN_BUF *why;
 /*     char    *dest;
 /*     char    *host;
 /*     unsigned port;
 /*     int     valid;
 /*
-/*     SMTP_TLS_SESS *smtp_tls_sess_free(tls)
-/*     SMTP_TLS_SESS *tls;
+/*     void    smtp_tls_policy_free(tls)
+/*     SMTP_TLS_POLICY *tls;
+/*
+/*     void    smtp_tls_policy_flush()
 /* DESCRIPTION
 /*     smtp_tls_list_init() initializes lookup tables used by the TLS
 /*     policy engine.
 /*
-/*     smtp_tls_sess_alloc() allocates memory for an SMTP_TLS_SESS structure
-/*     and initializes it based on the given information.  Any required
-/*     table and DNS lookups can fail.  When this happens, "why" is updated
-/*     with the error reason and a null pointer is returned.  NOTE: the
-/*     port is in network byte order.  If "dest" is null, no policy checks are
-/*     made, rather a trivial policy with tls disabled is returned (the
-/*     remaining arguments are unused in this case and may be null).
+/*     smtp_tls_policy() returns the SMTP_TLS_POLICY structure for
+/*     the destination, host, port and DNSSEC validation status. Any of
+/*     the required table and DNS lookups can fail.  When this happens, and
+/*     "why" is non-null, it is updated with the error reason and a null
+/*     policy is returned.  NOTE: the port is in network byte order.  If
+/*     "dest" is null, no policy checks are made, rather a trivial policy
+/*     with TLS disabled is returned.  The caller must free the policy via
+/*     smtp_tls_policy_free().
+/*
+/*     smtp_tls_policy_free() frees the SMTP_TLS_POLICY object.  The
+/*     objects are reference counted, so storage is deallocated when
+/*     the reference count drops to zero.  Since the objects are also
+/*     cached, this typically happens when the cached is flushed, provided
+/*     all other references have been released.
 /*
-/*     smtp_tls_sess_free() destroys an SMTP_TLS_SESS structure and its
-/*     members.  A null pointer is returned for convenience.
+/*     smtp_tls_policy_flush() frees the cache contents and cache object.
 /*
 /*     Arguments:
+/* .IP why
+/*     A pointer to a DSN_BUF which holds error status information when
+/*     the TLS policy lookup fails.
 /* .IP dest
 /*     The unmodified next-hop or fall-back destination including
 /*     the optional [] and including the optional port or service.
@@ -47,6 +58,8 @@
 /*     The remote port, network byte order.
 /* .IP valid
 /*     The DNSSEC validation status of the host name.
+/* .IP tls
+/*     An SMTP_TLS_POLICY object.
 /* LICENSE
 /* .ad
 /* .fi
@@ -73,6 +86,8 @@
 
 #ifdef USE_TLS
 
+#include <netinet/in.h>                        /* ntohs() for Solaris or BSD */
+#include <arpa/inet.h>                 /* ntohs() for Linux or BSD */
 #include <stdlib.h>
 #include <string.h>
 
 #include <vstring.h>
 #include <stringops.h>
 #include <valid_hostname.h>
+#include <ctable.h>
 
 /* Global library. */
 
 
 #include "smtp.h"
 
+#define CACHE_SIZE 20
+static CTABLE *policy_cache;
+
+static int global_tls_level(void);
 static MAPS *tls_policy;               /* lookup table(s) */
 static MAPS *tls_per_site;             /* lookup table(s) */
 
@@ -129,11 +149,14 @@ static const char *policy_name(int tls_level)
     return name;
 }
 
+#define MARK_INVALID(why, levelp) do { \
+           dsb_simple((why), "4.7.5", "client TLS configuration problem"); \
+           *(levelp) = TLS_LEV_INVALID; } while (0)
+
 /* tls_site_lookup - look up per-site TLS security level */
 
-static void tls_site_lookup(SMTP_TLS_SESS *tls, int *site_level,
-                             const char *site_name, const char *site_class,
-                                   DSN_BUF *why)
+static void tls_site_lookup(SMTP_TLS_POLICY *tls, int *site_level,
+                             const char *site_name, const char *site_class)
 {
     const char *lookup;
 
@@ -162,14 +185,13 @@ static void tls_site_lookup(SMTP_TLS_SESS *tls, int *site_level,
        } else {
            msg_warn("%s: unknown TLS policy '%s' for %s %s",
                     tls_per_site->title, lookup, site_class, site_name);
-           dsb_simple(why, "4.7.5", "client TLS configuration problem");
-           *site_level = TLS_LEV_INVALID;
+           MARK_INVALID(tls->why, site_level);
            return;
        }
     } else if (tls_per_site->error) {
        msg_warn("%s: %s \"%s\": per-site table lookup error",
                 tls_per_site->title, site_class, site_name);
-       dsb_simple(why, "4.3.0", "Temporary lookup error");
+       dsb_simple(tls->why, "4.3.0", "Temporary lookup error");
        *site_level = TLS_LEV_INVALID;
        return;
     }
@@ -178,9 +200,9 @@ static void tls_site_lookup(SMTP_TLS_SESS *tls, int *site_level,
 
 /* tls_policy_lookup_one - look up destination TLS policy */
 
-static void tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
-                                         const char *site_name,
-                                      const char *site_class, DSN_BUF *why)
+static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
+                                         const char *site_name,
+                                         const char *site_class)
 {
     const char *lookup;
     char   *policy;
@@ -194,6 +216,9 @@ static void tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
 #undef FREE_RETURN
 #define FREE_RETURN do { myfree(saved_policy); return; } while (0)
 
+#define INVALID_RETURN(why, levelp) do { \
+           MARK_INVALID((why), (levelp)); FREE_RETURN; } while (0)
+
 #define WHERE \
     vstring_str(vstring_sprintf(cbuf, "%s, %s \"%s\"", \
                tls_policy->title, site_class, site_name))
@@ -204,8 +229,7 @@ static void tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
     if ((lookup = maps_find(tls_policy, site_name, 0)) == 0) {
        if (tls_policy->error) {
            msg_warn("%s: policy table lookup error", WHERE);
-           dsb_simple(why, "4.3.0", "Temporary lookup error");
-           *site_level = TLS_LEV_INVALID;
+           MARK_INVALID(tls->why, site_level);
        }
        return;
     }
@@ -213,16 +237,13 @@ static void tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
 
     if ((tok = mystrtok(&policy, "\t\n\r ,")) == 0) {
        msg_warn("%s: invalid empty policy", WHERE);
-       dsb_simple(why, "4.7.5", "client TLS configuration problem");
-       *site_level = TLS_LEV_INVALID;
-       FREE_RETURN;
+       INVALID_RETURN(tls->why, site_level);
     }
     *site_level = tls_level_lookup(tok);
     if (*site_level == TLS_LEV_INVALID) {
        /* tls_level_lookup() logs no warning. */
        msg_warn("%s: invalid security level \"%s\"", WHERE, tok);
-       dsb_simple(why, "4.7.5", "client TLS configuration problem");
-       FREE_RETURN;
+       INVALID_RETURN(tls->why, site_level);
     }
 
     /*
@@ -235,6 +256,13 @@ static void tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
        FREE_RETURN;
     }
 
+    /*
+     * The fingerprint, verify and secure levels require or
+     * support explicit TA or EE certificate digest match lists.
+     */
+    if (*site_level >= TLS_LEV_FPRINT)
+       tls->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
+
     /*
      * Errors in attributes may have security consequences, don't ignore
      * errors that can degrade security.
@@ -243,24 +271,18 @@ static void tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
        if ((err = split_nameval(tok, &name, &val)) != 0) {
            msg_warn("%s: malformed attribute/value pair \"%s\": %s",
                     WHERE, tok, err);
-           dsb_simple(why, "4.7.5", "client TLS configuration problem");
-           *site_level = TLS_LEV_INVALID;
-           FREE_RETURN;
+           INVALID_RETURN(tls->why, site_level);
        }
        /* Only one instance per policy. */
        if (!strcasecmp(name, "ciphers")) {
            if (*val == 0) {
                msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
-               dsb_simple(why, "4.7.5", "client TLS configuration problem");
-               *site_level = TLS_LEV_INVALID;
-               FREE_RETURN;
+               INVALID_RETURN(tls->why, site_level);
            }
            if (tls->grade) {
                msg_warn("%s: attribute \"%s\" is specified multiple times",
                         WHERE, name);
-               dsb_simple(why, "4.7.5", "client TLS configuration problem");
-               *site_level = TLS_LEV_INVALID;
-               FREE_RETURN;
+               INVALID_RETURN(tls->why, site_level);
            }
            tls->grade = mystrdup(val);
            continue;
@@ -270,34 +292,35 @@ static void tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
            if (tls->protocols) {
                msg_warn("%s: attribute \"%s\" is specified multiple times",
                         WHERE, name);
-               dsb_simple(why, "4.7.5", "client TLS configuration problem");
-               *site_level = TLS_LEV_INVALID;
-               FREE_RETURN;
+               INVALID_RETURN(tls->why, site_level);
            }
            tls->protocols = mystrdup(val);
            continue;
        }
        /* Multiple instances per policy. */
        if (!strcasecmp(name, "match")) {
-           char   *delim = *site_level == TLS_LEV_FPRINT ? "|" : ":";
-
            if (*site_level <= TLS_LEV_ENCRYPT) {
                msg_warn("%s: attribute \"%s\" invalid at security level "
                         "\"%s\"", WHERE, name, policy_name(*site_level));
-               dsb_simple(why, "4.7.5", "client TLS configuration problem");
-               *site_level = TLS_LEV_INVALID;
-               FREE_RETURN;
+               INVALID_RETURN(tls->why, site_level);
            }
            if (*val == 0) {
                msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
-               dsb_simple(why, "4.7.5", "client TLS configuration problem");
-               *site_level = TLS_LEV_INVALID;
-               FREE_RETURN;
+               INVALID_RETURN(tls->why, site_level);
+           }
+           switch (*site_level) {
+           case TLS_LEV_FPRINT:
+               tls_dane_split(tls->dane, TLS_DANE_EE, TLS_DANE_PKEY,
+                              var_smtp_tls_fpt_dgst, val, "|");
+               break;
+           case TLS_LEV_VERIFY:
+           case TLS_LEV_SECURE:
+               if (tls->matchargv == 0)
+                   tls->matchargv = argv_split(val, ":");
+               else
+                   argv_split_append(tls->matchargv, val, ":");
+               break;
            }
-           if (tls->matchargv == 0)
-               tls->matchargv = argv_split(val, delim);
-           else
-               argv_split_append(tls->matchargv, val, delim);
            continue;
        }
        /* Only one instance per policy. */
@@ -305,17 +328,31 @@ static void tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
            if (tls->exclusions) {
                msg_warn("%s: attribute \"%s\" is specified multiple times",
                         WHERE, name);
-               dsb_simple(why, "4.7.5", "client TLS configuration problem");
-               *site_level = TLS_LEV_INVALID;
-               FREE_RETURN;
+               INVALID_RETURN(tls->why, site_level);
            }
            tls->exclusions = vstring_strcpy(vstring_alloc(10), val);
            continue;
        }
+       /* Multiple instances per policy. */
+       if (!strcasecmp(name, "tafile")) {
+           /* Only makes sense if we're using CA-based trust */
+           if (*site_level <= TLS_LEV_ENCRYPT) {
+               msg_warn("%s: attribute \"%s\" invalid at security level"
+                        " \"%s\"", WHERE, name, policy_name(*site_level));
+               INVALID_RETURN(tls->why, site_level);
+           }
+           if (*val == 0) {
+               msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
+               INVALID_RETURN(tls->why, site_level);
+           }
+           if (!tls_dane_load_trustfile(tls->dane, val)) {
+               INVALID_RETURN(tls->why, site_level);
+           }
+           continue;
+       }
+
        msg_warn("%s: invalid attribute name: \"%s\"", WHERE, name);
-       dsb_simple(why, "4.7.5", "client TLS configuration problem");
-       *site_level = TLS_LEV_INVALID;
-       FREE_RETURN;
+       INVALID_RETURN(tls->why, site_level);
     }
 
     FREE_RETURN;
@@ -323,9 +360,9 @@ static void tls_policy_lookup_one(SMTP_TLS_SESS *tls, int *site_level,
 
 /* tls_policy_lookup - look up destination TLS policy */
 
-static void tls_policy_lookup(SMTP_TLS_SESS *tls, int *site_level,
+static void tls_policy_lookup(SMTP_TLS_POLICY *tls, int *site_level,
                                      const char *site_name,
-                                     const char *site_class, DSN_BUF *why)
+                                     const char *site_class)
 {
 
     /*
@@ -336,18 +373,36 @@ static void tls_policy_lookup(SMTP_TLS_SESS *tls, int *site_level,
      * sub-domains of the recipient domain.
      */
     if (!valid_hostname(site_name, DONT_GRIPE)) {
-       tls_policy_lookup_one(tls, site_level, site_name, site_class, why);
+       tls_policy_lookup_one(tls, site_level, site_name, site_class);
        return;
     }
     do {
-       tls_policy_lookup_one(tls, site_level, site_name, site_class, why);
+       tls_policy_lookup_one(tls, site_level, site_name, site_class);
     } while (*site_level == TLS_LEV_NOTFOUND
             && (site_name = strchr(site_name + 1, '.')) != 0);
 }
 
+/* load_tas - load one or more ta files */
+
+static int load_tas(TLS_DANE *dane, const char *files)
+{
+    int     ret = 0;
+    char   *save = mystrdup(files);
+    char   *buf = save;
+    char   *file;
+
+    do {
+       if ((file = mystrtok(&buf, "\t\n\r ,")) != 0)
+           ret = tls_dane_load_trustfile(dane, file);
+    } while (file && ret);
+
+    myfree(save);
+    return (ret);
+}
+
 /* set_cipher_grade - Set cipher grade and exclusions */
 
-static void set_cipher_grade(SMTP_TLS_SESS *tls)
+static void set_cipher_grade(SMTP_TLS_POLICY *tls)
 {
     const char *mand_exclude = "";
     const char *also_exclude = "";
@@ -403,60 +458,35 @@ static void set_cipher_grade(SMTP_TLS_SESS *tls)
     ADD_EXCLUDE(tls->exclusions, also_exclude);
 }
 
-/* smtp_tls_sess_alloc - session TLS policy parameters */
+/* tls_policy_init - initialize policy in an embryonic cache entry */
 
-SMTP_TLS_SESS *smtp_tls_sess_alloc(DSN_BUF *why, const char *dest,
-                                const char *host, unsigned port, int valid)
+static void tls_policy_init(SMTP_TLS_POLICY *tls, const char *dest,
+                                const char *host, unsigned port, int valid)
 {
-    const char *myname = "smtp_tls_sess_alloc";
-    int     global_level;
+    const char *myname = "tls_policy_init";
     int     site_level;
-    SMTP_TLS_SESS *tls = (SMTP_TLS_SESS *) mymalloc(sizeof(*tls));
-
-    tls->level = TLS_LEV_NONE;
-    tls->protocols = 0;
-    tls->grade = 0;
-    tls->exclusions = 0;
-    tls->matchargv = 0;
-
-    if (!dest)
-       return (tls);
 
-    /*
-     * Compute the global TLS policy. This is the default policy level when
-     * no per-site policy exists. It also is used to override a wild-card
-     * per-site policy.
-     */
-    if (*var_smtp_tls_level) {
-       /* Require that var_smtp_tls_level is sanitized upon startup. */
-       global_level = tls_level_lookup(var_smtp_tls_level);
-       if (global_level == TLS_LEV_INVALID)
-           msg_panic("%s: invalid TLS security level: \"%s\"",
-                     myname, var_smtp_tls_level);
-    } else if (var_smtp_enforce_tls) {
-       global_level = var_smtp_tls_enforce_peername ?
-           TLS_LEV_VERIFY : TLS_LEV_ENCRYPT;
-    } else {
-       global_level = var_smtp_use_tls ?
-           TLS_LEV_MAY : TLS_LEV_NONE;
+    /* Caller requested trivial policy */
+    if (!dest) {
+       tls->level = TLS_LEV_NONE;
+       return;
     }
-    if (msg_verbose)
-       msg_info("%s TLS level: %s", "global", policy_name(global_level));
 
     /*
      * Compute the per-site TLS enforcement level. For compatibility with the
      * original TLS patch, this algorithm is gives equal precedence to host
      * and next-hop policies.
      */
+    tls->level = global_tls_level();
     site_level = TLS_LEV_NOTFOUND;
 
     if (tls_policy) {
-       tls_policy_lookup(tls, &site_level, dest, "next-hop destination", why);
+       tls_policy_lookup(tls, &site_level, dest, "next-hop destination");
     } else if (tls_per_site) {
-       tls_site_lookup(tls, &site_level, dest, "next-hop destination", why);
+       tls_site_lookup(tls, &site_level, dest, "next-hop destination");
        if (site_level != TLS_LEV_INVALID
            && strcasecmp(dest, host) != 0)
-           tls_site_lookup(tls, &site_level, host, "server hostname", why);
+           tls_site_lookup(tls, &site_level, host, "server hostname");
 
        /*
         * Override a wild-card per-site policy with a more specific global
@@ -474,18 +504,16 @@ SMTP_TLS_SESS *smtp_tls_sess_alloc(DSN_BUF *why, const char *dest,
         * (non-wildcard) per-site policies consistently override global
         * policies.
         */
-       if (site_level == TLS_LEV_MAY && global_level > TLS_LEV_MAY)
-           site_level = global_level;
+       if (site_level == TLS_LEV_MAY && tls->level > TLS_LEV_MAY)
+           site_level = tls->level;
     }
     switch (site_level) {
-    case TLS_LEV_INVALID:
-       return (smtp_tls_sess_free(tls));
-    case TLS_LEV_NOTFOUND:
-       tls->level = global_level;
-       break;
     default:
        tls->level = site_level;
+    case TLS_LEV_NOTFOUND:
        break;
+    case TLS_LEV_INVALID:
+       return;
     }
 
     /*
@@ -514,36 +542,58 @@ SMTP_TLS_SESS *smtp_tls_sess_alloc(DSN_BUF *why, const char *dest,
     case TLS_LEV_DANE:
        break;
     case TLS_LEV_FPRINT:
-       if (tls->matchargv == 0)
-           tls->matchargv =
-               argv_split(var_smtp_tls_fpt_cmatch, "\t\n\r, |");
+       if (tls->dane == 0)
+           tls->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
+        if (!TLS_DANE_HASEE(tls->dane)) {
+           tls_dane_split(tls->dane, TLS_DANE_EE, TLS_DANE_PKEY,
+                          var_smtp_tls_fpt_dgst, var_smtp_tls_fpt_cmatch,
+                          "\t\n\r, ");
+           if (!TLS_DANE_HASEE(tls->dane)) {
+               msg_warn("nexthop domain %s: configured at fingerprint "
+                        "security level, but with no fingerprints to match.",
+                        dest);
+               MARK_INVALID(tls->why, &tls->level);
+               return;
+           }
+       }
+       tls_dane_final(tls->dane);
        break;
     case TLS_LEV_VERIFY:
-       if (tls->matchargv == 0)
-           tls->matchargv =
-               argv_split(var_smtp_tls_vfy_cmatch, "\t\n\r, :");
-       break;
     case TLS_LEV_SECURE:
        if (tls->matchargv == 0)
            tls->matchargv =
-               argv_split(var_smtp_tls_sec_cmatch, "\t\n\r, :");
+               argv_split(tls->level == TLS_LEV_VERIFY ?
+                          var_smtp_tls_vfy_cmatch : var_smtp_tls_sec_cmatch,
+                          "\t\n\r, :");
+       if (*var_smtp_tls_tafile) {
+           if (tls->dane == 0)
+               tls->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
+           if (!TLS_DANE_HASTA(tls->dane)) {
+               if (load_tas(tls->dane, var_smtp_tls_tafile))
+                   tls_dane_final(tls->dane);
+               else {
+                   MARK_INVALID(tls->why, &tls->level);
+                   return;
+               }
+           }
+       }
        break;
     default:
-       msg_panic("unexpected TLS security level: %d",
-                 tls->level);
+       msg_panic("unexpected TLS security level: %d", tls->level);
     }
 
-    if (msg_verbose && (tls_policy || tls_per_site))
+    if (msg_verbose && tls->level != global_tls_level())
        msg_info("%s TLS level: %s", "effective", policy_name(tls->level));
-
-    return (tls);
 }
 
-/* smtp_sess_tls_free - free and return null pointer of same type */
+/* tls_policy_free - free no longer cached policy */
 
-SMTP_TLS_SESS *smtp_tls_sess_free(SMTP_TLS_SESS *tls)
+void   smtp_tls_policy_free(SMTP_TLS_POLICY *tls)
 {
 
+    if (--tls->refs > 0)
+       return;
+
     if (tls->protocols)
        myfree(tls->protocols);
     if (tls->grade)
@@ -552,9 +602,117 @@ SMTP_TLS_SESS *smtp_tls_sess_free(SMTP_TLS_SESS *tls)
        vstring_free(tls->exclusions);
     if (tls->matchargv)
        argv_free(tls->matchargv);
+    if (tls->dane)
+       tls_dane_free(tls->dane);
+    dsb_free(tls->why);
 
     myfree((char *) tls);
+}
+
+/* policy_create - create embryonic SMTP TLS policy, ctable glue. */
+
+static void *policy_create(const char *key, void *unused_context)
+{
+    SMTP_TLS_POLICY *tls = (SMTP_TLS_POLICY *)mymalloc(sizeof(*tls));
+
+    tls->refs = 1;
+
+    /* First use will update level to a real level or TLS_LEV_INVALID */
+    tls->level = TLS_LEV_NOTFOUND;
+    tls->protocols = 0;
+    tls->grade = 0;
+    tls->exclusions = 0;
+    tls->matchargv = 0;
+    tls->why = dsb_create();
+    tls->dane = 0;
+
+    return ((void *) tls);
+}
+
+/* policy_delete - delete SMTP TLS policy, ctable glue. */
+
+static void policy_delete(void *item, void *unused_context)
+{
+    smtp_tls_policy_free((SMTP_TLS_POLICY *)item);
+}
+
+/* smtp_tls_policy - cached lookup of TLS policy */
+
+SMTP_TLS_POLICY *smtp_tls_policy(DSN_BUF *why, const char *dest,
+                                const char *host, unsigned port, int valid)
+{
+    SMTP_TLS_POLICY *tls;
+    VSTRING *key;
+
+    if (policy_cache == 0)
+       policy_cache =
+           ctable_create(CACHE_SIZE, policy_create, policy_delete, 0);
+
+    if (dest) {
+       key = vstring_alloc(strlen(dest) + strlen(host) + 10);
+       vstring_sprintf(key, "%s %s:%u %d", dest, host, ntohs(port), !!valid);
+       tls = (SMTP_TLS_POLICY *)ctable_locate(policy_cache, vstring_str(key));
+       vstring_free(key);
+    } else {
+       tls = (SMTP_TLS_POLICY *)ctable_locate(policy_cache, "");
+    }
+
+    /* One-time initialization */
+    if (tls->level == TLS_LEV_NOTFOUND)
+       tls_policy_init(tls, dest, host, port, valid);
+
+    if (tls->level != TLS_LEV_INVALID) {
+       ++tls->refs;
+       return (tls);
+    }
+
+    if (why)
+       dsb_update(why,
+                  STR(tls->why->status), STR(tls->why->action),
+                  STR(tls->why->mtype), STR(tls->why->mname),
+                  STR(tls->why->dtype), STR(tls->why->dtext),
+                  "%s", STR(tls->why->reason));
     return (0);
 }
 
+/* smtp_tls_policy_flush - flush TLS policy cache */
+
+void    smtp_tls_policy_flush(void)
+{
+    if (policy_cache == 0)
+       return;
+    ctable_free(policy_cache);
+    policy_cache = 0;
+}
+
+/* global_tls_level - parse and cache var_smtp_tls_level */
+
+static int global_tls_level(void)
+{
+    static int l = TLS_LEV_NOTFOUND;
+
+    if (l != TLS_LEV_NOTFOUND)
+       return l;
+
+    /*
+     * Compute the global TLS policy. This is the default policy level when
+     * no per-site policy exists. It also is used to override a wild-card
+     * per-site policy.
+     *
+     * We require that the global level is valid on startup.
+     */
+    if (*var_smtp_tls_level) {
+       if ((l = tls_level_lookup(var_smtp_tls_level)) == TLS_LEV_INVALID)
+           msg_fatal("invalid tls security level: \"%s\"", var_smtp_tls_level);
+    } else if (var_smtp_enforce_tls)
+       l = var_smtp_tls_enforce_peername ? TLS_LEV_VERIFY : TLS_LEV_ENCRYPT;
+    else
+       l = var_smtp_use_tls ? TLS_LEV_MAY : TLS_LEV_NONE;
+
+    if (msg_verbose)
+       msg_info("%s TLS level: %s", "global", policy_name(l));
+
+    return l;
+}
+
 #endif
index f1565f3bb8ddfa05527839f6097b07dc0eef8617..92bf678180ddcf129a822fb27c41c849369a0b2f 100644 (file)
@@ -1,13 +1,13 @@
 SHELL  = /bin/sh
 SRCS   = tls_prng_dev.c tls_prng_egd.c tls_prng_file.c tls_fprint.c \
        tls_prng_exch.c tls_stream.c tls_bio_ops.c tls_misc.c tls_dh.c \
-       tls_rsa.c tls_verify.c tls_certkey.c tls_session.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
 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_certkey.o tls_session.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
@@ -128,6 +128,21 @@ tls_client.o: ../../include/vstring.h
 tls_client.o: tls.h
 tls_client.o: tls_client.c
 tls_client.o: tls_mgr.h
+tls_dane.o: ../../include/argv.h
+tls_dane.o: ../../include/dns.h
+tls_dane.o: ../../include/msg.h
+tls_dane.o: ../../include/myaddrinfo.h
+tls_dane.o: ../../include/mymalloc.h
+tls_dane.o: ../../include/name_code.h
+tls_dane.o: ../../include/name_mask.h
+tls_dane.o: ../../include/sock_addr.h
+tls_dane.o: ../../include/stringops.h
+tls_dane.o: ../../include/sys_defs.h
+tls_dane.o: ../../include/vbuf.h
+tls_dane.o: ../../include/vstream.h
+tls_dane.o: ../../include/vstring.h
+tls_dane.o: tls.h
+tls_dane.o: tls_dane.c
 tls_dh.o: ../../include/argv.h
 tls_dh.o: ../../include/mail_params.h
 tls_dh.o: ../../include/msg.h
index a89f456ac324c3668e9394fa8cbf745def6c2893..dff4cf566c470d1860684101b82516489b89bf89 100644 (file)
@@ -71,6 +71,96 @@ extern const NAME_CODE tls_level_table[];
 #define TLS_MGR_SCACHE_SMTP    "smtp"
 #define TLS_MGR_SCACHE_LMTP    "lmtp"
 
+ /*
+  * RFC 6698 DANE
+  */
+#define TLS_DANE_TA    0               /* Match trust-anchor digests */
+#define TLS_DANE_EE    1               /* Match end-entity digests */
+
+#define TLS_DANE_CERT  0               /* Match the certificate digest */
+#define TLS_DANE_PKEY  1               /* Match the public key digest */
+
+#define TLS_DANE_FLAG_MIXED    (1<<0)  /* Combined pkeys and certs */
+#define TLS_DANE_FLAG_FINAL    (1<<1)  /* No further changes */
+
+ /*
+  * Linked list of either t = X509 certs or t = EVP_PKEY public keys
+  */
+#define TLS_DANE_L(t)          TLS_DANE_##t##_L
+
+#define DECL_TLS_DANE_L(t) \
+    typedef struct TLS_DANE_L(t) { \
+       t *item; \
+       struct TLS_DANE_L(t) *next; \
+    } TLS_DANE_L(t)
+
+#define TLS_DANE_Z(t)          ((TLS_DANE_L(t) *)0)
+#define TLS_DANE_H(t)          t##_head
+#define TLS_DANE_HEAD(d, t)    ((d) ? (d)->TLS_DANE_H(t) : TLS_DANE_Z(t))
+#define TLS_DANE_MEMB(t)       TLS_DANE_L(t) *TLS_DANE_H(t)
+
+DECL_TLS_DANE_L(X509);
+DECL_TLS_DANE_L(EVP_PKEY);
+
+ /*
+  * Certificate and public key digests (typically from TLSA RRs),
+  * grouped by algorithm.
+  */
+typedef struct TLS_TLSA {
+    char   *mdalg;                     /* Algorithm for this digest list */
+    ARGV   *certs;                     /* Complete certificate digests */
+    ARGV   *pkeys;                     /* SubjectPublicKeyInfo digests */
+    struct TLS_TLSA *next;             /* Chain to next algorithm */
+} TLS_TLSA;
+
+ /*
+  * When TLS_DANE_FLAG_MIXED is set, the pkeys digest list is not allocated
+  * separately, and aliases the certs digest list for each algorithm.
+  */
+typedef struct TLS_DANE {
+    TLS_TLSA *ta;                      /* Trust-anchor cert/pubkey digests */
+    TLS_TLSA *ee;                      /* End-entity cert/pubkey digests */
+    TLS_DANE_MEMB(X509);               /* Full trust-anchor certificates */
+    TLS_DANE_MEMB(EVP_PKEY);           /* Full trust-anchor public keys */
+    int     flags;                     /* Conflate cert and pkey digests */
+} TLS_DANE;
+
+#define TLS_DANE_HASTA(d)      ((d) ? (d)->ta : 0)
+#define TLS_DANE_HASEE(d)      ((d) ? (d)->ee : 0)
+
+#define TLS_DANE_LINSERT(t)    dane_##t##_list_insert
+#define TLS_DANE_LFREE(t)      dane_##t##_list_free
+#define IMPL_TLS_DANE_L(t) \
+    static void TLS_DANE_LINSERT(t)(TLS_DANE *d, t *i) \
+    { \
+       TLS_DANE_L(t) *new = (TLS_DANE_L(t) *) mymalloc(sizeof(*new)); \
+       CRYPTO_add(&i->references, 1, CRYPTO_LOCK_##t); \
+       new->item = i; \
+       new->next = d->TLS_DANE_H(t); \
+       d->TLS_DANE_H(t) = new; \
+    } \
+    static void TLS_DANE_LFREE(t)(TLS_DANE *d) \
+    { \
+       TLS_DANE_L(t) *head; \
+       TLS_DANE_L(t) *next; \
+       for (head = TLS_DANE_HEAD(d, t); head; head = next) { \
+           next = head->next; \
+           t##_free(head->item); \
+       } \
+    }
+
+ /*
+  * tls_dane.c
+  */
+extern int tls_dane_avail(void);
+extern void tls_dane_verbose(int);
+extern TLS_DANE *tls_dane_alloc(int);
+extern void tls_dane_split(TLS_DANE *, int, int, const char *, const char *,
+                          const char *);
+extern TLS_DANE *tls_dane_final(TLS_DANE *);
+extern void tls_dane_free(TLS_DANE *);
+extern int tls_dane_load_trustfile(TLS_DANE *, const char *);
+
  /*
   * TLS session context, also used by the VSTREAM call-back routines for SMTP
   * input/output, and by OpenSSL call-back routines for key verification.
@@ -101,7 +191,11 @@ typedef struct {
     const char *mdalg;                 /* default message digest algorithm */
     /* Built-in vs external SSL_accept/read/write/shutdown support. */
     VSTREAM *stream;                   /* Blocking-mode SMTP session */
+    /* RFC 6698 DANE trust input and verification state */
+    const TLS_DANE *dane;              /* DANE TLSA digests */
+    int     trustdepth;                        /* Chain depth of trusted cert */
     int     errordepth;                        /* Chain depth of error cert */
+    int     chaindepth;                        /* Chain depth of top cert */
     int     errorcode;                 /* First error at error depth */
     X509   *errorcert;                 /* Error certificate closest to leaf */
 } TLS_SESS_STATE;
@@ -256,6 +350,7 @@ typedef struct {
     const char *cipher_exclusions;     /* Ciphers to exclude */
     const ARGV *matchargv;             /* Cert match patterns */
     const char *mdalg;                 /* default message digest algorithm */
+    const TLS_DANE *dane;              /* RFC 6698 verification */
 } TLS_CLIENT_START_PROPS;
 
 extern TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *);
@@ -272,11 +367,11 @@ 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) \
+    a10, a11, a12, a13, a14, a15) \
     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)))
+    ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15), (props)))
 
  /*
   * tls_server.c
@@ -402,6 +497,7 @@ extern RSA *tls_tmp_rsa_cb(SSL *, int, int);
 extern char *tls_peer_CN(X509 *, const TLS_SESS_STATE *);
 extern char *tls_issuer_CN(X509 *, const TLS_SESS_STATE *);
 extern const char *tls_dns_name(const GENERAL_NAME *, const TLS_SESS_STATE *);
+extern int tls_cert_match(TLS_SESS_STATE *, int, X509 *, int);
 extern int tls_verify_certificate_callback(int, X509_STORE_CTX *);
 extern void tls_log_verify_error(TLS_SESS_STATE *);
 
index 2282c448903b63abc75a73380e887fffb82a73d2..9af4b7f3f0c43abb40382e1c222c1a775ffa1c6e 100644 (file)
@@ -595,7 +595,8 @@ static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert,
     if (SSL_get_verify_result(TLScontext->con) == X509_V_OK)
        TLScontext->peer_status |= TLS_CERT_FLAG_TRUSTED;
 
-    if (TLS_CERT_IS_TRUSTED(TLScontext) && props->tls_level >= TLS_LEV_VERIFY)
+    if (TLS_CERT_IS_TRUSTED(TLScontext)
+        && (props->tls_level >= TLS_LEV_VERIFY || TLS_DANE_HASTA(props->dane)))
        verify_peername = 1;
 
     /* Force cert processing so we can log the data? */
@@ -720,26 +721,13 @@ static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert,
 static void verify_extract_print(TLS_SESS_STATE *TLScontext, X509 *peercert,
                                         const TLS_CLIENT_START_PROPS *props)
 {
-    char  **cpp;
-
-    /* Non-null by contract */
     TLScontext->peer_fingerprint = tls_fingerprint(peercert, props->mdalg);
     TLScontext->peer_pkey_fprint = tls_pkey_fprint(peercert, props->mdalg);
 
-    /*
-     * Compare the fingerprint against each acceptable value, ignoring
-     * upper/lower case differences.
-     */
-    if (props->tls_level == TLS_LEV_FPRINT) {
-       for (cpp = props->matchargv->argv; *cpp; ++cpp) {
-           if (strcasecmp(TLScontext->peer_fingerprint, *cpp) == 0
-               || strcasecmp(TLScontext->peer_pkey_fprint, *cpp) == 0) {
-               TLScontext->peer_status |=
-                   TLS_CERT_FLAG_TRUSTED | TLS_CERT_FLAG_MATCHED;
-               break;
-           }
-       }
-    }
+    if (TLS_DANE_HASEE(props->dane)
+       && tls_cert_match(TLScontext, TLS_DANE_EE, peercert, 0))
+           TLScontext->peer_status |=
+               TLS_CERT_FLAG_TRUSTED | TLS_CERT_FLAG_MATCHED;
 }
 
  /*
@@ -764,7 +752,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
      * When certificate verification is required, log trust chain validation
      * errors even when disabled by default for opportunistic sessions.
      */
-    if (props->tls_level >= TLS_LEV_VERIFY)
+    if (props->tls_level >= TLS_LEV_VERIFY || TLS_DANE_HASTA(props->dane))
        log_mask |= TLS_LOG_UNTRUSTED;
 
     if (log_mask & TLS_LOG_VERBOSE)
@@ -814,6 +802,15 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
      * not attempt to re-use sessions whose ciphers are too weak. We salt the
      * session lookup key with the cipher list, so that sessions found in the
      * cache are always acceptable.
+     *
+     * With DANE, (more generally any TLScontext where we specified explicit
+     * trust-anchor or end-entity certificates) the verification status of
+     * the SSL session depends on the specified list.  Since we verify the
+     * certificate only during the initial handshake, we must segregate
+     * sessions with different TA lists.  Note, that TA re-verification
+     * is not possible with cached sessions, since these don't hold the complete
+     * peer trust chain.  Therefore, we compute a digest of the sorted TA
+     * parameters and append it to the serverid.
      */
     myserverid = tls_serverid_digest(props, protomask, cipher_list);
 
@@ -832,6 +829,9 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
     TLScontext->stream = props->stream;
     TLScontext->mdalg = props->mdalg;
 
+    /* Alias DANE digest info from props */
+    TLScontext->dane = props->dane;
+
     if ((TLScontext->con = SSL_new(app_ctx->ssl_ctx)) == NULL) {
        msg_warn("Could not allocate 'TLScontext->con' with SSL_new()");
        tls_print_errors();
@@ -973,9 +973,11 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
        /*
         * Peer name or fingerprint verification as requested.
         * Unconditionally set peer_CN, issuer_CN and peer_fingerprint.
+        * Check fingerprint first, and avoid logging verified as untrusted
+        * in the call to verify_extract_name().
         */
-       verify_extract_name(TLScontext, peercert, props);
        verify_extract_print(TLScontext, peercert, props);
+       verify_extract_name(TLScontext, peercert, props);
 
        if (TLScontext->log_mask &
            (TLS_LOG_CERTMATCH | TLS_LOG_VERBOSE | TLS_LOG_PEERCERT))
diff --git a/postfix/src/tls/tls_dane.c b/postfix/src/tls/tls_dane.c
new file mode 100644 (file)
index 0000000..2694259
--- /dev/null
@@ -0,0 +1,458 @@
+/*++
+/* NAME
+/*     tls_dane 3
+/* SUMMARY
+/*     Support for RFC 6698 (DANE) certificate matching
+/* SYNOPSIS
+/*     #include <tls.h>
+/*
+/*     int     tls_dane_avail()
+/*
+/*     void    tls_dane_verbose(on)
+/*     int     on;
+/*
+/*     TLS_DANE *tls_dane_alloc(flags)
+/*     int     flags;
+/*
+/*     void    tls_dane_free(dane)
+/*     TLS_DANE *dane;
+/*
+/*     void    tls_dane_split(dane, certusage, selector, mdalg, digest, delim)
+/*     TLS_DANE *dane;
+/*     int     certusage;
+/*     int     selector;
+/*     const char *mdalg;
+/*     const char *digest;
+/*     const char *delim;
+/*
+/*     int     tls_dane_load_trustfile(dane, tafile)
+/*     TLS_DANE *dane;
+/*     const char *tafile;
+/*
+/*     TLS_DANE *tls_dane_final(dane)
+/*     TLS_DANE *dane;
+/* DESCRIPTION
+/*     tls_dane_avail() returns true if the features required to support DANE
+/*     are present in OpenSSL's libcrypto and in libresolv.  Since OpenSSL's
+/*     libcrypto is not initialized until we call tls_client_init(), calls
+/*     to tls_dane_avail() must be deferred until this initialization is
+/*     completed successufully.
+/*
+/*     tls_dane_verbose() turns on verbose logging of TLSA record lookups.
+/*
+/*     tls_dane_alloc() returns a pointer to a newly allocated TLS_DANE
+/*     structure with null ta and ee digest sublists.  If "flags" includes
+/*     TLS_DANE_FLAG_MIXED, the cert and pkey digests are stored together on
+/*     the pkeys list with the certs list empty, otherwise they are stored
+/*     separately.
+/*
+/*     tls_dane_free() frees the structure allocated by tls_dane_alloc().
+/*
+/*     tls_dane_split() splits "digest" using the characters in "delim" as
+/*     delimiters and stores the results with the requested "certusage"
+/*     and "selector".  This is an incremental interface, that builds a
+/*     TLS_DANE structure outside the cache by manually adding entries.
+/*     Once all the entries have been added, the caller must call
+/*     tls_dane_final() to complete its construction.
+/*
+/*     tls_dane_load_trustfile() imports trust-anchor certificates and
+/*     public keys from a file (rather than DNS TLSA records).
+/*
+/*     tls_dane_final() completes the construction of a TLS_DANE structure,
+/*     obtained via tls_dane_alloc() and populated via tls_dane_split() or
+/*     tls_dane_load_trustfile().  After tls_dane_final() is called, the
+/*     structure must not be modified.  The return value is the input
+/*     argument.
+/*
+/*     Arguments:
+/* .IP dane
+/*     Pointer to a TLS_DANE structure that lists the valid trust-anchor
+/*     and end-entity full-certificate and/or public-key digests.
+/* .IP host
+/*     DNSSEC validated (cname resolved) FQDN of target service.
+/* .IP proto
+/*     Almost certainly "tcp".
+/* .IP port
+/*     The TCP port in network byte order.
+/* .IP flags
+/*     Only one flag is part of the public interface at this time:
+/* .RS
+/* .IP TLS_DANE_FLAG_MIXED
+/*     Don't distinguish between certificate and public-key digests.
+/*     A single digest list for both certificates and keys with be
+/*     stored for each algorithm in the "pkeys" field, the "certs"
+/*     field will be null.
+/* .RE
+/* .IP certusage
+/*     Trust anchor (TLS_DANE_TA) or end-entity (TLS_DANE_EE) digests?
+/* .IP selector
+/*     Full certificate (TLS_DANE_CERT) or pubkey (TLS_DANE_PKEY) digests?
+/* .IP mdalg
+/*     Name of a message digest algorithm suitable for computing secure
+/*     (1st pre-image resistant) message digests of certificates. For now,
+/*     md5, sha1, or member of SHA-2 family if supported by OpenSSL.
+/* .IP digest
+/*     The digest (or list of digests concatenated with characters from
+/*     "delim") to be added to the TLS_DANE record.
+/* .IP delim
+/*     The set of delimiter characters used above.
+/* LICENSE
+/* .ad
+/* .fi
+/*     This software is free. You can do with it whatever you want.
+/*     The original author kindly requests that you acknowledge
+/*     the use of his software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*
+/*     Viktor Dukhovni
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#ifdef USE_TLS
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <vstring.h>
+
+#define STR(x) vstring_str(x)
+
+/* DNS library. */
+
+#include <dns.h>
+
+/* TLS library. */
+
+#define TLS_INTERNAL
+#include <tls.h>
+
+/* Application-specific. */
+
+static const char *sha256 = "sha256";
+static const char *sha512 = "sha512";
+static int sha256len;
+static int sha512len;
+static int dane_verbose;
+static TLS_TLSA **dane_locate(TLS_TLSA **, const char *);
+
+IMPL_TLS_DANE_L(X509)
+IMPL_TLS_DANE_L(EVP_PKEY)
+
+/* tls_dane_verbose - enable/disable verbose logging */
+
+void   tls_dane_verbose(int on)
+{
+    dane_verbose = on;
+}
+
+/* tls_dane_avail - check for availability of dane required digests */
+
+int tls_dane_avail(void)
+{
+    static int avail = -1;
+    const EVP_MD *sha256md;
+    const EVP_MD *sha512md;
+
+    if (avail >= 0)
+       return avail;
+
+    sha256md = EVP_get_digestbyname(sha256);
+    sha512md = EVP_get_digestbyname(sha512);
+
+    if (sha256md == 0 || sha512md == 0
+       || RES_USE_DNSSEC == 0 || RES_USE_EDNS0 == 0)
+       return (avail = 0);
+
+    sha256len = EVP_MD_size(sha256md);
+    sha512len = EVP_MD_size(sha512md);
+
+    return (avail = 1);
+}
+
+/* tls_dane_alloc - allocate a TLS_DANE structure */
+
+TLS_DANE *tls_dane_alloc(int flags)
+{
+    TLS_DANE *dane = (TLS_DANE *)mymalloc(sizeof(*dane));
+
+    dane->ta = 0;
+    dane->ee = 0;
+    dane->TLS_DANE_H(X509) = 0;
+    dane->TLS_DANE_H(EVP_PKEY) = 0;
+    dane->flags = flags;
+    return (dane);
+}
+
+static void tlsa_free(TLS_TLSA *tlsa)
+{
+
+    myfree(tlsa->mdalg);
+    if (tlsa->certs)
+       argv_free(tlsa->certs);
+    if (tlsa->pkeys)
+       argv_free(tlsa->pkeys);
+    myfree((char *)tlsa);
+}
+
+/* tls_dane_free - free a TLS_DANE structure */
+
+void   tls_dane_free(TLS_DANE *dane)
+{
+    TLS_TLSA *tlsa;
+    TLS_TLSA *next;
+
+    /* De-allocate TA and EE lists */
+    for (tlsa = dane->ta; tlsa; tlsa = next) {
+       next = tlsa->next;
+       tlsa_free(tlsa);
+    }
+    for (tlsa = dane->ee; tlsa; tlsa = next) {
+       next = tlsa->next;
+       tlsa_free(tlsa);
+    }
+    TLS_DANE_LFREE(X509)(dane);
+    TLS_DANE_LFREE(EVP_PKEY)(dane);
+    myfree((char *)dane);
+}
+
+/* dane_free - ctable style */
+
+static void dane_free(void *dane, void *unused_context)
+{
+    tls_dane_free((TLS_DANE *)dane);
+}
+
+/* tlsa_sort - sort digests for a single certusage */
+
+static void tlsa_sort(TLS_TLSA *tlsa)
+{
+    for (; tlsa; tlsa = tlsa->next) {
+       if (tlsa->pkeys)
+           argv_sort(tlsa->pkeys);
+       if (tlsa->certs)
+           argv_sort(tlsa->certs);
+    }
+}
+
+/* tls_dane_final - finish by sorting into canonical order */
+
+TLS_DANE *tls_dane_final(TLS_DANE *dane)
+{
+    /*
+     * We only sort the trust anchors, see tls_serverid_digest().
+     */
+    if (dane->ta)
+       tlsa_sort(dane->ta);
+    dane->flags |= TLS_DANE_FLAG_FINAL;
+    return (dane);
+}
+
+/* dane_locate - list head address of TLSA sublist for given algorithm */
+
+static TLS_TLSA **dane_locate(TLS_TLSA **tlsap, const char *mdalg)
+{
+    TLS_TLSA *new;
+
+    /*
+     * Correct computation of the session cache serverid requires a TLSA
+     * digest list that is sorted by algorithm name.  Below we maintain
+     * the sort order (by algorithm name canonicalized to lowercase).
+     */
+    for (; *tlsap; tlsap = &(*tlsap)->next) {
+       int     cmp = strcasecmp(mdalg, (*tlsap)->mdalg);
+
+       if (cmp == 0)
+           return (tlsap);
+       if (cmp < 0)
+           break;
+    }
+
+    new = (TLS_TLSA *)mymalloc(sizeof(*new));
+    new->mdalg = lowercase(mystrdup(mdalg));
+    new->certs = 0;
+    new->pkeys = 0;
+    new->next = *tlsap;
+    *tlsap = new;
+
+    return (tlsap);
+}
+
+/* tls_dane_split - split and append digests */
+
+void tls_dane_split(TLS_DANE *dane, int certusage, int selector,
+                   const char *mdalg, const char *digest, const char *delim)
+{
+    TLS_TLSA **tlsap;
+    TLS_TLSA *tlsa;
+    ARGV  **argvp;
+
+    if (dane->flags & TLS_DANE_FLAG_FINAL)
+       msg_panic("updating frozen TLS_DANE object");
+
+    tlsap = (certusage == TLS_DANE_EE) ? &dane->ee : &dane->ta;
+    tlsa = *(tlsap = dane_locate(tlsap, mdalg));
+    argvp = ((dane->flags & TLS_DANE_FLAG_MIXED) || selector == TLS_DANE_PKEY) ?
+       &tlsa->pkeys : &tlsa->certs;
+
+    /* Delimited append, may append nothing */
+    if (*argvp == 0)
+       *argvp = argv_split(digest, delim);
+    else
+       argv_split_append(*argvp, digest, delim);
+
+    if ((*argvp)->argc == 0) {
+       argv_free(*argvp);
+       *argvp = 0;
+
+       /* Remove empty elements from the list */
+       if (tlsa->pkeys == 0 && tlsa->certs == 0) {
+           *tlsap = tlsa->next;
+           tlsa_free(tlsa);
+       }
+    }
+}
+
+/* dane_add - add a digest entry */
+
+static void dane_add(TLS_DANE *dane, int certusage, int selector,
+                    const char *mdalg, char *digest)
+{
+    TLS_TLSA **tlsap;
+    TLS_TLSA *tlsa;
+    ARGV  **argvp;
+
+    if (dane->flags & TLS_DANE_FLAG_FINAL)
+       msg_panic("updating frozen TLS_DANE object");
+
+    switch (certusage) {
+    case DNS_TLSA_USAGE_CA_CONSTRAINT:
+    case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION:
+       certusage = TLS_DANE_TA;        /* Collapse 0/2 -> 2 */
+       break;
+    case DNS_TLSA_USAGE_SERVICE_CERTIFICATE_CONSTRAINT:
+    case DNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE:
+       certusage = TLS_DANE_EE;        /* Collapse 1/3 -> 3 */
+       break;
+    }
+
+    switch (selector) {
+    case DNS_TLSA_SELECTOR_FULL_CERTIFICATE:
+       selector = TLS_DANE_CERT;
+       break;
+    case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO:
+       selector = TLS_DANE_PKEY;
+       break;
+    }
+
+    tlsap = (certusage == TLS_DANE_EE) ? &dane->ee : &dane->ta;
+    tlsa = *(tlsap = dane_locate(tlsap, mdalg));
+    argvp = ((dane->flags & TLS_DANE_FLAG_MIXED) || selector == TLS_DANE_PKEY) ?
+       &tlsa->pkeys : &tlsa->certs;
+
+    if (*argvp == 0)
+       *argvp = argv_alloc(1);
+    argv_add(*argvp, digest, ARGV_END);
+}
+
+/* tls_dane_load_trustfile - load trust anchor certs or keys from file */
+
+int    tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile)
+{
+    FILE   *fp;
+    char   *name = 0;
+    char   *header = 0;
+    long    error = 0;
+    long    len;
+    int     ret = 0;
+    unsigned char *data = 0;
+
+    /* nop */
+    if (tafile == 0 || *tafile == 0)
+       return (1);
+
+    if ((fp = fopen(tafile, "r")) == NULL) {
+       msg_warn("error opening trust anchor file: %s: %m", tafile);
+       return (0);
+    }
+
+    /* Don't report old news */
+    ERR_clear_error();
+
+    while (PEM_read(fp, &name, &header, &data, &len)) {
+       const unsigned char *p = data;
+       int     selector = 0;
+
+       if (strcmp(name, PEM_STRING_X509) == 0
+           || strcmp(name, PEM_STRING_X509_OLD) == 0) {
+           X509   *cert = d2i_X509(0, &p, len);
+
+           if (!(ret = (cert && (p - data) == len))) {
+               msg_warn("error reading: %s: malformed trust-anchor %s",
+                        "certificate", tafile);
+               tls_print_errors();
+               break;
+           }
+           TLS_DANE_LINSERT(X509)(dane, cert);
+           X509_free(cert);
+           selector = DNS_TLSA_SELECTOR_FULL_CERTIFICATE;
+       } else if (strcmp(name, PEM_STRING_PUBLIC) == 0) {
+           EVP_PKEY *pkey = d2i_PUBKEY(0, &p, len);
+
+           if (!(ret = (pkey && (p - data) == len))) {
+               msg_warn("error reading: %s: malformed trust-anchor %s",
+                        "public key", tafile);
+               tls_print_errors();
+               break;
+           }
+           TLS_DANE_LINSERT(EVP_PKEY)(dane, pkey);
+           EVP_PKEY_free(pkey);
+           selector = DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO;
+       }
+
+       if (ret) {
+           int     usage = DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION;
+           char   *digest = tls_fprint((char *)data, len, sha256);
+
+           dane_add(dane, usage, selector, sha256, digest);
+           myfree(digest);
+       }
+       if (name)
+           OPENSSL_free(name);
+       if (header)
+           OPENSSL_free(header);
+       if (data)
+           OPENSSL_free(data);
+       name = header = (char *) (data = 0);
+    }
+
+    if (fclose(fp) == EOF) {
+       msg_warn("error reading trust anchor file: %s: %m", tafile);
+       return (0);
+    }
+
+    switch(ERR_GET_REASON(ERR_peek_last_error())) {
+    case PEM_R_NO_START_LINE:
+       ERR_clear_error();
+       break;
+    default:   /* Something to report */
+       tls_print_errors();
+    case 0:    /* All errors reported */
+       ret = 0;
+       break;
+    }
+    return (ret);
+}
+
+#endif
index 182b71b6e313349120197e8cc674688fb3855b48..2fddbe05c56e0b42a410dc9c48a7bae5769e85a4 100644 (file)
@@ -76,8 +76,9 @@
 /* .IP len
 /*     The length of the input data.
 /* .IP props
-/*     The client start properties for the session, which include the
-/*     initial serverid from the SMTP client.
+/*     The client start properties for the session, which contains the
+/*     initial serverid from the SMTP client and the DANE verification
+/*     parameters.
 /* .IP protomask
 /*     The mask of protocol exclusions.
 /* .IP ciphers
@@ -129,6 +130,34 @@ static const char hexcodes[] = "0123456789ABCDEF";
 #define digestptr(p) digestpl((p), sizeof(*(p)))
 #define digeststr(s) digestpl((s), strlen(s)+1)
 
+#define digestdane(dane, memb) do { \
+       if ((dane)->memb != 0) \
+           chknonzero(tlsa_digest(mdctx, (dane)->memb, #memb)); \
+    } while (0)
+
+#define digesttlsa(tlsa, memb) do { \
+       if ((tlsa)->memb) { \
+           digeststr(#memb); \
+           for (dgst = (tlsa)->memb->argv; *dgst; ++dgst) \
+               digeststr(*dgst); \
+       } \
+    } while (0)
+
+/* tlsa_digest - digest a pre-sorted by caller TLSA match list */
+
+static int tlsa_digest(EVP_MD_CTX *mdctx, TLS_TLSA *tlsa, const char *usage)
+{
+    char  **dgst;
+    int     ok = 1;
+
+    for (digeststr(usage); tlsa; tlsa = tlsa->next) {
+       digeststr(tlsa->mdalg);
+       digesttlsa(tlsa, pkeys);
+       digesttlsa(tlsa, certs);
+    }
+    return (ok);
+}
+
 /* tls_serverid_digest - suffix props->serverid with parameter digest */
 
 char   *tls_serverid_digest(const TLS_CLIENT_START_PROPS *props, long protomask,
@@ -166,6 +195,37 @@ char   *tls_serverid_digest(const TLS_CLIENT_START_PROPS *props, long protomask,
     digestptr(&sslversion);
     digestptr(&protomask);
     digeststr(ciphers);
+
+    /*
+     * All we get from the session cache is a single bit telling us whether
+     * the certificate is trusted or not, but we need to know whether the
+     * trust is CA-based (in that case we must do name checks) or whether it
+     * is a direct end-point match.  We mustn't confuse the two, so it is best
+     * to process only TA trust in the verify callback and check the EE trust
+     * after. This works since re-used sessions always have access to the leaf
+     * certificate, while only the original session has the leaf and the full
+     * trust chain.
+     *
+     * Only the trust anchor matchlist is hashed into the session key.
+     * The end entity certs are not used to determine whether a certificate
+     * is trusted or not, rather these are rechecked against the leaf cert
+     * outside the verification callback, each time a session is created or
+     * reused.
+     *
+     * Therefore, the security context of the session does not depend on the
+     * EE matching data, which is checked separately each time.  So we exclude
+     * the EE part of the DANE structure from the serverid digest.
+     *
+     * If this changes, also update tls_dane_final() in tls_dane.c.
+     */
+    if (props->dane) {
+       int     mixed = (props->dane->flags & TLS_DANE_FLAG_MIXED);
+       digestptr(&mixed);
+       digestdane(props->dane, ta);
+#if 0
+       digestdane(props->dane, ee);            /* See above */
+#endif
+    }
     chknonzero(EVP_DigestFinal_ex(mdctx, md_buf, &md_len));
     EVP_MD_CTX_destroy(mdctx);
     if (!ok)
index 8143da29607bafe474861414b3d778214aee0b62..ef7e0f817bae03ac7462b64de5dc7aff68c2903f 100644 (file)
@@ -746,7 +746,10 @@ TLS_SESS_STATE *tls_alloc_sess_context(int log_mask, const char *namaddr)
     TLScontext->cipher_name = 0;
     TLScontext->log_mask = log_mask;
     TLScontext->namaddr = lowercase(mystrdup(namaddr));
-    TLScontext->mdalg = 0;                     /* Alias for props->mdalg */
+    TLScontext->mdalg = 0;             /* Alias for props->mdalg */
+    TLScontext->dane = 0;              /* Alias for client props->dane */
+    TLScontext->trustdepth = -1;
+    TLScontext->chaindepth = -1;
     TLScontext->errordepth = -1;
     TLScontext->errorcode = X509_V_OK;
     TLScontext->errorcert = 0;
index f3daf2a5c31a93da53465bdefe3e87e7dd6bcefa..44b9b62c40db9ccbda6fab69874131c9207d5a6e 100644 (file)
@@ -7,6 +7,12 @@
 /*     #define TLS_INTERNAL
 /*     #include <tls.h>
 /*
+/*     int     tls_cert_match(TLSContext, usage, cert, depth)
+/*     TLS_SESS_STATE *TLScontext;
+/*     int     usage;
+/*     X509    *cert;
+/*     int     depth;
+/*
 /*     int     tls_verify_certificate_callback(ok, ctx)
 /*     int     ok;
 /*     X509_STORE_CTX *ctx;
 /*     const GENERAL_NAME *gn;
 /*     TLS_SESS_STATE *TLScontext;
 /* DESCRIPTION
+/*     tls_cert_match() matches the full and/or public key digest of
+/*     "cert" against each candidate digest in TLScontext->dane. If usage
+/*     is TLS_DANE_EE, the match is against end-entity digests, otherwise
+/*     it is against trust-anchor digests.  Returns true if a match is found,
+/*     false otherwise.
+/*
 /*     tls_verify_certificate_callback() is called several times (directly
 /*     or indirectly) from crypto/x509/x509_vfy.c. It collects errors
 /*     and trust information at each element of the trust chain.
 /* .IP gn
 /*     An OpenSSL GENERAL_NAME structure holding a DNS subjectAltName
 /*     to be decoded and checked for validity.
+/* .IP usage
+/*     Trust anchor (TLS_DANE_TA) or end-entity (TLS_DANE_EE) digests?
+/* .IP cert
+/*     Certificate from peer trust chain (CA or leaf server).
+/* .IP depth
+/*     The certificate depth for logging.
 /* .IP peercert
 /*     Server or client X.509 certificate.
 /* .IP TLScontext
@@ -126,7 +144,8 @@ static void update_error_state(TLS_SESS_STATE *TLScontext, int depth,
                                       X509 *errorcert, int errorcode)
 {
     /* No news is good news */
-    if (TLScontext->errordepth >= 0 && TLScontext->errordepth <= depth)
+    if ((TLScontext->trustdepth >= 0 && TLScontext->trustdepth < depth) ||
+       (TLScontext->errordepth >= 0 && TLScontext->errordepth <= depth))
        return;
 
     /*
@@ -141,6 +160,169 @@ static void update_error_state(TLS_SESS_STATE *TLScontext, int depth,
        CRYPTO_add(&errorcert->references, 1, CRYPTO_LOCK_X509);
     TLScontext->errorcert = errorcert;
     TLScontext->errorcode = errorcode;
+
+    /*
+     * Maintain an invariant, at most one of errordepth and trustdepth
+     * is non-negative at any given time.
+     */
+    TLScontext->errordepth = depth;
+    TLScontext->trustdepth = -1;
+}
+
+/* update_trust_state - safely stash away trust state */
+
+static void update_trust_state(TLS_SESS_STATE *TLScontext, int depth)
+{
+    /* No news is bad news */
+    if ((TLScontext->trustdepth >= 0 && TLScontext->trustdepth <= depth)
+        || (TLScontext->errordepth >= 0 && TLScontext->errordepth <= depth))
+       return;
+
+    /*
+     * Maintain an invariant, at most one of errordepth and trustdepth
+     * is non-negative at any given time.
+     */
+    TLScontext->trustdepth = depth;
+    TLScontext->errordepth = -1;
+}
+
+/* tls_cert_match - match cert against given list of TA or EE digests */
+
+int tls_cert_match(TLS_SESS_STATE *TLScontext, int usage, X509 *cert, int depth)
+{
+    const TLS_DANE *dane = TLScontext->dane;
+    TLS_TLSA *tlsa = (usage == TLS_DANE_EE) ? dane->ee : dane->ta;
+    const char *namaddr = TLScontext->namaddr;
+    const char *ustr = (usage == TLS_DANE_EE) ? "end entity" : "trust anchor";
+    int            mixed = (dane->flags & TLS_DANE_FLAG_MIXED);
+    int     matched;
+
+    for (matched = 0; tlsa && !matched; tlsa = tlsa->next) {
+       char  **dgst;
+       ARGV   *certs;
+
+       if (tlsa->pkeys) {
+           char   *pkey_dgst = tls_pkey_fprint(cert, tlsa->mdalg);
+           for (dgst = tlsa->pkeys->argv; !matched && *dgst; ++dgst)
+               if (strcasecmp(pkey_dgst, *dgst) == 0)
+                   matched = 1;
+           if (TLScontext->log_mask & (TLS_LOG_VERBOSE|TLS_LOG_CERTMATCH))
+               msg_info("%s: depth=%d matched=%d %s public-key %s digest=%s",
+                        namaddr, depth, matched, ustr, tlsa->mdalg, pkey_dgst);
+           myfree(pkey_dgst);
+       }
+
+       certs = mixed ? tlsa->pkeys : tlsa->certs;
+       if (certs != 0 && !matched) {
+           char   *cert_dgst = tls_fingerprint(cert, tlsa->mdalg);
+           for (dgst = certs->argv; !matched && *dgst; ++dgst)
+               if (strcasecmp(cert_dgst, *dgst) == 0)
+                   matched = 1;
+           if (TLScontext->log_mask & (TLS_LOG_VERBOSE|TLS_LOG_CERTMATCH))
+               msg_info("%s: depth=%d matched=%d %s certificate %s digest %s",
+                        namaddr, depth, matched, ustr, tlsa->mdalg, cert_dgst);
+           myfree(cert_dgst);
+       }
+    }
+
+    return (matched);
+}
+
+/* ta_match - match cert against out-of-band TA keys or digests */
+
+static int ta_match(TLS_SESS_STATE *TLScontext, X509_STORE_CTX *ctx,
+                   X509 *cert, int depth, int expired)
+{
+    const TLS_DANE *dane = TLScontext->dane;
+    int     matched = tls_cert_match(TLScontext, TLS_DANE_TA, cert, depth);
+
+    /*
+     * If we are the TA, the first trusted certificate is one level below!
+     * As a degenerate case a self-signed TA at depth 0 is also treated as
+     * a TA validated trust chain, (even if the certificate is expired).
+     *
+     * Note: OpenSSL will flag an error when the chain contains just one
+     * certificate that is not self-issued.
+     */
+    if (matched) {
+       if (--depth < 0)
+           depth = 0;
+       update_trust_state(TLScontext, depth);
+       return (1);
+    }
+
+    /*
+     * If expired, no need to check for a trust-anchor signature.  The TA
+     * itself is matched by its digest, so we're at best looking at some
+     * other expired certificate issued by the TA, which we don't accept.
+     */
+    if (expired)
+       return (0);
+
+    /*
+     * Compute the index of the topmost chain certificate; it may need to be
+     * verified via one of our out-of-band trust-anchors.  Since we're here,
+     * the chain contains at least one certificate.
+     *
+     * Optimization: if the top is self-issued, we don't need to try to check
+     * whether it is signed by any ancestor TAs.  If it is trusted, it will be
+     * matched by its fingerprint.
+     */
+    if (TLScontext->trustdepth < 0 && TLScontext->chaindepth < 0) {
+       STACK_OF(X509) *chain = X509_STORE_CTX_get_chain(ctx);
+       int     i = sk_X509_num(chain) - 1;
+       X509   *top = sk_X509_value(chain, i);
+
+       if (X509_check_issued(top, top) == X509_V_OK)
+           TLScontext->chaindepth = i + 1;
+       else
+           TLScontext->chaindepth = i;
+    }
+
+    /*
+     * Last resort, check whether signed by out-of-band TA public key.
+     *
+     * Only the top certificate of the server chain needs this logic, since
+     * any certs below are signed by their parent, which we checked against
+     * the TA list more cheaply.  Do this at most once (by incrementing the
+     * depth when we're done).
+     */
+    if (depth == TLScontext->chaindepth) {
+       TLS_DANE_L(EVP_PKEY) *k;
+       TLS_DANE_L(X509) *x;
+
+       /*
+        * First check whether issued and signed by a TA cert, this is cheaper
+        * than the bare-public key checks below, since we can determine
+        * whether the candidate TA certificate issued the certificate
+        * to be checked first (name comparisons), before we bother with
+        * signature checks (public key operations).
+        */
+       for (x = TLS_DANE_HEAD(dane, X509); !matched && x; x = x->next) {
+           if (X509_check_issued(x->item, cert) == X509_V_OK) {
+               EVP_PKEY *pk = X509_get_pubkey(x->item);
+               matched = pk && X509_verify(cert, pk) > 0;
+               EVP_PKEY_free(pk);
+           }
+       }
+
+       /*
+        * With bare TA public keys, we can't check whether the trust chain
+        * is issued by the key, but we can determine whether it is signed
+        * by the key, so we go with that.  Ideally, the corresponding
+        * certificate was presented in the chain, and we matched it by
+        * its public key digest one level up.  This code is here to handle
+        * adverse conditions imposed by sloppy administrators of receiving
+        * systems with poorly constructed chains.
+        */
+       for (k = TLS_DANE_HEAD(dane, EVP_PKEY); !matched && k; k = k->next)
+           matched = X509_verify(cert, k->item) > 0;
+
+       if (matched)
+           update_trust_state(TLScontext, depth);
+       ++TLScontext->chaindepth;
+    }
+    return (matched);
 }
 
 /* tls_verify_certificate_callback - verify peer certificate info */
@@ -193,18 +375,92 @@ int     tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx)
     if (max_depth >= 0 && depth > max_depth) {
        update_error_state(TLScontext, depth, cert,
                           X509_V_ERR_CERT_CHAIN_TOO_LONG);
-       ok = 0;
+       return (1);
+    }
+
+    /*
+     * Per RFC 5280 and its upstream ITU documents, a trust anchor is just
+     * a public key, no more no less, and thus certificates bearing the
+     * trust-anchor public key are just public keys in X.509v3 garb.  Any
+     * meaning attached to their expiration, ... is simply local policy.
+     *
+     * We don't punish server administrators for including an expired optional
+     * TA certificate in their chain.  Had they left it out, and provided us
+     * instead with only the TA public-key via a "2 1 0" TLSA record, there'd
+     * be no TA certificate from which to learn the expiration dates.
+     *
+     * Therefore, in the interests of consistent behavior, we only enforce
+     * expiration dates BELOW the TA signature.  When we find an expired
+     * certificate, we only check whether it is a TA, and not whether it is
+     * signed by a TA.
+     *
+     * Other than allowing TA certificate expiration, the only errors we
+     * allow are failure to chain to a trusted root.  Our TA set includes
+     * out-of-band data not available to the X509_STORE_CTX.
+     *
+     * More than one of the allowed errors may be reported at a given depth,
+     * trap all instances, but run the matching code at most once.  If the
+     * current cert is ok, we have a trusted ancestor, and we're not verbose,
+     * don't bother with matching.
+     */
+    if (cert != 0
+       && (ok == 0
+           || TLScontext->trustdepth < 0
+           || (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH)))
+       && TLS_DANE_HASTA(TLScontext->dane)
+       && (TLScontext->trustdepth == -1 || depth <= TLScontext->trustdepth)
+       && (TLScontext->errordepth == -1 || depth < TLScontext->errordepth)) {
+       int     expired = 0; /* or not yet valid */
+
+       switch (ok ? X509_V_OK : err) {
+       case X509_V_ERR_CERT_NOT_YET_VALID:
+       case X509_V_ERR_CERT_HAS_EXPIRED:
+           expired = 1;
+           /* FALLTHROUGH */
+       case X509_V_OK:
+       case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+       case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+       case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+       case X509_V_ERR_CERT_UNTRUSTED:
+           if ((!expired && depth == TLScontext->trustdepth)
+               || ta_match(TLScontext, ctx, cert, depth, expired))
+               ok = 1;
+           break;
+       }
     }
     if (ok == 0)
        update_error_state(TLScontext, depth, cert, err);
 
     /*
-     * The final depth zero call sets the verification status.
+     * Perhaps the chain is verified, or perhaps we'll get called again,
+     * either way the best we know is that if trust depth is below error
+     * depth we win and otherwise we lose. Set the error state accordingly.
+     *
+     * If we are given explicit TA match list, we must match one of them
+     * at a non-negative depth below any errors, otherwise we just need
+     * no errors.
      */
     if (depth == 0) {
-       ok = TLScontext->errordepth < 0 ? 1 : 0;
+       ok = 0;
+        if (TLScontext->trustdepth < 0 && TLS_DANE_HASTA(TLScontext->dane)) {
+           /* Required Policy or DANE certs not present */
+           if (TLScontext->errordepth < 0) {
+               /*
+                * For lack of a better choice log the trust problem against
+                * the leaf cert when PKI says yes, but local policy or DANE
+                * says no. Logging a root cert as untrusted would far more
+                * likely confuse users!
+                */
+               update_error_state(TLScontext, depth, cert,
+                                  X509_V_ERR_CERT_UNTRUSTED);
+           }
+       } else if (TLScontext->errordepth < 0) {
+           /* No PKI trust errors, or only above a good policy or DANE CA. */
+           ok = 1;
+       }
        X509_STORE_CTX_set_error(ctx, ok ? X509_V_OK : TLScontext->errorcode);
     }
+
     if (TLScontext->log_mask & TLS_LOG_VERBOSE) {
        if (cert)
            X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
index 7a0ce1f152b98df4dadb5fbaf5b8698facc0950f..35bb56db5d5cc7d84cc26cfebb915bd0e749a1e0 100644 (file)
@@ -9,6 +9,9 @@
 /*     ARGV    *argv_alloc(len)
 /*     ssize_t len;
 /*
+/*     ARGV    *argv_sort(argvp)
+/*     ARGV    *argvp;
+/*
 /*     ARGV    *argv_free(argvp)
 /*     ARGV    *argvp;
 /*
@@ -56,6 +59,9 @@
 /*     length. The result is ready for use by argv_add(). The array
 /*     is null terminated.
 /*
+/*     argv_sort() sorts the elements of argvp in place returning
+/*     the original array.
+/*
 /*     argv_add() copies zero or more strings and adds them to the
 /*     specified string array. The array is null terminated.
 /*     Terminate the argument list with a null pointer. The manifest
@@ -148,6 +154,21 @@ ARGV   *argv_alloc(ssize_t len)
     return (argvp);
 }
 
+static int argv_cmp(const void *e1, const void *e2)
+{
+    const char *s1 = *(const char **)e1;
+    const char *s2 = *(const char **)e2;
+    return strcmp(s1, s2);
+}
+
+/* argv_sort - sort array in place */
+
+ARGV   *argv_sort(ARGV *argvp)
+{
+    qsort(argvp->argv, argvp->argc, sizeof(argvp->argv[0]), argv_cmp);
+    return (argvp);
+}
+
 /* argv_extend - extend array */
 
 static void argv_extend(ARGV *argvp)
index 7c56f581a04e6e787b37d2274e6231fe91ba40bb..fe0322422b6d34510b93ed419474d4d8e2387263 100644 (file)
@@ -21,6 +21,7 @@ typedef struct ARGV {
 } ARGV;
 
 extern ARGV *argv_alloc(ssize_t);
+extern ARGV *argv_sort(ARGV *);
 extern void argv_add(ARGV *,...);
 extern void argv_addn(ARGV *,...);
 extern void argv_terminate(ARGV *);