Docs typo
[users/jgh/exim.git] / doc / doc-txt / experimental-spec.txt
index 1ec323433b70263e948603b2c754236152748c4c..aae8338f17b55fd0b09a7ec4e80e42b3f4392670 100644 (file)
@@ -6,125 +6,6 @@ about experimental  features, all  of which  are unstable and
 liable to incompatible change.
 
 
 liable to incompatible change.
 
 
-PRDR support
---------------------------------------------------------------
-
-Per-Recipient Data Reponse is an SMTP extension proposed by Eric Hall
-in a (now-expired) IETF draft from 2007.  It's not hit mainstream
-use, but has apparently been implemented in the META1 MTA.
-
-There is mention at http://mail.aegee.org/intern/sendmail.html
-of a patch to sendmail "to make it PRDR capable".
-
- ref: http://www.eric-a-hall.com/specs/draft-hall-prdr-00.txt
-
-If Exim is built with EXPERIMENTAL_PRDR there is a new config
-boolean "prdr_enable" which controls whether PRDR is advertised
-as part of an EHLO response, a new "acl_data_smtp_prdr" ACL
-(called for each recipient, after data arrives but before the
-data ACL), and a new smtp transport option "hosts_try_prdr".
-
-PRDR may be used to support per-user content filtering.  Without it
-one must defer any recipient after the first that has a different
-content-filter configuration.  With PRDR, the RCPT-time check
-for this can be disabled when the MAIL-time $smtp_command included
-"PRDR".  Any required difference in behaviour of the main DATA-time
-ACL should however depend on the PRDR-time ACL having run, as Exim
-will avoid doing so in some situations (eg.  single-recipient mails).
-
-
-
-OCSP Stapling support
---------------------------------------------------------------
-
-X.509 PKI certificates expire and can be revoked; to handle this, the
-clients need some way to determine if a particular certificate, from a
-particular Certificate Authority (CA), is still valid.  There are three
-main ways to do so.
-
-The simplest way is to serve up a Certificate Revocation List (CRL) with
-an ordinary web-server, regenerating the CRL before it expires.  The
-downside is that clients have to periodically re-download a potentially
-huge file from every certificate authority it knows of.
-
-The way with most moving parts at query time is Online Certificate
-Status Protocol (OCSP), where the client verifies the certificate
-against an OCSP server run by the CA.  This lets the CA track all
-usage of the certs.  This requires running software with access to the
-private key of the CA, to sign the responses to the OCSP queries.  OCSP
-is based on HTTP and can be proxied accordingly.
-
-The only widespread OCSP server implementation (known to this writer)
-comes as part of OpenSSL and aborts on an invalid request, such as
-connecting to the port and then disconnecting.  This requires
-re-entering the passphrase each time some random client does this.
-
-The third way is OCSP Stapling; in this, the server using a certificate
-issued by the CA periodically requests an OCSP proof of validity from
-the OCSP server, then serves it up inline as part of the TLS
-negotiation.   This approach adds no extra round trips, does not let the
-CA track users, scales well with number of certs issued by the CA and is
-resilient to temporary OCSP server failures, as long as the server
-starts retrying to fetch an OCSP proof some time before its current
-proof expires.  The downside is that it requires server support.
-
-If Exim is built with EXPERIMENTAL_OCSP and it was built with OpenSSL,
-or with GnuTLS 3.1.3 or later, then it gains a new global option:
-"tls_ocsp_file".
-
-The file specified therein is expected to be in DER format, and contain
-an OCSP proof.  Exim will serve it as part of the TLS handshake.  This
-option will be re-expanded for SNI, if the tls_certificate option
-contains $tls_sni, as per other TLS options.
-
-Exim does not at this time implement any support for fetching a new OCSP
-proof.  The burden is on the administrator to handle this, outside of
-Exim.  The file specified should be replaced atomically, so that the
-contents are always valid.  Exim will expand the "tls_ocsp_file" option
-on each connection, so a new file will be handled transparently on the
-next connection.
-
-Under OpenSSL Exim will check for a valid next update timestamp in the
-OCSP proof; if not present, or if the proof has expired, it will be
-ignored.
-
-Also, given EXPERIMENTAL_OCSP, the smtp transport gains two options:
-- "hosts_require_ocsp"; a host-list for which an OCSP Stapling
-is requested and required for the connection to proceed.  The default
-value is empty.
-- "hosts_request_ocsp"; a host-list for which (additionally) an OCSP
-Stapling is requested (but not necessarily verified).  The default
-value is "*" meaning that requests are made unless configured
-otherwise.
-
-The host(s) should also be in "hosts_require_tls", and
-"tls_verify_certificates" configured for the transport.
-
-For the client to be able to verify the stapled OCSP the server must
-also supply, in its stapled information, any intermediate
-certificates for the chain leading to the OCSP proof from the signer
-of the server certificate.  There may be zero or one such. These
-intermediate certificates should be added to the server OCSP stapling
-file (named by tls_ocsp_file).
-
-Note that the proof only covers the terminal server certificate,
-not any of the chain from CA to it.
-
-At this point in time, we're gathering feedback on use, to determine if
-it's worth adding complexity to the Exim daemon to periodically re-fetch
-OCSP files and somehow handling multiple files.
-
-  A helper script "ocsp_fetch.pl" for fetching a proof from a CA
-  OCSP server is supplied.  The server URL may be included in the
-  server certificate, if the CA is helpful.
-
-  One failure mode seen was the OCSP Signer cert expiring before the end
-  of validity of the OCSP proof. The checking done by Exim/OpenSSL
-  noted this as invalid overall, but the re-fetch script did not.
-
-
-
-
 Brightmail AntiSpam (BMI) suppport
 --------------------------------------------------------------
 
 Brightmail AntiSpam (BMI) suppport
 --------------------------------------------------------------
 
@@ -878,90 +759,103 @@ b. Configure, somewhere before the DATA ACL, the control option to
 
 
 
 
 
 
-Transport post-delivery actions
+Event Actions
 --------------------------------------------------------------
 
 --------------------------------------------------------------
 
-An arbitrary per-transport string can be expanded on successful delivery,
-and (for SMTP transports) a second string on deferrals caused by a host error.
+(Renamed from TPDA, Transport post-delivery actions)
+
+An arbitrary per-transport string can be expanded upon various transport events.
+Additionally a main-section configuration option can be expanded on some
+per-message events.
 This feature may be used, for example, to write exim internal log information
 (not available otherwise) into a database.
 
 This feature may be used, for example, to write exim internal log information
 (not available otherwise) into a database.
 
-In order to use the feature, you must set
+In order to use the feature, you must compile with
 
 
-EXPERIMENTAL_TPDA=yes
+EXPERIMENTAL_EVENT=yes
 
 in your Local/Makefile
 
 
 in your Local/Makefile
 
-and define the expandable strings in the runtime config file, to
-be executed at end of delivery.
+and define one or both of
+- the event_action option in the transport
+- the event_action main option
+to be expanded when the event fires.
 
 
-Additionally, there are 6 more variables, available at end of
-delivery:
+A new variable, $event_name, is set to the event type when the
+expansion is done.  The current list of events is:
 
 
-tpda_delivery_ip             IP of host, which has accepted delivery
-tpda_delivery_port           Port of remote host which has accepted delivery
-tpda_delivery_fqdn           FQDN of host, which has accepted delivery
-tpda_delivery_local_part     local part of address being delivered
-tpda_delivery_domain         domain part of address being delivered
-tpda_delivery_confirmation   SMTP confirmation message
+ msg:complete          after  main       per message
+ msg:delivery          after  transport  per recipient
+ msg:host:defer                after  transport  per attempt
+ msg:fail:delivery     after  main       per recipient
+ msg:fail:internal     after  main       per recipient
+ tcp:connect           before transport  per connection
+ tcp:close             after  transport  per connection
+ tls:cert              before both       per certificate in verification chain
+ smtp:connect          after  transport  per connection
 
 
-In case of a deferral caused by a host-error:
-tpda_defer_errno             Error number
-tpda_defer_errstr            Error string possibly containing more details
+The expansion is called for all event types, and should use the $event_name
+value to decide when to act.  The variable data is a colon-separated
+list, describing an event tree.
 
 
-The $router_name and $transport_name variables are also usable.
+There is an auxilary variable, $event_data, for which the
+content is event_dependent:
 
 
+       msg:delivery            smtp confirmation mssage
+       msg:host:defer          error string
+       tls:cert                verification chain depth
+       smtp:connect            smtp banner
 
 
-To take action after successful deliveries, set the following option
-on any transport of interest.
+The msg:host:defer event populates one extra variable, $event_defer_errno.
+
+The following variables are likely to be useful depending on the event type:
+
+       router_name, transport_name
+       local_part, domain
+       host, host_address, host_port
+       tls_out_peercert
+       lookup_dnssec_authenticated, tls_out_dane
+       sending_ip_address, sending_port
+       message_exim_id, verify_mode
 
 
-tpda_delivery_action
 
 An example might look like:
 
 
 An example might look like:
 
-tpda_delivery_action = \
-${lookup pgsql {SELECT * FROM record_Delivery( \
+event_action = ${if = {msg:delivery}{$event_name} \
+{${lookup pgsql {SELECT * FROM record_Delivery( \
     '${quote_pgsql:$sender_address_domain}',\
     '${quote_pgsql:${lc:$sender_address_local_part}}', \
     '${quote_pgsql:$sender_address_domain}',\
     '${quote_pgsql:${lc:$sender_address_local_part}}', \
-    '${quote_pgsql:$tpda_delivery_domain}', \
-    '${quote_pgsql:${lc:$tpda_delivery_local_part}}', \
-    '${quote_pgsql:$tpda_delivery_ip}', \
-    '${quote_pgsql:${lc:$tpda_delivery_fqdn}}', \
-    '${quote_pgsql:$message_exim_id}')}}
-
-The string is expanded after the delivery completes and any
-side-effects will happen.  The result is then discarded.
+    '${quote_pgsql:$domain}', \
+    '${quote_pgsql:${lc:$local_part}}', \
+    '${quote_pgsql:$host_address}', \
+    '${quote_pgsql:${lc:$host}}', \
+    '${quote_pgsql:$message_exim_id}')}} \
+} {}}
+
+The string is expanded when each of the supported events occur
+and any side-effects of the expansion will happen.
 Note that for complex operations an ACL expansion can be used.
 
 
 Note that for complex operations an ACL expansion can be used.
 
 
-In order to log host deferrals, add the following option to an SMTP
-transport:
+The expansion of the event_action option should normally
+return an empty string.  Should it return anything else the
+following will be forced:
 
 
-tpda_host_defer_action
+       msg:delivery    (ignored)
+       msg:host:defer  (ignored)
+       msg:fail:delivery (ignored)
+       tcp:connect     do not connect
+       tcp:close       (ignored)
+       tls:cert        refuse verification
+       smtp:connect    close connection
 
 
-This is a private option of the SMTP transport. It is intended to
-log failures of remote hosts. It is executed only when exim has
-attempted to deliver a message to a remote host and failed due to
-an error which doesn't seem to be related to the individual
-message, sender, or recipient address.
-See section 47.2 of the exim documentation for more details on how
-this is determined.
+No other use is made of the result string.
 
 
-Example:
 
 
-tpda_host_defer_action = \
-${lookup mysql {insert into delivlog set \
-    msgid = '${quote_mysql:$message_exim_id}', \
-    senderlp = '${quote_mysql:${lc:$sender_address_local_part}}', \
-    senderdom = '${quote_mysql:$sender_address_domain}', \
-    delivlp = '${quote_mysql:${lc:$tpda_delivery_local_part}}', \
-    delivdom = '${quote_mysql:$tpda_delivery_domain}', \
-    delivip = '${quote_mysql:$tpda_delivery_ip}', \
-    delivport = '${quote_mysql:$tpda_delivery_port}', \
-    delivfqdn = '${quote_mysql:$tpda_delivery_fqdn}', \
-    deliverrno = '${quote_mysql:$tpda_defer_errno}', \
-    deliverrstr = '${quote_mysql:$tpda_defer_errstr}' \
-    }}
+Known issues:
+- the tls:cert event is only called for the cert chain elements
+  received over the wire, with GnuTLS.  OpenSSL gives the entire
+  chain including thse loaded locally.
 
 
 Redis Lookup
 
 
 Redis Lookup
@@ -1053,6 +947,8 @@ Proxy Protocol Support
 Exim now has Experimental "Proxy Protocol" support.  It was built on
 specifications from:
 http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt
 Exim now has Experimental "Proxy Protocol" support.  It was built on
 specifications from:
 http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt
+Above URL revised May 2014 to change version 2 spec:
+http://git.1wt.eu/web?p=haproxy.git;a=commitdiff;h=afb768340c9d7e50d8e
 
 The purpose of this function is so that an application load balancer,
 such as HAProxy, can sit in front of several Exim servers and Exim
 
 The purpose of this function is so that an application load balancer,
 such as HAProxy, can sit in front of several Exim servers and Exim
@@ -1134,11 +1030,27 @@ an example, in my connect ACL, I have:
        logwrite = Internal Server Address: $received_ip_address:$received_port
 
 
        logwrite = Internal Server Address: $received_ip_address:$received_port
 
 
-4. Runtime issues to be aware of:
+4. Recommended ACL additions:
    - Since the real connections are all coming from your proxy, and the
      per host connection tracking is done before Proxy Protocol is
      evaluated, smtp_accept_max_per_host must be set high enough to
      handle all of the parallel volume you expect per inbound proxy.
    - Since the real connections are all coming from your proxy, and the
      per host connection tracking is done before Proxy Protocol is
      evaluated, smtp_accept_max_per_host must be set high enough to
      handle all of the parallel volume you expect per inbound proxy.
+   - With the smtp_accept_max_per_host set so high, you lose the ability
+     to protect your server from massive numbers of inbound connections
+     from one IP.  In order to prevent your server from being DOS'd, you
+     need to add a per connection ratelimit to your connect ACL.  I
+     suggest something like this:
+
+  # Set max number of connections per host
+  LIMIT   = 5
+  # Or do some kind of IP lookup in a flat file or database
+  # LIMIT = ${lookup{$sender_host_address}iplsearch{/etc/exim/proxy_limits}}
+
+  defer   message        = Too many connections from this IP right now
+          ratelimit      = LIMIT / 5s / per_conn / strict
+
+
+5. Runtime issues to be aware of:
    - The proxy has 3 seconds (hard-coded in the source code) to send the
      required Proxy Protocol header after it connects.  If it does not,
      the response to any commands will be:
    - The proxy has 3 seconds (hard-coded in the source code) to send the
      required Proxy Protocol header after it connects.  If it does not,
      the response to any commands will be:
@@ -1157,7 +1069,7 @@ an example, in my connect ACL, I have:
      mail programs from working because that would require mail from
      localhost to use Proxy Protocol.  Again, not advised!
 
      mail programs from working because that would require mail from
      localhost to use Proxy Protocol.  Again, not advised!
 
-5. Example of a refused connection because the Proxy Protocol header was
+6. Example of a refused connection because the Proxy Protocol header was
 not sent from a host configured to use Proxy Protocol.  In the example,
 the 3 second timeout occurred (when a Proxy Protocol banner should have
 been sent), the banner was displayed to the user, but all commands are
 not sent from a host configured to use Proxy Protocol.  In the example,
 the 3 second timeout occurred (when a Proxy Protocol banner should have
 been sent), the banner was displayed to the user, but all commands are
@@ -1173,6 +1085,153 @@ QUIT
 
 
 
 
 
 
+
+DANE
+------------------------------------------------------------
+DNS-based Authentication of Named Entities, as applied
+to SMTP over TLS, provides assurance to a client that
+it is actually talking to the server it wants to rather
+than some attacker operating a Man In The Middle (MITM)
+operation.  The latter can terminate the TLS connection
+you make, and make another one to the server (so both
+you and the server still think you have an encrypted
+connection) and, if one of the "well known" set of
+Certificate Authorities has been suborned - something
+which *has* been seen already (2014), a verifiable
+certificate (if you're using normal root CAs, eg. the
+Mozilla set, as your trust anchors).
+
+What DANE does is replace the CAs with the DNS as the
+trust anchor.  The assurance is limited to a) the possibility
+that the DNS has been suborned, b) mistakes made by the
+admins of the target server.   The attack surface presented
+by (a) is thought to be smaller than that of the set
+of root CAs.
+
+It also allows the server to declare (implicitly) that
+connections to it should use TLS.  An MITM could simply
+fail to pass on a server's STARTTLS.
+
+DANE scales better than having to maintain (and
+side-channel communicate) copies of server certificates
+for every possible target server.  It also scales
+(slightly) better than having to maintain on an SMTP
+client a copy of the standard CAs bundle.  It also
+means not having to pay a CA for certificates.
+
+DANE requires a server operator to do three things:
+1) run DNSSEC.  This provides assurance to clients
+that DNS lookups they do for the server have not
+been tampered with.  The domain MX record applying
+to this server, its A record, its TLSA record and
+any associated CNAME records must all be covered by
+DNSSEC.
+2) add TLSA DNS records.  These say what the server
+certificate for a TLS connection should be.
+3) offer a server certificate, or certificate chain,
+in TLS connections which is traceable to the one
+defined by (one of?) the TSLA records
+
+There are no changes to Exim specific to server-side
+operation of DANE.
+
+The TLSA record for the server may have "certificate
+usage" of DANE-TA(2) or DANE-EE(3).  The latter specifies
+the End Entity directly, i.e. the certificate involved
+is that of the server (and should be the sole one transmitted
+during the TLS handshake); this is appropriate for a
+single system, using a self-signed certificate.
+  DANE-TA usage is effectively declaring a specific CA
+to be used; this might be a private CA or a public,
+well-known one.  A private CA at simplest is just
+a self-signed certificate which is used to sign
+cerver certificates, but running one securely does
+require careful arrangement.  If a private CA is used
+then either all clients must be primed with it, or
+(probably simpler) the server TLS handshake must transmit
+the entire certificate chain from CA to server-certificate.
+If a public CA is used then all clients must be primed with it
+(losing one advantage of DANE) - but the attack surface is
+reduced from all public CAs to that single CA.
+DANE-TA is commonly used for several services and/or
+servers, each having a TLSA query-domain CNAME record,
+all of which point to a single TLSA record.
+
+The TLSA record should have a Selector field of SPKI(1)
+and a Matching Type field of SHA2-512(2).
+
+At the time of writing, https://www.huque.com/bin/gen_tlsa
+is useful for quickly generating TLSA records; and commands like
+
+  openssl x509 -in -pubkey -noout <certificate.pem \
+  | openssl rsa -outform der -pubin 2>/dev/null \
+  | openssl sha512 \
+  | awk '{print $2}'
+
+are workable for 4th-field hashes.
+
+For use with the DANE-TA model, server certificates
+must have a correct name (SubjectName or SubjectAltName).
+
+The use of OCSP-stapling should be considered, allowing
+for fast revocation of certificates (which would otherwise
+be limited by the DNS TTL on the TLSA records).  However,
+this is likely to only be usable with DANE-TA.  NOTE: the
+default of requesting OCSP for all hosts is modified iff
+DANE is in use, to:
+
+  hosts_request_ocsp = ${if or { {= {0}{$tls_out_tlsa_usage}} \
+                                {= {4}{$tls_out_tlsa_usage}} } \
+                         {*}{}}
+
+The (new) variable $tls_out_tlsa_usage is a bitfield with
+numbered bits set for TLSA record usage codes.
+The zero above means DANE was not in use,
+the four means that only DANE-TA usage TLSA records were
+found. If the definition of hosts_request_ocsp includes the
+string "tls_out_tlsa_usage", they are re-expanded in time to
+control the OCSP request.
+
+This modification of hosts_request_ocsp is only done if
+it has the default value of "*".  Admins who change it, and
+those who use hosts_require_ocsp, should consider the interaction
+with DANE in their OCSP settings.
+
+
+For client-side DANE there are two new smtp transport options,
+hosts_try_dane and hosts_require_dane.  They do the obvious thing.
+[ should they be domain-based rather than host-based? ]
+
+DANE will only be usable if the target host has DNSSEC-secured
+MX, A and TLSA records.
+
+A TLSA lookup will be done if either of the above options match
+and the host-lookup succeded using dnssec.
+If the TLSA lookup succeeds, a TLS connection will be required
+for the host.
+
+(TODO: specify when fallback happens vs. when the host is not used)
+
+If dane is in use the following transport options are ignored:
+  hosts_require_tls
+  tls_verify_hosts
+  tls_try_verify_hosts
+  tls_verify_certificates
+  tls_crl
+  tls_verify_cert_hostnames
+
+Currently dnssec_request_domains must be active (need to think about that)
+and dnssec_require_domains is ignored.
+
+If verification was successful using DANE then the "CV" item
+in the delivery log line will show as "CV=dane".
+
+There is a new variable $tls_out_dane which will have "yes" if
+verification succeeded using DANE and "no" otherwise (only useful
+in combination with EXPERIMENTAL_EVENT), and a new variable
+$tls_out_tlsa_usage (detailed above).
+
+
 --------------------------------------------------------------
 End of file
 --------------------------------------------------------------
 --------------------------------------------------------------
 End of file
 --------------------------------------------------------------