summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorAleksander Machniak <alec@alec.pl>2013-12-27 13:14:40 +0100
committerAleksander Machniak <alec@alec.pl>2013-12-27 13:14:40 +0100
commit3e98f8be718578644bb15ee6a992a875f6468e8f (patch)
treea0721e608a9ba04ca23d5535f90e579942581e6e /plugins
parentc97625e02a95ebd995af8a06c27229581a071ddd (diff)
Add some code for S/MIME signatures verification, update Crypt_GPG package
Diffstat (limited to 'plugins')
-rw-r--r--plugins/enigma/README1
-rw-r--r--plugins/enigma/enigma.php21
-rw-r--r--plugins/enigma/lib/enigma_driver_gnupg.php54
-rw-r--r--plugins/enigma/lib/enigma_driver_phpssl.php238
-rw-r--r--plugins/enigma/lib/enigma_engine.php51
-rw-r--r--plugins/enigma/lib/enigma_error.php4
-rw-r--r--plugins/enigma/localization/en_US.inc7
7 files changed, 323 insertions, 53 deletions
diff --git a/plugins/enigma/README b/plugins/enigma/README
index 22d6e513a..c4e474be2 100644
--- a/plugins/enigma/README
+++ b/plugins/enigma/README
@@ -12,6 +12,7 @@ Enigma Plugin Status:
- Handling of PGP keys files attached to incoming messages
- PGP encrypted messages decryption (started)
- PGP keys management UI (started)
+- S/MIME signatures verification (started)
* TODO (must have):
diff --git a/plugins/enigma/enigma.php b/plugins/enigma/enigma.php
index 25520a27d..870b923b6 100644
--- a/plugins/enigma/enigma.php
+++ b/plugins/enigma/enigma.php
@@ -179,10 +179,11 @@ class enigma extends rcube_plugin
{
// add labels
$this->add_texts('localization/');
-
+/*
$p['list']['enigmasettings'] = array(
'id' => 'enigmasettings', 'section' => $this->gettext('enigmasettings'),
);
+*/
$p['list']['enigmacerts'] = array(
'id' => 'enigmacerts', 'section' => $this->gettext('enigmacerts'),
);
@@ -203,11 +204,13 @@ class enigma extends rcube_plugin
*/
function preferences_list($p)
{
+/*
if ($p['section'] == 'enigmasettings') {
// This makes that section is not removed from the list
$p['blocks']['dummy']['options']['dummy'] = array();
}
- else if ($p['section'] == 'enigmacerts') {
+ else */
+ if ($p['section'] == 'enigmacerts') {
// This makes that section is not removed from the list
$p['blocks']['dummy']['options']['dummy'] = array();
}
@@ -313,18 +316,24 @@ class enigma extends rcube_plugin
$attrib['id'] = 'enigma-message';
if ($sig instanceof enigma_signature) {
- if ($sig->valid) {
+ $sender = ($sig->name ? $sig->name . ' ' : '') . '<' . $sig->email . '>';
+
+ if ($sig->valid === enigma_error::E_UNVERIFIED) {
+ $attrib['class'] = 'enigmawarning';
+ $msg = str_replace('$sender', $sender, $this->gettext('sigunverified'));
+ $msg = str_replace('$keyid', $sig->id, $msg);
+ $msg = rcube::Q($msg);
+ }
+ else if ($sig->valid) {
$attrib['class'] = 'enigmanotice';
- $sender = ($sig->name ? $sig->name . ' ' : '') . '<' . $sig->email . '>';
$msg = rcube::Q(str_replace('$sender', $sender, $this->gettext('sigvalid')));
}
else {
$attrib['class'] = 'enigmawarning';
- $sender = ($sig->name ? $sig->name . ' ' : '') . '<' . $sig->email . '>';
$msg = rcube::Q(str_replace('$sender', $sender, $this->gettext('siginvalid')));
}
}
- else if ($sig->getCode() == enigma_error::E_KEYNOTFOUND) {
+ else if ($sig && $sig->getCode() == enigma_error::E_KEYNOTFOUND) {
$attrib['class'] = 'enigmawarning';
$msg = rcube::Q(str_replace('$keyid', enigma_key::format_id($sig->getData('id')),
$this->gettext('signokey')));
diff --git a/plugins/enigma/lib/enigma_driver_gnupg.php b/plugins/enigma/lib/enigma_driver_gnupg.php
index 5aa32217e..c4280a089 100644
--- a/plugins/enigma/lib/enigma_driver_gnupg.php
+++ b/plugins/enigma/lib/enigma_driver_gnupg.php
@@ -76,7 +76,7 @@ class enigma_driver_gnupg extends enigma_driver
// Create Crypt_GPG object
try {
- $this->gpg = new Crypt_GPG(array(
+ $this->gpg = new Crypt_GPG(array(
'homedir' => $this->homedir,
// 'debug' => true,
));
@@ -89,20 +89,20 @@ class enigma_driver_gnupg extends enigma_driver
function encrypt($text, $keys)
{
/*
- foreach ($keys as $key) {
- $this->gpg->addEncryptKey($key);
- }
- $enc = $this->gpg->encrypt($text);
- return $enc;
+ foreach ($keys as $key) {
+ $this->gpg->addEncryptKey($key);
+ }
+ $enc = $this->gpg->encrypt($text);
+ return $enc;
*/
}
function decrypt($text, $key, $passwd)
{
-// $this->gpg->addDecryptKey($key, $passwd);
+// $this->gpg->addDecryptKey($key, $passwd);
try {
- $dec = $this->gpg->decrypt($text);
- return $dec;
+ $dec = $this->gpg->decrypt($text);
+ return $dec;
}
catch (Exception $e) {
return $this->get_error_from_exception($e);
@@ -112,17 +112,17 @@ class enigma_driver_gnupg extends enigma_driver
function sign($text, $key, $passwd)
{
/*
- $this->gpg->addSignKey($key, $passwd);
- $signed = $this->gpg->sign($text, Crypt_GPG::SIGN_MODE_DETACHED);
- return $signed;
+ $this->gpg->addSignKey($key, $passwd);
+ $signed = $this->gpg->sign($text, Crypt_GPG::SIGN_MODE_DETACHED);
+ return $signed;
*/
}
function verify($text, $signature)
{
try {
- $verified = $this->gpg->verify($text, $signature);
- return $this->parse_signature($verified[0]);
+ $verified = $this->gpg->verify($text, $signature);
+ return $this->parse_signature($verified[0]);
}
catch (Exception $e) {
return $this->get_error_from_exception($e);
@@ -141,11 +141,11 @@ class enigma_driver_gnupg extends enigma_driver
return $this->get_error_from_exception($e);
}
}
-
+
public function list_keys($pattern='')
{
try {
- $keys = $this->gpg->getKeys($pattern);
+ $keys = $this->gpg->getKeys($pattern);
$result = array();
//print_r($keys);
foreach ($keys as $idx => $key) {
@@ -153,13 +153,13 @@ class enigma_driver_gnupg extends enigma_driver
unset($keys[$idx]);
}
//print_r($result);
- return $result;
+ return $result;
}
catch (Exception $e) {
return $this->get_error_from_exception($e);
}
}
-
+
public function get_key($keyid)
{
$list = $this->list_keys($keyid);
@@ -167,7 +167,7 @@ class enigma_driver_gnupg extends enigma_driver
if (is_array($list))
return array_shift($list);
- // error
+ // error
return $list;
}
@@ -178,14 +178,12 @@ class enigma_driver_gnupg extends enigma_driver
public function del_key($keyid)
{
// $this->get_key($keyid);
-
-
}
-
+
public function del_privkey($keyid)
{
try {
- $this->gpg->deletePrivateKey($keyid);
+ $this->gpg->deletePrivateKey($keyid);
return true;
}
catch (Exception $e) {
@@ -196,14 +194,14 @@ class enigma_driver_gnupg extends enigma_driver
public function del_pubkey($keyid)
{
try {
- $this->gpg->deletePublicKey($keyid);
+ $this->gpg->deletePublicKey($keyid);
return true;
}
catch (Exception $e) {
return $this->get_error_from_exception($e);
}
}
-
+
/**
* Converts Crypt_GPG exception into Enigma's error object
*
@@ -281,7 +279,7 @@ class enigma_driver_gnupg extends enigma_driver
$ekey->users[$idx] = $id;
}
-
+
$ekey->name = trim($ekey->users[0]->name . ' <' . $ekey->users[0]->email . '>');
foreach ($key->getSubKeys() as $idx => $subkey) {
@@ -297,9 +295,9 @@ class enigma_driver_gnupg extends enigma_driver
$ekey->subkeys[$idx] = $skey;
};
-
+
$ekey->id = $ekey->subkeys[0]->id;
-
+
return $ekey;
}
}
diff --git a/plugins/enigma/lib/enigma_driver_phpssl.php b/plugins/enigma/lib/enigma_driver_phpssl.php
new file mode 100644
index 000000000..50af44762
--- /dev/null
+++ b/plugins/enigma/lib/enigma_driver_phpssl.php
@@ -0,0 +1,238 @@
+<?php
+/*
+ +-------------------------------------------------------------------------+
+ | S/MIME driver for the Enigma Plugin |
+ | |
+ | This program is free software; you can redistribute it and/or modify |
+ | it under the terms of the GNU General Public License version 2 |
+ | as published by the Free Software Foundation. |
+ | |
+ | This program 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 this program; if not, write to the Free Software Foundation, Inc., |
+ | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
+ | |
+ +-------------------------------------------------------------------------+
+ | Author: Aleksander Machniak <alec@alec.pl> |
+ +-------------------------------------------------------------------------+
+*/
+
+class enigma_driver_phpssl extends enigma_driver
+{
+ private $rc;
+ //private $gpg;
+ private $homedir;
+ private $user;
+
+ function __construct($user)
+ {
+ $rcmail = rcmail::get_instance();
+ $this->rc = $rcmail;
+ $this->user = $user;
+ }
+
+ /**
+ * Driver initialization and environment checking.
+ * Should only return critical errors.
+ *
+ * @return mixed NULL on success, enigma_error on failure
+ */
+ function init()
+ {
+ $homedir = $this->rc->config->get('enigma_smime_homedir', INSTALL_PATH . '/plugins/enigma/home');
+
+ if (!$homedir)
+ return new enigma_error(enigma_error::E_INTERNAL,
+ "Option 'enigma_smime_homedir' not specified");
+
+ // check if homedir exists (create it if not) and is readable
+ if (!file_exists($homedir))
+ return new enigma_error(enigma_error::E_INTERNAL,
+ "Keys directory doesn't exists: $homedir");
+ if (!is_writable($homedir))
+ return new enigma_error(enigma_error::E_INTERNAL,
+ "Keys directory isn't writeable: $homedir");
+
+ $homedir = $homedir . '/' . $this->user;
+
+ // check if user's homedir exists (create it if not) and is readable
+ if (!file_exists($homedir))
+ mkdir($homedir, 0700);
+
+ if (!file_exists($homedir))
+ return new enigma_error(enigma_error::E_INTERNAL,
+ "Unable to create keys directory: $homedir");
+ if (!is_writable($homedir))
+ return new enigma_error(enigma_error::E_INTERNAL,
+ "Unable to write to keys directory: $homedir");
+
+ $this->homedir = $homedir;
+
+ }
+
+ function encrypt($text, $keys)
+ {
+ }
+
+ function decrypt($text, $key, $passwd)
+ {
+ }
+
+ function sign($text, $key, $passwd)
+ {
+ }
+
+ function verify($struct, $message)
+ {
+ // use common temp dir
+ $temp_dir = $this->rc->config->get('temp_dir');
+ $msg_file = tempnam($temp_dir, 'rcmMsg');
+ $cert_file = tempnam($temp_dir, 'rcmCert');
+
+ $fh = fopen($msg_file, "w");
+ if ($struct->mime_id) {
+ $message->get_part_content($struct->mime_id, $fh, true, 0, false);
+ }
+ else {
+ $this->rc->storage->get_raw_body($message->uid, $fh);
+ }
+ fclose($fh);
+
+ // @TODO: use stored certificates
+
+ // try with certificate verification
+ $sig = openssl_pkcs7_verify($msg_file, 0, $cert_file);
+ $validity = true;
+
+ if ($sig !== true) {
+ // try without certificate verification
+ $sig = openssl_pkcs7_verify($msg_file, PKCS7_NOVERIFY, $cert_file);
+ $validity = enigma_error::E_UNVERIFIED;
+ }
+
+ if ($sig === true) {
+ $sig = $this->parse_sig_cert($cert_file, $validity);
+ }
+ else {
+ $errorstr = $this->get_openssl_error();
+ $sig = new enigma_error(enigma_error::E_INTERNAL, $errorstr);
+ }
+
+ // remove temp files
+ @unlink($msg_file);
+ @unlink($cert_file);
+
+ return $sig;
+ }
+
+ public function import($content, $isfile=false)
+ {
+ }
+
+ public function list_keys($pattern='')
+ {
+ }
+
+ public function get_key($keyid)
+ {
+ }
+
+ public function gen_key($data)
+ {
+ }
+
+ public function del_key($keyid)
+ {
+ }
+
+ public function del_privkey($keyid)
+ {
+ }
+
+ public function del_pubkey($keyid)
+ {
+ }
+
+ /**
+ * Converts Crypt_GPG_Key object into Enigma's key object
+ *
+ * @param Crypt_GPG_Key Key object
+ *
+ * @return enigma_key Key object
+ */
+ private function parse_key($key)
+ {
+/*
+ $ekey = new enigma_key();
+
+ foreach ($key->getUserIds() as $idx => $user) {
+ $id = new enigma_userid();
+ $id->name = $user->getName();
+ $id->comment = $user->getComment();
+ $id->email = $user->getEmail();
+ $id->valid = $user->isValid();
+ $id->revoked = $user->isRevoked();
+
+ $ekey->users[$idx] = $id;
+ }
+
+ $ekey->name = trim($ekey->users[0]->name . ' <' . $ekey->users[0]->email . '>');
+
+ foreach ($key->getSubKeys() as $idx => $subkey) {
+ $skey = new enigma_subkey();
+ $skey->id = $subkey->getId();
+ $skey->revoked = $subkey->isRevoked();
+ $skey->created = $subkey->getCreationDate();
+ $skey->expires = $subkey->getExpirationDate();
+ $skey->fingerprint = $subkey->getFingerprint();
+ $skey->has_private = $subkey->hasPrivate();
+ $skey->can_sign = $subkey->canSign();
+ $skey->can_encrypt = $subkey->canEncrypt();
+
+ $ekey->subkeys[$idx] = $skey;
+ };
+
+ $ekey->id = $ekey->subkeys[0]->id;
+
+ return $ekey;
+*/
+ }
+
+ private function get_openssl_error()
+ {
+ $tmp = array();
+ while ($errorstr = openssl_error_string()) {
+ $tmp[] = $errorstr;
+ }
+
+ return join("\n", array_values($tmp));
+ }
+
+ private function parse_sig_cert($file, $validity)
+ {
+ $cert = openssl_x509_parse(file_get_contents($file));
+
+ if (empty($cert) || empty($cert['subject'])) {
+ $errorstr = $this->get_openssl_error();
+ return new enigma_error(enigm_error::E_INTERNAL, $errorstr);
+ }
+
+ $data = new enigma_signature();
+
+ $data->id = $cert['hash']; //?
+ $data->valid = $validity;
+ $data->fingerprint = $cert['serialNumber'];
+ $data->created = $cert['validFrom_time_t'];
+ $data->expires = $cert['validTo_time_t'];
+ $data->name = $cert['subject']['CN'];
+// $data->comment = '';
+ $data->email = $cert['subject']['emailAddress'];
+
+ return $data;
+ }
+
+}
diff --git a/plugins/enigma/lib/enigma_engine.php b/plugins/enigma/lib/enigma_engine.php
index 8a64c07ff..e4972c6a9 100644
--- a/plugins/enigma/lib/enigma_engine.php
+++ b/plugins/enigma/lib/enigma_engine.php
@@ -92,9 +92,6 @@ class enigma_engine
if ($this->smime_driver)
return;
- // NOT IMPLEMENTED!
- return;
-
$driver = 'enigma_driver_' . $this->rc->config->get('enigma_smime_driver', 'phpssl');
$username = $this->rc->user->get_username();
@@ -246,7 +243,7 @@ class enigma_engine
fclose($fh);
}
-
+
/**
* Handler for PGP/MIME signed message.
* Verifies signature.
@@ -255,14 +252,14 @@ class enigma_engine
*/
private function parse_pgp_signed(&$p)
{
- $this->load_pgp_driver();
- $struct = $p['structure'];
-
// Verify signature
if ($this->rc->action == 'show' || $this->rc->action == 'preview') {
+ $this->load_pgp_driver();
+ $struct = $p['structure'];
+
$msg_part = $struct->parts[0];
$sig_part = $struct->parts[1];
-
+
// Get bodies
$this->set_part_body($msg_part, $p['object']->uid);
$this->set_part_body($sig_part, $p['object']->uid);
@@ -294,7 +291,31 @@ class enigma_engine
*/
private function parse_smime_signed(&$p)
{
- $this->load_smime_driver();
+ // Verify signature
+ if ($this->rc->action == 'show' || $this->rc->action == 'preview') {
+ $this->load_smime_driver();
+
+ $struct = $p['structure'];
+ $msg_part = $struct->parts[0];
+
+ // Verify
+ $sig = $this->smime_driver->verify($struct, $p['object']);
+
+ // 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;
+ }
+
+ // Remove signature file from attachments list
+ unset($struct->parts[1]);
+ }
}
/**
@@ -306,22 +327,22 @@ class enigma_engine
{
$this->load_pgp_driver();
$part = $p['structure'];
-
+
// Get body
$this->set_part_body($part, $p['object']->uid);
- // Decrypt
+ // Decrypt
$result = $this->pgp_decrypt($part->body);
-
+
// Store decryption status
$this->decryptions[$part->mime_id] = $result;
-
+
// Parse decrypted message
if ($result === true) {
// @TODO
}
}
-
+
/**
* Handler for PGP/MIME encrypted message.
*
@@ -359,7 +380,7 @@ class enigma_engine
*/
private function parse_smime_encrypted(&$p)
{
- $this->load_smime_driver();
+// $this->load_smime_driver();
}
/**
diff --git a/plugins/enigma/lib/enigma_error.php b/plugins/enigma/lib/enigma_error.php
index 9f424dc2b..ab8d01557 100644
--- a/plugins/enigma/lib/enigma_error.php
+++ b/plugins/enigma/lib/enigma_error.php
@@ -34,7 +34,9 @@ class enigma_error
const E_KEYNOTFOUND = 3;
const E_DELKEY = 4;
const E_BADPASS = 5;
-
+ const E_EXPIRED = 6;
+ const E_UNVERIFIED = 7;
+
function __construct($code = null, $message = '', $data = array())
{
$this->code = $code;
diff --git a/plugins/enigma/localization/en_US.inc b/plugins/enigma/localization/en_US.inc
index e0f03d9a0..f1ff8d1fb 100644
--- a/plugins/enigma/localization/en_US.inc
+++ b/plugins/enigma/localization/en_US.inc
@@ -1,9 +1,9 @@
<?php
$labels = array();
-$labels['enigmasettings'] = 'Enigma: Settings';
-$labels['enigmacerts'] = 'Enigma: Certificates (S/MIME)';
-$labels['enigmakeys'] = 'Enigma: Keys (PGP)';
+$labels['enigmasettings'] = 'Enigma Settings';
+$labels['enigmacerts'] = 'S/MIME Certificates';
+$labels['enigmakeys'] = 'PGP Keys';
$labels['keysfromto'] = 'Keys $from to $to of $count';
$labels['keyname'] = 'Name';
$labels['keyid'] = 'Key ID';
@@ -36,6 +36,7 @@ $labels['signmsg'] = 'Digitally sign this message';
$messages = array();
$messages['sigvalid'] = 'Verified signature from $sender.';
$messages['siginvalid'] = 'Invalid signature from $sender.';
+$messages['sigunverified'] = 'Unverified signature. Certificate not verified. Certificate ID: $keyid.';
$messages['signokey'] = 'Unverified signature. Public key not found. Key ID: $keyid.';
$messages['sigerror'] = 'Unverified signature. Internal error.';
$messages['decryptok'] = 'Message decrypted.';