From 91fb816bb28f55cca749d6da2c74497cb05c4793 Mon Sep 17 00:00:00 2001 From: Hadi Nategh Date: Tue, 25 Jul 2017 17:13:49 +0200 Subject: [PATCH] W.I.P. SMIME: - Override verify method in order to pass certs bundle for verification - Catch exception if decryption fails because encrypted message perhaps is not encrypted by receiver public key - Define different states for signature verification --- api/src/Mail.php | 14 +++++++---- api/src/Mail/Smime.php | 17 ++++++++++++++ mail/inc/class.mail_ui.inc.php | 14 ++++++++--- mail/js/app.js | 40 ++++++++++++++++++++++++++------ mail/templates/default/app.css | 21 ++++++++++++++++- mail/templates/mobile/app.css | 18 ++++++++++++++ mail/templates/pixelegg/app.css | 18 ++++++++++++++ mail/templates/pixelegg/app.less | 2 +- 8 files changed, 128 insertions(+), 16 deletions(-) diff --git a/api/src/Mail.php b/api/src/Mail.php index 811aab2f32..c98d4a66e4 100644 --- a/api/src/Mail.php +++ b/api/src/Mail.php @@ -5611,7 +5611,7 @@ class Mail if (is_object($mail)) { $structure = $mail->getStructure(); - $isSmime = Mail\Smime::isSmime($structure->getType()); + $isSmime = Mail\Smime::isSmime($structure->getType()) || Mail\Smime::isSmimeSignatureOnly($structure->getType()); if ($isSmime) { return $this->resolveSmimeMessage($structure, array( @@ -7349,13 +7349,19 @@ class Mail $message = $this->getMessageRawBody($params['uid'], null, $params['mailbox']); if (!Mail\Smime::isSmimeSignatureOnly($params['mimeType'])) { - $message = $this->_decryptSmimeBody($message, $params['passphrase'] !='' ? - $params['passphrase'] : Api\Cache::getSession('mail', 'smime_passphrase')); + try{ + $message = $this->_decryptSmimeBody($message, $params['passphrase'] !='' ? + $params['passphrase'] : Api\Cache::getSession('mail', 'smime_passphrase')); + } + catch(\Horde_Crypt_Exception $e) + { + throw new Mail\Smime\PassphraseMissing(lang('Could not decrypt S/MIME data. This message may not be encrypted by your public key.')); + } $metadata['encrypted'] = true; } try { - $cert = $this->smime->verify($message); + $cert = $this->smime->verifySignature($message); } catch (\Exception $ex) { // passphrase is required to decrypt the message if (isset($message['password_required'])) diff --git a/api/src/Mail/Smime.php b/api/src/Mail/Smime.php index e38661375f..781c7e1c28 100644 --- a/api/src/Mail/Smime.php +++ b/api/src/Mail/Smime.php @@ -39,6 +39,7 @@ class Smime extends Horde_Crypt_Smime static $SMIME_SIGNATURE_ONLY_TYPES = array ( 'application/x-pkcs7-signature', 'application/pkcs7-signature', + 'multipart/signed' ); /** @@ -180,4 +181,20 @@ class Smime extends Horde_Crypt_Smime return Horde_Mime_Part::parseMessage(parent::extractSignedContents($data), array('forcemime' => true)); } + /** + * Verify a signature + * + * @param type $message + * @return type + */ + public function verifySignature($message) + { + $cert_locations = openssl_get_cert_locations(); + $certs = array(); + foreach (scandir($cert_locations['default_cert_dir']) as &$file) + { + if (!is_dir($cert_locations['default_cert_dir'].'/'.$file)) $certs[]= $cert_locations['default_cert_dir'].'/'.$file; + } + return $this->verify($message, $certs); + } } diff --git a/mail/inc/class.mail_ui.inc.php b/mail/inc/class.mail_ui.inc.php index 4d53527f0e..b95efac722 100644 --- a/mail/inc/class.mail_ui.inc.php +++ b/mail/inc/class.mail_ui.inc.php @@ -3070,7 +3070,12 @@ $filter['before']= date("d-M-Y", $cutoffdate2); { if ($smimePassphrase) { - Api\Cache::setSession('mail', 'smime_passphrase', $smimePassphrase); + if ($this->mail_bo->mailPreferences['smime_pass_exp'] != $_POST['smime_pass_exp']) + { + $GLOBALS['egw']->preferences->add('mail', 'smime_pass_exp', $_POST['smime_pass_exp']); + $GLOBALS['egw']->preferences->save_repository(); + } + Api\Cache::setSession('mail', 'smime_passphrase', $smimePassphrase, $_POST['smime_pass_exp'] * 60); } $structure = $this->mail_bo->getStructure($uid, $partID, $mailbox, false); if (($smime = $structure->getMetadata('X-EGroupware-Smime'))) @@ -3092,7 +3097,7 @@ $filter['before']= date("d-M-Y", $cutoffdate2); { self::callWizard($e->getMessage().' '.lang('Please configure your S/MIME private key in Encryption tab located at Edit Account dialog.')); } - + Framework::message($e->getMessage()); // do NOT include any default CSS $smimeHtml = $this->get_email_header(). '
'.lang("This message is smime encrypted and password protected.").'
'. @@ -3102,7 +3107,10 @@ $filter['before']= date("d-M-Y", $cutoffdate2); ''. ''. ''. - ''; + '
'. + lang("Remember the password for ").' '.lang("minutes."). + '
'. + ''; return $smimeHtml; } $calendar_part = null; diff --git a/mail/js/app.js b/mail/js/app.js index 7b88ffd046..00edb14abb 100644 --- a/mail/js/app.js +++ b/mail/js/app.js @@ -998,8 +998,10 @@ app.classes.mail = AppJS.extend( default: widget.set_disabled(true); } + + this.smime_clear_flags([jQuery(widget.getDOMNode())]); } - + this.smime_clear_flags([jQuery('#mail-index_mailPreviewContainer')]); // Blank first, so we don't show previous email while loading var IframeHandle = this.et2.getWidgetById('messageIFRAME'); IframeHandle.set_src('about:blank'); @@ -5815,12 +5817,36 @@ app.classes.mail = AppJS.extend( if (attachmentArea) attachmentArea.getDOMNode().classList.remove('loading'); var smime_signature = this.et2.getWidgetById('smime_signature'); var smime_encryption = this.et2.getWidgetById('smime_encryption'); + var $mail_container = egw(window).is_popup() ? + jQuery('.mailDisplayContainer'): + jQuery(this.et2.getWidgetById('mailPreviewContainer').getDOMNode()); + + smime_signature.set_disabled(!_data.signed); + smime_encryption.set_disabled(!_data.encrypted); + if (!_data.signed) + { + this.smime_clear_flags([$mail_container]); + } + else if (_data.verify) + { + $mail_container.addClass('smime_cert_verified'); + smime_signature.set_class('smime_cert_verified'); + } + else if (!_data.verify) + { + $mail_container.addClass('smime_cert_notverified'); + smime_signature.set_class('smime_cert_notverified'); + smime_signature.set_statustext(_data.msg); + } + }, + + smime_clear_flags: function (_nodes) + { + for(var i=0;i<_nodes.length;i++) + { + var smime_classes = 'smime_cert_verified smime_cert_notverified'; + _nodes[i].removeClass(smime_classes); + } - smime_signature.set_disabled(!_data.signed); - smime_encryption.set_disabled(!_data.encrypted); - if (!_data.verify) - { - smime_signature.set_statustext(_data.msg); - } } }); diff --git a/mail/templates/default/app.css b/mail/templates/default/app.css index b20b52de8d..42b4a8c2d3 100644 --- a/mail/templates/default/app.css +++ b/mail/templates/default/app.css @@ -911,4 +911,23 @@ div.mailComposeHeaderSection>table { #mail-compose_mailaccount {max-width: 100% !important;} .header_row_right.vertical_splitter {float:left;} -.header_row_right.vertical_splitter div#mail-index_mail-index-vacationnotice .et2_vbox {margin-right: 0;} \ No newline at end of file +.header_row_right.vertical_splitter div#mail-index_mail-index-vacationnotice .et2_vbox {margin-right: 0;} + +div.smime_cert_notverified { + border-top: 4px solid lightgreen !important; +} +img.smime_cert_notverified { + background: lightgreen !important; +} +div.smime_cert_verified { + border-top: 4px solid green; +} +img.smime_cert_verified { + background: green; +} +div.smime_cert_notvalid { + border-top: 4px solid red; +} +img.smime_cert_notvalid { + background: red; +} \ No newline at end of file diff --git a/mail/templates/mobile/app.css b/mail/templates/mobile/app.css index 867ae31aaa..240938765a 100644 --- a/mail/templates/mobile/app.css +++ b/mail/templates/mobile/app.css @@ -901,6 +901,24 @@ div.mailComposeHeaderSection > table { .header_row_right.vertical_splitter div#mail-index_mail-index-vacationnotice .et2_vbox { margin-right: 0; } +div.smime_cert_notverified { + border-top: 4px solid lightgreen !important; +} +img.smime_cert_notverified { + background: lightgreen !important; +} +div.smime_cert_verified { + border-top: 4px solid green; +} +img.smime_cert_verified { + background: green; +} +div.smime_cert_notvalid { + border-top: 4px solid red; +} +img.smime_cert_notvalid { + background: red; +} #popupMainDiv { padding: 5px; } diff --git a/mail/templates/pixelegg/app.css b/mail/templates/pixelegg/app.css index 6a94edc0af..eb2ea6af5f 100755 --- a/mail/templates/pixelegg/app.css +++ b/mail/templates/pixelegg/app.css @@ -889,6 +889,24 @@ div.mailComposeHeaderSection > table { .header_row_right.vertical_splitter div#mail-index_mail-index-vacationnotice .et2_vbox { margin-right: 0; } +div.smime_cert_notverified { + border-top: 4px solid lightgreen !important; +} +img.smime_cert_notverified { + background: lightgreen !important; +} +div.smime_cert_verified { + border-top: 4px solid green; +} +img.smime_cert_verified { + background: green; +} +div.smime_cert_notvalid { + border-top: 4px solid red; +} +img.smime_cert_notvalid { + background: red; +} #popupMainDiv { padding: 5px; } diff --git a/mail/templates/pixelegg/app.less b/mail/templates/pixelegg/app.less index aaa8b4ca16..617f425c6f 100755 --- a/mail/templates/pixelegg/app.less +++ b/mail/templates/pixelegg/app.less @@ -76,7 +76,7 @@ body { select {margin-top: 9px;} } // Ende Filter } // Ende Header - + // Bilder in den Listen tr.mail td img { max-height: 50px;