Dear Jake,
I am ready to provide all the details, please let me know what information or
config file you want to provide to you.
use strict;
use warnings;
our $VERSION = '0.2';
use Mail::DKIM 0.29;
use Mail::DKIM::Signer;
# enable support for "pretty" signatures, if available
eval 'require Mail::DKIM::TextWrap';
=head
config file structure
- missing settings will be merged from the global-node
- domain-entry will also match its subdomains
- create empty domain-node to omit signing (or specify "none" as id)
<dkimsign>
<!-- per default sign all mails using dkim -->
<global algorithm="rsa-sha256" domain="/var/qmail/control/me"
keyfile="/var/qmail/control/dkim/global.key" method="simple" selector="beta">
<types id="dkim" />
</global>
<!-- use dkim + domainkey for example.com -->
<example.com selector="beta2">
<types id="dkim" />
<types id="domainkey" method="nofws" />
</example.com>
<!-- no signing for example2.com -->
<example2.com />
</dkimsign>
=cut
my $configfile = undef;
$configfile = '/var/qmail/control/dkim/signconf.xml';
my $debugfile = undef;
#$debugfile = '/tmp/dkim.debug';
my $qremote = '/var/qmail/bin/qmail-remote.orig';
my $binary = 0;
our $config;
$config->{'global'} = {
types => { dkim => {} },
keyfile => '/var/qmail/control/dkim/global.key',
algorithm => 'rsa-sha256',
method => 'simple',
selector => 'beta',
# either string or file (first line of file will be used)
domain => '/var/qmail/control/me'
};
#-------------------------------------------------------------------------------
# read config file. safely
if (defined($configfile) && -r $configfile)
{
eval 'use XML::Simple';
if (!$@)
{
my $xmlconf;
eval { $xmlconf = XMLin($configfile, ForceArray => ['types'], KeyAttr =>
['id']); };
qexit_deferral('Unable to read config file: ', $@)
if ($@);
ConfigMerge::merge($config, $xmlconf);
}
}
# open debug file
my $debugfh = undef;
if (defined($debugfile))
{
open($debugfh, '>', $debugfile)
or qexit_deferral('Unable to open ', $debugfile, ' to writing: ', $!);
}
# generate signatures
my $dkim;
my $mailbuf = '';
eval
{
my $conf = $config->{'global'};
$dkim = Mail::DKIM::Signer->new(
Policy => 'MySignerPolicy',
Debug_Canonicalization => $debugfh
);
if ($binary)
{
binmode STDIN;
}
while (<STDIN>)
{
$mailbuf .= $_;
unless ($binary)
{
chomp $_;
s/\015?$/\015\012/s;
}
$dkim->PRINT($_);
}
$dkim->CLOSE();
};
qexit_deferral('Error while signing: ', $@)
if ($@);
# close debug file
close($debugfh)
if (defined($debugfh));
# execute qmail-remote
unshift(@ARGV, $qremote);
open(QR, '|-') || exec { $ARGV[0] } @ARGV
or qexit_deferral('Unable to run qmail-remote: ', $!);
foreach my $dkim_signature ($dkim->signatures)
{
my $sig = $dkim_signature->as_string;
$sig =~ s/\015\012\t/\012\t/g;
print QR $sig."\012";
}
print QR $mailbuf;
close(QR);
#-------------------------------------------------------------------------------
sub qexit
{
print @_, "\0";
exit(0);
}
sub qexit_deferral
{
return qexit('Z', @_);
}
sub qexit_failure
{
return qexit('D', @_);
}
sub qexit_success
{
return qexit('K', @_);
}
#-------------------------------------------------------------------------------
package ConfigMerge;
# merge config hashes. arrays and scalars will be copied.
sub merge
{
my ($left, $right) = @_;
foreach my $rkey (keys(%$right))
{
my $rtype = ref($right->{$rkey}) eq 'HASH' ? 'HASH'
: ref($right->{$rkey}) eq 'ARRAY' ? 'ARRAY'
: defined($right->{$rkey}) ? 'SCALAR'
: '';
my $ltype = ref($left->{$rkey}) eq 'HASH' ? 'HASH'
: ref($left->{$rkey}) eq 'ARRAY' ? 'ARRAY'
: defined($left->{$rkey}) ? 'SCALAR'
: '';
if ($rtype ne 'HASH' || $ltype ne 'HASH')
{
$left->{$rkey} = $right->{$rkey};
}
else
{
merge($left->{$rkey}, $right->{$rkey});
}
}
return;
}
#-------------------------------------------------------------------------------
package MySignerPolicy;
use Mail::DKIM::SignerPolicy;
use base 'Mail::DKIM::SignerPolicy';
use Mail::DKIM::Signature;
use Mail::DKIM::DkSignature;
use Carp;
use strict;
use warnings;
sub apply
{
my ($self, $signer) = @_;
my $domain = undef;
$domain = lc($signer->message_sender->host)
if (defined($signer->message_sender));
# merge configs
while($domain)
{
if (defined($config->{$domain}))
{
$config->{'global'}->{'types'} = undef;
ConfigMerge::merge($config->{'global'}, $config->{$domain});
last;
}
(undef, $domain) = split(/\./, $domain, 2);
}
my $conf = $config->{'global'};
return 0
if (!defined($conf->{'types'}) || defined($conf->{'types'}->{'none'}));
# set key file
$signer->key_file($conf->{'keyfile'});
# parse (signature) domain
if (substr($conf->{'domain'}, 0, 1) eq '/')
{
open(FH, '<', $conf->{'domain'})
or croak('Unable to open domain-file: '.$!);
my $newdom = (split(/ /, <FH>))[0];
close(FH);
croak("Unable to read domain-file. Maybe empty file.")
if (!$newdom);
chomp($newdom);
$conf->{'domain'} = $newdom;
}
# generate signatures
my $sigdone = 0;
foreach my $type (keys(%{$conf->{'types'}}))
{
my $sigconf = $conf->{'types'}->{$type};
if ($type eq 'dkim')
{
$signer->add_signature(
new Mail::DKIM::Signature(
Algorithm => $sigconf->{'algorithm'} || $conf->{'algorithm'} ||
$signer->algorithm,
Method => $sigconf->{'method'} || $conf->{'method'} || $signer->method,
Headers => $sigconf->{'headers'} || $conf->{'headers'} || $signer->headers,
Domain => $sigconf->{'domain'} || $conf->{'domain'} || $signer->domain,
Selector => $sigconf->{'selector'} || $conf->{'selector'} || $signer->selector,
Query => $sigconf->{'query'} || $conf->{'query'},
Identity => $sigconf->{'identity'} || $conf->{'identity'},
Expiration => $sigconf->{'expiration'} || $conf->{'expiration'}
)
);
$sigdone = 1;
}
elsif ($type eq 'domainkey')
{
$signer->add_signature(
new Mail::DKIM::DkSignature(
Algorithm => 'rsa-sha1', # only rsa-sha1 supported
Method => $sigconf->{'method'} || $conf->{'method'} || $signer->method,
Headers => $sigconf->{'selector'} || $conf->{'headers'} || $signer->headers,
Domain => $sigconf->{'domain'} || $conf->{'domain'} || $signer->domain,
Selector => $sigconf->{'selector'} || $conf->{'selector'} || $signer->selector,
Query => $sigconf->{'query'} || $conf->{'query'}
)
);
$sigdone = 1;
}
}
return $sigdone;
}
Regards,
Anil Aliyan
----- Original Message -----
From: Jake Vickers
To: [email protected]
Sent: Monday, November 02, 2009 11:42 PM
Subject: Re: [qmailtoaster] DKIM Error
Anil Aliyan wrote:
Dear Jakes,
Please find some time to help resolve my problem also, will very greatful
to you.
Thanks & Regards,
Anil Aliyan
----- Original Message -----
From: Anil Aliyan
To: [email protected]
Sent: Saturday, October 31, 2009 2:28 AM
Subject: [qmailtoaster] DKIM Error
Jakes,
Please find the config file attached with this mail.
Pasting the config file into the email is easier for us to help you.
It looks like your config file may be wrong, depending on how you configured
your system.
Right now, your system is going to sign every domain sending email as the
domain listed in your /var/qmail/control/me file since you only defined a
global rule. This is fine if the me file only contain your domain name
(gnvfc.com) but you probably have your hostname in the me file as well
(mail.gnvfc.com) so it's signing using that domain.
You can define individual domains to sign for in this format:
<dkimsign>
<!-- per default sign all mails using dkim -->
<global algorithm="rsa-sha1" domain="/var/qmail/control/me"
keyfile="/var/qmail/control/dkim/global.key" method="simple" selector="dkim1">
<types id="dkim" />
</global>
<gnvfc.com selector="dkim1">
<types id="dkim" />
</gnvcf>
</dkimsign>
----------------
This will inherit any declarations from the <global> tags that you do not
override (so it will inherit the algorithm="rsa-sha1", keyfile, method).
But without seeing how you configured other things it's hard to guess.