From 0e9abcd26d4c8407987f2f16904e7709fd384055 Mon Sep 17 00:00:00 2001 From: Dominik Meyer Date: Tue, 13 Aug 2013 22:44:17 +0200 Subject: [PATCH] RM/ADD: removed old method added qpsmtpd smtp queueing module with encryption --- src/encrypt_gpg | 117 -------------------------------------- src/queue_encrypt_gpg | 129 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 117 deletions(-) delete mode 100644 src/encrypt_gpg create mode 100644 src/queue_encrypt_gpg diff --git a/src/encrypt_gpg b/src/encrypt_gpg deleted file mode 100644 index 04e6468..0000000 --- a/src/encrypt_gpg +++ /dev/null @@ -1,117 +0,0 @@ - -=head1 NAME - -encrypt_gpg - encrypt incoming emails whith the recipients PGP public key - - -=head1 DESCRIPTION - -Plugin checks, if there is a trusted public key for each of the recipients -of the incoming email, in the GnuPG public key ring of the user running qpsmtpd. -If such a key exists the email is encrypted with these keys. - - -=head1 PER USER CONFIG - -The file encrypt_gpg_user is a lookup up table for eMails in the form: - -email encryption_type - -possible values for encryption_type are none and pgpmime. The standard is -pgpmime. With none the encryption can be deactivated completely. - -=cut - -use Qpsmtpd::DSN; -use Mail::GnuPG; -use GnuPG::Interface; -use MIME::Parser; - -#init -sub init { - my ( $self, $qp, @args ) = @_; - - - # get us the user config file - my @encrypted_user_config = $self->qp->config("encrypt_gpg_user"); - my %user_hash; - - for my $e (@encrypted_user_config) { - my ( $email, $encryption_type ) = split /\s+/, $e, 2; - $user_hash{$email}=$encryption_type; - } - - $self->{_gpg_user}=\%user_hash; - -} - -# search for the users public key -# the key id can be given in a file or is search for on a keyserver -# and create a Mail::GnuPG object -sub hook_rcpt { - my ( $self, $transaction, $recipient, %param ) = @_; - my $rcpt = $recipient; - $rcpt =~ s///; - - #get us the user hash - my %user_hash = %{$self->{_gpg_user}}; - - if (defined($user_hash{$rcpt}) && $user_hash{$rcpt} eq "none" ) { - $self->log( LOGINFO, "GPG: encryption deactivated for email " .$rcpt ); - $self->{_gpg_on}=0; - return OK; - } - $self->{_gpg_on}=1; - - my $gpg = new Mail::GnuPG( keydir => '/var/spool/qpsmtpd/.gnupg/', always_trust => 1 ); - - if ( !$gpg->has_public_key($rcpt) ) { - $self->log( LOGINFO, "GPG: no key for -" . $rcpt . "- found !" ); - $self->{_gpg_on}=0; - return OK; - } - - # save gnupg object for rest of the session - $self->{_gpg} = $gpg; - $self->{_gpg_recipient} = $rcpt; - - return (OK); -} - -#check if mail is already encrypted, otherwise encrypt it -sub hook_data_post { - my ( $self, $transaction ) = @_; - - # if gpg is deactivated, skip this hook - if ($self->{_gpg_on}==0) { - return OK; - } - - #parse queued message - my $parser = new MIME::Parser(); - $parser->decode_bodies(1); - $parser->output_to_core(1); - my $mime = $parser->parse_open( $transaction->body_filename() ); - - #check if email is already encrypted - if ( $self->{_gpg}->is_encrypted($mime) ) { - $self->log( LOGINFO, "GPG: email already PGP encrypted" ); - return OK; - } - - #encrypt message - my $code; - $code = $self->{_gpg}->mime_encrypt( $mime, $self->{_gpg_recipient} ); - if ( $code != 0 ) { - $self->log( LOGERROR, "GPG: " . $self->{_gpg}->{last_message}->[0] ); - return OK; - } - - # rewrite the queued message - open my $queued_mail, ">" . $transaction->body_filename(); - print $queued_mail $mime->stringify; - close $queued_mail; - - return OK; -} diff --git a/src/queue_encrypt_gpg b/src/queue_encrypt_gpg new file mode 100644 index 0000000..990003d --- /dev/null +++ b/src/queue_encrypt_gpg @@ -0,0 +1,129 @@ +use Qpsmtpd::DSN; +use Mail::GnuPG; +use MIME::Parser; +use Net::SMTP; + +#init +sub init { + my ( $self, $qp, @args ) = @_; + + if ( @args > 0 ) { + if ( $args[0] =~ /^([\.\w_-]+)$/ ) { + $self->{_smtp_server} = $1; + } + else { + die "Bad data in smtp server: $args[0]"; + } + $self->{_smtp_port} = 25; + if ( @args > 1 and $args[1] =~ /^(\d+)$/ ) { + $self->{_smtp_port} = $1; + } + $self->log( LOGWARN, "WARNING: Ignoring additional arguments." ) if ( @args > 2 ); + } + else { + die("No SMTP server specified in smtp-forward config"); + } + + # get us the user config file + my @encrypted_user_config = $self->qp->config("encrypt_gpg_user"); + my %user_hash; + + for my $e (@encrypted_user_config) { + my ( $email, $encryption_type ) = split /\s+/, $e, 2; + $user_hash{$email} = $encryption_type; + } + + $self->{_gpg_user} = \%user_hash; + +} + +# this subroutite returns the encrypted email, if a valid and trusted PGP Public Key is found +# in the GnuPG keyring of the user running QPSMTPD for all the recipients of the eMail +# if no key is found, the plain unencrypted eMail is returned +sub encrypt_mail { + my ( $self, $transaction ) = @_; + + # set correct HOMEDIR and get us an GPG object + $ENV{HOME} = ( getpwuid($>) )[7]; + my $gpg = new Mail::GnuPG( keydir => '/var/spool/qpsmtpd/.gnupg/', always_trust => 1 ); + + # get us all recipients and remove the brackets around them + my @recipients_temp = $transaction->recipients(); + my @recipients; + + for my $r (@recipients_temp) { + $r =~ s///; + push( @recipients, $r ); + } + + # check if we have a trusted public key available for every recipient + my $keys_available = 1; + + for my $r (@recipients) { + if ( !$gpg->has_public_key($r) ) { + $self->log( LOGINFO, "GPG: no public key found for " . $r ); + $keys_available = 0; + } + } + + # + #parse eMail into a Mime::Entity Object + # + + #enforce correct rights + chmod 0600, $transaction->body_filename; + + my $parser = new MIME::Parser(); + $parser->decode_bodies(1); + $parser->output_to_core(1); + my $mime = $parser->parse_open( $transaction->body_filename ); + + #if we have all keys available and eMail is not encrypted already + if ( $keys_available == 1 && !$gpg->is_encrypted($mime) ) { + + $self->log( LOGINFO, "GPG: encrypting email" ); + + #encrypt message + my $code = $gpg->mime_encrypt( $mime, @recipients ); + if ( $code != 0 ) { + $self->log( LOGERROR, "GPG: " . $self->{_gpg}->{last_message}->[0] ); + } + + ## Remove some headers which might have been broken by the process of encryption + $mime->head()->delete($_) foreach qw( DKIM-Signature DomainKey-Signature ); + + } + return $mime; +} + +sub hook_queue { + my ( $self, $transaction ) = @_; + + #encrypt email or not + my $email = $self->encrypt_mail($transaction); + + $self->log( LOGINFO, "forwarding to $self->{_smtp_server}:$self->{_smtp_port}" ); + + my $smtp = Net::SMTP->new( + $self->{_smtp_server}, + Port => $self->{_smtp_port}, + Timeout => 60, + Hello => $self->qp->config("me"), + ) || die $!; + + $smtp->mail( $transaction->sender->address || "" ) + or return ( DECLINED, "Unable to queue message ($!)" ); + + for ( $transaction->recipients ) { + $smtp->to( $_->address ) or return ( DECLINED, "Unable to queue message ($!)" ); + } + + $smtp->data() or return ( DECLINED, "Unable to queue message ($!)" ); + + $smtp->datasend( $email->stringify ); + $smtp->dataend() or return ( DECLINED, "Unable to queue message ($!)" ); + $smtp->quit() or return ( DECLINED, "Unable to queue message ($!)" ); + $self->log( LOGINFO, "finished queueing" ); + return ( OK, "Queued!" ); +}