From 1ca7a52ec412c6bb5e7c7cd67bacc3b37b396c4a 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 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. Squashed a few minor changes. Bug 8897 Follow-up: plack fix NOTE: I rebased this so it applies nicely. I'm setting into 'In Discussion', so the original author can rebase himself or confirm this is good. I found the following: - Bug 8368 (only a couple days after the last rebase) patch (recoded the line, for the first conflict -- New version is correct) (renamed shelf to list, for the second conflict -- Second version is this patch) - Bug 11124 triggers a conflict in C4/Installer/PerlDependencies.pm - Bug 9611 also affected C4/Installer/PerlDependencies.pm and debian/control - Bug 5544 added code which caused a conflict for C4/Letter. --- C4/GnuPG.pm | 120 ++++++++++++++++++++ C4/Installer/PerlDependencies.pm | 4 + C4/Letters.pm | 12 +- debian/control | 3 + debian/control.in | 1 + install_misc/debian.packages | 2 + install_misc/ubuntu.10.04.packages | 2 + install_misc/ubuntu.12.04.packages | 2 + install_misc/ubuntu.packages | 2 + .../opac-tmpl/bootstrap/en/includes/usermenu.inc | 7 ++ .../opac-tmpl/bootstrap/en/modules/opac-gnupg.tt | 59 ++++++++++ koha-tmpl/opac-tmpl/prog/en/modules/opac-gnupg.tt | 59 ++++++++++ opac/opac-gnupg.pl | 83 ++++++++++++++ opac/opac-sendbasket.pl | 23 +++- opac/opac-sendshelf.pl | 22 +++- 15 files changed, 392 insertions(+), 9 deletions(-) create mode 100644 C4/GnuPG.pm create mode 100644 koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-gnupg.tt 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..7c6ab9d --- /dev/null +++ b/C4/GnuPG.pm @@ -0,0 +1,120 @@ +package C4::GnuPG; + +# Copyright 2012/2016 Mirko Tietgen koha.abunchofthings.net +# +# 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 3 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, see . + + +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 + +our $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/Installer/PerlDependencies.pm b/C4/Installer/PerlDependencies.pm index 778e101..0dd5871 100644 --- a/C4/Installer/PerlDependencies.pm +++ b/C4/Installer/PerlDependencies.pm @@ -786,6 +786,10 @@ our $PERL_DEPS = { 'usage' => 'PayPal', 'required' => '0', 'min_ver' => '0.03', + 'Crypt::GPG' => { + 'usage' => 'Messaging (encryption)', + 'required' => '1', + 'min_ver' => '1.52', }, }; diff --git a/C4/Letters.pm b/C4/Letters.pm index dc9574f..5c56e5e 100644 --- a/C4/Letters.pm +++ b/C4/Letters.pm @@ -30,6 +30,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; @@ -433,7 +434,12 @@ 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 $message = Koha::Email->new(); my %mail = $message->create_message_headers( @@ -1203,6 +1209,10 @@ sub _send_message_by_email { $branch_replyto = $branchdetail->{'branchreplyto'}; $branch_returnpath = $branchdetail->{'branchreturnpath'}; } + if ( C4::GnuPG->does_exist($to_address) ) { + $subject = 'A message from your library'; + $content = C4::GnuPG->encrypt($content, $to_address); + } my $email = Koha::Email->new(); my %sendmail_params = $email->create_message_headers( { diff --git a/debian/control b/debian/control index ae1f9ab..c2aa8f0 100644 --- a/debian/control +++ b/debian/control @@ -28,6 +28,7 @@ Build-Depends: libalgorithm-checkdigits-perl, libconvert-basen-perl, libcrypt-eksblowfish-perl, libcrypt-gcrypt-perl, + libcrypt-gpg-perl, libdata-ical-perl, libdate-calc-perl, libdate-manip-perl, @@ -194,6 +195,7 @@ Depends: ${misc:Depends}, cron-daemon, daemon, debconf, + gnupg, idzebra-2.0, libjs-jquery, mysql-client | virtual-mysql-client, @@ -236,6 +238,7 @@ Depends: libalgorithm-checkdigits-perl, libconvert-basen-perl, libcrypt-eksblowfish-perl, libcrypt-gcrypt-perl, + libcrypt-gpg-perl, libdata-ical-perl, libdate-calc-perl, libdate-manip-perl, diff --git a/debian/control.in b/debian/control.in index ad9b891..cd5bca9 100644 --- a/debian/control.in +++ b/debian/control.in @@ -70,6 +70,7 @@ Depends: ${misc:Depends}, cron-daemon, daemon, debconf, + gnupg, idzebra-2.0, libjs-jquery, mysql-client | virtual-mysql-client, diff --git a/install_misc/debian.packages b/install_misc/debian.packages index c4bcb93..81ca964 100644 --- a/install_misc/debian.packages +++ b/install_misc/debian.packages @@ -2,6 +2,7 @@ apache2 install at install cvs install daemon install +gnupg install gcc install gettext install idzebra-2.0 install @@ -23,6 +24,7 @@ libcgi-session-perl install libcgi-session-serialize-yaml-perl install libclass-accessor-perl install libclass-factory-util-perl install +libcypt-gpg-perl install libdata-ical-perl install libdata-paginator-perl install libdate-calc-perl install diff --git a/install_misc/ubuntu.10.04.packages b/install_misc/ubuntu.10.04.packages index 42fc68a..74375ea 100644 --- a/install_misc/ubuntu.10.04.packages +++ b/install_misc/ubuntu.10.04.packages @@ -35,6 +35,7 @@ libidzebra-2.0-modules install # crypto packages +gnupg install libgcrypt11 install libgcrypt11-dev install @@ -62,6 +63,7 @@ libcgi-session-perl install libcgi-session-serialize-yaml-perl install libclass-accessor-perl install libclass-factory-util-perl install +libcrypt-gpg-perl install libdata-ical-perl install libdata-paginator-perl install libdate-calc-perl install diff --git a/install_misc/ubuntu.12.04.packages b/install_misc/ubuntu.12.04.packages index b78dab1..43ee643 100644 --- a/install_misc/ubuntu.12.04.packages +++ b/install_misc/ubuntu.12.04.packages @@ -34,6 +34,7 @@ libidzebra-2.0-modules install # crypto packages +gnupg install libgcrypt11 install libgcrypt11-dev install @@ -64,6 +65,7 @@ libchi-driver-memcached-perl install libchi-perl install libclass-accessor-perl install libclass-factory-util-perl install +libcrypt-gpg-perl install libdata-ical-perl install libdata-paginator-perl install libdate-calc-perl install diff --git a/install_misc/ubuntu.packages b/install_misc/ubuntu.packages index 67d84da..e82c2a8 100644 --- a/install_misc/ubuntu.packages +++ b/install_misc/ubuntu.packages @@ -34,6 +34,7 @@ libidzebra-2.0-modules install # crypto packages +gnupg install libgcrypt11 install libgcrypt11-dev install @@ -64,6 +65,7 @@ libchi-driver-memcached-perl install libchi-perl install libclass-accessor-perl install libclass-factory-util-perl install +libcrypt-gpg-perl install libdata-ical-perl install libdata-paginator-perl install libdate-calc-perl install diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc index d166b6e..3c21423 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc +++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc @@ -41,6 +41,13 @@ change your password [% END %] + [% IF ( gnupgview ) %] +
  • + [% ELSE %] +
  • + [% END %] + email encryption
  • + [% IF EnableOpacSearchHistory %] [% IF ( searchhistoryview ) %]
  • diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-gnupg.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-gnupg.tt new file mode 100644 index 0000000..b1d9e26 --- /dev/null +++ b/koha-tmpl/opac-tmpl/bootstrap/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/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..b1d9e26 --- /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..6cefb69 --- /dev/null +++ b/opac/opac-gnupg.pl @@ -0,0 +1,83 @@ +#!/usr/bin/perl + +# Copyright 2013/2016 Mirko Tietgen koha.abunchofthings.net +# +# 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 3 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, see . + + + +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 6b2d62f..8dad7e8 100755 --- a/opac/opac-sendbasket.pl +++ b/opac/opac-sendbasket.pl @@ -34,6 +34,7 @@ use C4::Output; use C4::Biblio; use C4::Members; use Koha::Email; +use C4::GnuPG; my $query = new CGI; @@ -156,6 +157,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} = <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} = <