DMARC RFC Summary

14 Apr 2022

This is my short summary of the DMARC RFC. This can be a quick reference or a starting point for anyone interested in knowing more about the protocol. I would recommend reading the full RFC document. With that said, let’s get into the summary.

It would be better if the reader is familiar with the SPF and DKIM mechanisms before proceeding further

Overview

DMARC is a mechanism through which a mail sending organisation can communicate its’ domain-level policies and preferences for email validation, reporting and disposition. It also allows the domain owners to see the impact of the deployed authentication mechanisms.

It is not an authentication mechanism. It is currently dependent on SPF and DKIM for authentication, however it is extensible by design and allows addition of new mechanisms, if the need arises.

High Level Goals

  • Allow Domain Owners to assert the preferred handling of authentication failures, for messages purporting to have authorship within the domain.
  • Allow Domain Owners to verify their authentication deployment.
  • Minimise implementation complexity for both senders and receivers, as well as the impact on handling and delivery of legitimate messages.
  • Reduce the amount of successfully delivered spoofed email.
  • Work at Internet scale.

Operation

  1. Domain owners publish SPF, DKIM and DMARC records
  2. The owner sends an email using a mail service which handles DKIM signing.
  3. Mail passes through multiple relays and arrives at the destination.
  4. Extract the domain in From field from the message.
  5. DMARC module attempts to retrieve a policy from the DNS for that domain. If none is found, the DMARC module determines the Organisational Domain and repeats the attempt to retrieve a policy from the DNS. Continue if one is found, or terminate DMARC evaluation otherwise.
  6. Perform DKIM signature verification checks. A single email could contain multiple DKIM signatures. The results of this step are passed to the remainder of the algorithm and MUST include the value of the d= tag from each checked DKIM signature.
  7. Perform SPF validation checks. The results of this step are passed to the remainder of the algorithm and MUST include the domain name in MailFrom used to complete the SPF check.
  8. Conduct Identifier Alignment checks. With authentication checks and policy discovery performed, the Mail Receiver checks to see if d= and MailFrom domains align with From domain according to the policies described by tags in the table below. If one or more domains align, the message is considered to pass the DMARC mechanism check.
  9. Apply policy for failed messages.
  10. If any feedback is requested, data needed for the feedback is collected.

Records

  • The DMARC record is a TXT record that is published for the subdomain _dmarc.domain.com.
  • Only one DMARC record can be present for a domain, if there are more than one, then all the records are ignored and DMARC processing does not happen.
  • It can contain only the tags given below, everything else is ignored
Tag Name Description Possible Values Default Value Optional
adkim DKIM alignment mode r: relaxed, the d=<domain_name> from DKIM record must be an exact match to the domain obtained from the From field.s: strict, the d=<domain_name> from DKIM record can be a match with the domain obtained from the From field and all its subdomains. r Yes
aspf SPF alignment mode r: relaxed, the domain in the MailFrom field evaluated during SPF checks must be an exact match to the domain obtained from the From field.s: strict, the domain in the MailFrom field evaluated during SPF checks can be a match with the domain obtained from the From field and all its subdomains. r Yes
fo Failure reporting options Applies only if ruf tag is also present.Multiple values can be present, separated by a colon.0:Generates a DMARC failure report only if all the authentication mechanisms fail.1:Generates a DMARC failure report only if any one of the authentication mechanisms fail.d:Generates a DKIM failure report if DKIM authentication fails.s:Generates a SPF failure report if SPF authentication fails. 0 Yes
p Requested handling policy Indicates the policy to be enacted if a message fails DMARC checks.Policy is applies to the domain and subdomains, unless sp tag is present.none: Do nothingquarantine: Treat as suspiciousreject: Reject the mail none No
pct Sampling rate Percentage of messages from the domain to which the DMARC policy is to be applied.It enables slow rollout of the policy and allows debugging.The actual selection logic is left to the receiver.A value between 0 and 100. 100 Yes
rf Failure reporting format Multiple values can be present, separated by a colon.Currently supports only afrf format. afrf Yes
ri Failure reporting interval Interval requested between aggregate reports in seconds.Daily reports will be supported by everyone. Intervals less than that are not guaranteed to be supported. 86400 Yes
rua Emails for receiving aggregate reports Comma separated values of valid email addresses in mailto: URI format. An exclamation mark can be used with each address to specify the maximum size of a report that can be received by the email.Two emails must be supported by the receiver, anything more is optional.Ex: [email protected]!25600k,[email protected]!25m Yes
ruf Emails for receiving failure reports Same as rua tag Yes
sp Policy for subdomains Same as p tag none Yes
v Identifies the record as DMARC record DMARC1 No

Organisational Domain

An organisational domain is defined as the domain that was registered with the name registrar. It is obtained using the algorithm given below:

  1. Acquire a “public suffix” list, i.e., a list of DNS domain names reserved for registrations. Some country Top-Level Domains (TLDs) make specific registration requirements, e.g., the United Kingdom places company registrations under “.co.uk”; other TLDs such as “.com” appear in the IANA registry of top-level DNS domains. A public suffix list is the union of all of these. Public Suffix List is one such example.
  2. Break the subject DNS domain name into a set of “n” ordered labels. Number these labels from right to left; e.g., for “example.com”, “com” would be label 1 and “example” would be label 2.
  3. Search the public suffix list for the name that matches the largest number of labels found in the subject DNS domain. Let that number be “x”.
  4. Construct a new DNS domain name using the name that matched from the public suffix list and prefixing to it the “x+1"th label from the subject domain. This new name is the Organisational Domain.

Thus, since “com” is an IANA-registered TLD, a subject domain of “a.b.c.d.example.com” would have an Organisational Domain of “example.com”.

The process of determining a suffix is currently a heuristic one. No list is guaranteed to be accurate or current.

Feedback

External Services for Receiving Reports

If there is an email address is present in the DMARC record in rua and/or ruf tags whose host part does not match the Organisational Domain obtained from the received mail, the following steps need to be done. It is optional to do this now, but it is better for us (third party report receiving service) to be complaint with this.

If we mvigil.com have cloudsek.com as client and we need to be able to receive reports for them. 1. They have to publish an email provided by us in their DMARC record. 2. We will have to create the TXT record v=DMARC1 for the subdomain cloudsek.com._report._dmarc.mvigil.com 3. (Optional) We can also include the same record in the above step to the subdomain *._report._dmarc.mvigil.com to enable receiving for any domain.

The algorithm used by the receiver to validate the third party is as follows:

  1. Extract the host portion of the authority component of the URI. Call this the “destination host”, as it refers to a Report Receiver.
  2. Prepend the string _report._dmarc.
  3. Prepend the domain name from which the policy was retrieved, after conversion to an A-label if needed.
  4. Query the DNS for a TXT record at the constructed name. If the result of this request is a temporary DNS error of some kind (e.g., a timeout), the Mail Receiver MAY elect to temporarily fail the delivery so the verification test can be repeated later.
  5. For each record returned, parse the result as a series of “tag=value” pairs, i.e., the same overall format as the policy record. In particular, the v=DMARC1 tag is mandatory and MUST appear first in the list. Discard any that do not pass this test.
  6. If the result includes no TXT resource records that pass basic parsing, a positive determination of the external reporting relationship cannot be made; stop.
  7. If at least one TXT resource record remains in the set after parsing, then the external reporting arrangement was authorised by the Report Receiver.
  8. If a rua or ruf tag is thus discovered, replace the corresponding value extracted from the domain’s DMARC policy record with the one found in this record. This permits the Report Receiver to override the report destination. However, to prevent loops or indirect abuse, the overriding URI MUST use the same destination host from the first step.

Aggregate Report

The DMARC aggregate feedback report is designed to provide domain owners with precise insight into:

  • authentication results,
  • corrective action that needs to be taken by Domain Owners, and
  • the effect of Domain Owner DMARC policy on email streams processed by mail receivers.

It is an aggregation of DMARC policy data over a specified time period. The aggregate data will be an XML file compressed using GZIP.

Note: Take additional care while unzipping and parsing this data due to the possibility for (de)serialisation vulnerabilities to creep in.

The XML format is given below:

<?xml version="1.0"?>
   <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
     targetNamespace="http://dmarc.org/dmarc-xml/0.1">

   <!-- The time range in UTC covered by messages in this report, specified in seconds since epoch. -->
   <xs:complexType name="DateRangeType">
     <xs:all>
       <xs:element name="begin" type="xs:integer"/>
       <xs:element name="end" type="xs:integer"/>
     </xs:all>
   </xs:complexType>

   <!-- Report generator metadata. -->
   <xs:complexType name="ReportMetadataType">
     <xs:sequence>
       <xs:element name="org_name" type="xs:string"/>
       <xs:element name="email" type="xs:string"/>
       <xs:element name="extra_contact_info" type="xs:string"
                   minOccurs="0"/>
       <xs:element name="report_id" type="xs:string"/>
       <xs:element name="date_range" type="DateRangeType"/>
       <xs:element name="error" type="xs:string" minOccurs="0"
                   maxOccurs="unbounded"/>
     </xs:sequence>
   </xs:complexType>

   <!-- Alignment mode (relaxed or strict) for DKIM and SPF. -->
   <xs:simpleType name="AlignmentType">
     <xs:restriction base="xs:string">
       <xs:enumeration value="r"/>
       <xs:enumeration value="s"/>
     </xs:restriction>
   </xs:simpleType>

   <!-- The policy actions specified by p and sp in the DMARC record. -->
   <xs:simpleType name="DispositionType">
     <xs:restriction base="xs:string">
       <xs:enumeration value="none"/>
       <xs:enumeration value="quarantine"/>
       <xs:enumeration value="reject"/>
     </xs:restriction>
   </xs:simpleType>

   <!-- The DMARC policy that applied to the messages in this report. -->
   <xs:complexType name="PolicyPublishedType">
     <xs:all>
       <!-- The domain at which the DMARC record was found. -->
       <xs:element name="domain" type="xs:string"/>
       <!-- The DKIM alignment mode. -->
       <xs:element name="adkim" type="AlignmentType"
                   minOccurs="0"/>
       <!-- The SPF alignment mode. -->
       <xs:element name="aspf" type="AlignmentType"
                   minOccurs="0"/>
       <!-- The policy to apply to messages from the domain. -->
       <xs:element name="p" type="DispositionType"/>
       <!-- The policy to apply to messages from subdomains. -->
       <xs:element name="sp" type="DispositionType"/>
       <!-- The percent of messages to which policy applies. -->
       <xs:element name="pct" type="xs:integer"/>
       <!-- Failure reporting options in effect. -->
       <xs:element name="fo" type="xs:string"/>
     </xs:all>
   </xs:complexType>

   <!-- The DMARC-aligned authentication result. -->
   <xs:simpleType name="DMARCResultType">
     <xs:restriction base="xs:string">
       <xs:enumeration value="pass"/>
       <xs:enumeration value="fail"/>
     </xs:restriction>
   </xs:simpleType>

   <!-- Reasons that may affect DMARC disposition or execution thereof. -->
   <xs:simpleType name="PolicyOverrideType">
     <xs:restriction base="xs:string">
       <xs:enumeration value="forwarded"/>
       <xs:enumeration value="sampled_out"/>
       <xs:enumeration value="trusted_forwarder"/>
       <xs:enumeration value="mailing_list"/>
       <xs:enumeration value="local_policy"/>
       <xs:enumeration value="other"/>
     </xs:restriction>
   </xs:simpleType>

   <!-- How do we allow report generators to include new classes of override reasons if they want to be more specific than "other"? -->
   <xs:complexType name="PolicyOverrideReason">
     <xs:all>
       <xs:element name="type" type="PolicyOverrideType"/>
       <xs:element name="comment" type="xs:string"
                   minOccurs="0"/>
     </xs:all>
   </xs:complexType>

   <!-- Taking into account everything else in the record, the results of applying DMARC. -->
   <xs:complexType name="PolicyEvaluatedType">
     <xs:sequence>
       <xs:element name="disposition" type="DispositionType"/>
       <xs:element name="dkim" type="DMARCResultType"/>
       <xs:element name="spf" type="DMARCResultType"/>
       <xs:element name="reason" type="PolicyOverrideReason"
                   minOccurs="0" maxOccurs="unbounded"/>
     </xs:sequence>
   </xs:complexType>

   <xs:simpleType name="IPAddress">
     <xs:restriction base="xs:string">
       <xs:pattern value="((1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5]).){3}
                   (1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])|
                   ([A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}"/>
     </xs:restriction>
   </xs:simpleType>

   <xs:complexType name="RowType">
     <xs:all>
       <!-- The connecting IP. -->
       <xs:element name="source_ip" type="IPAddress"/>
       <!-- The number of matching messages. -->
       <xs:element name="count" type="xs:integer"/>
       <!-- The DMARC disposition applying to matching messages. -->
       <xs:element name="policy_evaluated"
                   type="PolicyEvaluatedType"
                   minOccurs="1"/>
     </xs:all>
   </xs:complexType>

   <xs:complexType name="IdentifierType">
     <xs:all>
       <!-- The envelope recipient domain. -->
       <xs:element name="envelope_to" type="xs:string"
                   minOccurs="0"/>
       <!-- The [RFC5321](https://datatracker.ietf.org/doc/html/rfc5321).MailFrom domain. -->
       <xs:element name="envelope_from" type="xs:string"
                   minOccurs="1"/>
       <!-- The [RFC5322](https://datatracker.ietf.org/doc/html/rfc5322).From domain. -->
       <xs:element name="header_from" type="xs:string"
                   minOccurs="1"/>
     </xs:all>
   </xs:complexType>

   <!-- DKIM verification result, according to [RFC 7001 Section 2.6.1](https://datatracker.ietf.org/doc/html/rfc7001#section-2.6.1). -->
   <xs:simpleType name="DKIMResultType">
     <xs:restriction base="xs:string">
       <xs:enumeration value="none"/>
       <xs:enumeration value="pass"/>
       <xs:enumeration value="fail"/>
       <xs:enumeration value="policy"/>
       <xs:enumeration value="neutral"/>
       <xs:enumeration value="temperror"/>
       <xs:enumeration value="permerror"/>
     </xs:restriction>
   </xs:simpleType>

   <xs:complexType name="DKIMAuthResultType">
     <xs:all>
       <!-- The "d=" parameter in the signature. -->
       <xs:element name="domain" type="xs:string"
                   minOccurs="1"/>
       <!-- The "s=" parameter in the signature. -->
       <xs:element name="selector" type="xs:string"
                   minOccurs="0"/>
       <!-- The DKIM verification result. -->
       <xs:element name="result" type="DKIMResultType"
                   minOccurs="1"/>
       <!-- Any extra information (e.g., from
            Authentication-Results). -->
       <xs:element name="human_result" type="xs:string"
                   minOccurs="0"/>
     </xs:all>
   </xs:complexType>

   <!-- SPF domain scope. -->
   <xs:simpleType name="SPFDomainScope">
     <xs:restriction base="xs:string">
       <xs:enumeration value="helo"/>
       <xs:enumeration value="mfrom"/>
     </xs:restriction>
   </xs:simpleType>

   <!-- SPF result. -->
   <xs:simpleType name="SPFResultType">
     <xs:restriction base="xs:string">
       <xs:enumeration value="none"/>
       <xs:enumeration value="neutral"/>
       <xs:enumeration value="pass"/>
       <xs:enumeration value="fail"/>
       <xs:enumeration value="softfail"/>
       <!-- "TempError" commonly implemented as "unknown". -->
       <xs:enumeration value="temperror"/>
       <!-- "PermError" commonly implemented as "error". -->
       <xs:enumeration value="permerror"/>
     </xs:restriction>
   </xs:simpleType>

   <xs:complexType name="SPFAuthResultType">
     <xs:all>
       <!-- The checked domain. -->
       <xs:element name="domain" type="xs:string" minOccurs="1"/>
       <!-- The scope of the checked domain. -->
       <xs:element name="scope" type="SPFDomainScope" minOccurs="1"/>
       <!-- The SPF verification result. -->
       <xs:element name="result" type="SPFResultType"
                   minOccurs="1"/>
     </xs:all>
   </xs:complexType>

   <!-- This element contains DKIM and SPF results, uninterpreted with respect to DMARC. -->
   <xs:complexType name="AuthResultType">
     <xs:sequence>
       <!-- There may be no DKIM signatures, or multiple DKIM signatures. -->
       <xs:element name="dkim" type="DKIMAuthResultType"
         minOccurs="0" maxOccurs="unbounded"/>
       <!-- There will always be at least one SPF result. -->
       <xs:element name="spf" type="SPFAuthResultType" minOccurs="1"
                   maxOccurs="unbounded"/>
     </xs:sequence>
   </xs:complexType>

   <!-- This element contains all the authentication results that were evaluated by the receiving system for the given set of messages. -->
   <xs:complexType name="RecordType">
     <xs:sequence>
       <xs:element name="row" type="RowType"/>
       <xs:element name="identifiers" type="IdentifierType"/>
       <xs:element name="auth_results" type="AuthResultType"/>
     </xs:sequence>
   </xs:complexType>

   <!-- Parent -->
   <xs:element name="feedback">
     <xs:complexType>
       <xs:sequence>
         <xs:element name="version"
                     type="xs:decimal"/>
         <xs:element name="report_metadata"
                     type="ReportMetadataType"/>
         <xs:element name="policy_published"
                     type="PolicyPublishedType"/>
         <xs:element name="record" type="RecordType"
                     maxOccurs="unbounded"/>
       </xs:sequence>
     </xs:complexType>
   </xs:element>
   </xs:schema>

Descriptions of the PolicyOverrideTypes:

forwarded: The message was relayed via a known forwarder, or local heuristics identified the message as likely having been forwarded. There is no expectation that authentication would pass.

local_policy: The Mail Receiver’s local policy exempted the message from being subjected to the Domain Owner’s requested policy action.

mailing_list: Local heuristics determined that the message arrived via a mailing list, and thus authentication of the original message was not expected to succeed.

other: Some policy exception not covered by the other entries in this list occurred. Additional detail can be found in the PolicyOverrideReason’s “comment” field.

sampled_out: The message was exempted from application of policy by the “pct” setting in the DMARC policy record.

trusted_forwarder: Message authentication failure was anticipated by other evidence linking the message to a locally maintained list of known and trusted forwarders.

The “version” for reports generated per this specification MUST be the value 1.0.

Failure Reports

Failure reports are normally generated and sent almost immediately after the Mail Receiver detects a DMARC failure. Rather than waiting for an aggregate report, these reports are useful for quickly notifying the Domain Owners when there is an authentication failure. Whether the failure is due to an infrastructure problem or the message is inauthentic, failure reports also provide more information about the failed message than is available in an aggregate report.

The format is specified here. Another good reference

Noteworthy Points

  • A subdomain takeover, if present, will allow the attacker to bypass all these checks and spoof the domain’s email.

Tags