Okay, here's a working version of the plugin I have. You can get Mail::DKIM from here http://jason.long.name/dkimproxy/ along with a program that I used as a template, dkimproxy, and that works with Postfix. Rather than puzzling out how to get that kind of a program to work for qmail I integrated the methods into the attached plugin. I run the plugin with this line in the plugins file
dkimsign selector=alecto domains=bittwiddlers.com,bitnerd.com keyfile=/etc/ssl/certs/dkim-alecto.private and have it set up to only sign messages being sent from allowable relay clients (so I know it's originating here) and being sent from one of the specified domains. It seems to work fine with all of the automated test filters I tried it against. If anyone finds a problem or fixes anything please let me know so I can update my version. My next task will be to finish my dkimcheck plugin which will check the DKIM / DomainKey signature on incoming messages -- Matthew Harrell I don't suffer from insanity - Bit Twiddlers, Inc. I enjoy every minute of it. [EMAIL PROTECTED]
=head1 NAME dkimsign -- Compute and insert a DKIM signature into a message =head1 DESCRIPTION This plugin will check the message against the specified list of domains and DKIM sign it if it's from an address that it's authorized to sign for. =head1 CONFIG There are three required parameters for this plugin to work correctly: the selector name, the domains it can sign for, and the private keyfile. All other arguments are optional and have sane default values. =over 4 =item domains=[signing domains] This parameter defines the comma separated list of domains for which the plugin will sign messages. =item keyfile=[/path/to/private.key] This is the path to the private DKIM key that messages will be signed with. =item selector=[selector name] This is the selector name for the key that is signing. =item method=[simple|nowsp|relaxed|nofws] Select the canonicalization method. Currently defaults to "relaxed" =item type=[dkim|domainkeys] Whether to do DKIM or DomainKeys signing. Currently only DKIM is supported =back =head1 TODO Add in the ability to specify a regex for the key name so different keys can be specified for different domains. Add in DomainKeys signing (inherent in the DKIM library). =cut use strict; use Mail::DKIM; use Mail::DKIM::Signer; # enable support for "pretty" signatures, if available # seems to break when using qmail but works for postfix? #eval "require Mail::DKIM::TextWrap"; sub register { my ( $self, $qp, @args ) = @_; my %args; $self->{_method} = "relaxed"; $self->{_type} = "dkim"; for ( @args ) { if ( /^domains=([\.\,a-z0-9A-Z]*)$/ ) { $self->{_domains} = $1; } elsif ( /^keyfile=(\/[\/\-\_\.a-z0-9A-Z]*)$/ ) { $self->{_keyfile} = $1; } elsif ( /^method=(simple|nowsp|relaxed|nofws)$/ ) { $self->{_method} = $1; } elsif ( /^selector=([\.a-z0-9A-Z]*)$/ ) { $self->{_selector} = $1; } elsif ( /^type=(dkim|domainkeys)$/ ) { $self->{_type} = $1; } else { $self->log(LOGERROR, "Unrecognized argument '$_' to dkimsign plugin"); return undef; } } # $self->log ( LOGNOTICE, "dkimsign args: domains: " . $self->{_domains} # . " keyfile: " . $self->{_keyfile} . " method: " # . $self->{_method} . " selector: " . $self->{_selector} # . " type: " . $self->{_type} ); unless ( $self->{_domains} ) { $self->log ( LOGERROR, "No domains defined" ); return undef; } unless ( $self->{_keyfile} ) { $self->log ( LOGERROR, "No keyfile defined" ); return undef; } unless ( $self->{_selector} ) { $self->log ( LOGERROR, "No selector defined" ); return undef; } 1; } sub hook_data_post { my ( $self, $transaction ) = @_; # don't bother to continue if we're not allowed to relay for this client # unless ( $self->qp->connection->relay_client ) { return DECLINED; } my @domains = split ( ",", $self->{_domains} ); my $address = $transaction->sender->host; # ensure that the domain we're sending from is one of the signing domains # foreach my $domain ( @domains ) { # $self->log ( LOGNOTICE, "DKIM: comparing $domain to $address" ); if ( $domain eq $address ) { my $dkim = new Mail::DKIM::Signer ( Domain => $address, KeyFile => $self->{_keyfile}, Method => $self->{_method}, Selector => $self->{_selector}, ); # take all the headers, reformat them to eliminate cr/lf and push into # dkim. dkim seems particular about the cr/lf # my %hdrs = %{ $transaction->header->header_hashref() }; foreach my $key ( keys %hdrs ) { my $val = join ( "", @{$hdrs{$key}} ); $val =~ s/[\n\r]//g; # $self->log ( LOGNOTICE, "Hdr: " . $key . ": " . $val ); $dkim->PRINT ( $key . ": " . $val . "\x0D\x0A" ); } # push the body of the message on ensuring the cr/lf are correct # $transaction->body_resetpos; while ( my $line = $transaction->body_getline ) { chomp ( $line ); $line =~ s/\015$//; # $self->log ( LOGNOTICE, "Body: " . $line ); $dkim->PRINT ( $line . "\x0D\x0A" ); } $dkim->CLOSE; $self->log ( LOGNOTICE, "dkimsign: " . $dkim->result_detail . "; " . join(", ", $dkim->message_attributes) ); # add each signature # foreach my $sig ( $dkim->signatures ) { # $self->log ( LOGNOTICE, $sig->as_string ); $transaction->header->add ( undef, $sig->as_string ); } # eventually we need the capability to do multiple signatures return ( DECLINED ); } } return ( DECLINED ); }