Hi, Bob;

On Mon, Feb 13, 2017 at 06:03:10PM -0700, Bob Proulx wrote:
>
... 
> I think it would need to be a persistent
> user preference option saved in the database.  One could set it as a
> preference for their account.

Agreed.

> As to implementation I would prefer if this were implemented in two
> parts with a second part being an external standalone helper.  This is
> just my preference and philosophy and others might disagree but
> approaching Savannah's web code from the outside it is best for me if
> I can operate components in isolation.  So that it can be tested
> standalone.  This touches upon several different components and when
> things change, such as GnuPG version, then how will we unit test the
> feature?  Therefore my preference would be to have the encrypted email
> feature be implemented with a standalone helper that could operate
> independently of the web server.  Your prototype already makes use of
> external processes so this would be no different.  It would just
> locate more of the work in a helper script.  But then the helper
> script would be run standalone for unit testing and verifying the
> interface to gpg and the system.

I think I come up with something like this, but the arguments
for mysql are far from intuitive, I wonder how robust this part is.
From d8a34a47c1ab14ee22d1c75a385bab1c25f56786 Mon Sep 17 00:00:00 2001
From: Ineiev <ine...@gnu.org>
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 <ineiev--gnu.org>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+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 <<EOF;
+Usage: $0 [OPTIONS]
+
+Encrypt a message to user's registered GPG key.
+
+  -h, --help            Display this help and exit
+      --user            Savannah user to encrypt to
+      --dbname          Savannah database name
+      --dbuser          Savannah database user
+      --dbhost          Savannah database host
+      --dbparams        Savannah database parameters
+      --dbpasswd        Savannah database password
+
+EOF
+}
+
+if($help) {
+    print_help();
+    exit(0);
+}
+
+our $dbd = DBI->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 <yeupou--gnu.org>
 #                          Joxean Koret <joxeankoret--yahoo.es>
+# Copyright 2017 (c) Ineiev <ineiev--gnu.org>
 #
 # 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 '<p>'._("An email has been sent to the address you have on file.").'</p>';
 print '<p>'._("Follow the instructions in the email to change your account password.").'</p>';
+if($encrypted_message != "")
+  {
+    print '<p>'._("Note that it was encrypted with your registered GPG key.").'</p>';
+  }
 ;
 
 $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 <yeupou--gnu.org>
 # Copyright (C) 2007  Sylvain Beucler
+# Copyright (C) 2017 Ineiev <ineiev--gnu.org>
 #
 # 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 '<input type="checkbox" name="form_email_hide" value="1" '.($row_user['ema
 
 print '<p class="smaller">'._("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.").'</p>';
 
+print '<input type="checkbox" name="form_email_encrypted" value="1" '
+.(user_get_preference("email_encrypted") ? 'checked="checked"':'').' /> '
+._("Encrypt emails when resetting password");
+print '<p class="smaller">'._("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.").'</p>';
 
 print $HTML->box_bottom();
 print "<br />\n";
-- 
1.9.1

Attachment: signature.asc
Description: Digital signature

Reply via email to