initial commit of the GPG QPSMTPD Plugin

This commit is contained in:
Dominik Meyer 2013-08-11 21:49:44 +02:00
commit adf73fcc7a
2 changed files with 154 additions and 0 deletions

README Normal file
View File

@ -0,0 +1,37 @@
- -
- qptsmtpd-plugin-gpg -
- -
Author : Dominik Meyer <>
Initial Date: 2013-08-12
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:
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.

src/encrypt_gpg Normal file
View File

@ -0,0 +1,117 @@
=head1 NAME
encrypt_gpg - encrypt incoming emails whith the recipients PGP public key
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.
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.
use Qpsmtpd::DSN;
use Mail::GnuPG;
use GnuPG::Interface;
use MIME::Parser;
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;
# 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/<//;
$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 );
return OK;
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 !" );
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();
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;