From d8a34a47c1ab14ee22d1c75a385bab1c25f56786 Mon Sep 17 00:00:00 2001
From: Ineiev
Date: Fri, 10 Feb 2017 15:10:55 +0300
Subject: [PATCH] Encrypt message to GPG key when available.
---
frontend/perl/encrypt-to-user/index.pl | 134 ++++++++++++++++++++++++++++++++
frontend/php/account/lostpw-confirm.php | 34 ++++++++
frontend/php/my/admin/index.php | 13 +++-
3 files changed, 180 insertions(+), 1 deletion(-)
create mode 100644 frontend/perl/encrypt-to-user/index.pl
diff --git a/frontend/perl/encrypt-to-user/index.pl b/frontend/perl/encrypt-to-user/index.pl
new file mode 100644
index 0000000..be1345d
--- /dev/null
+++ b/frontend/perl/encrypt-to-user/index.pl
@@ -0,0 +1,134 @@
+#! /usr/bin/perl
+# Encrypt a message to specified savane user
+#
+# Copyright 2017 (c) Ineiev
+#
+# This file is part of Savane.
+#
+# Savane is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# Savane 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+use strict;
+use DBI;
+use File::Temp qw(tempdir tempfile);
+use Getopt::Long;
+my $getopt;
+my $help;
+my $user;
+my $sys_dbname;
+my $sys_dbhost;
+my $sys_dbuser;
+my $sys_dbparams;
+my $sys_dbpasswd;
+
+eval {
+ $getopt = GetOptions("help" => \$help,
+ "user=s" => \$user,
+ "dbname=s" => \$sys_dbname,
+ "dbhost:s" => \$sys_dbhost,
+ "dbuser=s" => \$sys_dbuser,
+ "dbparams:s" => \$sys_dbparams,
+ "dbpasswd:s" => \$sys_dbpasswd);
+};
+
+sub print_help {
+ print STDERR <connect('DBI:mysql:database='.$sys_dbname
+ .':host='.$sys_dbhost
+ .':'.$sys_dbparams,
+ $sys_dbuser, $sys_dbpasswd,
+ { RaiseError => 1, AutoCommit => 1});
+
+## Encrypt to user GPG key if available
+# arg1 : user id
+# arg2 : message
+# return encrypted message when encryption succeeded,
+# empty string encryption failed.
+sub UserEncrypt {
+ my ($user, $message) = @_;
+ my $key = $dbd->selectrow_array("SELECT gpg_key FROM user WHERE user_id=".$user);
+
+ return "" unless $key ne "";
+
+ my ($mh, $mname) = tempfile(UNLINK => 1);
+ my $temp_dir = tempdir(CLEANUP => 1);
+ my $input;
+ my $key_id = "";
+ my $msg = "";
+
+ print $mh $message;
+
+ open($input, '|-', 'gpg --homedir='.$temp_dir.' -q --import');
+ print $input $key;
+ close($input);
+
+# Get the first ID of a public key with encryption capability.
+ open($input, '-|', 'gpg --homedir='.$temp_dir.
+ ' --list-keys --with-colons 2> /dev/null');
+ while(<$input>)
+ {
+ if(!/^pub/)
+ {
+ next;
+ }
+ my @fields = split /:/;
+ if(@fields[11] !~ /[eE]/)
+ {
+ next;
+ }
+ $key_id = @fields[4];
+ last unless $key_id eq "";
+ }
+ close($input);
+ return "" unless $key_id ne "";
+ open($input, '-|', 'gpg --homedir='.$temp_dir.
+ ' --trust-model always --batch -a --encrypt -r '
+ .$key_id." -o - ".$mname);
+ while(<$input>)
+ {
+ $msg = $msg.$_;
+ }
+ return "" unless $msg ne "";
+ return $msg;
+}
+
+my $msg = "";
+
+while(<>)
+ {
+ $msg = $msg.$_;
+ }
+
+print UserEncrypt($user, $msg);
+
+exit 0;
diff --git a/frontend/php/account/lostpw-confirm.php b/frontend/php/account/lostpw-confirm.php
index a4dc5cd..e41b744 100644
--- a/frontend/php/account/lostpw-confirm.php
+++ b/frontend/php/account/lostpw-confirm.php
@@ -4,6 +4,7 @@
# Copyright 1999-2000 (c) The SourceForge Crew
# Copyright 2004-2005 (c) Mathieu Roy
# Joxean Koret
+# Copyright 2017 (c) Ineiev
#
# This file is part of Savane.
#
@@ -24,6 +25,7 @@ require_once('../include/init.php');
require_once('../include/sane.php');
require_once('../include/session.php');
require_once('../include/sendmail.php');
+require_once('../include/database.php');
register_globals_off();
@@ -140,6 +142,34 @@ $message_for_admin =
. gmdate('D, d M Y H:i:s \G\M\T')
. "\n";
+$encrypted_message = "";
+if(user_get_preference("email_encrypted", $row_user['user_id']))
+ {
+ $cmd = '/usr/bin/perl ../../perl/encrypt-to-user/index.pl '
+ .'--user="'.$row_user['user_id'].'" '
+ .'--dbuser="'.$sys_dbuser.'" '
+ .'--dbname="'.$sys_dbname.'" '
+ .'--dbpasswd="'.$sys_dbpasswd.'" '
+ .'--dbparams="mysql_socket='.substr($sys_dbhost, 1).'"';
+
+ $d_spec = array(
+ 0 => array("pipe", "r"), 1 => array("pipe", "w"),
+ 2 => array("file", "/dev/null", "a"));
+
+ $gpg_proc = proc_open($cmd, $d_spec, $pipes, NULL, $_ENV);
+ fwrite($pipes[0], $message);
+ fclose($pipes[0]);
+ $encrypted_message = stream_get_contents($pipes[1]);
+ fclose($pipes[1]);
+ $gpg_result = proc_close($gpg_proc);
+
+ if($gpg_result != 0 or $encrypted_message == FALSE)
+ $encrypted_message = "";
+ }
+
+if($encrypted_message != "")
+ $message = $encrypted_message;
+
sendmail_mail($GLOBALS['sys_mail_replyto']."@".$GLOBALS['sys_mail_domain'],
$row_user['email'],
$GLOBALS['sys_default_domain']." Verification",
@@ -159,6 +189,10 @@ $HTML->header(array('title'=>_("Lost Password Confirmation")));
print '
'._("An email has been sent to the address you have on file.").'
';
print '
'._("Follow the instructions in the email to change your account password.").'
';
+if($encrypted_message != "")
+ {
+ print '
'._("Note that it was encrypted with your registered GPG key.").'
';
+ }
;
$HTML->footer(array());
diff --git a/frontend/php/my/admin/index.php b/frontend/php/my/admin/index.php
index 3b19a28..ecc7315 100644
--- a/frontend/php/my/admin/index.php
+++ b/frontend/php/my/admin/index.php
@@ -3,6 +3,7 @@
# Copyright 1999-2000 (c) The SourceForge Crew
# Copyright 2002-2006 (c) Mathieu Roy
# Copyright (C) 2007 Sylvain Beucler
+# Copyright (C) 2017 Ineiev
#
# This file is part of Savane.
#
@@ -30,7 +31,7 @@ extract(sane_import('post',
'form_keep_only_one_session',
'form_timezone', 'user_theme', 'theme_rotate_jump',
'form_reverse_comments_order', 'form_stone_age_menu', 'form_nonfixed_feedback',
- 'form_use_bookmarks', 'form_email_hide',
+ 'form_use_bookmarks', 'form_email_hide', 'form_email_encrypted'
)));
if ($update and $user_theme != "random" and $user_theme != "rotate")
@@ -79,6 +80,12 @@ if ($update)
else
{ user_unset_preference("use_bookmarks"); }
+ # Encryption preferences
+ if ($form_email_encrypted == "1")
+ { user_set_preference("email_encrypted", 1); }
+ else
+ { user_unset_preference("email_encrypted"); }
+
# Relative position feedback
if ($form_nonfixed_feedback == "1")
{ user_set_preference("nonfixed_feedback", 1); }
@@ -287,6 +294,10 @@ print ''._("When checked, the only way for users to get in touch with you would be to use the form available to logged-in users. It is generally a bad idea to choose this option, especially if you are a project administrator.").'';
+print ' '
+._("Encrypt emails when resetting password");
+print '
'._("When checked, the Savannah will encrypt email messages with your registered public GPG key when you (or someone else) request resetting password. If no suitable key is available, the messages still go unencrypted.").'