-DMARC Support
---------------------------------------------------------------
-
-DMARC combines feedback from SPF, DKIM, and header From: in order
-to attempt to provide better indicators of the authenticity of an
-email. This document does not explain the fundamentals, you
-should read and understand how it works by visiting the website at
-http://www.dmarc.org/.
-
-DMARC support is added via the libopendmarc library. Visit:
-
- http://sourceforge.net/projects/opendmarc/
-
-to obtain a copy, or find it in your favorite rpm package
-repository. If building from source, this description assumes
-that headers will be in /usr/local/include, and that the libraries
-are in /usr/local/lib.
-
-1. To compile Exim with DMARC support, you must first enable SPF.
-Please read the Local/Makefile comments on enabling the SUPPORT_SPF
-feature. You must also have DKIM support, so you cannot set the
-DISABLE_DKIM feature. Once both of those conditions have been met
-you can enable DMARC in Local/Makefile:
-
-EXPERIMENTAL_DMARC=yes
-LDFLAGS += -lopendmarc
-# CFLAGS += -I/usr/local/include
-# LDFLAGS += -L/usr/local/lib
-
-The first line sets the feature to include the correct code, and
-the second line says to link the libopendmarc libraries into the
-exim binary. The commented out lines should be uncommented if you
-built opendmarc from source and installed in the default location.
-Adjust the paths if you installed them elsewhere, but you do not
-need to uncomment them if an rpm (or you) installed them in the
-package controlled locations (/usr/include and /usr/lib).
-
-
-2. Use the following global settings to configure DMARC:
-
-Required:
-dmarc_tld_file Defines the location of a text file of valid
- top level domains the opendmarc library uses
- during domain parsing. Maintained by Mozilla,
- the most current version can be downloaded
- from a link at http://publicsuffix.org/list/.
- See also util/renew-opendmarc-tlds.sh script.
-
-Optional:
-dmarc_history_file Defines the location of a file to log results
- of dmarc verification on inbound emails. The
- contents are importable by the opendmarc tools
- which will manage the data, send out DMARC
- reports, and expire the data. Make sure the
- directory of this file is writable by the user
- exim runs as.
-
-dmarc_forensic_sender The email address to use when sending a
- forensic report detailing alignment failures
- if a sender domain's dmarc record specifies it
- and you have configured Exim to send them.
- Default: do-not-reply@$default_hostname
-
-
-3. By default, the DMARC processing will run for any remote,
-non-authenticated user. It makes sense to only verify DMARC
-status of messages coming from remote, untrusted sources. You can
-use standard conditions such as hosts, senders, etc, to decide that
-DMARC verification should *not* be performed for them and disable
-DMARC with a control setting:
-
- control = dmarc_disable_verify
-
-A DMARC record can also specify a "forensic address", which gives
-exim an email address to submit reports about failed alignment.
-Exim does not do this by default because in certain conditions it
-results in unintended information leakage (what lists a user might
-be subscribed to, etc). You must configure exim to submit forensic
-reports to the owner of the domain. If the DMARC record contains a
-forensic address and you specify the control statement below, then
-exim will send these forensic emails. It's also advised that you
-configure a dmarc_forensic_sender because the default sender address
-construction might be inadequate.
-
- control = dmarc_enable_forensic
-
-(AGAIN: You can choose not to send these forensic reports by simply
-not putting the dmarc_enable_forensic control line at any point in
-your exim config. If you don't tell it to send them, it will not
-send them.)
-
-There are no options to either control. Both must appear before
-the DATA acl.
-
-
-4. You can now run DMARC checks in incoming SMTP by using the
-"dmarc_status" ACL condition in the DATA ACL. You are required to
-call the spf condition first in the ACLs, then the "dmarc_status"
-condition. Putting this condition in the ACLs is required in order
-for a DMARC check to actually occur. All of the variables are set
-up before the DATA ACL, but there is no actual DMARC check that
-occurs until a "dmarc_status" condition is encountered in the ACLs.
-
-The dmarc_status condition takes a list of strings on its
-right-hand side. These strings describe recommended action based
-on the DMARC check. To understand what the policy recommendations
-mean, refer to the DMARC website above. Valid strings are:
-
- o accept The DMARC check passed and the library recommends
- accepting the email.
- o reject The DMARC check failed and the library recommends
- rejecting the email.
- o quarantine The DMARC check failed and the library recommends
- keeping it for further inspection.
- o none The DMARC check passed and the library recommends
- no specific action, neutral.
- o norecord No policy section in the DMARC record for this
- sender domain.
- o nofrom Unable to determine the domain of the sender.
- o temperror Library error or dns error.
- o off The DMARC check was disabled for this email.
-
-You can prefix each string with an exclamation mark to invert its
-meaning, for example "!accept" will match all results but
-"accept". The string list is evaluated left-to-right in a
-short-circuit fashion. When a string matches the outcome of the
-DMARC check, the condition succeeds. If none of the listed
-strings matches the outcome of the DMARC check, the condition
-fails.
-
-Of course, you can also use any other lookup method that Exim
-supports, including LDAP, Postgres, MySQL, etc, as long as the
-result is a list of colon-separated strings.
-
-Performing the check sets up information used by the
-${authresults } expansion item.
-
-Several expansion variables are set before the DATA ACL is
-processed, and you can use them in this ACL. The following
-expansion variables are available:
-
- o $dmarc_status
- This is a one word status indicating what the DMARC library
- thinks of the email. It is a combination of the results of
- DMARC record lookup and the SPF/DKIM/DMARC processing results
- (if a DMARC record was found). The actual policy declared
- in the DMARC record is in a separate expansion variable.
-
- o $dmarc_status_text
- This is a slightly longer, human readable status.
-
- o $dmarc_used_domain
- This is the domain which DMARC used to look up the DMARC
- policy record.
-
- o $dmarc_domain_policy
- This is the policy declared in the DMARC record. Valid values
- are "none", "reject" and "quarantine". It is blank when there
- is any error, including no DMARC record.
-
-A now-redundant variable $dmarc_ar_header has now been withdrawn.
-Use the ${authresults } expansion instead.
-
-
-5. How to enable DMARC advanced operation:
-By default, Exim's DMARC configuration is intended to be
-non-intrusive and conservative. To facilitate this, Exim will not
-create any type of logging files without explicit configuration by
-you, the admin. Nor will Exim send out any emails/reports about
-DMARC issues without explicit configuration by you, the admin (other
-than typical bounce messages that may come about due to ACL
-processing or failure delivery issues).
-
-In order to log statistics suitable to be imported by the opendmarc
-tools, you need to:
-a. Configure the global setting dmarc_history_file.
-b. Configure cron jobs to call the appropriate opendmarc history
- import scripts and truncating the dmarc_history_file.
-
-In order to send forensic reports, you need to:
-a. Configure the global setting dmarc_forensic_sender.
-b. Configure, somewhere before the DATA ACL, the control option to
- enable sending DMARC forensic reports.
-
-
-6. Example usage:
-(RCPT ACL)
- warn domains = +local_domains
- hosts = +local_hosts
- control = dmarc_disable_verify
-
- warn !domains = +screwed_up_dmarc_records
- control = dmarc_enable_forensic
-
- warn condition = (lookup if destined to mailing list)
- set acl_m_mailing_list = 1
-
-(DATA ACL)
- warn dmarc_status = accept : none : off
- !authenticated = *
- log_message = DMARC DEBUG: $dmarc_status $dmarc_used_domain
-
- warn dmarc_status = !accept
- !authenticated = *
- log_message = DMARC DEBUG: '$dmarc_status' for $dmarc_used_domain
-
- warn dmarc_status = quarantine
- !authenticated = *
- set $acl_m_quarantine = 1
- # Do something in a transport with this flag variable
-
- deny condition = ${if eq{$dmarc_domain_policy}{reject}}
- condition = ${if eq{$acl_m_mailing_list}{1}}
- message = Messages from $dmarc_used_domain break mailing lists
-
- deny dmarc_status = reject
- !authenticated = *
- message = Message from $dmarc_used_domain failed sender's DMARC policy, REJECT
-
- warn add_header = :at_start:${authresults {$primary_hostname}}
-
-
-
DSN extra information
---------------------
If compiled with EXPERIMENTAL_DSN_INFO extra information will be added
The spool files can then be processed by external processes and then
requeued into exim spool directories for final delivery.
+However, note carefully the warnings in the main documentation on
+qpool file formats.
The motivation/inspiration for the transport is to allow external
processes to access email queued by exim and have access to all the
Note that it would be wise to strip incoming messages of A-R headers
that claim to be from our own <admd-identifier>.
-There are two new variables: $arc_state and $arc_state_reason.
+There are four new variables:
+
+ $arc_state One of pass, fail, none
+ $arc_state_reason (if fail, why)
+ $arc_domains colon-sep list of ARC chain domains, in chain order.
+ problematic elements may have empty list elements
+ $arc_oldest_pass lowest passing instance number of chain
+
+Example:
+ logwrite = oldest-p-ams: <${reduce {$lh_ARC-Authentication-Results:} \
+ {} \
+ {${if = {$arc_oldest_pass} \
+ {${extract {i}{${extract {1}{;}{$item}}}}} \
+ {$item} {$value}}} \
+ }>
Receive log lines for an ARC pass will be tagged "ARC".
an ARC set of headers. The textually-first Authentication-Results: header
is used as a basis (you must have added one on entry to the ADMD).
Expanded as a whole; if unset, empty or forced-failure then no signing is done.
-If it is set, all three elements must be non-empty.
+If it is set, all of the first three elements must be non-empty.
The fourth element is optional, and if present consists of a comma-separated list
-of options. The only option implemented so far is
- timestamps Add a t= tag to the generated AMS and AS headers, with the
- current time.
+of options. The options implemented are
+
+ timestamps Add a t= tag to the generated AMS and AS headers, with the
+ current time.
+ expire[=<val>] Add an x= tag to the generated AMS header, with an expiry time.
+ If the value <val> is an plain number it is used unchanged.
+ If it starts with a '+' then the following number is added
+ to the current time, as an offset in seconds.
+ If a value is not given it defaults to a one month offset.
[As of writing, gmail insist that a t= tag on the AS is mandatory]
* There must be an Authentication-Results header, presumably added by an ACL
while receiving the message, for the same ADMD, for arc_sign to succeed.
This requires careful coordination between inbound and outbound logic.
+
+ Only one A-R header is taken account of. This is a limitation versus
+ the ARC spec (which says that all A-R headers from within the ADMD must
+ be used).
+
* If passing a message to another system, such as a mailing-list manager
(MLM), between receipt and sending, be wary of manipulations to headers made
by the MLM.
+ For instance, Mailman with REMOVE_DKIM_HEADERS==3 might improve
deliverability in a pre-ARC world, but that option also renames the
Authentication-Results header, which breaks signing.
+
* Even if you use multiple DKIM keys for different domains, the ARC concept
should try to stick to one ADMD, so pick a primary domain and use that for
AR headers and outbound signing.
+
+TLS Session Resumption
+----------------------
+TLS Session Resumption for TLS 1.2 and TLS 1.3 connections can be used (defined
+in RFC 5077 for 1.2). The support for this can be included by building with
+EXPERIMENTAL_TLS_RESUME defined. This requires GnuTLS 3.6.3 or OpenSSL 1.1.1
+(or later).
+
+Session resumption (this is the "stateless" variant) involves the server sending
+a "session ticket" to the client on one connection, which can be stored by the
+client and used for a later session. The ticket contains sufficient state for
+the server to reconstruct the TLS session, avoiding some expensive crypto
+calculation and one full packet roundtrip time.
+
+Operational cost/benefit:
+ The extra data being transmitted costs a minor amount, and the client has
+ extra costs in storing and retrieving the data.
+
+ In the Exim/Gnutls implementation the extra cost on an initial connection
+ which is TLS1.2 over a loopback path is about 6ms on 2017-laptop class hardware.
+ The saved cost on a subsequent connection is about 4ms; three or more
+ connections become a net win. On longer network paths, two or more
+ connections will have an average lower startup time thanks to the one
+ saved packet roundtrip. TLS1.3 will save the crypto cpu costs but not any
+ packet roundtrips.
+
+ Since a new hints DB is used, the hints DB maintenance should be updated
+ to additionally handle "tls".
+
+Security aspects:
+ The session ticket is encrypted, but is obviously an additional security
+ vulnarability surface. An attacker able to decrypt it would have access
+ all connections using the resumed session.
+ The session ticket encryption key is not committed to storage by the server
+ and is rotated regularly (OpenSSL: 1hr, and one previous key is used for
+ overlap; GnuTLS 6hr but does not specify any overlap).
+ Tickets have limited lifetime (2hr, and new ones issued after 1hr under
+ OpenSSL. GnuTLS 2hr, appears to not do overlap).
+
+ There is a question-mark over the security of the Diffie-Helman parameters
+ used for session negotiation. TBD. q-value; cf bug 1895
+
+Observability:
+ New log_selector "tls_resumption", appends an asterisk to the tls_cipher "X="
+ element.
+
+ Variables $tls_{in,out}_resumption have bits 0-4 indicating respectively
+ support built, client requested ticket, client offered session,
+ server issued ticket, resume used. A suitable decode list is provided
+ in the builtin macro _RESUME_DECODE for ${listextract {}{}}.
+
+Issues:
+ In a resumed session:
+ $tls_{in,out}_cipher will have values different to the original (under GnuTLS)
+ $tls_{in,out}_ocsp will be "not requested" or "no response", and
+ hosts_require_ocsp will fail
+
+
--------------------------------------------------------------
End of file
--------------------------------------------------------------