commit adf73fcc7a9edb00c1379206dba107acfec1ed13 Author: Dominik Meyer Date: Sun Aug 11 21:49:44 2013 +0200 initial commit of the GPG QPSMTPD Plugin diff --git a/README b/README new file mode 100644 index 0000000..3c24be4 --- /dev/null +++ b/README @@ -0,0 +1,37 @@ +--------------------------------------------------------------------------------- +- - +- qptsmtpd-plugin-gpg - +- - +--------------------------------------------------------------------------------- + +Author : Dominik Meyer +Initial Date: 2013-08-12 + + +Description: +------------ + +This plugin for the SMTP MTA qpsmtpd will encrypt every incoming eMail, which +is not already PGP encrypted, with the recipients pgp public key. + +I got the idea for this project, while reading: https://grepular.com/Automatically_Encrypting_all_Incoming_Email +A lot of code is inspired from the above project. + + +The pgp encryption ensures, that the eMail body is stored encrypted in the backend storage, for +example an IMAP server. A lot of current eMail clients support pgp/gpg encrypted emails and ask +for the pgp passphrase, if you select an encrypted email. + + + + +Problems/Security Considerations: +--------------------------------- + + - The eMails are encrypted while getting into the mailserver. The eMails can be read in plain + from the network line by your backend provider, if the connection is not SSL/TLS encrypted + + - The eMail lies unencrypted for some time into the spool directory of qpstmpd. This can not be + secured. But to reduce recovery attempts by an attacker you can use an encrypted spool directory + or a RAM Disk. Perhaps an encfs encrypted spool directory only readable by the qpstmpd user, created + manually at every boot may help, if you are paranoid. \ No newline at end of file diff --git a/src/encrypt_gpg b/src/encrypt_gpg new file mode 100644 index 0000000..04e6468 --- /dev/null +++ b/src/encrypt_gpg @@ -0,0 +1,117 @@ + +=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; +}