Skip to content

Make libspf2 a hard dependency; remove internal SPF implementation #304

@thegushi

Description

@thegushi

Summary

The internal SPF implementation in libopendmarc/opendmarc_spf.c (compiled when libspf2 is not present) has several known compliance gaps relative to RFC 7208. We believe libspf2 should be a hard build dependency and the internal fallback should be removed.

Known failure modes in the internal implementation

1. Void lookup limit not enforced (RFC 7208 §4.6.4)

RFC 7208 requires that void DNS lookups — queries that return NXDOMAIN or empty results — be limited to 2 separately from the overall 10-lookup cap. The internal implementation tracks total lookups (MAX_SPF_DNS_LOOKUPS) but has no void lookup counter. A domain with a pathological SPF record can cause more void lookups than the spec permits, leading to divergent results from a compliant evaluator.

2. Multiple SPF TXT records not detected (RFC 7208 §3.2)

If a domain publishes more than one v=spf1 TXT record, the result MUST be permerror. The internal DNS layer (opendmarc_spf_dns.c) returns the first matching TXT record and silently ignores any subsequent ones. A domain with duplicate or conflicting SPF records will be evaluated rather than returning permerror, potentially producing a pass where a conformant evaluator would refuse to evaluate.

3. IPv4-mapped IPv6 addresses (RFC 7208 §5)

When a sender connects over IPv6 with an IPv4-mapped address (::ffff:192.0.2.1), SPF evaluation should use the IPv4 address and mechanisms. The code contains comments acknowledging uncertainty about this mapping ("we don't care at this point if it is ipv6 or ipv4"), suggesting this case is not reliably handled.

4. Dual CIDR-length notation (RFC 7208 §5.6, §5.7)

The a and mx mechanisms support a dual CIDR notation (a/24//48) specifying separate prefix lengths for IPv4 and IPv6 matches. Incorrect or absent handling here produces wrong pass/fail results for affected records.

Why libspf2 should be required

libspf2 is a dedicated SPF implementation with years of real-world testing and bug fixes across all of the above cases. Maintaining a parallel SPF implementation inside OpenDMARC duplicates that effort poorly. The internal fallback also creates a situation where two installations of OpenDMARC with identical configuration can produce different DMARC verdicts depending on whether libspf2 happened to be present at build time — which is a hard problem to diagnose in production.

The HAVE_SPF2_H conditional should become a hard build requirement, and the #else branch should be removed.

We would welcome a patch to do this, or to at minimum make ./configure fail with a clear error when libspf2 is absent and SPFSelfValidate support is requested.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions