2012-04-29 10:35:59 +02:00
#!perl -w
2012-05-04 20:02:12 +02:00
=head1 NAME
domainkeys: validate a DomainKeys signature on an incoming mail
=head1 SYNOPSIS
2012-05-05 09:54:47 +02:00
domainkeys [reject 1]
2012-05-04 20:02:12 +02:00
2012-05-05 09:54:47 +02:00
Performs a DomainKeys validation on the message.
2012-05-04 20:02:12 +02:00
2013-03-28 22:47:02 +01:00
=head1 DEPRECATION
You should probably not be using this plugin. DomainKeys has been deprecated in favor of DKIM. That being said, it's March 2013 and I'll still seeing quite a few hams arrive with DomainKeys signatures.
2012-05-05 09:54:47 +02:00
=head1 CONFIGURATION
2012-05-04 20:02:12 +02:00
2012-05-05 09:54:47 +02:00
=head2 reject
reject 1
Reject is a boolean that toggles message rejection on or off. Messages failing
DomainKeys validation are rejected by default.
=head2 reject_type
reject_type [ temp | perm ]
The default rejection type is permanent.
=head2 warn_only
A deprecated option that disables message rejection. See reject instead.
2012-05-04 20:02:12 +02:00
=head1 COPYRIGHT
Copyright (C) 2005-2006 John Peacock.
Portions Copyright (C) 2004 Anthony D. Urso. All rights reserved. This
program is free software; you can redistribute it and/or modify it under
the same terms as Perl itself.
2012-05-05 09:54:47 +02:00
=head1 AUTHORS
Matt Simerson - 2012
John Peacock - 2005-2006
Anthony D. Urso. - 2004
2012-05-04 20:02:12 +02:00
=cut
2012-05-05 09:54:47 +02:00
use strict;
use warnings;
use Qpsmtpd::Constants;
2006-07-12 20:10:00 +02:00
sub init {
2006-07-24 21:10:38 +02:00
my ($self, $qp, %args) = @_;
2006-07-12 20:10:00 +02:00
foreach my $key ( %args ) {
2012-05-04 20:02:12 +02:00
$self->{$key} = $args{$key};
2006-07-12 20:10:00 +02:00
}
2012-05-05 09:54:47 +02:00
$self->{reject} = 1 if ! defined $self->{reject}; # default reject
$self->{reject_type} = 'perm' if ! defined $self->{reject_type};
if ( $args{'warn_only'} ) {
$self->log(LOGNOTICE, "warn_only is deprecated. Use reject instead");
$self->{'reject'} = 0;
};
2006-07-12 20:10:00 +02:00
}
2012-05-21 11:59:44 +02:00
sub register {
my $self = shift;
for my $m ( qw/ Mail::DomainKeys::Message Mail::DomainKeys::Policy / ) {
eval "use $m";
if ( $@ ) {
warn "skip: plugin disabled, could not load $m\n";
$self->log(LOGERROR, "skip: plugin disabled, is $m installed?");
return;
};
};
$self->register_hook('data_post', 'data_post_handler');
};
sub data_post_handler {
2006-07-11 19:41:48 +02:00
my ($self, $transaction) = @_;
2006-07-09 02:58:39 +02:00
2012-06-22 11:38:01 +02:00
return DECLINED if $self->is_immune();
2012-05-05 09:54:47 +02:00
if ( ! $transaction->header->get('DomainKey-Signature') ) {
2012-06-25 08:41:43 +02:00
$self->log(LOGINFO, "skip, unsigned");
2012-05-05 09:54:47 +02:00
return DECLINED;
};
2012-06-03 03:44:46 +02:00
2012-05-05 09:54:47 +02:00
my $body = $self->assemble_body( $transaction );
2006-07-09 02:58:39 +02:00
2006-07-11 19:41:48 +02:00
my $message = load Mail::DomainKeys::Message(
HeadString => $transaction->header->as_string,
2012-05-05 09:54:47 +02:00
BodyReference => $body) or do {
2012-06-25 08:41:43 +02:00
$self->log(LOGWARN, "skip, unable to load message"),
2006-07-09 02:58:39 +02:00
return DECLINED;
2012-05-05 09:54:47 +02:00
};
2006-07-09 02:58:39 +02:00
2012-05-05 09:54:47 +02:00
# no sender domain means no verification
if ( ! $message->senderdomain ) {
2012-06-25 08:41:43 +02:00
$self->log(LOGINFO, "skip, failed to parse sender domain"),
2006-07-09 02:58:39 +02:00
return DECLINED;
2012-05-05 09:54:47 +02:00
};
2006-07-09 02:58:39 +02:00
2012-05-05 09:54:47 +02:00
my $status = $self->get_message_status( $message );
2006-07-09 02:58:39 +02:00
if ( defined $status ) {
2012-06-25 08:41:43 +02:00
$transaction->header->add("DomainKey-Status", $status, 0);
$self->log(LOGINFO, "pass, $status");
2012-05-05 09:54:47 +02:00
return DECLINED;
};
2012-06-25 08:41:43 +02:00
$self->log(LOGERROR, "fail, signature invalid");
2012-05-05 09:54:47 +02:00
return DECLINED if ! $self->{reject};
my $deny = $self->{reject_type} eq 'temp' ? DENYSOFT : DENY;
2012-06-25 08:41:43 +02:00
return ($deny, "DomainKeys signature validation failed");
2006-07-09 02:58:39 +02:00
}
2012-05-05 09:54:47 +02:00
sub get_message_status {
my ($self, $message) = @_;
if ( $message->testing ) {
return "testing"; # key testing, don't do anything else
};
if ( $message->signed && $message->verify ) {
return $message->signature->status; # verified: add good header
};
# not signed or not verified
my $policy = fetch Mail::DomainKeys::Policy(
Protocol => 'dns',
Domain => $message->senderdomain
);
if ( ! $policy ) {
return $message->signed ? "non-participant" : "no signature";
};
if ( $policy->testing ) {
return "testing"; # Don't do anything else
};
if ( $policy->signall ) {
return undef; # policy requires all mail to be signed
};
# $policy->signsome
return "no signature"; # not signed and domain doesn't sign all
};
sub assemble_body {
my ($self, $transaction) = @_;
$transaction->body_resetpos;
$transaction->body_getline; # \r\n seperator is NOT part of the body
my @body;
while (my $line = $transaction->body_getline) {
push @body, $line;
}
return \@body;
};