Commit Graph
70 Commits
Author SHA1 Message Date
kay ozaki 1109639c0d enrich props for dkim and dmarc
Surface the underlying DKIM signature tags and the published DMARC
policy tags as auth-result props, so callers (and downstream
Authentication-Results headers) can see what was actually checked.

DKIM (crates/dkim):

* Factor populate_props() over the parsed tagged-header and emit
  header.d, header.i, header.a, header.s, header.c, header.t and
  header.x. The previous code only emitted d/i/a/s on the success
  path.
* On DKIMHeader::parse failure, fall back to a generic TaggedHeader
  parse so the resulting permerror AuthenticationResult still carries
  whatever tags were extractable (e.g. for expired signatures we now
  surface header.d and header.x). Covered by a new roundtrip test.

DMARC (crates/kumo-dmarc, crates/kumod):

* Record now retains the raw key=value tags it parsed, exposed via
  Record::tags().
* DispositionWithContext carries a new props map. Record::evaluate
  initialises it empty; drop the no-longer-relevant ToXml derive.
* DmarcContext::check copies the matched record's tags into the
  result props as policy.<tag> via a new policy_tags() helper.
* kumod's dmarc.rs threads result.props through into the
  AuthenticationResult returned to Lua (rather than starting empty),
  preserving the existing policy.published-domain-policy insertion
  for Quarantine/Reject.
2026-05-06 13:29:15 +00:00
Wez Furlong 502d0fd0dc dkim/spf/message: split_once('@') -> rsplit_once
This is a nod to better handling of weirdness like "info@"example.com
2026-04-08 14:12:59 +01:00
Wez Furlong fa33990106 dkim: enable signing legacy 8-bit content
This is really what all of the recent binary message commits
have been all about, allowing us to parse and sign legacy 8-bit japanese
content.
2026-03-28 07:20:34 +00:00
Wez Furlong 56f3535a85 mailparsing: refactor: parsing functions return BString
This is ostensibly "just" making the nom parsing functions return
BString instead of String, but the fan-out makes the commit much larger,
as a number of surfaces (eg: as_unstructured) have now changed types.
2026-03-28 07:20:31 +00:00
Wez Furlong e77579ad1e mailparsing: refactor: more BString 2026-03-28 07:20:29 +00:00
Wez Furlong c5129fb314 mailparsing: *-Results: refactor method/result -> BString 2026-03-28 07:20:29 +00:00
Wez Furlong 59784b9ab6 mailparsing: refactor serv_id fields String -> BString 2026-03-28 07:20:29 +00:00
Wez Furlong 5f191700ae dkim: upgrade nom to 8.0 2026-03-28 07:20:28 +00:00
Wez Furlong 4b84181479 SharedString: remove deref to str, deal with fanout
Removes the deref to str as this is now a fallible operation that
can panic.

Process the fanout of this change, which is dealing with more
possibility that things might be binary in more places.

There are some transitional APIs introduced as deprecated so that I'm
nagged to remove them already: I want to get the bulk of the code aware
of the possibility of binary sorted before I tackle adjusting the
message parser for that, so that that particular commit is more
self-contained.
2026-03-28 07:20:27 +00:00
Wez Furlong 084389b7ae dkim: remove implicit policy status for mismatching From/Signing domains
It was pointed out that we had this policy and that it wasn't part of
the spec.

That behavior was imported along with the crate when we forked it from
the cloudflare implementation.

Let's remove it; folks that need it can use a couple of lines of lua to
recreate it if they need it.
2026-03-28 07:20:08 +00:00
Aditya Ganti f0e48bdb27 dkim: fix wrapping issue
It was reported that AWS SES could mark the DKIM signature as failed
when using a specific combination of headers in the header list,
with specific lengths of the other fields.

The reason for this is that we had two passes of wrapping applied
to the header, and they might not agree on the formatting of
the header.

The solution is to remove the second pass and just take a bit more
care to emit the header in a wrapped form in the first instance,
that way there can be no discrepancy or conflict.

closes: https://github.com/KumoCorp/kumomta/pull/483
2026-02-28 08:17:09 +00:00
Wez Furlong 1da468ea9a arc: we can now produce an arc seal
refs: https://github.com/KumoCorp/kumomta/issues/16
2025-10-31 12:16:32 +00:00
Wez Furlong 4bd42ae57f arc: we can now validate an arc chain
refs: https://github.com/KumoCorp/kumomta/issues/16
2025-10-31 12:16:32 +00:00
Wez Furlong 823ee09db9 dkim: refactor and improve some of the test logic
refs: https://github.com/KumoCorp/kumomta/issues/16
2025-10-31 12:16:31 +00:00
Wez Furlong ee4266cd19 WIP: building out ARC processing
This is not fully baked yet, but I want to checkpoint.

refs: https://github.com/KumoCorp/kumomta/issues/16
2025-10-31 12:16:31 +00:00
Wez Furlong 93210b8346 dkim: add ARCSealHeader type
refs: https://github.com/KumoCorp/kumomta/issues/16
2025-10-31 12:16:31 +00:00
Wez Furlong 714abd4910 dkim: add ARCMessageSignature type
This parses an ARC-Message-Signature header field

refs: https://github.com/KumoCorp/kumomta/issues/16
2025-10-31 12:16:30 +00:00
Wez Furlong 1f6e4ea72d dkim: refactor: rename HEADER to DKIM_SIGNATURE_HEADER_NAME
refs: https://github.com/KumoCorp/kumomta/issues/16
2025-10-31 12:16:30 +00:00
Wez Furlong e9deb2500d dkim: fix subdomain checking issue 2025-10-31 12:16:30 +00:00
Wez Furlong 6ccb337d8f dkim: extract header parsing guts to TaggedHeader struct
This should facilitate the implementation of ARC in a later commit.

refs: https://github.com/KumoCorp/kumomta/issues/16
2025-10-31 12:16:29 +00:00
Wez Furlong b73d81b0fc dns: utilities and plumbing for querying DNSBLs
This commit does some plumbing work to allow defining alternative
resolvers with different names that can be used to query different
upstream DNS servers.

This is most useful when implement DNSBLs where you might have a custom
DNS server configured with one or more RBL zones that is reserved purely
for RBL lookups.

The plumbing introduces a more regular syntax for defining one of the
various implementations of the Resolver trait, as well as a new
Aggregate resolver impl that can query across multiple Resolvers until a
query is satisfied.

This allows some interesting and powerful configurations, such as
loading a static zone file into memory to query it directly, and/or
blending that together with querying either the system or some other
upstream DNS server as a fallback.

The various lookup functions (except for MX!) have been updated to
accept an optional alternate resolver name, so that they work together
with the above.

A new rbl_lookup function is also provided as a convenience for querying
the most common form of RBLS.

ptr_host and reverse_ip are two string utility functions that are likely
not going to be widely used, but are very convenient to have when you
do have a usecase that requires it!

closes: https://github.com/KumoCorp/kumomta/issues/269
2025-10-10 18:45:52 +01:00
Sophia Turner 9c0b7511b1 fix up some lifetime hiding 2025-08-25 23:55:24 -07:00
Wez Furlong a1f4172f4f more clippy 2025-04-09 10:26:12 -07:00
Wez Furlong e3fdcaa628 cargo clippy --fix 2025-04-09 10:26:07 -07:00
Wez Furlong c7fe9489c3 fix: failure to load ed25519 dkim private key
This was probably the casualty of some earlier refactoring
that has gone unnoticed until now.

Since we don't have explicit context on which key format to
parse in this helper function that is used in multiple places,
let's just make it try to parse both rsa and ed25519.

closes: #368
2025-04-03 10:11:24 -07:00
Wez Furlong 501d1f26e5 dkim: support verifying multiple keys
While auditing Answer::as_txt usage as a follow up from the recent
SPF fix, I noticed a TODO in the dkim code (which we forked from
another implementation) to support processing multiple TXT
records.

This commit implements the necessary tweaks to extract multiple
signatures and attempt to verify them against the incoming message.
2025-03-07 09:26:55 -07:00
Wez Furlong 6cf315c37d dkim: extract public key parsing guts
No overall functional change, this is just moving the guts
around a bit to support validating from multiple signatures
in a later commit.
2025-03-07 09:05:02 -07:00
Wez Furlong 054508321b lruttl: drop moka, roll our own cache on top of dashmap
I've been having trouble with moka's eviction policy leading
to some unwanted duplicate work, especially when the system
is under load and the latency is higher.

This commit swaps it out in favor of a relatively simple
implementation on top of dashmap; that gives us a decent
concurrent base and better respects the concurrency
limit and LRU around the contentious insertion case.

For LRU, since dashmap is a concurrent data structure, it
is ~impossible to use the classic doubly linked LRU approach
safely.

The strategy used here is a proabalistic sampling LRU using a technique
similar to that used in redis.

We use atomics to tag read and write entries with a monotonic counter.
Entries with smaller counter numbers are least-recent than entries with
a larger number.

During eviction we take a random sample of 10 entries from the cache
map, remove any that have expired due to TTL, and if we still need space
after that, we'll pick up to half of those to evict.  That guarantees
that we don't pick the most recent entry of the sample, and on aggregate
this should be a reasonable approximation of the "true" LRU.

In the background, every 30s, a maintenance task will remove expired
entries from the caches.  This is primarily to reduce memory utilization
when the cache is otherwise idle.  When a cache is hot, expiration is
performed as part of the eviction logic described above.
2025-03-04 16:23:16 -07:00
Wez Furlong d331df5965 dkim: add separate key cache to signer machinery
The signer cache maps the signer parameters to a pre-made
signing context.

It is essentially a map of (domain, key) -> signer.

Assuming that the (domain,key) tuple is unique, then this is
a good, effective use of that cache.

However, if multiple domains can share the same key then we
can end up re-parsing the same key data for each of them,
which is moderately expensive and a waste of CPU.

This commit introduces an additional cache for the key source to
the resultant compiled key.

This allows sharing of the same compiled key across signing
parameters that otherwise vary, and should help to shave off
some latency.
2025-02-06 16:47:15 -07:00
Wez Furlong f637db6b36 misc: fix some warnings with rust 1.83 2024-12-03 08:44:32 -07:00
Dirkjan Ochtman 603fd50ec5 dns-resolver: improve TestResolver logic 2024-11-07 07:52:37 -07:00
Dirkjan Ochtman 197b73330b dkim: migrate to dns-resolver Resolver trait 2024-10-17 09:46:59 -07:00
Dirkjan Ochtman bf896f5365 Replace once_cell with LazyLock/OnceLock 2024-09-24 06:31:21 -07:00
Wez Furlong 261999bde0 fix: dkim signer must set time before setting expiry
otherwise an error will always be thrown
2024-06-11 06:53:27 -07:00
Wez Furlong bcf30af5c0 remove duplicate quick-error dep; move to thiserror in dkim 2024-05-23 08:31:05 -07:00
Wez Furlong fa1dec61a2 dkim: remove usage of rsa crate
We keep getting asked about
https://rustsec.org/advisories/RUSTSEC-2023-0071.html and how it impacts
kumomta.

The answer to that question is: in the default build configuration, we
use openssl's RSA signing implementation rather than that of the rsa
crate.  The reason for this is that OpenSSL's RSA implementation is due
to the performance gap between the two implementations
(https://github.com/RustCrypto/RSA/issues/339). The result of this is
that the problematic code and attack vector described in the security
advisory does not apply to KumoMTA, because it is not used to compute
any signatures.

In the interest of not raising any false alarms as more and more people
perform security analyses on kumomta, this commit removes the `rsa`
crate from the build graph. In order to do so, we need to port
verification over to the openssl RSA implementation which is what this
commit does.

I look forward to a future version of the `rsa` crate being published
that has this issue resolved, and that closes the performance gap!

refs: https://github.com/RustCrypto/RSA/issues/390
2024-04-10 13:26:49 -07:00
Wez Furlong 78e062ad64 chrono: fixup now-deprecated calls 2024-03-11 07:50:01 -07:00
Wez Furlong 334a19963c deps: base64 -> data_encoding
dependabot wanted to upgrade to base64 0.21, but I resent that
the API for the common case of base64 encoding got more difficult
in the move from 0.13.

So, I'm switching us to data_encoding's implementation instead.

Note that the overall rust ecosystem on which we depend here,
transitively depends on 3 different versions of the base64 crate, so
even though we've removed direct deps on base64 from kumomta, the lock
file still references base64 0.10, 0.13 and 0.21, so that kinda sucks.
2024-02-06 13:56:41 -07:00
Wez Furlong 02cd1ef7bf NEW: dkim over signing by setting over_sign = true in the signer
refs: https://github.com/KumoCorp/kumomta/issues/111
2024-01-18 12:59:41 -07:00
Wez Furlong f979bdc6b0 explicitly wrap dkim h value on colons
My reading of the spec says that we can swap `:` for `:FWS` when
wrapping, so let's try that.

Previously, we'd wrap on the hyphens in header names and that
seems to cause some problems for some verifiers.
2023-11-17 11:30:57 -07:00
Wez Furlong 546237d11c deps: trust-dns -> hickory
The project was rebranded relatively recently; switch
to the new version
2023-11-04 10:26:49 -07:00
Wez Furlong d8bea99caa docs: fixup some rustdoc warnings 2023-09-14 10:29:50 -07:00
Wez Furlong 92332a7a65 dkim: have verification return Vec<AuthenticationResult>
Replaces the dkim result types from this crate with
AuthenticationResults which contain the additional
data required to construct high-quality Authentication-Result
headers.

refs: https://github.com/KumoCorp/kumomta/issues/82
2023-09-14 08:33:10 -07:00
Wez Furlong 10ab7d8f11 mailparsing: conformance: bundle in some message checks
Include line length and non-7-bit-ness checks.
Rename from HeaderConformance to a more general MessageConformance.
2023-08-29 12:44:42 -07:00
Wez Furlong 5b27d4de71 dkim: tidy up verification result type 2023-08-25 08:55:28 -07:00
Wez Furlong 34bae0755c dkim: remove dead code 2023-08-25 08:55:28 -07:00
Wez Furlong 909cc95001 dkim: improve error reporting around message parsing
Use Result rather than Option.
Bubble up non-canonical line endings explicitly as an issue.
2023-08-25 08:55:28 -07:00
Wez Furlong 452bede2e1 dkim: switch to our own mailparsing implementation 2023-08-25 08:55:28 -07:00
Wez Furlong 413566cd94 dkim: simplify building test resolver 2023-08-25 08:55:28 -07:00
Wez Furlong a42d8967cd dkim: remove Arc requirement from verification interface
It seems like this was working around an issue that does
not appear to be an issue any more.
2023-08-25 08:55:28 -07:00