From 63c27b251384cfabdc7953462f7765c175042ee2 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 - … Test plan: -apply patch * Add/delete key: - in OPAC, log in as a user you got GPG kes for (primary email needs to match the key) - go to user details, email encryption - paste your public key into the form, save. when the page reloads, your key should be displayed in the form and a delete button at the bottom. in the terminal (as the koha-user!), do 'gpg --list-keys' and verify the key you just added is present - press the delete button under the form. on reload the form should be empty and your key gone. verify with (as the koha user) 'gpg --list-keys' in the terminal. - paste your key into the form again and save to do the following tests. * Encrypted cart emails - do a search, save a record in the the cart. open the cart, send it to the adress you added the key for. - check if the mail you receive has encrypted text and an encrypted attachment - decrypt both the text and the attachment, check if they are what you would expect * Encrypted list emails - if you do not have any lists, set up one with one record. - open the list from the opac, send it to you. enter the address you just added the key for - check that the mail you receive has encrypted text and an encrypted attachment. - decrypt both the text and the attachment, check if they are what you would expect * Encrypted message queue emails - check some items in and out for the patron you added the key for - wait for the message queue cronjob to run or run it manually - check if the email(s) you receive are encrypted and after decryption contain what you would expect Check all this again with another patron (without a gpg key) or use the same but delete the key before. Check that all mails and attachments are unencrypted and contain what you would expect (nothing is broken). If all that works for you, sign off the patch. --- C4/GnuPG.pm | 119 +++++++++++++++++++++ C4/Letters.pm | 19 +++- koha-tmpl/opac-tmpl/prog/en/includes/usermenu.inc | 1 + koha-tmpl/opac-tmpl/prog/en/modules/opac-gnupg.tt | 59 ++++++++++ opac/opac-gnupg.pl | 82 ++++++++++++++ opac/opac-sendbasket.pl | 23 ++++- opac/opac-sendshelf.pl | 26 ++++- 7 files changed, 316 insertions(+), 13 deletions(-) create mode 100644 C4/GnuPG.pm create mode 100644 koha-tmpl/opac-tmpl/prog/en/modules/opac-gnupg.tt create mode 100755 opac/opac-gnupg.pl diff --git a/C4/GnuPG.pm b/C4/GnuPG.pm new file mode 100644 index 0000000..9f658fc --- /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->encryptsafe(0); # we have to allow untrusted keys + +=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; + } +} + +1; 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/koha-tmpl/opac-tmpl/prog/en/modules/opac-gnupg.tt b/koha-tmpl/opac-tmpl/prog/en/modules/opac-gnupg.tt new file mode 100644 index 0000000..3808e1d --- /dev/null +++ b/koha-tmpl/opac-tmpl/prog/en/modules/opac-gnupg.tt @@ -0,0 +1,59 @@ +[% INCLUDE 'doc-head-open.inc' %][% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog › Add or change your GPG key +[% INCLUDE 'doc-head-close.inc' %] + + +
    +
    +[% INCLUDE 'masthead.inc' %] + +
    +
    +
    +

    [% firstname %] [% surname %]'s account ⇢ Add or change your GPG key

    +

    If you paste your GPG public key here, we will try to encrypt the emails we send you.
    + If you want to learn about email encryption, you will find some information at link

    + + [% IF ( Error_messages ) %] +

    There was a problem with your submission

    +

    + [% IF ( NoGnuPG ) %] + Could not find the path to GnuPG. This has to be set by the library for encryption to work. Please ask our staff about it. + [% END %] +

    + [% END %] + +

    Path to GPG [% IF (gpgbinary) %]found[% ELSE %]not found[% END %].

    + +
    +
    +

    +
    +
    +
    Cancel
    +
    + + [% IF ( does_exist ) %] +

    Delete the key from our server if you wish to receive unencrypted emails in the future.

    +
    + +
    +
    + [% END %] + +
    +
    +
    +
    +
    +
    +[% INCLUDE 'navigation.inc' IsPatronPage=1 %] +
    +
    +
    +[% INCLUDE 'opac-bottom.inc' %] diff --git a/opac/opac-gnupg.pl b/opac/opac-gnupg.pl new file mode 100755 index 0000000..2a2497e --- /dev/null +++ b/opac/opac-gnupg.pl @@ -0,0 +1,82 @@ +#!/usr/bin/perl +# This script lets the users change the passwords by themselves. +# +# (c) 2005 Universidad ORT Uruguay. +# +# This file is part of the extensions and enhacments made to koha by Universidad ORT Uruguay +# +# 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 CGI; + +use C4::Auth; # checkauth, getborrowernumber. +use C4::Context; +use Digest::MD5 qw(md5_base64); +use C4::Circulation; +use C4::Members; +use C4::Output; +use C4::GnuPG; + +my $query = new CGI; +my $dbh = C4::Context->dbh; + +my ( $template, $borrowernumber, $cookie ) = get_template_and_user( + { + template_name => "opac-gnupg.tt", + query => $query, + type => "opac", + authnotrequired => 0, + flagsrequired => { borrow => 1 }, + debug => 1, + } +); + +# get borrower information .... +my ( $borr ) = GetMemberDetails( $borrowernumber ); +my $patronemail = $borr->{'email'}; +# save key if key received +if ( $query->param('PublicKey') ) { + C4::GnuPG->add_key($query->param('PublicKey'), $patronemail); +# $template->param( 'addedkey' => $query->param('PublicKey') ); +# $template->param( 'added' => '1' ); +} +elsif ( $query->param('DeleteKey') ) { + C4::GnuPG->del_key($patronemail); +} + if ( C4::GnuPG->does_exist($patronemail) ) { + # TODO: display public key if present + my $keythingy = C4::GnuPG->get_key($patronemail); + $template->param( 'does_exist' => '1' ); + $template->param( 'keythingy' => $keythingy ); + } + # ask for key + #$template->param( 'Ask_data' => '1' ); + #$template->param( 'Error_messages' => '1' ); + + my $gnupgbinary = '/usr/bin/gpg'; + if (-e $gnupgbinary) { + $template->param( 'gpgbinary' => '1' ); + } + +$template->param( + firstname => $borr->{'firstname'}, + surname => $borr->{'surname'}, + patronemail => $patronemail, + gnupgview => 1, +); + +output_html_with_http_headers $query, $cookie, $template->output; diff --git a/opac/opac-sendbasket.pl b/opac/opac-sendbasket.pl index da74745..2b2915e 100755 --- a/opac/opac-sendbasket.pl +++ b/opac/opac-sendbasket.pl @@ -33,6 +33,7 @@ use C4::Auth; use C4::Output; use C4::Biblio; use C4::Members; +use C4::GnuPG; my $query = new CGI; @@ -149,6 +150,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} = <