From e24a9e1978c905be0116b872f83fabd367d98441 Mon Sep 17 00:00:00 2001 From: Mirko Tietgen Date: Thu, 18 Jul 2013 23:10:11 +0200 Subject: [PATCH] Bug 8897 [ENH] GPG Mail encryption -- New version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Koha should offer patrons the option to receive their emails encrypted. This patch adds a dialog to the user interface (OPAC side) for patrons to add or delete a GPG public key. The key is directly added to or deleted from gpg (gnupg needs to be installed, binary expected at /usr/bin/gpg). This feature does not require sysadmins/librarians to do anything as long as the gpg binary if found. So far, mails are encrypted if a key for the recipient is avaliable - when sending lists or carts (encryption of email text + attachment) - when mails are sent through the message queue - … Missing so far: - hide public key interface from OPAC/userdetails when no binary is found - de-hardcode path to gpg (syspref) - deal with BCC mails (send message explaining that an encrypted email has been send to the patron instead of a BCC) - … Maybe later: - library-side key management: secret key for signing - encryption of emails from library to vendors - … --- C4/GnuPG.pm | 119 +++++++++++++++++++++ C4/Letters.pm | 19 +++- koha-tmpl/opac-tmpl/prog/en/includes/usermenu.inc | 1 + opac/opac-sendbasket.pl | 22 +++- opac/opac-sendshelf.pl | 26 ++++- 5 files changed, 174 insertions(+), 13 deletions(-) create mode 100644 C4/GnuPG.pm diff --git a/C4/GnuPG.pm b/C4/GnuPG.pm new file mode 100644 index 0000000..f5d2081 --- /dev/null +++ b/C4/GnuPG.pm @@ -0,0 +1,119 @@ +package C4::GnuPG; + +# Copyright 2012 Mirko Tietgen +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; +use C4::Context; +use Crypt::GPG; + +=head1 C4::GnuPG + +C4::GnuPG + +=head1 Description + +Provides methods to interact with GnuPG via Crypt::GPG for the purpose of encrypting emails to patrons + +=cut + +my $gpg = new Crypt::GPG; +$gpg->gpgbin('/usr/bin/gpg'); +#$gpg->gpgbin(C4::Context->preference('GnuPGPPath')); +#$gpg->secretkey(C4::Context->preference('KohaAdminEmailAddress')); +#$gpg->passphrase(C4::Context->preference('GnuPGPassphrase')); + +=head2 encrypt + +Take email content and patron's email address, send back encrypted content. Call like + +$content = C4::GnuPG->encrypt($content, $patronemail); + +=cut + +sub encrypt { + my ($self, $content, $patronemail) = @_; + $content = $gpg->encrypt($content, $patronemail); + return $content; +} + +=head2 does_exist + +Check if there is a GnuPG key for the patron's email address. Call like + +if ( C4::GnuPG->does_exist($patronemail) = 1 ) { + # … encrypt … +} + +=cut + +sub does_exist { + # TODO: check if gpg binary is found before encrypting to avoid errors + my ($self, $patronemail) = @_; + if ( $gpg->keydb($patronemail) ) { + return 1; + } + else { + return 0; + } +} + +=head2 add_key + +Add or replace a patron's key. If there is already a key present, delete. Then add the new key. + +=cut + +sub add_key { + my ($self, $key, $patronemail) = @_; + # check if it is a public key before doing anything + if ( $key =~ /BEGIN PGP PUBLIC KEY BLOCK/ ) { + # if we already got a key, delete + if ( does_exist($patronemail) ) { + my @oldkey = $gpg->keydb($patronemail); + if ( $oldkey[0] ) { + $gpg->delkey($oldkey[0]); + } + } + # … add new key + $gpg->addkey($key); + } + else { + return 0; + } +} + +sub del_key { + my ($self, $patronemail) = @_; + my @key = $gpg->keydb($patronemail); + $gpg->delkey($key[0]); +} + +=head2 get_key + +=cut + +sub get_key { + # TODO: check security issue: can i get a secret key with this? + my ($self, $patronemail) = @_; + my @key = $gpg->keydb($patronemail); + my $keystring = $gpg->export($key[0]); + if ( $keystring =~ /BEGIN PGP PUBLIC KEY BLOCK/) { + return $keystring; + } +} diff --git a/C4/Letters.pm b/C4/Letters.pm index 37883c6..498ec4c 100644 --- a/C4/Letters.pm +++ b/C4/Letters.pm @@ -29,6 +29,7 @@ use C4::Branch; use C4::Log; use C4::SMS; use C4::Debug; +use C4::GnuPG; use Koha::DateUtils; use Date::Calc qw( Add_Delta_Days ); use Encode; @@ -300,13 +301,19 @@ sub SendAlerts { }, want_librarian => 1, ) or return; - + my $subject = Encode::encode( "utf8", "" . $letter->{title} ); + my $content = Encode::encode( "utf8", "" . $letter->{content} ); + if ( C4::GnuPG->does_exist($email) ) { + $subject = 'A message from your library'; + $content = C4::GnuPG->encrypt($content, $email); + } # ... then send mail my %mail = ( To => $email, - From => $email, - Subject => Encode::encode( "utf8", "" . $letter->{title} ), - Message => Encode::encode( "utf8", "" . $letter->{content} ), + #From => $email, # this looks very wrong! + From => C4::Context->preference("KohaAdminEmailAddress"), + Subject => $subject, + Message => $content, 'Content-Type' => 'text/plain; charset="utf8"', ); sendmail(%mail) or carp $Mail::Sendmail::error; @@ -944,6 +951,10 @@ sub _send_message_by_email { my $content = encode('utf8', $message->{'content'}); my $content_type = $message->{'content_type'} || 'text/plain; charset="UTF-8"'; my $is_html = $content_type =~ m/html/io; + if ( C4::GnuPG->does_exist($to_address) ) { + $subject = 'A message from your library'; + $content = C4::GnuPG->encrypt($content, $to_address); + } my %sendmail_params = ( To => $to_address, From => $message->{'from_address'} || C4::Context->preference('KohaAdminEmailAddress'), diff --git a/koha-tmpl/opac-tmpl/prog/en/includes/usermenu.inc b/koha-tmpl/opac-tmpl/prog/en/includes/usermenu.inc index 5a07d95..d0a2012 100644 --- a/koha-tmpl/opac-tmpl/prog/en/includes/usermenu.inc +++ b/koha-tmpl/opac-tmpl/prog/en/includes/usermenu.inc @@ -12,6 +12,7 @@ [% IF ( OpacPasswordChange ) %] [% IF ( passwdview ) %]
  • [% ELSE %]
  • [% END %]change my password
  • [% END %] + [% IF ( gnupgview ) %]
  • [% ELSE %]
  • [% END %]email encryption
  • [% IF ( ShowOpacRecentSearchLink ) %] [% IF ( searchhistoryview ) %]
  • [% ELSE %]
  • [% END %]my search history
  • [% END %] diff --git a/opac/opac-sendbasket.pl b/opac/opac-sendbasket.pl index da74745..128009b 100755 --- a/opac/opac-sendbasket.pl +++ b/opac/opac-sendbasket.pl @@ -149,6 +149,21 @@ if ( $email_add ) { my $boundary = "====" . time() . "===="; $mail{'content-type'} = "multipart/mixed; boundary=\"$boundary\""; + + my $email_header_and_body = $email_header . $body; + + my $attachmentfilename; + # encrypt the attachment if we have a key + if ( C4::GnuPG->does_exist($email_add) ) { + $mail{'subject'} = "A message from your library"; + $email_header_and_body = C4::GnuPG->encrypt($email_header_and_body, $email_add); + $iso2709 = C4::GnuPG->encrypt($iso2709, $email_add); + $attachmentfilename = 'attachment.gpg'; + } + else { + $attachmentfilename = 'basket.iso2709'; + } + my $isofile = encode_base64(encode("UTF-8", $iso2709)); $boundary = '--' . $boundary; $mail{body} = <\n(.*)\n/s ) { $body = encode_qp($1); } + if ( $template_res =~ /\n(.*)\n/s ) { + $body = encode_qp($1); + } my $boundary = "====" . time() . "===="; # We set and put the multipart content $mail{'content-type'} = "multipart/mixed; boundary=\"$boundary\""; + my $email_header_and_body = $email_header . $body; + + my $attachmentfilename; + # encrypt the attachment if we have a key + if ( C4::GnuPG->does_exist($email) ) { + $mail{'subject'} = "A message from your library"; + $email_header_and_body = C4::GnuPG->encrypt($email_header_and_body, $email); + $iso2709 = C4::GnuPG->encrypt($iso2709, $email); + $attachmentfilename = 'attachment.gpg'; + } + else { + $attachmentfilename = 'shelf.iso2709'; + } my $isofile = encode_base64(encode("UTF-8", $iso2709)); + $boundary = '--' . $boundary; $mail{body} = <