@@ -, +, @@ - when sending lists or carts (encryption of email text + attachment) - when mails are sent through the message queue - … - 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) - … - library-side key management: secret key for signing - encryption of emails from library to vendors - … -apply patch - 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. - 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 - 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 - 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 --- 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 | 81 ++++++++++++++ opac/opac-sendbasket.pl | 23 ++++- opac/opac-sendshelf.pl | 26 ++++- 7 files changed, 315 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 --- a/C4/GnuPG.pm +++ a/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; --- a/C4/Letters.pm +++ a/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'), --- a/koha-tmpl/opac-tmpl/prog/en/includes/usermenu.inc +++ a/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 %] --- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-gnupg.tt +++ a/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' %] --- a/opac/opac-gnupg.pl +++ a/opac/opac-gnupg.pl @@ -0,0 +1,81 @@ +#!/usr/bin/perl + +# Copyright 2013 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 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; --- a/opac/opac-sendbasket.pl +++ a/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} = <