]>
git.proxmox.com Git - pmg-api.git/blob - src/PMG/DKIMSign.pm
5 use Mail
::DKIM
::Signer
;
6 use Mail
::DKIM
::TextWrap
;
7 use Crypt
::OpenSSL
::RSA
;
15 use base
qw(Mail::DKIM::Signer);
18 my ($class, $selector, $sign_all) = @_;
20 die "no selector provided\n" if ! $selector;
23 Algorithm
=> 'rsa-sha256',
24 Method
=> 'relaxed/relaxed',
25 Selector
=> $selector,
26 KeyFile
=> "/etc/pmg/dkim/$selector.private",
29 my $self = $class->SUPER::new
(%opts);
31 $self->{sign_all
} = $sign_all;
36 # MIME::Entity can output to all objects responding to 'print' (and does so in
37 # chunks) Mail::DKIM::Signer has a 'PRINT' method and expects each line
38 # terminated with "\r\n"
42 my ($self, $chunk) = @_;
43 $chunk =~ s/\012/\015\012/g;
47 sub create_signature
{
51 return $self->signature->as_string();
54 #determines which domain should be used for signing based on the e-mailaddress
56 my ($self, $sender_email) = @_;
58 my @parts = split('@', $sender_email);
59 die "no domain in sender e-mail\n" if scalar(@parts) < 2;
60 my $input_domain = $parts[-1];
62 if ($self->{sign_all
}) {
63 $self->domain($input_domain) if $self->{sign_all
};
67 # check that input_domain is in/a subdomain of in the
68 # dkimdomains, falling back to the relay domains.
69 my $dkimdomains = PVE
::INotify
::read_file
('dkimdomains');
70 $dkimdomains = PVE
::INotify
::read_file
('domains') if !scalar(%$dkimdomains);
72 foreach my $domain (sort keys %$dkimdomains) {
73 if ( $input_domain =~ /\Q$domain\E$/i ) {
74 $self->domain($domain);
79 syslog
('info', "not DKIM signing mail from $sender_email");
86 my ($entity, $selector, $sender, $sign_all) = @_;
88 die "no selector provided\n" if ! $selector;
90 #oversign certain headers
91 my @oversign_headers = (
103 push(@oversign_headers, grep { $entity->head->mime_attr($_) } @cond_headers);
105 my $extended_headers = { map { $_ => '+' } @oversign_headers };
107 my $signer = __PACKAGE__-
>new($selector, $sign_all);
109 $signer->extended_headers($extended_headers);
111 if ($signer->signing_domain($sender)) {
112 $entity->print($signer);
113 my $signature = $signer->create_signature();
114 $entity->head->add('DKIM-Signature', $signature, 0);
121 # key-handling and utility methods
122 sub get_selector_info
{
125 die "no selector provided\n" if !defined($selector);
128 my $privkeytext = PVE
::Tools
::file_get_contents
("/etc/pmg/dkim/$selector.private");
129 my $privkey = Crypt
::OpenSSL
::RSA-
>new_private_key($privkeytext);
130 $size = $privkey->size() * 8;
132 $pubkey = $privkey->get_public_key_x509_string();
136 $pubkey =~ s/-----(?:BEGIN|END) PUBLIC KEY-----//g;
139 # split record into 250 byte chunks for DNS-server compatibility
140 # see opendkim-genkey
141 my $record = qq{$selector._domainkey\tIN\tTXT\t( "v=DKIM1; h=sha256; k=rsa; "\n\t "p=};
142 my $len = length($pubkey);
146 $record .= substr($pubkey, $cur);
149 $record .= substr($pubkey, $cur, 250) . qq{"\n\t "};
154 $record .= qq{" ) ; ----- DKIM key $selector};
156 return ($record, $size);
160 my ($selector, $keysize) = @_;
162 die "no selector provided\n" if !defined($selector);
163 die "no keysize provided\n" if !defined($keysize);
164 die "invalid keysize\n" if ($keysize < 1024);
165 my $privkey_file = "/etc/pmg/dkim/$selector.private";
168 my $cmd = ['openssl', 'genrsa', '-out', $privkey_file, $keysize];
169 PMG
::Utils
::run_silent_cmd
($cmd);
170 my $cfg = PMG
::Config-
>new();
171 $cfg->set('admin', 'dkim_selector', $selector);
173 PMG
::Utils
::reload_smtp_filter
();
176 PMG
::Config
::lock_config
($code, "unable to generate DKIM key ($selector - $keysize bits)");