Transport post-delivery actions
--------------------------------------------------------------
-An arbitrary per-transport string can be expanded on successful delivery,
+An arbitrary per-transport string can be expanded upon various transport events
and (for SMTP transports) a second string on deferrals caused by a host error.
+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.
in your Local/Makefile
-and define the tpda_event_action option in the transport, to
-be expanded when the event fires.
+and define one or both of
+- the tpda_event_action option in the transport
+- the delivery_event_action
+to be expanded when the event fires.
A new variable, $tpda_event, is set to the event type when the
expansion is done. The current list of events is:
- msg:delivery
- msg:host:defer
- tcp:connect
- tcp:close
- tls:cert
- smtp:connect
+ msg:complete main per message
+ msg:delivery transport per recipient
+ msg:host:defer transport per attempt
+ msg:fail:delivery main per recipient
+ msg:fail:internal main per recipient
+ tcp:connect transport per connection
+ tcp:close transport per connection
+ tls:cert transport per certificate in verification chain
+ smtp:connect transport per connection
The expansion is called for all event types, and should use the $tpda_event
value to decide when to act. The variable data is a colon-separated
The msg:host:defer event populates one extra variable, $tpda_defer_errno.
-The following variables are likely to be useful for most event types:
+The following variables are likely to be useful depending on the event type:
router_name, transport_name
local_part, domain
tls_out_peercert
lookup_dnssec_authenticated, tls_out_dane
sending_ip_address, sending_port
+ message_exim_id
An example might look like:
'${quote_pgsql:$message_exim_id}')}} \
} {}}
-The string is expanded after the delivery completes and any
+The string is expanded for each of the supported events and any
side-effects will happen. The result is then discarded.
Note that for complex operations an ACL expansion can be used.
-During the expansion the tpda_event variable will contain the
-string-list "msg:delivery".
-
The expansion of the tpda_event_action option should normally
return an empty string. Should it return anything else the
msg:delivery (ignored)
msg:host:defer (ignored)
+ msg:fail:delivery (ignored)
tcp:connect do not connect
tcp:close (ignored)
tls:cert refuse verification
the next hop does not support DSN.
Adding it to a redirect router makes no difference.
+
Certificate name checking
--------------------------------------------------------------
The X509 certificates used for TLS are supposed be verified
component FQDN).
+ 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.
+
+ 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_require_ocsp or
+ 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.
+
+ (TODO: specify when fallback happens vs. when the host is not used)
+
+ If dane is in use the following transport options are ignored:
+ 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_TPDA), and a new variable
+ $tls_out_tlsa_usage (detailed above).
+
--------------------------------------------------------------
End of file
if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
addr->cipher != NULL)
s = string_append(s, sizep, ptrp, 2, US" CV=",
- testflag(addr, af_cert_verified)? "yes":"no");
+ testflag(addr, af_cert_verified)
+ ?
+ #ifdef EXPERIMENTAL_DANE
+ testflag(addr, af_dane_verified)
+ ? "dane"
+ :
+ #endif
+ "yes"
+ : "no");
if ((log_extra_selector & LX_tls_peerdn) != 0 && addr->peerdn != NULL)
s = string_append(s, sizep, ptrp, 3, US" DN=\"",
string_printing(addr->peerdn), US"\"");
#endif
+
+
#ifdef EXPERIMENTAL_TPDA
int
tpda_raise_event(uschar * action, uschar * event, uschar * ev_data)
}
return OK;
}
-#endif
+
+static void
+tpda_msg_event(uschar * event, address_item * addr)
+{
+uschar * save_domain = deliver_domain;
+uschar * save_local = deliver_localpart;
+
+if (!addr->transport)
+ return;
+
+router_name = addr->router ? addr->router->name : NULL;
+transport_name = addr->transport->name;
+deliver_domain = addr->domain;
+deliver_localpart = addr->local_part;
+
+(void) tpda_raise_event(addr->transport->tpda_event_action, event,
+ addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0
+ ? addr->message : NULL);
+
+deliver_localpart = save_local;
+deliver_domain = save_domain;
+router_name = transport_name = NULL;
+}
+#endif /*EXPERIMENTAL_TPDA*/
+
+
/* If msg is NULL this is a delivery log and logchar is used. Otherwise
this is a nonstandard call; no two-character delivery flag is written
log_write(0, flags, "%s", s);
#ifdef EXPERIMENTAL_TPDA
- {
- uschar * save_domain = deliver_domain;
- uschar * save_local = deliver_localpart;
-
- router_name = addr->router ? addr->router->name : NULL;
- transport_name = addr->transport ? addr->transport->name : NULL;
- deliver_domain = addr->domain;
- deliver_localpart = addr->local_part;
-
- (void) tpda_raise_event(addr->transport->tpda_event_action, US"msg:delivery",
- addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0
- ? addr->message : NULL);
-
- deliver_localpart = save_local;
- deliver_domain = save_domain;
- router_name = transport_name = NULL;
- }
+/*XXX cutthrough calls this also for non-delivery...*/
+tpda_msg_event(US"msg:delivery", addr);
#endif
+
store_reset(reset_point);
return;
}
tls_out.cipher = addr->cipher;
tls_out.peerdn = addr->peerdn;
tls_out.ocsp = addr->ocsp;
+ # ifdef EXPERIMENTAL_DANE
+ tls_out.dane_verified = testflag(addr, af_dane_verified);
+ # endif
#endif
delivery_log(LOG_MAIN, addr, logchar, NULL);
tls_out.cipher = NULL;
tls_out.peerdn = NULL;
tls_out.ocsp = OCSP_NOT_REQ;
+ # ifdef EXPERIMENTAL_DANE
+ tls_out.dane_verified = FALSE;
+ # endif
#endif
}
deliver_msglog("%s %s\n", now, s);
log_write(0, LOG_MAIN, "** %s", s);
+
+#ifdef EXPERIMENTAL_TPDA
+ tpda_msg_event(US"msg:fail:delivery", addr);
+#endif
+
store_reset(reset_point);
}
/* The certificate verification status goes into the flags */
if (tls_out.certificate_verified) setflag(addr, af_cert_verified);
+ #ifdef EXPERIMENTAL_DANE
+ if (tls_out.dane_verified) setflag(addr, af_dane_verified);
+ #endif
/* Use an X item only if there's something to send */
#ifdef SUPPORT_TLS
addr_last = new;
break;
}
+
+#ifdef EXPERIMENTAL_TPDA
+ if (process_recipients != RECIP_ACCEPT)
+ {
+ uschar * save_local = deliver_localpart;
+ uschar * save_domain = deliver_domain;
+
+ deliver_localpart = expand_string(
+ string_sprintf("${local_part:%s}", new->address));
+ deliver_domain = expand_string(
+ string_sprintf("${domain:%s}", new->address));
+
+ (void) tpda_raise_event(delivery_event_action,
+ US"msg:fail:internal", new->message);
+
+ deliver_localpart = save_local;
+ deliver_domain = save_domain;
+ }
+#endif
}
}
}
{
struct stat statbuf;
if (fstat(deliver_datafile, &statbuf) == 0 && statbuf.st_size > max)
+ {
if (emf_text)
fprintf(f, "%s", CS emf_text);
else
fprintf(f,
"------ The body of the message is " OFF_T_FMT " characters long; only the first\n"
"------ %d or so are included here.\n", statbuf.st_size, max);
+ }
}
fputc('\n', f);
/* Unset deliver_freeze so that we won't try to move the spool files further down */
deliver_freeze = FALSE;
- }
+
+#ifdef EXPERIMENTAL_TPDA
+ (void) tpda_raise_event(delivery_event_action, US"msg:complete", NULL);
+#endif
+}
/* If there are deferred addresses, we are keeping this message because it is
not yet completed. Lose any temporary files that were catching output from