From bd0551b22076b82a6d49e9f7a2b2e0c90a1b2326 Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Fri, 05 Feb 2016 07:25:27 -0500
Subject: [PATCH] Secure also downloads of addressbook exports, managesieve script exports and Enigma keys exports

---
 plugins/enigma/lib/enigma_engine.php |  196 ++++++++++++++++++++++++++++++++++++------------
 1 files changed, 146 insertions(+), 50 deletions(-)

diff --git a/plugins/enigma/lib/enigma_engine.php b/plugins/enigma/lib/enigma_engine.php
index 6c5ee3c..b95ed1a 100644
--- a/plugins/enigma/lib/enigma_engine.php
+++ b/plugins/enigma/lib/enigma_engine.php
@@ -1,5 +1,6 @@
 <?php
-/*
+
+/**
  +-------------------------------------------------------------------------+
  | Engine of the Enigma Plugin                                             |
  |                                                                         |
@@ -14,26 +15,25 @@
  +-------------------------------------------------------------------------+
 */
 
-/*
-    RFC2440: OpenPGP Message Format
-    RFC3156: MIME Security with OpenPGP
-    RFC3851: S/MIME
-*/
-
+/**
+ * Enigma plugin engine.
+ *
+ * RFC2440: OpenPGP Message Format
+ * RFC3156: MIME Security with OpenPGP
+ * RFC3851: S/MIME
+ */
 class enigma_engine
 {
     private $rc;
     private $enigma;
     private $pgp_driver;
     private $smime_driver;
+    private $password_time;
 
     public $decryptions     = array();
     public $signatures      = array();
     public $signed_parts    = array();
     public $encrypted_parts = array();
-
-
-    const PASSWORD_TIME = 120;
 
     const SIGN_MODE_BODY     = 1;
     const SIGN_MODE_SEPARATE = 2;
@@ -51,8 +51,12 @@
         $this->rc     = rcmail::get_instance();
         $this->enigma = $enigma;
 
+        $this->password_time = $this->rc->config->get('enigma_password_time') * 60;
+
         // this will remove passwords from session after some time
-        $this->get_passwords();
+        if ($this->password_time) {
+            $this->get_passwords();
+        }
     }
 
     /**
@@ -142,7 +146,7 @@
         $key = $this->find_key($from, true);
 
         if (empty($key)) {
-            return new enigma_error(enigma_error::E_KEYNOTFOUND);
+            return new enigma_error(enigma_error::KEYNOTFOUND);
         }
 
         // check if we have password for this key
@@ -152,7 +156,7 @@
         if ($pass === null) {
             // ask for password
             $error = array('missing' => array($key->id => $key->name));
-            return new enigma_error(enigma_error::E_BADPASS, '', $error);
+            return new enigma_error(enigma_error::BADPASS, '', $error);
         }
 
         // select mode
@@ -193,10 +197,10 @@
         $result = $this->pgp_sign($body, $key->id, $pass, $pgp_mode);
 
         if ($result !== true) {
-            if ($result->getCode() == enigma_error::E_BADPASS) {
+            if ($result->getCode() == enigma_error::BADPASS) {
                 // ask for password
                 $error = array('missing' => array($key->id => $key->name));
-                return new enigma_error(enigma_error::E_BADPASS, '', $error);
+                return new enigma_error(enigma_error::BADPASS, '', $error);
             }
 
             return $result;
@@ -234,7 +238,7 @@
         }
 
         if (empty($recipients)) {
-            return new enigma_error(enigma_error::E_KEYNOTFOUND);
+            return new enigma_error(enigma_error::KEYNOTFOUND);
         }
 
         $recipients = array_unique($recipients);
@@ -244,7 +248,7 @@
             $key = $this->find_key($email);
 
             if (empty($key)) {
-                return new enigma_error(enigma_error::E_KEYNOTFOUND, '', array(
+                return new enigma_error(enigma_error::KEYNOTFOUND, '', array(
                     'missing' => $email
                 ));
             }
@@ -440,11 +444,15 @@
      */
     private function parse_plain_signed(&$p, $body)
     {
+        if (!$this->rc->config->get('enigma_signatures', true)) {
+            return;
+        }
+
         $this->load_pgp_driver();
         $part = $p['structure'];
 
         // Verify signature
-        if ($this->rc->action == 'show' || $this->rc->action == 'preview') {
+        if ($this->rc->action == 'show' || $this->rc->action == 'preview' || $this->rc->action == 'print') {
             $sig = $this->pgp_verify($body);
         }
 
@@ -481,7 +489,7 @@
         // Store signature data for display
         if (!empty($sig)) {
             $this->signed_parts[$part->mime_id] = $part->mime_id;
-            $this->signatures[$part->mime_id] = $sig;
+            $this->signatures[$part->mime_id]   = $sig;
         }
 
         fclose($fh);
@@ -491,38 +499,43 @@
      * Handler for PGP/MIME signed message.
      * Verifies signature.
      *
-     * @param array  Reference to hook's parameters
+     * @param array Reference to hook's parameters
      */
     private function parse_pgp_signed(&$p)
     {
-        // Verify signature
-        if ($this->rc->action == 'show' || $this->rc->action == 'preview') {
-            $this->load_pgp_driver();
-            $struct = $p['structure'];
+        if (!$this->rc->config->get('enigma_signatures', true)) {
+            return;
+        }
 
-            $msg_part = $struct->parts[0];
-            $sig_part = $struct->parts[1];
+        if ($this->rc->action != 'show' && $this->rc->action != 'preview' && $this->rc->action != 'print') {
+            return;
+        }
 
-            // Get bodies
-            // Note: The first part body need to be full part body with headers
-            //       it also cannot be decoded
-            $msg_body = $this->get_part_body($p['object'], $msg_part->mime_id, true);
-            $sig_body = $this->get_part_body($p['object'], $sig_part->mime_id);
+        $this->load_pgp_driver();
+        $struct = $p['structure'];
 
-            // Verify
-            $sig = $this->pgp_verify($msg_body, $sig_body);
+        $msg_part = $struct->parts[0];
+        $sig_part = $struct->parts[1];
 
-            // Store signature data for display
-            $this->signatures[$struct->mime_id] = $sig;
+        // Get bodies
+        // Note: The first part body need to be full part body with headers
+        //       it also cannot be decoded
+        $msg_body = $this->get_part_body($p['object'], $msg_part->mime_id, true);
+        $sig_body = $this->get_part_body($p['object'], $sig_part->mime_id);
 
-            // Message can be multipart (assign signature to each subpart)
-            if (!empty($msg_part->parts)) {
-                foreach ($msg_part->parts as $part)
-                    $this->signed_parts[$part->mime_id] = $struct->mime_id;
-            }
-            else {
-                $this->signed_parts[$msg_part->mime_id] = $struct->mime_id;
-            }
+        // Verify
+        $sig = $this->pgp_verify($msg_body, $sig_body);
+
+        // Store signature data for display
+        $this->signatures[$struct->mime_id] = $sig;
+
+        // Message can be multipart (assign signature to each subpart)
+        if (!empty($msg_part->parts)) {
+            foreach ($msg_part->parts as $part)
+                $this->signed_parts[$part->mime_id] = $struct->mime_id;
+        }
+        else {
+            $this->signed_parts[$msg_part->mime_id] = $struct->mime_id;
         }
     }
 
@@ -536,8 +549,12 @@
     {
         return; // @TODO
 
+        if (!$this->rc->config->get('enigma_signatures', true)) {
+            return;
+        }
+
         // Verify signature
-        if ($this->rc->action == 'show' || $this->rc->action == 'preview') {
+        if ($this->rc->action == 'show' || $this->rc->action == 'preview' || $this->rc->action == 'print') {
             $this->load_smime_driver();
 
             $struct   = $p['structure'];
@@ -568,6 +585,10 @@
      */
     private function parse_plain_encrypted(&$p, $body)
     {
+        if (!$this->rc->config->get('enigma_decryption', true)) {
+            return;
+        }
+
         $this->load_pgp_driver();
         $part = $p['structure'];
 
@@ -642,6 +663,10 @@
      */
     private function parse_pgp_encrypted(&$p)
     {
+        if (!$this->rc->config->get('enigma_decryption', true)) {
+            return;
+        }
+
         $this->load_pgp_driver();
 
         $struct = $p['structure'];
@@ -672,6 +697,10 @@
             // Make sure decryption status message will be displayed
             $part->type = 'content';
             $p['object']->parts[] = $part;
+
+            // don't show encrypted part on attachments list
+            // don't show "cannot display encrypted message" text
+            $p['abort'] = true;
         }
     }
 
@@ -682,6 +711,10 @@
      */
     private function parse_smime_encrypted(&$p)
     {
+        if (!$this->rc->config->get('enigma_decryption', true)) {
+            return;
+        }
+
 //        $this->load_smime_driver();
     }
 
@@ -698,7 +731,7 @@
         // @TODO: Handle big bodies using (temp) files
         $sig = $this->pgp_driver->verify($msg_body, $sig_body);
 
-        if (($sig instanceof enigma_error) && $sig->getCode() != enigma_error::E_KEYNOTFOUND)
+        if (($sig instanceof enigma_error) && $sig->getCode() != enigma_error::KEYNOTFOUND)
             rcube::raise_error(array(
                 'code' => 600, 'type' => 'php',
                 'file' => __FILE__, 'line' => __LINE__,
@@ -723,7 +756,7 @@
 
         if ($result instanceof enigma_error) {
             $err_code = $result->getCode();
-            if (!in_array($err_code, array(enigma_error::E_KEYNOTFOUND, enigma_error::E_BADPASS)))
+            if (!in_array($err_code, array(enigma_error::KEYNOTFOUND, enigma_error::BADPASS)))
                 rcube::raise_error(array(
                     'code' => 600, 'type' => 'php',
                     'file' => __FILE__, 'line' => __LINE__,
@@ -754,7 +787,7 @@
 
         if ($result instanceof enigma_error) {
             $err_code = $result->getCode();
-            if (!in_array($err_code, array(enigma_error::E_KEYNOTFOUND, enigma_error::E_BADPASS)))
+            if (!in_array($err_code, array(enigma_error::KEYNOTFOUND, enigma_error::BADPASS)))
                 rcube::raise_error(array(
                     'code' => 600, 'type' => 'php',
                     'file' => __FILE__, 'line' => __LINE__,
@@ -783,7 +816,7 @@
 
         if ($result instanceof enigma_error) {
             $err_code = $result->getCode();
-            if (!in_array($err_code, array(enigma_error::E_KEYNOTFOUND, enigma_error::E_BADPASS)))
+            if (!in_array($err_code, array(enigma_error::KEYNOTFOUND, enigma_error::BADPASS)))
                 rcube::raise_error(array(
                     'code' => 600, 'type' => 'php',
                     'file' => __FILE__, 'line' => __LINE__,
@@ -900,6 +933,29 @@
     }
 
     /**
+     * PGP keys pair generation.
+     *
+     * @param array Key pair parameters
+     *
+     * @return mixed enigma_key or enigma_error
+     */
+    function generate_key($data)
+    {
+        $this->load_pgp_driver();
+        $result = $this->pgp_driver->gen_key($data);
+
+        if ($result instanceof enigma_error) {
+            rcube::raise_error(array(
+                'code' => 600, 'type' => 'php',
+                'file' => __FILE__, 'line' => __LINE__,
+                'message' => "Enigma plugin: " . $result->getMessage()
+                ), true, false);
+        }
+
+        return $result;
+    }
+
+    /**
      * PGP keys/certs importing.
      *
      * @param mixed   Import file name or content
@@ -952,6 +1008,40 @@
         $this->rc->output->send();
     }
 
+    /**
+     * PGP keys/certs export..
+     *
+     * @param string   Key ID
+     * @param resource Optional output stream
+     *
+     * @return mixed Key content or enigma_error
+     */
+    function export_key($key, $fp = null)
+    {
+        $this->load_pgp_driver();
+        $result = $this->pgp_driver->export($key, $fp);
+
+        if ($result instanceof enigma_error) {
+            rcube::raise_error(array(
+                'code' => 600, 'type' => 'php',
+                'file' => __FILE__, 'line' => __LINE__,
+                'message' => "Enigma plugin: " . $result->getMessage()
+                ), true, false);
+
+            return $result;
+        }
+
+        if ($fp) {
+            fwrite($fp, $result);
+        }
+        else {
+            return $result;
+        }
+    }
+
+    /**
+     * Registers password for specified key/cert sent by the password prompt.
+     */
     function password_handler()
     {
         $keyid  = rcube_utils::get_input_value('_keyid', rcube_utils::INPUT_POST);
@@ -962,6 +1052,9 @@
         }
     }
 
+    /**
+     * Saves key/cert password in user session
+     */
     function save_password($keyid, $password)
     {
         // we store passwords in session for specified time
@@ -975,6 +1068,9 @@
         $_SESSION['enigma_pass'] = $this->rc->encrypt(serialize($config));
     }
 
+    /**
+     * Returns currently stored passwords
+     */
     function get_passwords()
     {
         if ($config = $_SESSION['enigma_pass']) {
@@ -982,12 +1078,12 @@
             $config = @unserialize($config);
         }
 
-        $threshold = time() - self::PASSWORD_TIME;
+        $threshold = $this->password_time ? time() - $this->password_time : 0;
         $keys      = array();
 
         // delete expired passwords
         foreach ((array) $config as $key => $value) {
-            if ($value[1] < $threshold) {
+            if ($threshold && $value[1] < $threshold) {
                 unset($config[$key]);
                 $modified = true;
             }

--
Gitblit v1.9.1