Author: fkento
Date: Tue Mar 17 19:01:30 2026
New Revision: 1932360
Log:
ARC plugin: re-use arc= results from AuthRes plugin when available
The ARC plugin now checks for parsed arc= results from the AuthRes
plugin before performing its own Mail::DKIM::ARC::Verifier-based
cryptographic verification. Plugin priorities swapped so AuthRes (-20)
runs before ARC (-10).
Trusted AAR parsing refactored into a shared helper so it works both
with the native verifier path and the AuthRes path (which parses
ARC-Seal headers directly for trusted domain detection).
If AuthRes is not loaded, behavior is unchanged.
Modified:
spamassassin/trunk/UPGRADE
spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/ARC.pm
spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/AuthRes.pm
Modified: spamassassin/trunk/UPGRADE
==============================================================================
--- spamassassin/trunk/UPGRADE Tue Mar 17 18:19:19 2026 (r1932359)
+++ spamassassin/trunk/UPGRADE Tue Mar 17 19:01:30 2026 (r1932360)
@@ -13,6 +13,12 @@ Note for Users Upgrading to SpamAssassin
Without this setting, ARC signatures are verified but
ARC-Authentication-Results headers are not parsed.
+- Mail::SpamAssassin::Plugin::ARC re-uses ARC results from the
+ AuthRes plugin if available, avoiding redundant cryptographic
+ verification with Mail::DKIM. If no AuthRes results are available,
+ the plugin falls back to its own Mail::DKIM::ARC::Verifier-based
+ verification as before.
+
- Mail::SpamAssassin::Plugin::SPF no longer parses Authentication-Results
headers directly. It now uses results from
Mail::SpamAssassin::Plugin::AuthRes if available, falling back to
Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/ARC.pm
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/ARC.pm Tue Mar 17 18:19:19 2026 (r1932359)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/ARC.pm Tue Mar 17 19:01:30 2026 (r1932360)
@@ -32,8 +32,11 @@ Mail::SpamAssassin::Plugin::ARC - perfor
This SpamAssassin plugin implements ARC (Authenticated Received Chain)
verification as described by RFC 8617.
-It requires the C<Mail::DKIM> CPAN module version 0.50 or later to operate,
-specifically the C<Mail::DKIM::ARC::Verifier> module.
+If the C<AuthRes> plugin is loaded and has parsed C<arc=> results from
+Authentication-Results headers, those results will be re-used and native
+verification is skipped. Otherwise, the plugin performs its own
+cryptographic verification using the C<Mail::DKIM::ARC::Verifier> module
+(version 0.50 or later required).
=head1 SEE ALSO
@@ -69,8 +72,9 @@ sub new {
$self->register_eval_rule("check_arc_valid", $Mail::SpamAssassin::Conf::TYPE_FULL_EVALS);
$self->register_eval_rule("check_arc_trusted", $Mail::SpamAssassin::Conf::TYPE_FULL_EVALS);
- # run before DKIM, SPF, DMARC so arc_auth_results is available
- $self->register_method_priority("parsed_metadata", -20);
+ # run after AuthRes (-20) so we can re-use arc= results,
+ # but before DKIM, SPF, DMARC (0) so arc_auth_results is available
+ $self->register_method_priority("parsed_metadata", -10);
$self->set_config($mailsaobject->{conf});
@@ -165,6 +169,8 @@ sub parsed_metadata {
if ($pms->{arc_signatures_ready}) {
$self->_check_arc_valid_signature($pms, \@arc_signatures);
+ } elsif ($self->_check_arc_from_authres($pms)) {
+ # re-used ARC results from AuthRes plugin
} elsif (!$pms->is_dns_available()) {
dbg("arc: signature verification disabled, DNS resolving not available");
} elsif (!$self->_arc_load_modules()) {
@@ -204,6 +210,34 @@ sub check_arc_trusted {
return $pms->{arc_auth_results} ? 1 : 0;
}
+sub _check_arc_from_authres {
+ my ($self, $pms) = @_;
+
+ return 0 if !$pms->{authres_parsed} || !$pms->{authres_parsed}{arc}
+ || !@{$pms->{authres_parsed}{arc}};
+
+ dbg("arc: checking for AuthRes plugin ARC results");
+
+ foreach my $arc_result (@{$pms->{authres_parsed}{arc}}) {
+ my $result = lc($arc_result->{result} || '');
+ next unless $result =~ /^(pass|fail|none)$/;
+
+ dbg("arc: re-using result from AuthRes plugin: %s", $result);
+
+ if ($result eq 'pass') {
+ $pms->{arc_signed} = 1;
+ $pms->{arc_valid} = 1;
+ $self->_parse_trusted_aar_from_headers($pms);
+ } elsif ($result eq 'fail') {
+ $pms->{arc_signed} = 1;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
# ---------------------------------------------------------------------------
sub _arc_load_modules {
@@ -395,6 +429,38 @@ sub _parse_trusted_aar {
}
return if !%trusted_indices;
+ $self->_parse_aar_by_trusted_indices($pms, \%trusted_indices);
+}
+
+sub _parse_trusted_aar_from_headers {
+ my ($self, $pms) = @_;
+
+ my $trusted = $pms->{conf}->{arc_trusted_sealers};
+ return if !$trusted || !%$trusted;
+
+ # Build set of ARC instance indices from ARC-Seal headers
+ my %trusted_indices;
+ my @seals = $pms->{msg}->get_pristine_header('ARC-Seal');
+ foreach my $seal (@seals) {
+ chomp $seal;
+ my ($i) = $seal =~ /\bi\s*=\s*(\d+)/;
+ my ($d) = $seal =~ /\bd\s*=\s*([^\s;]+)/;
+ next if !defined $d || !defined $i;
+ if ($trusted->{lc $d}) {
+ $trusted_indices{$i} = 1;
+ dbg("arc: seal i=%s d=%s is trusted", $i, $d);
+ } else {
+ dbg("arc: seal i=%s d=%s is not trusted", $i, $d);
+ }
+ }
+ return if !%trusted_indices;
+
+ $self->_parse_aar_by_trusted_indices($pms, \%trusted_indices);
+}
+
+sub _parse_aar_by_trusted_indices {
+ my ($self, $pms, $trusted_indices) = @_;
+
# Parse ARC-Authentication-Results headers from trusted sealers
my @aar = $pms->{msg}->get_pristine_header('ARC-Authentication-Results');
return if !@aar;
@@ -417,7 +483,7 @@ sub _parse_trusted_aar {
next;
}
- if (!$trusted_indices{$arc_index}) {
+ if (!$trusted_indices->{$arc_index}) {
dbg("arc: AAR i=%s not from trusted sealer, skipping", $arc_index);
next;
}
Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/AuthRes.pm
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/AuthRes.pm Tue Mar 17 18:19:19 2026 (r1932359)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/AuthRes.pm Tue Mar 17 19:01:30 2026 (r1932360)
@@ -59,7 +59,7 @@ sub new {
$self->set_config($mailsa->{conf});
# process first as other plugins might depend on us
- $self->register_method_priority("parsed_metadata", -10);
+ $self->register_method_priority("parsed_metadata", -20);
$self->register_eval_rule("check_authres_result", $Mail::SpamAssassin::Conf::TYPE_HEAD_EVALS);
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.